From 61fd0e718632e3ac438b00436eb76b2db20b9b2f Mon Sep 17 00:00:00 2001 From: Andres Date: Mon, 16 Oct 2023 17:34:52 +0200 Subject: [PATCH 1/8] feat: i18n --- backend/routes/auth.js | 2 +- backend/services/auth.js | 4 +- frontend/package.json | 4 + frontend/public/locales/en/translation.json | 60 +++++++++ .../public/locales/es-ES/translation.json | 17 +++ frontend/public/locales/es/translation.json | 60 +++++++++ frontend/src/components/Bar/Bar.jsx | 15 ++- .../components/HomeLoggedIn/HomeLoggedIn.jsx | 16 ++- .../HomeLoggedOut/HomeLoggedOut.jsx | 11 +- frontend/src/components/LogIn/LogIn.jsx | 1 + .../components/LogInToken/LogInToken.jsx | 14 +- .../LogIn/components/LogInUser/LogInUser.jsx | 14 +- .../NetworkManagement/NetworkManagement.jsx | 18 +-- .../NetworkMembers/NetworkMembers.jsx | 23 ++-- .../components/AddMember/AddMember.jsx | 6 +- .../components/DeleteMember/DeleteMember.jsx | 12 +- .../components/MemberName/MemberName.jsx | 6 +- .../MemberSettings/MemberSettings.jsx | 15 ++- .../components/NetworkRules/NetworkRules.jsx | 8 +- .../NetworkSettings/NetworkSettings.jsx | 21 +-- .../IPv4AutoAssign/IPv4AutoAssign.jsx | 13 +- frontend/src/i18n.js | 32 +++++ frontend/src/index.jsx | 2 + yarn.lock | 121 ++++++++++++++++++ 24 files changed, 416 insertions(+), 79 deletions(-) create mode 100644 frontend/public/locales/en/translation.json create mode 100644 frontend/public/locales/es-ES/translation.json create mode 100644 frontend/public/locales/es/translation.json create mode 100644 frontend/src/i18n.js diff --git a/backend/routes/auth.js b/backend/routes/auth.js index 7cc1a80c..c93fb1b1 100644 --- a/backend/routes/auth.js +++ b/backend/routes/auth.js @@ -9,7 +9,7 @@ const loginLimiter = rateLimit({ max: Number(process.env.ZT_TRIES_TO_BAN) || 50, // limit each IP to 50 requests per windowMs message: { status: 429, - error: "Too many login attempts, please try again in 15 minutes.", + error: "tooManyAttemps", }, }); diff --git a/backend/services/auth.js b/backend/services/auth.js index c7e1d7cf..43832a5e 100644 --- a/backend/services/auth.js +++ b/backend/services/auth.js @@ -8,12 +8,12 @@ export async function authorize(username, password, callback) { throw err; } const user = users.find({ username: username }); - if (!user.value()) return callback(new Error("Invalid username or password")); // If return "user not found" someone can do a user listing + if (!user.value()) return callback(new Error("logInFailed")); // If return "user not found" someone can do a user listing const verified = await verifyHash(password, user.value()["password_hash"]); if (verified) { return callback(null, user.value()); } else { - return callback(new Error("Invalid username or password")); + return callback(new Error("logInFailed")); } } diff --git a/frontend/package.json b/frontend/package.json index ff003128..e55d3799 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,11 +11,15 @@ "codemirror": "^5.62.3", "date-fns": "^2.29.2", "history": "^5.3.0", + "i18next": "^23.5.1", + "i18next-browser-languagedetector": "^7.1.0", + "i18next-http-backend": "^2.2.2", "ipaddr.js": "^2.0.1", "lodash": "^4.17.21", "react": "^17.0.2", "react-data-table-component": "^6.11.8", "react-dom": "^17.0.2", + "react-i18next": "^13.3.0", "react-is": "^17.0.2", "react-router-dom": "^5.2.0", "react-use": "^17.4.0", diff --git a/frontend/public/locales/en/translation.json b/frontend/public/locales/en/translation.json new file mode 100644 index 00000000..72086468 --- /dev/null +++ b/frontend/public/locales/en/translation.json @@ -0,0 +1,60 @@ +{ + "flowRules": "Flow Rules", + "createNetwork": "Create A Network", + "createOneNetwork": "Please create at least one network", + "controllerNetworks": "Controller networks", + "network_one": "Network", + "network_other": "Networks", + "controllerAddress": "Network controller address", + "loginToContinue": "Please, Log In to continue", + "zerouiDesc": "ZeroUI - ZeroTier Controller Web UI - is a web user interface for a self-hosted ZeroTier network controller.", + "logIn": "Log In", + "logInToken": "Token Log In", + "cancel": "Cancel", + "management": "Management", + "deleteNetwork": "Delete Network", + "deleteAlert": "This action cannot be undone.", + "deleteNetworkConfirm": "Are you sure you want to delete this network?", + "deleteMemberConfirm": "Are you sure you want to delete this member?", + "delete": "Delete", + "logOut": "Log out", + "advancedFeature": "ADVANCED FEATURE", + "noDevices": "No devices have joined this network. Use the app on your devices to join", + "member_one": "Member", + "member_other": "Members", + "addMemberMan": "Manually Add Member", + "name": "Name", + "description": "Description", + "allowBridging": "Allow Ethernet Bridging", + "noAutoIP": "Do Not Auto-Assign IPs", + "capabilities": "Capabilities", + "noCapDef": "No capabilities defined", + "tags": "Tags", + "noTagDef": "No tags defined", + "authorized": "Authorized", + "address": "Address", + "ips": "Managed IPs", + "status": "Last seen", + "version": "Version", + "physIp": "Physical IP", + "latency": "Latency", + "settings": "Settings", + "generalSettings": "General settings", + "netId": "Network ID", + "accessControl": "Access control", + "public": "Public", + "private": "Private", + "managedRoutes": "Managed routes", + "addRoute": "Add route", + "target": "Target", + "via": "Via", + "start": "Start", + "end": "End", + "ipv4AutoAssign": "IPv4 Auto-Assign", + "autoAssignPool": "IPv4 Auto-Assign", + "addIPv4Pool": "Add IPv4 Pool", + "multicastLimit": "Multicast Recipient Limit", + "enaBroadcast": "Enable Broadcast", + "logInFailed": "Invalid username or password", + "tooManyAttemps": "Too many login attempts, please try again in 15 minutes." +} diff --git a/frontend/public/locales/es-ES/translation.json b/frontend/public/locales/es-ES/translation.json new file mode 100644 index 00000000..8d16e37b --- /dev/null +++ b/frontend/public/locales/es-ES/translation.json @@ -0,0 +1,17 @@ +{ + "flowRules": "Reglas de flujo", + "createNetwork": "Crear una red", + "controllerNetworks": "Controlador de redes", + "network_one": "Red", + "network_other": "Redes", + "controllerAddress": "Dirección del controlador", + "loginToContinue": "Por favor, inicia sesión para continuar", + "zerouiDesc": "ZeroUI - ZeroTier Controller Web UI - es una interfaz de usuario web para un controlador de red ZeroTier self-hosted.", + "logIn": "Iniciar sesión", + "cancel": "Cancelar", + "management": "Gestión", + "deleteNetwork": "Borrar red", + "deleteAlert": "Esta acción no puede ser revertida.", + "deleteNetworkConfirm": "¿Seguro que deseas borrar la red?", + "delete": "Borrar" +} diff --git a/frontend/public/locales/es/translation.json b/frontend/public/locales/es/translation.json new file mode 100644 index 00000000..a636135b --- /dev/null +++ b/frontend/public/locales/es/translation.json @@ -0,0 +1,60 @@ +{ + "flowRules": "Reglas de flujo", + "createNetwork": "Crear una red", + "createOneNetwork": "Por favor, crea al menos una red", + "controllerNetworks": "Controlador de redes", + "network_one": "Red", + "network_other": "Redes", + "controllerAddress": "Dirección del controlador", + "loginToContinue": "Por favor, inicia sesión para continuar", + "zerouiDesc": "ZeroUI - ZeroTier Controller Web UI - es una interfaz de usuario web para un controlador de red ZeroTier self-hosted.", + "logIn": "Iniciar sesión", + "logInToken": "Iniciar sesión con token", + "cancel": "Cancelar", + "management": "Gestión", + "deleteNetwork": "Borrar red", + "deleteAlert": "Esta acción no puede ser revertida.", + "deleteNetworkConfirm": "¿Seguro que deseas borrar esta red?", + "deleteMemberConfirm": "¿Seguro que deseas borrar este usuario?", + "delete": "Borrar", + "logOut": "Cerrar sesión", + "advancedFeature": "CARACTERÍSTICA AVANZADA", + "noDevices": "Ningún dispositivo se ha unido a esta red. Utilice la aplicación en sus dispositivos para unirse", + "member_one": "Miembro", + "member_other": "Miembros", + "addMemberMan": "Añadir miembro manualmente", + "name": "Nombre", + "description": "Descripción", + "allowBridging": "Permitir puente Ethernet", + "noAutoIP": "No autoasignar IPs", + "capabilities": "Permisos", + "noCapDef": "No hay permisos definidos", + "tags": "Etiquetas", + "noTagDef": "No hay etiquetas definidas", + "authorized": "Autorizado", + "address": "Dirección", + "ips": "IPs asignadas", + "status": "Visto por última vez", + "version": "Versión", + "physIp": "IP pública", + "latency": "Latencia", + "settings": "Ajustes", + "generalSettings": "Ajustes generales", + "netId": "ID de red", + "accessControl": "Control de acceso", + "public": "Público", + "private": "Privado", + "managedRoutes": "Rutas gestionadas", + "addRoute": "Añadir ruta", + "target": "Objetivo", + "via": "Vía", + "start": "Inicio", + "end": "Final", + "autoAssignPool": "Rango de IPv4 autoasignables", + "ipv4AutoAssign": "IPv4 Auto-Assign", + "addIPv4Pool": "Añadir rango IPv4", + "multicastLimit": "Límite de destinatarios multicast", + "enaBroadcast": "Habilitar broadcast", + "logInFailed": "Nombre de usuario o contraseña incorrecto", + "tooManyAttemps": "Demasiados intentos de inicio de sesión. Vuelvee a intentarlo en 15 minutos" +} diff --git a/frontend/src/components/Bar/Bar.jsx b/frontend/src/components/Bar/Bar.jsx index 2b91eda4..3de2252f 100644 --- a/frontend/src/components/Bar/Bar.jsx +++ b/frontend/src/components/Bar/Bar.jsx @@ -19,6 +19,8 @@ import MenuIcon from "@material-ui/icons/Menu"; import LogIn from "components/LogIn"; +import { useTranslation } from "react-i18next"; + function Bar() { const [loggedIn, setLoggedIn] = useLocalStorage("loggedIn", false); const [disabledAuth] = useLocalStorage("disableAuth", false); @@ -41,16 +43,18 @@ function Bar() { history.go(0); }; + const { t, i18n } = useTranslation(); + const menuItems = [ // TODO: add settings page - // { - // name: "Settings", - // to: "/settings", - // }, + { + name: "Settings", + to: "/settings", + }, ...(!disabledAuth ? [ { - name: "Log out", + name: t("logOut"), divide: true, onClick: onLogOutClick, }, @@ -115,7 +119,6 @@ function Bar() { key={index} onClick={() => { closeMenu(); - menuItem.onClick(); }} > diff --git a/frontend/src/components/HomeLoggedIn/HomeLoggedIn.jsx b/frontend/src/components/HomeLoggedIn/HomeLoggedIn.jsx index ff45f768..0dfb07c0 100644 --- a/frontend/src/components/HomeLoggedIn/HomeLoggedIn.jsx +++ b/frontend/src/components/HomeLoggedIn/HomeLoggedIn.jsx @@ -9,6 +9,8 @@ import NetworkButton from "./components/NetworkButton"; import API from "utils/API"; import { generateNetworkConfig } from "utils/NetworkConfig"; +import { useTranslation } from "react-i18next"; + function HomeLoggedIn() { const [networks, setNetworks] = useState([]); @@ -30,6 +32,8 @@ function HomeLoggedIn() { fetchData(); }, []); + const { t, i18n } = useTranslation(); + return (
- Controller networks - {networks[0] && "Network controller address"} + {t("controllerNetworks")} + {networks[0] && t("controllerAddress")} - {networks[0] && networks[0]["id"].slice(0, 10)} + {networks[0] && String(networks[0]["id"]).slice(0, 10)} - Networks + {t("network", { count: networks.length })} {networks[0] ? ( networks.map((network) => ( @@ -59,7 +63,7 @@ function HomeLoggedIn() { )) ) : ( -
Please create at least one network
+
{t("createOneNetwork")}
)}
diff --git a/frontend/src/components/HomeLoggedOut/HomeLoggedOut.jsx b/frontend/src/components/HomeLoggedOut/HomeLoggedOut.jsx index 0de5572d..7ec32181 100644 --- a/frontend/src/components/HomeLoggedOut/HomeLoggedOut.jsx +++ b/frontend/src/components/HomeLoggedOut/HomeLoggedOut.jsx @@ -3,6 +3,8 @@ import { Grid, Typography } from "@material-ui/core"; import { useLocalStorage } from "react-use"; import { useHistory } from "react-router-dom"; +import { useTranslation } from "react-i18next"; + import axios from "axios"; function HomeLoggedOut() { @@ -29,6 +31,8 @@ function HomeLoggedOut() { fetchData(); }, [history, setDisableAuth, setLoggedIn, setToken]); + const { t, i18n } = useTranslation(); + return ( - - ZeroUI - ZeroTier Controller Web UI - is a web user interface for a - self-hosted ZeroTier network controller. - + {t("zerouiDesc")} - Please Log In to continue + {t("loginToContinue")} diff --git a/frontend/src/components/LogIn/LogIn.jsx b/frontend/src/components/LogIn/LogIn.jsx index 36ea9a29..21465a34 100644 --- a/frontend/src/components/LogIn/LogIn.jsx +++ b/frontend/src/components/LogIn/LogIn.jsx @@ -12,6 +12,7 @@ function LogIn() { )} +   ); diff --git a/frontend/src/components/LogIn/components/LogInToken/LogInToken.jsx b/frontend/src/components/LogIn/components/LogInToken/LogInToken.jsx index a4140755..b39b4156 100644 --- a/frontend/src/components/LogIn/components/LogInToken/LogInToken.jsx +++ b/frontend/src/components/LogIn/components/LogInToken/LogInToken.jsx @@ -12,6 +12,8 @@ import { DialogTitle, } from "@material-ui/core"; +import { useTranslation } from "react-i18next"; + function LogInToken() { const [open, setOpen] = useState(false); const [errorText, setErrorText] = useState(""); @@ -41,6 +43,8 @@ function LogInToken() { } }; + const { t, i18n } = useTranslation(); + const LogIn = () => { if (token.length !== 32) { setErrorText("Token length error"); @@ -55,12 +59,12 @@ function LogInToken() { return (
- Log In + {t("logIn")} - ADVANCED FEATURE. + {t("advancedFeature")} { @@ -76,10 +80,10 @@ function LogInToken() { diff --git a/frontend/src/components/LogIn/components/LogInUser/LogInUser.jsx b/frontend/src/components/LogIn/components/LogInUser/LogInUser.jsx index 0658d3c3..d6d1af0f 100644 --- a/frontend/src/components/LogIn/components/LogInUser/LogInUser.jsx +++ b/frontend/src/components/LogIn/components/LogInUser/LogInUser.jsx @@ -13,6 +13,8 @@ import { import axios from "axios"; +import { useTranslation } from "react-i18next"; + function LogInUser() { const [open, setOpen] = useState(false); const [snackbarOpen, setSnackbarOpen] = useState(false); @@ -72,13 +74,15 @@ function LogInUser() { }); }; + const { t, i18n } = useTranslation(); + return ( <> - Log In + {t("logIn")} @@ -117,7 +121,7 @@ function LogInUser() { vertical: "top", horizontal: "center", }} - message={error} + message={t(error)} /> ); diff --git a/frontend/src/components/NetworkManagement/NetworkManagement.jsx b/frontend/src/components/NetworkManagement/NetworkManagement.jsx index fab98cce..ad4a0a84 100644 --- a/frontend/src/components/NetworkManagement/NetworkManagement.jsx +++ b/frontend/src/components/NetworkManagement/NetworkManagement.jsx @@ -18,6 +18,8 @@ import DeleteIcon from "@material-ui/icons/Delete"; import API from "utils/API"; +import { useTranslation } from "react-i18next"; + function NetworkManagement() { const { nwid } = useParams(); const history = useHistory(); @@ -42,10 +44,12 @@ function NetworkManagement() { history.go(0); }; + const { t, i18n } = useTranslation(); + return ( }> - Management + {t("management")} - - {"Are you sure you want to delete this network?"} - + {t("deleteNetworkConfirm")} - This action cannot be undone. + {t("deleteAlert")} diff --git a/frontend/src/components/NetworkMembers/NetworkMembers.jsx b/frontend/src/components/NetworkMembers/NetworkMembers.jsx index b6f843c8..3cfef3e3 100644 --- a/frontend/src/components/NetworkMembers/NetworkMembers.jsx +++ b/frontend/src/components/NetworkMembers/NetworkMembers.jsx @@ -21,6 +21,8 @@ import ManagedIP from "./components/ManagedIP"; import MemberName from "./components/MemberName"; import MemberSettings from "./components/MemberSettings"; +import { useTranslation } from "react-i18next"; + function NetworkMembers({ network }) { const { nwid } = useParams(); const [members, setMembers] = useState([]); @@ -46,6 +48,8 @@ function NetworkMembers({ network }) { console.log("Action:", req); }; + const { t, i18n } = useTranslation(); + const handleChange = (member, key1, key2 = null, mode = "text", id = null) => (event) => { @@ -67,7 +71,7 @@ function NetworkMembers({ network }) { const columns = [ { id: "auth", - name: "Authorized", + name: t("authorized"), minWidth: "80px", cell: (row) => ( ( {row.config.address} @@ -87,19 +91,19 @@ function NetworkMembers({ network }) { }, { id: "name", - name: "Name / Description", + name: t("name") + "/" + t("description"), minWidth: "250px", cell: (row) => , }, { id: "ips", - name: "Managed IPs", + name: t("ips"), minWidth: "220px", cell: (row) => , }, { id: "status", - name: "Last Seen", + name: t("status"), minWidth: "100px", cell: (row) => row.online === 1 ? ( @@ -121,7 +125,7 @@ function NetworkMembers({ network }) { }, { id: "physicalip", - name: "Version / Physical IP / Latency", + name: t("version") + " / " + t("physIp") + " / " + t("latency"), minWidth: "220px", cell: (row) => row.online === 1 ? ( @@ -143,7 +147,7 @@ function NetworkMembers({ network }) { }, { id: "delete", - name: "", + name: t("settings"), minWidth: "50px", right: true, cell: (row) => ( @@ -162,7 +166,7 @@ function NetworkMembers({ network }) { return ( }> - Members + {t("member", { count: members.length })} @@ -188,8 +192,7 @@ function NetworkMembers({ network }) { }} > - No devices have joined this network. Use the app on your - devices to join {nwid}. + {t("noDevices")} {nwid}. )} diff --git a/frontend/src/components/NetworkMembers/components/AddMember/AddMember.jsx b/frontend/src/components/NetworkMembers/components/AddMember/AddMember.jsx index af256d63..14dc2e34 100644 --- a/frontend/src/components/NetworkMembers/components/AddMember/AddMember.jsx +++ b/frontend/src/components/NetworkMembers/components/AddMember/AddMember.jsx @@ -5,6 +5,8 @@ import AddIcon from "@material-ui/icons/Add"; import API from "utils/API"; +import { useTranslation } from "react-i18next"; + function AddMember({ nwid, callback }) { const [member, setMember] = useState(""); @@ -24,9 +26,11 @@ function AddMember({ nwid, callback }) { setMember(""); }; + const { t, i18n } = useTranslation(); + return ( <> - Manually Add Member + {t("addMemberMan")} { @@ -37,18 +39,16 @@ function DeleteMember({ nwid, mid, callback }) { - - {"Are you sure you want to delete this member?"} - + {t("deleteMemberConfirm")} - This action cannot be undone. + {t("deleteAlert")} diff --git a/frontend/src/components/NetworkMembers/components/MemberName/MemberName.jsx b/frontend/src/components/NetworkMembers/components/MemberName/MemberName.jsx index 65032cc0..ea7f8e9d 100644 --- a/frontend/src/components/NetworkMembers/components/MemberName/MemberName.jsx +++ b/frontend/src/components/NetworkMembers/components/MemberName/MemberName.jsx @@ -1,12 +1,14 @@ import { Grid, TextField } from "@material-ui/core"; +import { useTranslation } from "react-i18next"; function MemberName({ member, handleChange }) { + const { t, i18n } = useTranslation(); return ( { @@ -43,7 +46,7 @@ function MemberSettings({ member, network, handleChange }) { "checkbox" )} /> - Allow Ethernet Bridging + {t("allowBridging")} - Do Not Auto-Assign IPs + {t("noAutoIP")} - Capabilities + {t("capabilities")} {Object.entries(network["capabilitiesByName"] || []).length === 0 - ? "No capabilities defined" + ? t("noCapDef") : ""} {Object.entries(network["capabilitiesByName"] || []).map( ([capName, capId]) => ( @@ -96,11 +99,11 @@ function MemberSettings({ member, network, handleChange }) { - Tags + {t("tags")} {Object.entries(network["tagsByName"] || []).length === 0 ? ( - No tags defined + {t("noTagDef")} ) : ( "" diff --git a/frontend/src/components/NetworkRules/NetworkRules.jsx b/frontend/src/components/NetworkRules/NetworkRules.jsx index 14dbd365..56eef495 100644 --- a/frontend/src/components/NetworkRules/NetworkRules.jsx +++ b/frontend/src/components/NetworkRules/NetworkRules.jsx @@ -17,7 +17,11 @@ import debounce from "lodash/debounce"; import { useState } from "react"; import API from "utils/API"; +import { useTranslation, Trans } from "react-i18next"; + function NetworkRules({ network, callback }) { + const { t, i18n } = useTranslation(); + const [editor, setEditor] = useState(null); const [flowData, setFlowData] = useState({ rules: [...network.config.rules], @@ -87,12 +91,12 @@ function NetworkRules({ network, callback }) { return ( }> - Flow Rules + {t("flowRules")} {/* Important note: value in CodeMirror instance means INITAIL VALUE or it could be used to replace editor state with the new value. - No need to update on every user character input + No need to update on every user character input Flow Rules */} { try { const req = await API.post("/network/" + network["config"]["id"], data); @@ -43,12 +46,12 @@ function NetworkSettings({ network, setNetwork }) { return ( }> - General settings + {t("generalSettings")} - Network ID + {t("netId")} {network["config"]["id"]} @@ -57,7 +60,7 @@ function NetworkSettings({ network, setNetwork }) { - Access Control + {t("accessControl")} @@ -111,7 +114,7 @@ function NetworkSettings({ network, setNetwork }) { - Enable Broadcast + {t("enaBroadcast")} {/* TODO: */} {/* diff --git a/frontend/src/components/NetworkSettings/components/IPv4AutoAssign/IPv4AutoAssign.jsx b/frontend/src/components/NetworkSettings/components/IPv4AutoAssign/IPv4AutoAssign.jsx index 0b4fd3de..2abdde70 100644 --- a/frontend/src/components/NetworkSettings/components/IPv4AutoAssign/IPv4AutoAssign.jsx +++ b/frontend/src/components/NetworkSettings/components/IPv4AutoAssign/IPv4AutoAssign.jsx @@ -18,7 +18,10 @@ import DataTable from "react-data-table-component"; import { addressPool } from "utils/NetworkConfig"; import { getCIDRAddress, validateIP, normilizeIP } from "utils/IP"; +import { useTranslation } from "react-i18next"; + function IPv4AutoAssign({ ipAssignmentPools, handleChange }) { + const { t, i18n } = useTranslation(); const [start, setStart] = useState(""); const [end, setEnd] = useState(""); @@ -89,19 +92,19 @@ function IPv4AutoAssign({ ipAssignmentPools, handleChange }) { }, { id: "Start", - name: "Start", + name: t("start"), cell: (row) => row["ipRangeStart"], }, { id: "End", - name: "End", + name: t("end"), cell: (row) => row["ipRangeEnd"], }, ]; return ( <> - IPv4 Auto-Assign + {t("ipv4AutoAssign")}
- Auto-Assign Pools + {t("autoAssignPool")} @@ -132,7 +135,7 @@ function IPv4AutoAssign({ ipAssignmentPools, handleChange }) { data={ipAssignmentPools} /> - Add IPv4 Pool + {t("addIPv4Pool")} diff --git a/yarn.lock b/yarn.lock index 4ca548d9..52a62fa1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -265,6 +265,15 @@ __metadata: languageName: node linkType: hard +"@babel/runtime@npm:^7.19.4, @babel/runtime@npm:^7.22.5": + version: 7.23.2 + resolution: "@babel/runtime@npm:7.23.2" + dependencies: + regenerator-runtime: "npm:^0.14.0" + checksum: abdcbdd590c7e31762e1bdab94dd466823c8bcedd3ff2fde85eeb94dac7cccaef151ac37c428bda7018ededd27c9a82b4dfeb621f978ad934232475a902f8e3a + languageName: node + linkType: hard + "@babel/template@npm:^7.22.15": version: 7.22.15 resolution: "@babel/template@npm:7.22.15" @@ -2992,6 +3001,15 @@ __metadata: languageName: node linkType: hard +"cross-fetch@npm:3.1.6": + version: 3.1.6 + resolution: "cross-fetch@npm:3.1.6" + dependencies: + node-fetch: "npm:^2.6.11" + checksum: e08325b813da37f2d5312b3e630af992c35681c1737707b029e8ef1c48ea034bda8b960000fc8bee6e0485e133347198aa6ecccadb530b06c47472f6c76bc27b + languageName: node + linkType: hard + "cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.1, cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3": version: 7.0.3 resolution: "cross-spawn@npm:7.0.3" @@ -4344,11 +4362,15 @@ __metadata: eslint-plugin-react-hooks: "npm:^4.6.0" eslint-plugin-react-refresh: "npm:^0.4.3" history: "npm:^5.3.0" + i18next: "npm:^23.5.1" + i18next-browser-languagedetector: "npm:^7.1.0" + i18next-http-backend: "npm:^2.2.2" ipaddr.js: "npm:^2.0.1" lodash: "npm:^4.17.21" react: "npm:^17.0.2" react-data-table-component: "npm:^6.11.8" react-dom: "npm:^17.0.2" + react-i18next: "npm:^13.3.0" react-is: "npm:^17.0.2" react-router-dom: "npm:^5.2.0" react-use: "npm:^17.4.0" @@ -4888,6 +4910,15 @@ __metadata: languageName: node linkType: hard +"html-parse-stringify@npm:^3.0.1": + version: 3.0.1 + resolution: "html-parse-stringify@npm:3.0.1" + dependencies: + void-elements: "npm:3.1.0" + checksum: 8743b76cc50e46d1956c1ad879d18eb9613b0d2d81e24686d633f9f69bb26b84676f64a926973de793cca479997017a63219278476d617b6c42d68246d7c07fe + languageName: node + linkType: hard + "http-cache-semantics@npm:^4.1.1": version: 4.1.1 resolution: "http-cache-semantics@npm:4.1.1" @@ -4968,6 +4999,33 @@ __metadata: languageName: node linkType: hard +"i18next-browser-languagedetector@npm:^7.1.0": + version: 7.1.0 + resolution: "i18next-browser-languagedetector@npm:7.1.0" + dependencies: + "@babel/runtime": "npm:^7.19.4" + checksum: 3b06c8a5df09092cffc0b6637b542bb572e8a25dcba97d0d8a5e5dd7539b90bf00000f3a279654693f4b5908c5fc4d1d4f3766dfb461dacab46be3d071266384 + languageName: node + linkType: hard + +"i18next-http-backend@npm:^2.2.2": + version: 2.2.2 + resolution: "i18next-http-backend@npm:2.2.2" + dependencies: + cross-fetch: "npm:3.1.6" + checksum: dbf09f2f309cb6070e691d0d382ccff3e94d2cfa9f30315dc4c03faa5d7d1f3b408d48c46c766b7e07527ec10c0542fde19240845905314ce0134ac10d6a6adb + languageName: node + linkType: hard + +"i18next@npm:^23.5.1": + version: 23.5.1 + resolution: "i18next@npm:23.5.1" + dependencies: + "@babel/runtime": "npm:^7.22.5" + checksum: 38e62d582b0f67eb2eee4f079c9cd512246496f2fb970f50a0be26c7c5e6ac5e772de9763ac1943919ecd816b2c0375f4b2071c67b1485a6a980c4d37348408f + languageName: node + linkType: hard + "iconv-lite@npm:0.4.24, iconv-lite@npm:^0.4.24": version: 0.4.24 resolution: "iconv-lite@npm:0.4.24" @@ -6532,6 +6590,20 @@ __metadata: languageName: node linkType: hard +"node-fetch@npm:^2.6.11": + version: 2.7.0 + resolution: "node-fetch@npm:2.7.0" + dependencies: + whatwg-url: "npm:^5.0.0" + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + checksum: b24f8a3dc937f388192e59bcf9d0857d7b6940a2496f328381641cb616efccc9866e89ec43f2ec956bbd6c3d3ee05524ce77fe7b29ccd34692b3a16f237d6676 + languageName: node + linkType: hard + "node-gyp@npm:latest": version: 9.4.0 resolution: "node-gyp@npm:9.4.0" @@ -7288,6 +7360,24 @@ __metadata: languageName: node linkType: hard +"react-i18next@npm:^13.3.0": + version: 13.3.0 + resolution: "react-i18next@npm:13.3.0" + dependencies: + "@babel/runtime": "npm:^7.22.5" + html-parse-stringify: "npm:^3.0.1" + peerDependencies: + i18next: ">= 23.2.3" + react: ">= 16.8.0" + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + checksum: 2ef46245ba1ba9fca8c43dbe1bcab4ac63ca68e67de7159cb5f93cd7fd10407599bcdf1999f6567091987be7b9aaba77cdb515a70d5ee2b935b985f4c40d6d9d + languageName: node + linkType: hard + "react-is@npm:^16.13.1, react-is@npm:^16.6.0, react-is@npm:^16.7.0": version: 16.13.1 resolution: "react-is@npm:16.13.1" @@ -8654,6 +8744,13 @@ __metadata: languageName: node linkType: hard +"tr46@npm:~0.0.3": + version: 0.0.3 + resolution: "tr46@npm:0.0.3" + checksum: 8f1f5aa6cb232f9e1bdc86f485f916b7aa38caee8a778b378ffec0b70d9307873f253f5cbadbe2955ece2ac5c83d0dc14a77513166ccd0a0c7fe197e21396695 + languageName: node + linkType: hard + "tree-kill@npm:^1.2.2": version: 1.2.2 resolution: "tree-kill@npm:1.2.2" @@ -9046,6 +9143,13 @@ __metadata: languageName: node linkType: hard +"void-elements@npm:3.1.0": + version: 3.1.0 + resolution: "void-elements@npm:3.1.0" + checksum: 0390f818107fa8fce55bb0a5c3f661056001c1d5a2a48c28d582d4d847347c2ab5b7f8272314cac58acf62345126b6b09bea623a185935f6b1c3bbce0dfd7f7f + languageName: node + linkType: hard + "wcwidth@npm:^1.0.1": version: 1.0.1 resolution: "wcwidth@npm:1.0.1" @@ -9055,6 +9159,23 @@ __metadata: languageName: node linkType: hard +"webidl-conversions@npm:^3.0.0": + version: 3.0.1 + resolution: "webidl-conversions@npm:3.0.1" + checksum: b65b9f8d6854572a84a5c69615152b63371395f0c5dcd6729c45789052296df54314db2bc3e977df41705eacb8bc79c247cee139a63fa695192f95816ed528ad + languageName: node + linkType: hard + +"whatwg-url@npm:^5.0.0": + version: 5.0.0 + resolution: "whatwg-url@npm:5.0.0" + dependencies: + tr46: "npm:~0.0.3" + webidl-conversions: "npm:^3.0.0" + checksum: f95adbc1e80820828b45cc671d97da7cd5e4ef9deb426c31bcd5ab00dc7103042291613b3ef3caec0a2335ed09e0d5ed026c940755dbb6d404e2b27f940fdf07 + languageName: node + linkType: hard + "which-boxed-primitive@npm:^1.0.2": version: 1.0.2 resolution: "which-boxed-primitive@npm:1.0.2" From 48485fc5467018927da2e3a9cb8bd0034d362ade Mon Sep 17 00:00:00 2001 From: Andres Date: Tue, 17 Oct 2023 18:05:02 +0200 Subject: [PATCH 2/8] feat: i18n add settings page --- .../en/{translation.json => common.json} | 3 +- .../translation.json => es-ES/common.json} | 3 +- .../public/locales/es-ES/translation.json | 17 ------ frontend/src/App.jsx | 2 + frontend/src/components/Bar/Bar.jsx | 2 +- .../SettingsComponent/SettingsComponent.jsx | 43 ++++++++++++++ .../components/SettingsComponent/index.jsx | 1 + frontend/src/i18n.js | 6 +- frontend/src/routes/Settings/Settings.jsx | 57 +++++++++++++++++++ .../src/routes/Settings/Settings.styles.jsx | 16 ++++++ frontend/src/routes/Settings/index.jsx | 1 + 11 files changed, 128 insertions(+), 23 deletions(-) rename frontend/public/locales/en/{translation.json => common.json} (98%) rename frontend/public/locales/{es/translation.json => es-ES/common.json} (97%) delete mode 100644 frontend/public/locales/es-ES/translation.json create mode 100644 frontend/src/components/SettingsComponent/SettingsComponent.jsx create mode 100644 frontend/src/components/SettingsComponent/index.jsx create mode 100644 frontend/src/routes/Settings/Settings.jsx create mode 100644 frontend/src/routes/Settings/Settings.styles.jsx create mode 100644 frontend/src/routes/Settings/index.jsx diff --git a/frontend/public/locales/en/translation.json b/frontend/public/locales/en/common.json similarity index 98% rename from frontend/public/locales/en/translation.json rename to frontend/public/locales/en/common.json index 72086468..a6e25f29 100644 --- a/frontend/public/locales/en/translation.json +++ b/frontend/public/locales/en/common.json @@ -56,5 +56,6 @@ "multicastLimit": "Multicast Recipient Limit", "enaBroadcast": "Enable Broadcast", "logInFailed": "Invalid username or password", - "tooManyAttemps": "Too many login attempts, please try again in 15 minutes." + "tooManyAttemps": "Too many login attempts, please try again in 15 minutes.", + "language": "Language" } diff --git a/frontend/public/locales/es/translation.json b/frontend/public/locales/es-ES/common.json similarity index 97% rename from frontend/public/locales/es/translation.json rename to frontend/public/locales/es-ES/common.json index a636135b..eb70d238 100644 --- a/frontend/public/locales/es/translation.json +++ b/frontend/public/locales/es-ES/common.json @@ -56,5 +56,6 @@ "multicastLimit": "Límite de destinatarios multicast", "enaBroadcast": "Habilitar broadcast", "logInFailed": "Nombre de usuario o contraseña incorrecto", - "tooManyAttemps": "Demasiados intentos de inicio de sesión. Vuelvee a intentarlo en 15 minutos" + "tooManyAttemps": "Demasiados intentos de inicio de sesión. Vuelvee a intentarlo en 15 minutos", + "language": "Idioma" } diff --git a/frontend/public/locales/es-ES/translation.json b/frontend/public/locales/es-ES/translation.json deleted file mode 100644 index 8d16e37b..00000000 --- a/frontend/public/locales/es-ES/translation.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "flowRules": "Reglas de flujo", - "createNetwork": "Crear una red", - "controllerNetworks": "Controlador de redes", - "network_one": "Red", - "network_other": "Redes", - "controllerAddress": "Dirección del controlador", - "loginToContinue": "Por favor, inicia sesión para continuar", - "zerouiDesc": "ZeroUI - ZeroTier Controller Web UI - es una interfaz de usuario web para un controlador de red ZeroTier self-hosted.", - "logIn": "Iniciar sesión", - "cancel": "Cancelar", - "management": "Gestión", - "deleteNetwork": "Borrar red", - "deleteAlert": "Esta acción no puede ser revertida.", - "deleteNetworkConfirm": "¿Seguro que deseas borrar la red?", - "delete": "Borrar" -} diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 04c9dc19..9142994f 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -8,6 +8,7 @@ import Bar from "./components/Bar"; import Home from "./routes/Home"; import NotFound from "./routes/NotFound"; import Network from "./routes/Network/Network"; +import Settings from "./routes/Settings"; function App() { return ( @@ -17,6 +18,7 @@ function App() { + diff --git a/frontend/src/components/Bar/Bar.jsx b/frontend/src/components/Bar/Bar.jsx index 3de2252f..e27b56c9 100644 --- a/frontend/src/components/Bar/Bar.jsx +++ b/frontend/src/components/Bar/Bar.jsx @@ -48,7 +48,7 @@ function Bar() { const menuItems = [ // TODO: add settings page { - name: "Settings", + name: t("settings"), to: "/settings", }, ...(!disabledAuth diff --git a/frontend/src/components/SettingsComponent/SettingsComponent.jsx b/frontend/src/components/SettingsComponent/SettingsComponent.jsx new file mode 100644 index 00000000..12aabe42 --- /dev/null +++ b/frontend/src/components/SettingsComponent/SettingsComponent.jsx @@ -0,0 +1,43 @@ +import { + Accordion, + AccordionSummary, + AccordionDetails, + Checkbox, + Divider, + Grid, + Typography, + TextField, + Select, +} from "@material-ui/core"; +import ExpandMoreIcon from "@material-ui/icons/ExpandMore"; + +import API from "utils/API"; +import { parseValue, replaceValue, setValue } from "utils/ChangeHelper"; + +import { useTranslation } from "react-i18next"; + +function SettingsComponent() { + const { t, i18n } = useTranslation(); + + const handleChange = () => (event) => { + i18n.changeLanguage(event.target.value); + }; + + return ( + + }> + {t("language")} + + + + + + + + ); +} + +export default SettingsComponent; diff --git a/frontend/src/components/SettingsComponent/index.jsx b/frontend/src/components/SettingsComponent/index.jsx new file mode 100644 index 00000000..88ef9940 --- /dev/null +++ b/frontend/src/components/SettingsComponent/index.jsx @@ -0,0 +1 @@ +export { default } from "./SettingsComponent"; diff --git a/frontend/src/i18n.js b/frontend/src/i18n.js index 89e0deea..1cf038f8 100644 --- a/frontend/src/i18n.js +++ b/frontend/src/i18n.js @@ -21,12 +21,12 @@ i18n react: { useSuspense: false, }, - supportedLngs: ["en", "es", "es-ES"], + supportedLngs: ["en", "es-ES"], backend: { loadPath: "/locales/{{lng}}/{{ns}}.json", }, - ns: ["translation"], - defaultNS: "translation", + ns: ["common"], + defaultNS: "common", }); export default i18n; diff --git a/frontend/src/routes/Settings/Settings.jsx b/frontend/src/routes/Settings/Settings.jsx new file mode 100644 index 00000000..b97e5bfc --- /dev/null +++ b/frontend/src/routes/Settings/Settings.jsx @@ -0,0 +1,57 @@ +import { Grid, Link, Typography } from "@material-ui/core"; +import ArrowBackIcon from "@material-ui/icons/ArrowBack"; +import SettingsComponent from "components/SettingsComponent"; + +import { useCallback, useEffect, useState } from "react"; +import { Link as RouterLink, useHistory, useParams } from "react-router-dom"; +import { useLocalStorage } from "react-use"; +import API from "utils/API"; +import useStyles from "./Settings.styles"; + +import { useTranslation } from "react-i18next"; + +function Settings() { + const { t, i18n } = useTranslation(); + const [loggedIn] = useLocalStorage("loggedIn", false); + const [network, setNetwork] = useState({}); + + const classes = useStyles(); + const history = useHistory(); + + if (loggedIn) { + return ( + <> +
+ + + {t("settings")} + +
+
+ +
+ + ); + } else { + return ( + + + + You are not authorized. Please Log In + + + + ); + } +} + +export default Settings; diff --git a/frontend/src/routes/Settings/Settings.styles.jsx b/frontend/src/routes/Settings/Settings.styles.jsx new file mode 100644 index 00000000..ac884a75 --- /dev/null +++ b/frontend/src/routes/Settings/Settings.styles.jsx @@ -0,0 +1,16 @@ +import { makeStyles } from "@material-ui/core/styles"; + +const useStyles = makeStyles((theme) => ({ + backIcon: { + fontSize: 12, + }, + container: { + margin: "3%", + }, + breadcrumbs: { + paddingTop: "2%", + paddingLeft: "2%", + }, +})); + +export default useStyles; diff --git a/frontend/src/routes/Settings/index.jsx b/frontend/src/routes/Settings/index.jsx new file mode 100644 index 00000000..41d66223 --- /dev/null +++ b/frontend/src/routes/Settings/index.jsx @@ -0,0 +1 @@ +export { default } from "./Settings"; From 80f1ab4185c3ff1ea44ae3c2fafdc334daed0d75 Mon Sep 17 00:00:00 2001 From: Andres Date: Tue, 17 Oct 2023 18:06:49 +0200 Subject: [PATCH 3/8] feat: i18n disabling debug mode --- frontend/src/i18n.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/i18n.js b/frontend/src/i18n.js index 1cf038f8..5770d511 100644 --- a/frontend/src/i18n.js +++ b/frontend/src/i18n.js @@ -13,7 +13,7 @@ i18n compatibilityJSON: "v4", lng: userLanguage || "en", fallbackLng: "en", - debug: true, + debug: false, //keySeparator: false, // we use content as keys interpolation: { escapeValue: true, From d19bf07382ec9ca28bd22db2a372c98ff6e3f116 Mon Sep 17 00:00:00 2001 From: Andres Date: Tue, 17 Oct 2023 20:24:02 +0200 Subject: [PATCH 4/8] feat: i18n persistence on logout --- .../components/SettingsComponent/SettingsComponent.jsx | 2 +- frontend/src/i18n.js | 10 +++++++--- frontend/src/index.jsx | 6 ++++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/frontend/src/components/SettingsComponent/SettingsComponent.jsx b/frontend/src/components/SettingsComponent/SettingsComponent.jsx index 12aabe42..0fe4c13b 100644 --- a/frontend/src/components/SettingsComponent/SettingsComponent.jsx +++ b/frontend/src/components/SettingsComponent/SettingsComponent.jsx @@ -32,7 +32,7 @@ function SettingsComponent() {
diff --git a/frontend/src/i18n.js b/frontend/src/i18n.js index 5770d511..f1967ace 100644 --- a/frontend/src/i18n.js +++ b/frontend/src/i18n.js @@ -11,15 +11,19 @@ i18n .use(Backend) .init({ compatibilityJSON: "v4", - lng: userLanguage || "en", + //lng: "en", fallbackLng: "en", - debug: false, + detection: { + order: ["path", "cookie", "localStorage", "htmlTag"], + caches: ["localStorage", "cookie"], // cache user language on + }, + debug: true, //keySeparator: false, // we use content as keys interpolation: { escapeValue: true, }, react: { - useSuspense: false, + useSuspense: true, }, supportedLngs: ["en", "es-ES"], backend: { diff --git a/frontend/src/index.jsx b/frontend/src/index.jsx index e66ad55d..e79267e8 100644 --- a/frontend/src/index.jsx +++ b/frontend/src/index.jsx @@ -1,6 +1,6 @@ import "./index.css"; -import React from "react"; +import React, { Suspense } from "react"; import ReactDOM from "react-dom"; import App from "./App"; @@ -9,7 +9,9 @@ import "./i18n"; ReactDOM.render( - + Loading...
}> + + , document.getElementById("root") ); From 3d67022b38806e23953d5cd9c15b4f6cd79d7782 Mon Sep 17 00:00:00 2001 From: Andres Date: Thu, 19 Oct 2023 13:18:38 +0200 Subject: [PATCH 5/8] feat: i18n fix --- backend/routes/auth.js | 2 +- frontend/public/locales/en/common.json | 11 ++++++----- frontend/public/locales/es-ES/common.json | 11 ++++++----- .../src/components/HomeLoggedIn/HomeLoggedIn.jsx | 2 +- frontend/src/components/LogIn/LogIn.jsx | 1 - .../components/AddMember/AddMember.jsx | 2 +- .../components/NetworkSettings/NetworkSettings.jsx | 4 ++-- .../SettingsComponent.jsx => Settings/Settings.jsx} | 4 ++-- frontend/src/components/Settings/index.jsx | 1 + frontend/src/components/SettingsComponent/index.jsx | 1 - frontend/src/i18n.js | 4 +--- frontend/src/routes/Settings/Settings.jsx | 13 ++++--------- 12 files changed, 25 insertions(+), 31 deletions(-) rename frontend/src/components/{SettingsComponent/SettingsComponent.jsx => Settings/Settings.jsx} (93%) create mode 100644 frontend/src/components/Settings/index.jsx delete mode 100644 frontend/src/components/SettingsComponent/index.jsx diff --git a/backend/routes/auth.js b/backend/routes/auth.js index c93fb1b1..93ffb2de 100644 --- a/backend/routes/auth.js +++ b/backend/routes/auth.js @@ -9,7 +9,7 @@ const loginLimiter = rateLimit({ max: Number(process.env.ZT_TRIES_TO_BAN) || 50, // limit each IP to 50 requests per windowMs message: { status: 429, - error: "tooManyAttemps", + error: "tooManyAttempts", }, }); diff --git a/frontend/public/locales/en/common.json b/frontend/public/locales/en/common.json index a6e25f29..e0cd9f8e 100644 --- a/frontend/public/locales/en/common.json +++ b/frontend/public/locales/en/common.json @@ -22,7 +22,7 @@ "noDevices": "No devices have joined this network. Use the app on your devices to join", "member_one": "Member", "member_other": "Members", - "addMemberMan": "Manually Add Member", + "addMemberManually": "Manually Add Member", "name": "Name", "description": "Description", "allowBridging": "Allow Ethernet Bridging", @@ -40,7 +40,7 @@ "latency": "Latency", "settings": "Settings", "generalSettings": "General settings", - "netId": "Network ID", + "networkId": "Network ID", "accessControl": "Access control", "public": "Public", "private": "Private", @@ -54,8 +54,9 @@ "autoAssignPool": "IPv4 Auto-Assign", "addIPv4Pool": "Add IPv4 Pool", "multicastLimit": "Multicast Recipient Limit", - "enaBroadcast": "Enable Broadcast", + "enableBroadcast": "Enable Broadcast", "logInFailed": "Invalid username or password", - "tooManyAttemps": "Too many login attempts, please try again in 15 minutes.", - "language": "Language" + "tooManyAttempts": "Too many login attempts, please try again in 15 minutes.", + "language": "Language", + "notAuthorized": "You are not authorized. Please Log In." } diff --git a/frontend/public/locales/es-ES/common.json b/frontend/public/locales/es-ES/common.json index eb70d238..798e5b21 100644 --- a/frontend/public/locales/es-ES/common.json +++ b/frontend/public/locales/es-ES/common.json @@ -22,7 +22,7 @@ "noDevices": "Ningún dispositivo se ha unido a esta red. Utilice la aplicación en sus dispositivos para unirse", "member_one": "Miembro", "member_other": "Miembros", - "addMemberMan": "Añadir miembro manualmente", + "addMemberManually": "Añadir miembro manualmente", "name": "Nombre", "description": "Descripción", "allowBridging": "Permitir puente Ethernet", @@ -40,7 +40,7 @@ "latency": "Latencia", "settings": "Ajustes", "generalSettings": "Ajustes generales", - "netId": "ID de red", + "networkId": "ID de red", "accessControl": "Control de acceso", "public": "Público", "private": "Privado", @@ -54,8 +54,9 @@ "ipv4AutoAssign": "IPv4 Auto-Assign", "addIPv4Pool": "Añadir rango IPv4", "multicastLimit": "Límite de destinatarios multicast", - "enaBroadcast": "Habilitar broadcast", + "enableBroadcast": "Habilitar broadcast", "logInFailed": "Nombre de usuario o contraseña incorrecto", - "tooManyAttemps": "Demasiados intentos de inicio de sesión. Vuelvee a intentarlo en 15 minutos", - "language": "Idioma" + "tooManyAttempts": "Demasiados intentos de inicio de sesión. Vuelvee a intentarlo en 15 minutos", + "language": "Idioma", + "notAuthorized": "No estás autorizado. Por favor, inicia sesión." } diff --git a/frontend/src/components/HomeLoggedIn/HomeLoggedIn.jsx b/frontend/src/components/HomeLoggedIn/HomeLoggedIn.jsx index 0dfb07c0..974a18b2 100644 --- a/frontend/src/components/HomeLoggedIn/HomeLoggedIn.jsx +++ b/frontend/src/components/HomeLoggedIn/HomeLoggedIn.jsx @@ -50,7 +50,7 @@ function HomeLoggedIn() { {t("controllerNetworks")} {networks[0] && t("controllerAddress")} - {networks[0] && String(networks[0]["id"]).slice(0, 10)} + {networks[0] && networks[0]["id"].slice(0, 10)} diff --git a/frontend/src/components/LogIn/LogIn.jsx b/frontend/src/components/LogIn/LogIn.jsx index 21465a34..36ea9a29 100644 --- a/frontend/src/components/LogIn/LogIn.jsx +++ b/frontend/src/components/LogIn/LogIn.jsx @@ -12,7 +12,6 @@ function LogIn() { )} -   ); diff --git a/frontend/src/components/NetworkMembers/components/AddMember/AddMember.jsx b/frontend/src/components/NetworkMembers/components/AddMember/AddMember.jsx index 14dc2e34..56c71ffc 100644 --- a/frontend/src/components/NetworkMembers/components/AddMember/AddMember.jsx +++ b/frontend/src/components/NetworkMembers/components/AddMember/AddMember.jsx @@ -30,7 +30,7 @@ function AddMember({ nwid, callback }) { return ( <> - {t("addMemberMan")} + {t("addMemberManually")} - {t("netId")} + {t("networkId")} {network["config"]["id"]} @@ -129,7 +129,7 @@ function NetworkSettings({ network, setNetwork }) { color="primary" onChange={handleChange("config", "enableBroadcast", "checkbox")} /> - {t("enaBroadcast")} + {t("enableBroadcast")} {/* TODO: */} {/* diff --git a/frontend/src/components/SettingsComponent/SettingsComponent.jsx b/frontend/src/components/Settings/Settings.jsx similarity index 93% rename from frontend/src/components/SettingsComponent/SettingsComponent.jsx rename to frontend/src/components/Settings/Settings.jsx index 0fe4c13b..0ee47aa0 100644 --- a/frontend/src/components/SettingsComponent/SettingsComponent.jsx +++ b/frontend/src/components/Settings/Settings.jsx @@ -16,7 +16,7 @@ import { parseValue, replaceValue, setValue } from "utils/ChangeHelper"; import { useTranslation } from "react-i18next"; -function SettingsComponent() { +function Settings() { const { t, i18n } = useTranslation(); const handleChange = () => (event) => { @@ -40,4 +40,4 @@ function SettingsComponent() { ); } -export default SettingsComponent; +export default Settings; diff --git a/frontend/src/components/Settings/index.jsx b/frontend/src/components/Settings/index.jsx new file mode 100644 index 00000000..41d66223 --- /dev/null +++ b/frontend/src/components/Settings/index.jsx @@ -0,0 +1 @@ +export { default } from "./Settings"; diff --git a/frontend/src/components/SettingsComponent/index.jsx b/frontend/src/components/SettingsComponent/index.jsx deleted file mode 100644 index 88ef9940..00000000 --- a/frontend/src/components/SettingsComponent/index.jsx +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./SettingsComponent"; diff --git a/frontend/src/i18n.js b/frontend/src/i18n.js index f1967ace..fd27dd55 100644 --- a/frontend/src/i18n.js +++ b/frontend/src/i18n.js @@ -11,14 +11,12 @@ i18n .use(Backend) .init({ compatibilityJSON: "v4", - //lng: "en", fallbackLng: "en", detection: { order: ["path", "cookie", "localStorage", "htmlTag"], - caches: ["localStorage", "cookie"], // cache user language on + caches: ["localStorage", "cookie"], }, debug: true, - //keySeparator: false, // we use content as keys interpolation: { escapeValue: true, }, diff --git a/frontend/src/routes/Settings/Settings.jsx b/frontend/src/routes/Settings/Settings.jsx index b97e5bfc..175edd8c 100644 --- a/frontend/src/routes/Settings/Settings.jsx +++ b/frontend/src/routes/Settings/Settings.jsx @@ -1,11 +1,10 @@ import { Grid, Link, Typography } from "@material-ui/core"; import ArrowBackIcon from "@material-ui/icons/ArrowBack"; -import SettingsComponent from "components/SettingsComponent"; +import SettingsComponent from "components/Settings"; -import { useCallback, useEffect, useState } from "react"; -import { Link as RouterLink, useHistory, useParams } from "react-router-dom"; +import { Link as RouterLink } from "react-router-dom"; import { useLocalStorage } from "react-use"; -import API from "utils/API"; + import useStyles from "./Settings.styles"; import { useTranslation } from "react-i18next"; @@ -13,10 +12,8 @@ import { useTranslation } from "react-i18next"; function Settings() { const { t, i18n } = useTranslation(); const [loggedIn] = useLocalStorage("loggedIn", false); - const [network, setNetwork] = useState({}); const classes = useStyles(); - const history = useHistory(); if (loggedIn) { return ( @@ -45,9 +42,7 @@ function Settings() { }} > - - You are not authorized. Please Log In - + {t("notAuthorized")} ); From 83502755d71ff2ebd901ba0b295ca850be3b8927 Mon Sep 17 00:00:00 2001 From: Andres Date: Thu, 19 Oct 2023 14:31:20 +0200 Subject: [PATCH 6/8] feat: i18n words --- frontend/public/locales/en/common.json | 4 ++-- frontend/public/locales/es-ES/common.json | 4 ++-- frontend/src/components/NetworkMembers/NetworkMembers.jsx | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/frontend/public/locales/en/common.json b/frontend/public/locales/en/common.json index e0cd9f8e..54c61abe 100644 --- a/frontend/public/locales/en/common.json +++ b/frontend/public/locales/en/common.json @@ -33,8 +33,8 @@ "noTagDef": "No tags defined", "authorized": "Authorized", "address": "Address", - "ips": "Managed IPs", - "status": "Last seen", + "managedIPs": "Managed IPs", + "lastSeen": "Last seen", "version": "Version", "physIp": "Physical IP", "latency": "Latency", diff --git a/frontend/public/locales/es-ES/common.json b/frontend/public/locales/es-ES/common.json index 798e5b21..6fa7dc83 100644 --- a/frontend/public/locales/es-ES/common.json +++ b/frontend/public/locales/es-ES/common.json @@ -33,8 +33,8 @@ "noTagDef": "No hay etiquetas definidas", "authorized": "Autorizado", "address": "Dirección", - "ips": "IPs asignadas", - "status": "Visto por última vez", + "managedIPs": "IPs asignadas", + "lastSeen": "Visto por última vez", "version": "Versión", "physIp": "IP pública", "latency": "Latencia", diff --git a/frontend/src/components/NetworkMembers/NetworkMembers.jsx b/frontend/src/components/NetworkMembers/NetworkMembers.jsx index 3cfef3e3..faedfb50 100644 --- a/frontend/src/components/NetworkMembers/NetworkMembers.jsx +++ b/frontend/src/components/NetworkMembers/NetworkMembers.jsx @@ -97,13 +97,13 @@ function NetworkMembers({ network }) { }, { id: "ips", - name: t("ips"), + name: t("managedIPs"), minWidth: "220px", cell: (row) => , }, { - id: "status", - name: t("status"), + id: "lastSeen", + name: t("lastSeen"), minWidth: "100px", cell: (row) => row.online === 1 ? ( From 2cf3a64cc8b8e1af1bbecf11b435de9472c56820 Mon Sep 17 00:00:00 2001 From: Andres Date: Thu, 19 Oct 2023 14:43:18 +0200 Subject: [PATCH 7/8] feat: i18n managedroutes --- .../components/ManagedRoutes/ManagedRoutes.jsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/NetworkSettings/components/ManagedRoutes/ManagedRoutes.jsx b/frontend/src/components/NetworkSettings/components/ManagedRoutes/ManagedRoutes.jsx index 48b21c8d..a4f2a322 100644 --- a/frontend/src/components/NetworkSettings/components/ManagedRoutes/ManagedRoutes.jsx +++ b/frontend/src/components/NetworkSettings/components/ManagedRoutes/ManagedRoutes.jsx @@ -16,7 +16,10 @@ import DataTable from "react-data-table-component"; import { validateIP, normilizeIP, validateCIDR } from "utils/IP"; +import { useTranslation } from "react-i18next"; + function ManagedRoutes({ routes, handleChange }) { + const { t, i18n } = useTranslation(); const [destination, setDestination] = useState(""); const [via, setVia] = useState(""); @@ -84,13 +87,13 @@ function ManagedRoutes({ routes, handleChange }) { return ( <> - Managed Routes ({routes.length + "/128"}) + {t("managedRoutes")} ({routes.length + "/128"}) - Add Routes + {t("addRoute")} Date: Thu, 19 Oct 2023 15:15:19 +0200 Subject: [PATCH 8/8] feat: i18n --- frontend/public/locales/en/common.json | 7 ++++++- frontend/public/locales/es-ES/common.json | 9 +++++++-- .../components/LogIn/components/LogInUser/LogInUser.jsx | 4 ++-- .../components/MemberSettings/MemberSettings.jsx | 4 +++- frontend/src/components/NetworkRules/NetworkRules.jsx | 2 +- .../components/IPv4AutoAssign/IPv4AutoAssign.jsx | 4 ++-- .../components/ManagedRoutes/ManagedRoutes.jsx | 8 ++++---- frontend/src/routes/Network/Network.jsx | 9 +++++---- 8 files changed, 30 insertions(+), 17 deletions(-) diff --git a/frontend/public/locales/en/common.json b/frontend/public/locales/en/common.json index 54c61abe..3dc09054 100644 --- a/frontend/public/locales/en/common.json +++ b/frontend/public/locales/en/common.json @@ -58,5 +58,10 @@ "logInFailed": "Invalid username or password", "tooManyAttempts": "Too many login attempts, please try again in 15 minutes.", "language": "Language", - "notAuthorized": "You are not authorized. Please Log In." + "notAuthorized": "You are not authorized. Please Log In.", + "saveChanges": "Save changes", + "optional": "Optional", + "destination": "Destination", + "username": "Username", + "password": "Password" } diff --git a/frontend/public/locales/es-ES/common.json b/frontend/public/locales/es-ES/common.json index 6fa7dc83..f314e3c4 100644 --- a/frontend/public/locales/es-ES/common.json +++ b/frontend/public/locales/es-ES/common.json @@ -51,12 +51,17 @@ "start": "Inicio", "end": "Final", "autoAssignPool": "Rango de IPv4 autoasignables", - "ipv4AutoAssign": "IPv4 Auto-Assign", + "ipv4AutoAssign": "Rangos de IPv4 automáticos", "addIPv4Pool": "Añadir rango IPv4", "multicastLimit": "Límite de destinatarios multicast", "enableBroadcast": "Habilitar broadcast", "logInFailed": "Nombre de usuario o contraseña incorrecto", "tooManyAttempts": "Demasiados intentos de inicio de sesión. Vuelvee a intentarlo en 15 minutos", "language": "Idioma", - "notAuthorized": "No estás autorizado. Por favor, inicia sesión." + "notAuthorized": "No estás autorizado. Por favor, inicia sesión.", + "saveChanges": "Guardar cambios", + "optional": "Opcional", + "destination": "Destino", + "username": "Nombre de usuario", + "password": "Contraseña" } diff --git a/frontend/src/components/LogIn/components/LogInUser/LogInUser.jsx b/frontend/src/components/LogIn/components/LogInUser/LogInUser.jsx index d6d1af0f..bb14cb1f 100644 --- a/frontend/src/components/LogIn/components/LogInUser/LogInUser.jsx +++ b/frontend/src/components/LogIn/components/LogInUser/LogInUser.jsx @@ -91,7 +91,7 @@ function LogInUser() { setUsername(e.target.value); }} margin="dense" - label="username" + label={t("username")} type="username" fullWidth /> @@ -101,7 +101,7 @@ function LogInUser() { setPassword(e.target.value); }} margin="dense" - label="password" + label={t("password")} type="password" fullWidth /> diff --git a/frontend/src/components/NetworkMembers/components/MemberSettings/MemberSettings.jsx b/frontend/src/components/NetworkMembers/components/MemberSettings/MemberSettings.jsx index c3f59779..09306322 100644 --- a/frontend/src/components/NetworkMembers/components/MemberSettings/MemberSettings.jsx +++ b/frontend/src/components/NetworkMembers/components/MemberSettings/MemberSettings.jsx @@ -33,7 +33,9 @@ function MemberSettings({ member, network, handleChange }) { - {"Member " + member.config.id + " settings"} + + {t("member") + member.config.id + t("settings")} + ) : ( )} diff --git a/frontend/src/components/NetworkSettings/components/IPv4AutoAssign/IPv4AutoAssign.jsx b/frontend/src/components/NetworkSettings/components/IPv4AutoAssign/IPv4AutoAssign.jsx index 2abdde70..d9d87bcf 100644 --- a/frontend/src/components/NetworkSettings/components/IPv4AutoAssign/IPv4AutoAssign.jsx +++ b/frontend/src/components/NetworkSettings/components/IPv4AutoAssign/IPv4AutoAssign.jsx @@ -145,7 +145,7 @@ function IPv4AutoAssign({ ipAssignmentPools, handleChange }) { row["target"], }, { id: "via", - name: "via", + name: t("via"), cell: (row) => (row["via"] ? row["via"] : "(LAN)"), }, ]; @@ -103,7 +103,7 @@ function ManagedRoutes({ routes, handleChange }) { - Networks + {t("network", { count: 2 })}
@@ -73,9 +76,7 @@ function Network() { }} > - - You are not authorized. Please Log In - + {t("notAuthorized")} );