mirror of
https://github.com/dec0dOS/zero-ui.git
synced 2025-07-06 04:51:44 -07:00
feat: i18n
This commit is contained in:
parent
d65e6fb71a
commit
61fd0e7186
24 changed files with 416 additions and 79 deletions
|
@ -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",
|
||||
|
|
60
frontend/public/locales/en/translation.json
Normal file
60
frontend/public/locales/en/translation.json
Normal file
|
@ -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."
|
||||
}
|
17
frontend/public/locales/es-ES/translation.json
Normal file
17
frontend/public/locales/es-ES/translation.json
Normal file
|
@ -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"
|
||||
}
|
60
frontend/public/locales/es/translation.json
Normal file
60
frontend/public/locales/es/translation.json
Normal file
|
@ -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"
|
||||
}
|
|
@ -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();
|
||||
}}
|
||||
>
|
||||
|
|
|
@ -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 (
|
||||
<div className={classes.root}>
|
||||
<Button
|
||||
|
@ -38,19 +42,19 @@ function HomeLoggedIn() {
|
|||
className={classes.createBtn}
|
||||
onClick={createNetwork}
|
||||
>
|
||||
Create A Network
|
||||
{t("createNetwork")}
|
||||
</Button>
|
||||
<Divider />
|
||||
<Grid container spacing={3} className={classes.container}>
|
||||
<Grid item xs={6}>
|
||||
<Typography variant="h5">Controller networks</Typography>
|
||||
{networks[0] && "Network controller address"}
|
||||
<Typography variant="h5">{t("controllerNetworks")}</Typography>
|
||||
{networks[0] && t("controllerAddress")}
|
||||
<Box fontWeight="fontWeightBold">
|
||||
{networks[0] && networks[0]["id"].slice(0, 10)}
|
||||
{networks[0] && String(networks[0]["id"]).slice(0, 10)}
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid item xs="auto">
|
||||
<Typography>Networks</Typography>
|
||||
<Typography>{t("network", { count: networks.length })}</Typography>
|
||||
<Grid item>
|
||||
{networks[0] ? (
|
||||
networks.map((network) => (
|
||||
|
@ -59,7 +63,7 @@ function HomeLoggedIn() {
|
|||
</Grid>
|
||||
))
|
||||
) : (
|
||||
<div>Please create at least one network</div>
|
||||
<div>{t("createOneNetwork")}</div>
|
||||
)}
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
|
|
@ -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 (
|
||||
<Grid
|
||||
container
|
||||
|
@ -42,14 +46,11 @@ function HomeLoggedOut() {
|
|||
>
|
||||
<Grid item xs={10}>
|
||||
<Typography variant="h5">
|
||||
<span>
|
||||
ZeroUI - ZeroTier Controller Web UI - is a web user interface for a
|
||||
self-hosted ZeroTier network controller.
|
||||
</span>
|
||||
<span>{t("zerouiDesc")}</span>
|
||||
</Typography>
|
||||
|
||||
<Typography>
|
||||
<span>Please Log In to continue</span>
|
||||
<span>{t("loginToContinue")}</span>
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
|
|
@ -12,6 +12,7 @@ function LogIn() {
|
|||
<Divider orientation="vertical" />
|
||||
</>
|
||||
)}
|
||||
|
||||
<LogInUser />
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -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 (
|
||||
<div>
|
||||
<Button onClick={handleClickOpen} color="inherit" variant="outlined">
|
||||
Token Log In
|
||||
{t("logInToken")}
|
||||
</Button>
|
||||
<Dialog open={open} onClose={handleClose} onKeyPress={handleKeyPress}>
|
||||
<DialogTitle>Log In</DialogTitle>
|
||||
<DialogTitle>{t("logIn")}</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>ADVANCED FEATURE.</DialogContentText>
|
||||
<DialogContentText>{t("advancedFeature")}</DialogContentText>
|
||||
<TextField
|
||||
value={token}
|
||||
onChange={(e) => {
|
||||
|
@ -76,10 +80,10 @@ function LogInToken() {
|
|||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={handleClose} color="primary">
|
||||
Cancel
|
||||
{t("cancel")}
|
||||
</Button>
|
||||
<Button onClick={LogIn} color="primary">
|
||||
Log In
|
||||
{t("logIn")}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
|
|
@ -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 (
|
||||
<>
|
||||
<Button onClick={handleClickOpen} color="primary" variant="contained">
|
||||
Log In
|
||||
{t("logIn")}
|
||||
</Button>
|
||||
<Dialog open={open} onClose={handleClose} onKeyPress={handleKeyPress}>
|
||||
<DialogTitle>Log In</DialogTitle>
|
||||
<DialogTitle>{t("logIn")}</DialogTitle>
|
||||
<DialogContent>
|
||||
<TextField
|
||||
autoFocus
|
||||
|
@ -104,10 +108,10 @@ function LogInUser() {
|
|||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={handleClose} color="primary">
|
||||
Cancel
|
||||
{t("cancel")}
|
||||
</Button>
|
||||
<Button onClick={LogIn} color="primary">
|
||||
Log In
|
||||
{t("logIn")}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
@ -117,7 +121,7 @@ function LogInUser() {
|
|||
vertical: "top",
|
||||
horizontal: "center",
|
||||
}}
|
||||
message={error}
|
||||
message={t(error)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -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 (
|
||||
<Accordion>
|
||||
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
||||
<Typography>Management</Typography>
|
||||
<Typography>{t("management")}</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<Button
|
||||
|
@ -54,21 +58,19 @@ function NetworkManagement() {
|
|||
startIcon={<DeleteIcon />}
|
||||
onClick={handleClickOpen}
|
||||
>
|
||||
Delete Network
|
||||
{t("deleteNetwork")}
|
||||
</Button>
|
||||
<Dialog open={open} onClose={handleClose}>
|
||||
<DialogTitle>
|
||||
{"Are you sure you want to delete this network?"}
|
||||
</DialogTitle>
|
||||
<DialogTitle>{t("deleteNetworkConfirm")}</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>This action cannot be undone.</DialogContentText>
|
||||
<DialogContentText>{t("deleteAlert")}</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={handleClose} color="primary">
|
||||
Cancel
|
||||
{t("cancel")}
|
||||
</Button>
|
||||
<Button onClick={deleteNetwork} color="secondary">
|
||||
Delete
|
||||
{t("delete")}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
|
|
@ -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) => (
|
||||
<Checkbox
|
||||
|
@ -79,7 +83,7 @@ function NetworkMembers({ network }) {
|
|||
},
|
||||
{
|
||||
id: "address",
|
||||
name: "Address",
|
||||
name: t("address"),
|
||||
minWidth: "150px",
|
||||
cell: (row) => (
|
||||
<Typography variant="body2">{row.config.address}</Typography>
|
||||
|
@ -87,19 +91,19 @@ function NetworkMembers({ network }) {
|
|||
},
|
||||
{
|
||||
id: "name",
|
||||
name: "Name / Description",
|
||||
name: t("name") + "/" + t("description"),
|
||||
minWidth: "250px",
|
||||
cell: (row) => <MemberName member={row} handleChange={handleChange} />,
|
||||
},
|
||||
{
|
||||
id: "ips",
|
||||
name: "Managed IPs",
|
||||
name: t("ips"),
|
||||
minWidth: "220px",
|
||||
cell: (row) => <ManagedIP member={row} handleChange={handleChange} />,
|
||||
},
|
||||
{
|
||||
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 (
|
||||
<Accordion defaultExpanded={true}>
|
||||
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
||||
<Typography>Members</Typography>
|
||||
<Typography>{t("member", { count: members.length })}</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<Grid container direction="column" spacing={3}>
|
||||
|
@ -188,8 +192,7 @@ function NetworkMembers({ network }) {
|
|||
}}
|
||||
>
|
||||
<Typography variant="h6" style={{ padding: "10%" }}>
|
||||
No devices have joined this network. Use the app on your
|
||||
devices to join <b>{nwid}</b>.
|
||||
{t("noDevices")} <b>{nwid}</b>.
|
||||
</Typography>
|
||||
</Grid>
|
||||
)}
|
||||
|
|
|
@ -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 (
|
||||
<>
|
||||
<Typography>Manually Add Member</Typography>
|
||||
<Typography>{t("addMemberMan")}</Typography>
|
||||
<List
|
||||
disablePadding={true}
|
||||
style={{
|
||||
|
|
|
@ -12,8 +12,10 @@ import {
|
|||
import DeleteOutlineIcon from "@material-ui/icons/DeleteOutline";
|
||||
|
||||
import API from "utils/API";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
function DeleteMember({ nwid, mid, callback }) {
|
||||
const { t, i18n } = useTranslation();
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const handleClickOpen = () => {
|
||||
|
@ -37,18 +39,16 @@ function DeleteMember({ nwid, mid, callback }) {
|
|||
<DeleteOutlineIcon color="secondary" style={{ fontSize: 20 }} />
|
||||
</IconButton>
|
||||
<Dialog open={open} onClose={handleClose}>
|
||||
<DialogTitle>
|
||||
{"Are you sure you want to delete this member?"}
|
||||
</DialogTitle>
|
||||
<DialogTitle>{t("deleteMemberConfirm")}</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>This action cannot be undone.</DialogContentText>
|
||||
<DialogContentText>{t("deleteAlert")}</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={handleClose} color="primary">
|
||||
Cancel
|
||||
{t("cancel")}
|
||||
</Button>
|
||||
<Button onClick={deleteMemberReq} color="secondary">
|
||||
Delete
|
||||
{t("delete")}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
|
|
@ -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 (
|
||||
<Grid>
|
||||
<TextField
|
||||
value={member.name}
|
||||
onChange={handleChange(member, "name")}
|
||||
label="Name"
|
||||
label={t("name")}
|
||||
variant="filled"
|
||||
InputLabelProps={{
|
||||
shrink: true,
|
||||
|
@ -15,7 +17,7 @@ function MemberName({ member, handleChange }) {
|
|||
<TextField
|
||||
value={member.description}
|
||||
onChange={handleChange(member, "description")}
|
||||
label="Description"
|
||||
label={t("description")}
|
||||
variant="filled"
|
||||
InputLabelProps={{
|
||||
shrink: true,
|
||||
|
|
|
@ -13,7 +13,10 @@ import BuildIcon from "@material-ui/icons/Build";
|
|||
import { useState } from "react";
|
||||
import Tag from "./components/Tag";
|
||||
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
function MemberSettings({ member, network, handleChange }) {
|
||||
const { t, i18n } = useTranslation();
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const handleClickOpen = () => {
|
||||
|
@ -43,7 +46,7 @@ function MemberSettings({ member, network, handleChange }) {
|
|||
"checkbox"
|
||||
)}
|
||||
/>
|
||||
<span>Allow Ethernet Bridging</span>
|
||||
<span>{t("allowBridging")}</span>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Checkbox
|
||||
|
@ -56,17 +59,17 @@ function MemberSettings({ member, network, handleChange }) {
|
|||
"checkbox"
|
||||
)}
|
||||
/>
|
||||
<span>Do Not Auto-Assign IPs</span>
|
||||
<span>{t("noAutoIP")}</span>
|
||||
</Grid>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="h6">Capabilities</Typography>
|
||||
<Typography variant="h6">{t("capabilities")}</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Paper style={{ padding: 20 }}>
|
||||
{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 }) {
|
|||
</Grid>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="h6">Tags</Typography>
|
||||
<Typography variant="h6">{t("tags")}</Typography>
|
||||
</Grid>
|
||||
{Object.entries(network["tagsByName"] || []).length === 0 ? (
|
||||
<Grid item xs={12}>
|
||||
<Paper style={{ padding: 20 }}>No tags defined</Paper>
|
||||
<Paper style={{ padding: 20 }}>{t("noTagDef")}</Paper>
|
||||
</Grid>
|
||||
) : (
|
||||
""
|
||||
|
|
|
@ -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 (
|
||||
<Accordion>
|
||||
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
||||
<Typography>Flow Rules</Typography>
|
||||
<Typography>{t("flowRules")}</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
{/* 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
|
||||
*/}
|
||||
<CodeMirror
|
||||
value={network["rulesSource"]}
|
||||
|
|
|
@ -17,7 +17,10 @@ import IPv4AutoAssign from "./components/IPv4AutoAssign";
|
|||
import API from "utils/API";
|
||||
import { parseValue, replaceValue, setValue } from "utils/ChangeHelper";
|
||||
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
function NetworkSettings({ network, setNetwork }) {
|
||||
const { t, i18n } = useTranslation();
|
||||
const sendReq = async (data) => {
|
||||
try {
|
||||
const req = await API.post("/network/" + network["config"]["id"], data);
|
||||
|
@ -43,12 +46,12 @@ function NetworkSettings({ network, setNetwork }) {
|
|||
return (
|
||||
<Accordion>
|
||||
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
||||
<Typography>General settings</Typography>
|
||||
<Typography>{t("generalSettings")}</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<Grid container direction="column" spacing={3}>
|
||||
<Grid item>
|
||||
<Typography>Network ID</Typography>
|
||||
<Typography>{t("netId")}</Typography>
|
||||
<Typography variant="h5">
|
||||
<span>{network["config"]["id"]}</span>
|
||||
</Typography>
|
||||
|
@ -57,7 +60,7 @@ function NetworkSettings({ network, setNetwork }) {
|
|||
<TextField
|
||||
value={network["config"]["name"]}
|
||||
onChange={handleChange("config", "name")}
|
||||
label="Name"
|
||||
label={t("name")}
|
||||
variant="filled"
|
||||
InputLabelProps={{
|
||||
shrink: true,
|
||||
|
@ -71,7 +74,7 @@ function NetworkSettings({ network, setNetwork }) {
|
|||
multiline
|
||||
minRows={2}
|
||||
maxRows={Infinity}
|
||||
label="Description"
|
||||
label={t("description")}
|
||||
variant="filled"
|
||||
InputLabelProps={{
|
||||
shrink: true,
|
||||
|
@ -80,14 +83,14 @@ function NetworkSettings({ network, setNetwork }) {
|
|||
</Grid>
|
||||
<Divider />
|
||||
<Grid item>
|
||||
<Typography>Access Control</Typography>
|
||||
<Typography>{t("accessControl")}</Typography>
|
||||
<Select
|
||||
native
|
||||
value={network["config"]["private"]}
|
||||
onChange={handleChange("config", "private", "json")}
|
||||
>
|
||||
<option value={true}>Private</option>
|
||||
<option value={false}>Public</option>
|
||||
<option value={1}>{t("private")}</option>
|
||||
<option value={0}>{t("public")}</option>
|
||||
</Select>
|
||||
</Grid>
|
||||
<Divider />
|
||||
|
@ -111,7 +114,7 @@ function NetworkSettings({ network, setNetwork }) {
|
|||
<Divider />
|
||||
<Grid item>
|
||||
<TextField
|
||||
label="Multicast Recipient Limit"
|
||||
label={t("multicastLimit")}
|
||||
type="number"
|
||||
value={network["config"]["multicastLimit"]}
|
||||
onChange={handleChange("config", "multicastLimit", "json")}
|
||||
|
@ -126,7 +129,7 @@ function NetworkSettings({ network, setNetwork }) {
|
|||
color="primary"
|
||||
onChange={handleChange("config", "enableBroadcast", "checkbox")}
|
||||
/>
|
||||
<span>Enable Broadcast</span>
|
||||
<span>{t("enaBroadcast")}</span>
|
||||
</Grid>
|
||||
{/* TODO: */}
|
||||
{/* <Grid item>
|
||||
|
|
|
@ -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 (
|
||||
<>
|
||||
<Typography>IPv4 Auto-Assign</Typography>
|
||||
<Typography>{t("ipv4AutoAssign")}</Typography>
|
||||
<div
|
||||
style={{
|
||||
padding: "30px",
|
||||
|
@ -122,7 +125,7 @@ function IPv4AutoAssign({ ipAssignmentPools, handleChange }) {
|
|||
</Grid>
|
||||
</div>
|
||||
<Typography style={{ paddingBottom: "10px" }}>
|
||||
Auto-Assign Pools
|
||||
{t("autoAssignPool")}
|
||||
</Typography>
|
||||
<Box border={1} borderColor="grey.300">
|
||||
<Grid item style={{ margin: "10px" }}>
|
||||
|
@ -132,7 +135,7 @@ function IPv4AutoAssign({ ipAssignmentPools, handleChange }) {
|
|||
data={ipAssignmentPools}
|
||||
/>
|
||||
<Divider />
|
||||
<Typography>Add IPv4 Pool</Typography>
|
||||
<Typography>{t("addIPv4Pool")}</Typography>
|
||||
<List
|
||||
style={{
|
||||
display: "flex",
|
||||
|
|
32
frontend/src/i18n.js
Normal file
32
frontend/src/i18n.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
import i18n from "i18next";
|
||||
import languageDetector from "i18next-browser-languagedetector";
|
||||
import { initReactI18next } from "react-i18next";
|
||||
import Backend from "i18next-http-backend";
|
||||
|
||||
const userLanguage = window.navigator.language;
|
||||
|
||||
i18n
|
||||
.use(languageDetector)
|
||||
.use(initReactI18next)
|
||||
.use(Backend)
|
||||
.init({
|
||||
compatibilityJSON: "v4",
|
||||
lng: userLanguage || "en",
|
||||
fallbackLng: "en",
|
||||
debug: true,
|
||||
//keySeparator: false, // we use content as keys
|
||||
interpolation: {
|
||||
escapeValue: true,
|
||||
},
|
||||
react: {
|
||||
useSuspense: false,
|
||||
},
|
||||
supportedLngs: ["en", "es", "es-ES"],
|
||||
backend: {
|
||||
loadPath: "/locales/{{lng}}/{{ns}}.json",
|
||||
},
|
||||
ns: ["translation"],
|
||||
defaultNS: "translation",
|
||||
});
|
||||
|
||||
export default i18n;
|
|
@ -5,6 +5,8 @@ import ReactDOM from "react-dom";
|
|||
|
||||
import App from "./App";
|
||||
|
||||
import "./i18n";
|
||||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue