4. React Login form
0. Introduction
These are the main steps:
- Create the project as indicated in a previous post
- Defining how to apply i18n to the project
- Create a component for selecting the language
- Create the login form
1. How to apply i18n?
Lets create a map with a key and an array of the traductions (we are using 6 languages: 0:Catalonian, 1:Spanish, 2:English, 3: French, 4: German, 5: Romanian, 6:Italian), and a funtion that retrieves the i18n text from the map knowing the key and the language number
//A map with the values in different languages //This data can be in the same component that uses it or in a separate module const i18nMap= new Map<string,string[]>([ //<--key-->|<-----Catalonian---->|<------Spanish--------->|<-English-->|<---French--->|<----German---->|<----Romanian--->|<---Italian-----> ['login' ,["Entrar al sistema" ,"Entrar al sistema" ,"Login" ,"Connexion" ,"Anmeldung" ,"Autentificare" ,"Login" ]], ['user' ,["Usuari" ,"Usuario" ,"User" ,"Nom" ,"Nutzername" ,"Nume" ,"Nome" ]], ['pwd' ,["Paraula de pas" ,"Contraseña" ,"Password" ,"Mot de passe","Passwort" ,"Parola" ,"Parola d'ordine"]], ['cancel',["Cancel·lar" ,"Cancelar" ,"Cancel" ,"Annuler" ,"Absagen" ,"Anulare" ,"Annulla" ]], ]) //Get the i18n Label from the previous map //This function should be in another modula and reuse it export function getI18nLabel(key: string, lang : number, i18nMap: Map<string, string[]>) { const myArr= i18nMap.get(key) if (!myArr) return 'NO-TROBAT-i18n' else if (myArr.length<=lang) return 'NO-TROBAT-i18n' else return myArr[lang] }
2. Passing data between components
The useState hook can be passed to components using this interface that in my case is stored in the file "Types.ts"import { Dispatch, SetStateAction } from "react"
export interface MySetter {
param: any
setParam: Dispatch<SetStateAction<any>>
}
This is a way of getting the hook const [param, setParam] = useState<any>()
The component for selecting the language has an input param of type "mySetter", so the caller component is aware of the changes of this param.
import { Dispatch, SetStateAction } from "react" export interface MySetter { param: any setParam: Dispatch<SetStateAction<any>> }
2. Select language component
The material UI Autocomplete component can add images to the select. It is copied from an example, but instead, the flags are picked from a local folder. The "require" function hels with this task. Finally the flag is displayed beside the element. Note as said in the previous point, the use of "MySetter" type as the input parameter.
import React from "react"; import Box from "@mui/material/Box"; import TextField from "@mui/material/TextField"; import Autocomplete from "@mui/material/Autocomplete"; import RoIcon from "../images/ro.png"; import { MySetter } from "./Types"; interface CountryType { id: number; code: string; label: string; } const countries: readonly CountryType[] = [ { id: 0, code: "ca", label: "Valencià" }, { id: 1, code: "es", label: "Español" }, { id: 2, code: "en", label: "English" }, { id: 3, code: "fr", label: "Francaise" }, { id: 4, code: "de", label: "Deustch" }, { id: 5, code: "ro", label: "Românesc" }, { id: 6, code: "it", label: "Italiano" }, ]; function getImgFileName(s: string) { return "../images/" + s + ".png"; } export default function LangSel4(myLang: MySetter) { const [value, setValue] = React.useState<CountryType>( countries[myLang.param] ); const [inputValue, setInputValue] = React.useState<string>(""); return ( <div className="container"> <div className="row align-items-center"> <div className="col"> <Autocomplete value={value} onChange={(event: any, newValue: CountryType | null) => { const aVal = newValue !== null ? newValue : countries[0]; setValue(aVal); myLang.setParam(aVal.id); }} inputValue={inputValue} onInputChange={(event, newInputValue) => { setInputValue(newInputValue); }} id="country-select-demo" sx={{ width: 300 }} options={countries} autoHighlight getOptionLabel={(option) => option.label} renderOption={(props, option) => ( <Box component="li" sx={{ "& > img": { mr: 2, flexShrink: 0 } }} {...props} > <img loading="lazy" width="20" //src={`https://flagcdn.com/w20/${option.code.toLowerCase()}.png`} //WORKS src={require(`../images/${option.code.toLowerCase()}.png`)} srcSet={`https://flagcdn.com/w40/${option.code.toLowerCase()}.png 2x`} alt="" /> {option.label} ({option.code}) </Box> )} renderInput={(params) => ( <TextField {...params} label=" " inputProps={{ ...params.inputProps, autoComplete: "new-password", // disable autocomplete and autofill }} /> )} /> </div> <div className="col"> <img src={require(`../images/${value.code.toLowerCase()}.png`)} width="40" alt="res" /> </div> </div> </div> ); }
3. Login form
A simple login form with user and password and a language selector
import blue from "@mui/material/colors/blue" import userEvent from "@testing-library/user-event" import React, { ChangeEvent, FormEvent, useState } from "react" import LangSel4 from "./LangSel4" import {MySetter, User} from './Types' import { getI18nLabel } from './Funcs' const i18nMap= new Map<string,string[]>([ ['login' ,["Entrar al sistema" ,"Entrar al sistema" ,"Login" ,"Connexion" ,"Anmeldung" ,"Autentificare" ,"Login" ]], ['user' ,["Usuari" ,"Usuario" ,"User" ,"Nom" ,"Nutzername" ,"Nume" ,"Nome" ]], ['pwd' ,["Paraula de pas" ,"Contraseña" ,"Password" ,"Mot de passe","Passwort" ,"Parola" ,"Parola d'ordine"]], ['cancel',["Cancel·lar" ,"Cancelar" ,"Cancel" ,"Annuler" ,"Absagen" ,"Anulare" ,"Annulla" ]], ]) export const Login2 = ( UserAndLang: MySetter[] ) => { const [user, setUser] = useState<User>({ name: "", pwd: "" }); const handleChange = (event: ChangeEvent<HTMLInputElement>) => { const name = event.target.name; const value = event.target.value; setUser((user) => ({ ...user, [name]: value })); // Uses the same change function for all fields!! }; const handleSubmit = (event: FormEvent<HTMLFormElement>) => { event.preventDefault(); UserAndLang[0].setParam(user.name) alert(user + "-- language -->" + UserAndLang[1].param); }; const opc='va' return ( <div className="container" style={{ width: 540, backgroundColor: blue[50], padding: 40, borderRadius:20 }} > <img src={require(`../images/logotipo-tavernes-valldigna-01-removebg.png`)} width="425" /> <br /> <br /> <br /> <h1 className='text-center '>{getI18nLabel('login',UserAndLang[1].param, i18nMap)}</h1> <br /> <LangSel4 {...UserAndLang[1]} /> <br /> <br /> <br /> <form onSubmit={handleSubmit}> <div className="mb-3"> <label htmlFor="name" className="form-label"> {getI18nLabel('user',UserAndLang[1].param, i18nMap)} </label> <input type="text" name="name" id="name" value={user.name || ""} onChange={handleChange} className="form-control" /> </div> <br /> <div className="mb-3"> <label htmlFor="pwd" className="form-label"> {getI18nLabel('pwd',UserAndLang[1].param, i18nMap)} </label> <input type="password" name="pwd" id="pwd" value={user.pwd || ""} onChange={handleChange} className="form-control" /> </div> <br /> <br /> <div className="d-flex justify-content-around"> <button type="button" className="btn btn-primary" > {getI18nLabel('login' ,UserAndLang[1].param, i18nMap)}</button> <button type="button" className="btn btn-secondary">{getI18nLabel('cancel',UserAndLang[1].param, i18nMap)}</button> </div> </form> </div> ); };
Important things:
1. To access the selected language, the MySetter structure is used.
4. App.tsx file
import React, { useEffect, useState } from 'react'; import logo from './logo.svg'; import './App.css'; import LangSel3 from './components/LangSel3' import { MySetter } from './components/Types'; import {Login2} from './components/Login2'; export default function App() { const [userName, setUserName] = useState<string>(''); const myUserName: MySetter ={param: userName, setParam: setUserName} const [lang, setLang] = useState<number>(6); const myLang: MySetter ={param: lang, setParam: setLang} const userAndLang: MySetter[] = [myUserName, myLang] return ( <> <Login2 {...userAndLang} /> </> ); }
5. Index.tsx
We should import the bootstrap CSS file !!
import React from 'react'; import ReactDOM from 'react-dom/client'; import './index.css'; import 'bootstrap/dist/css/bootstrap.min.css' // Import bootstrap css import App from './App'; const root = ReactDOM.createRoot( document.getElementById('root') as HTMLElement ); root.render( <React.StrictMode> <App /> </React.StrictMode> );
Comentarios
Publicar un comentario