feat: settings i18n [skip test] (#4372)

pull/4374/head
James Elliott 2022-11-14 14:49:34 +11:00 committed by GitHub
parent 1a1b85489c
commit 164fc5e80d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 98 additions and 30 deletions

View File

@ -1 +1 @@
{"defaults":{"language":{"display":"English","locale":"en"},"namespace":"portal"},"namespaces":["portal"],"languages":[{"display":"English","locale":"en","namespaces":["portal"],"fallbacks":["en"]},{"display":"Arabic","locale":"ar","namespaces":["portal"],"fallbacks":["en"]},{"display":"Arabic (Saudi Arabia)","locale":"ar-SA","namespaces":["portal"],"fallbacks":["ar","en"]},{"display":"Czech","locale":"cs","namespaces":["portal"],"fallbacks":["en"]},{"display":"Czech (Czechia)","locale":"cs-CZ","namespaces":["portal"],"fallbacks":["cs","en"]},{"display":"Danish","locale":"da","namespaces":["portal"],"fallbacks":["en"]},{"display":"Danish (Denmark)","locale":"da-DK","namespaces":["portal"],"fallbacks":["da","en"]},{"display":"German","locale":"de","namespaces":["portal"],"fallbacks":["en"]},{"display":"Greek","locale":"el","namespaces":["portal"],"fallbacks":["en"]},{"display":"Greek (Greece)","locale":"el-GR","namespaces":["portal"],"fallbacks":["el","en"]},{"display":"Spanish","locale":"es","namespaces":["portal"],"fallbacks":["en"]},{"display":"Finnish","locale":"fi","namespaces":["portal"],"fallbacks":["en"]},{"display":"French","locale":"fr","namespaces":["portal"],"fallbacks":["en"]},{"display":"Italian","locale":"it","namespaces":["portal"],"fallbacks":["en"]},{"display":"Japanese","locale":"ja","namespaces":["portal"],"fallbacks":["en"]},{"display":"Japanese (Japan)","locale":"ja-JP","namespaces":["portal"],"fallbacks":["ja","en"]},{"display":"Norwegian Bokmål","locale":"nb","namespaces":["portal"],"fallbacks":["en"]},{"display":"Norwegian Bokmål (Norway)","locale":"nb-NO","namespaces":["portal"],"fallbacks":["nb","en"]},{"display":"Dutch","locale":"nl","namespaces":["portal"],"fallbacks":["en"]},{"display":"Norwegian Bokmål","locale":"no","namespaces":["portal"],"fallbacks":["en"]},{"display":"Polish","locale":"pl","namespaces":["portal"],"fallbacks":["en"]},{"display":"Portuguese","locale":"pt","namespaces":["portal"],"fallbacks":["en"]},{"display":"Brazilian Portuguese","locale":"pt-BR","namespaces":["portal"],"fallbacks":["en"]},{"display":"Romanian","locale":"ro","namespaces":["portal"],"fallbacks":["en"]},{"display":"Russian","locale":"ru","namespaces":["portal"],"fallbacks":["en"]},{"display":"Swedish","locale":"sv","namespaces":["portal"],"fallbacks":["en"]},{"display":"Swedish (Sweden)","locale":"sv-SE","namespaces":["portal"],"fallbacks":["sv","en"]},{"display":"Ukrainian","locale":"uk","namespaces":["portal"],"fallbacks":["en"]},{"display":"Ukrainian (Ukraine)","locale":"uk-UA","namespaces":["portal"],"fallbacks":["uk","en"]},{"display":"Chinese","locale":"zh","namespaces":["portal"],"fallbacks":["en"]},{"display":"Chinese (China)","locale":"zh-CN","namespaces":["portal"],"fallbacks":["zh","en"]},{"display":"Chinese (Taiwan)","locale":"zh-TW","namespaces":["portal"],"fallbacks":["en"]}]}
{"defaults":{"language":{"display":"English","locale":"en"},"namespace":"portal"},"namespaces":["portal","settings"],"languages":[{"display":"English","locale":"en","namespaces":["portal","settings"],"fallbacks":["en"]},{"display":"Arabic","locale":"ar","namespaces":["portal"],"fallbacks":["en"]},{"display":"Arabic (Saudi Arabia)","locale":"ar-SA","namespaces":["portal"],"fallbacks":["ar","en"]},{"display":"Czech","locale":"cs","namespaces":["portal"],"fallbacks":["en"]},{"display":"Czech (Czechia)","locale":"cs-CZ","namespaces":["portal"],"fallbacks":["cs","en"]},{"display":"Danish","locale":"da","namespaces":["portal"],"fallbacks":["en"]},{"display":"Danish (Denmark)","locale":"da-DK","namespaces":["portal"],"fallbacks":["da","en"]},{"display":"German","locale":"de","namespaces":["portal"],"fallbacks":["en"]},{"display":"Greek","locale":"el","namespaces":["portal"],"fallbacks":["en"]},{"display":"Greek (Greece)","locale":"el-GR","namespaces":["portal"],"fallbacks":["el","en"]},{"display":"Spanish","locale":"es","namespaces":["portal"],"fallbacks":["en"]},{"display":"Finnish","locale":"fi","namespaces":["portal"],"fallbacks":["en"]},{"display":"French","locale":"fr","namespaces":["portal"],"fallbacks":["en"]},{"display":"Italian","locale":"it","namespaces":["portal"],"fallbacks":["en"]},{"display":"Japanese","locale":"ja","namespaces":["portal"],"fallbacks":["en"]},{"display":"Japanese (Japan)","locale":"ja-JP","namespaces":["portal"],"fallbacks":["ja","en"]},{"display":"Norwegian Bokmål","locale":"nb","namespaces":["portal"],"fallbacks":["en"]},{"display":"Norwegian Bokmål (Norway)","locale":"nb-NO","namespaces":["portal"],"fallbacks":["nb","en"]},{"display":"Dutch","locale":"nl","namespaces":["portal"],"fallbacks":["en"]},{"display":"Norwegian Bokmål","locale":"no","namespaces":["portal"],"fallbacks":["en"]},{"display":"Polish","locale":"pl","namespaces":["portal"],"fallbacks":["en"]},{"display":"Portuguese","locale":"pt","namespaces":["portal"],"fallbacks":["en"]},{"display":"Brazilian Portuguese","locale":"pt-BR","namespaces":["portal"],"fallbacks":["en"]},{"display":"Romanian","locale":"ro","namespaces":["portal"],"fallbacks":["en"]},{"display":"Russian","locale":"ru","namespaces":["portal"],"fallbacks":["en"]},{"display":"Swedish","locale":"sv","namespaces":["portal"],"fallbacks":["en"]},{"display":"Swedish (Sweden)","locale":"sv-SE","namespaces":["portal"],"fallbacks":["sv","en"]},{"display":"Ukrainian","locale":"uk","namespaces":["portal"],"fallbacks":["en"]},{"display":"Ukrainian (Ukraine)","locale":"uk-UA","namespaces":["portal"],"fallbacks":["uk","en"]},{"display":"Chinese","locale":"zh","namespaces":["portal"],"fallbacks":["en"]},{"display":"Chinese (China)","locale":"zh-CN","namespaces":["portal"],"fallbacks":["zh","en"]},{"display":"Chinese (Taiwan)","locale":"zh-TW","namespaces":["portal"],"fallbacks":["en"]}]}

View File

@ -0,0 +1,28 @@
{
"Actions": "Actions",
"Add": "Add",
"Add new Security Key": "Add new Security Key",
"Attestation Type": "Attestation Type",
"Authenticator Attestation GUID": "Authenticator Attestation GUID",
"Cancel": "Cancel",
"Clone Warning": "Clone Warning",
"Created": "Created",
"Delete": "Delete",
"Details": "Details",
"Edit": "Edit",
"Enabled": "Enabled",
"Last Used": "Last Used",
"Manage your security keys": "Manage your security keys",
"Name": "Name",
"No": "No",
"Provide the details for the new security key": "Provide the details for the new security key",
"Relying Party ID": "Relying Party ID",
"Security Keys": "Security Keys",
"Settings": "Settings",
"Show Details": "Show Details",
"Transports": "Transports",
"Usage Count": "Usage Count",
"Webauthn Credential Identifier": "Credential Identifier: {{id}}",
"Webauthn Public Key": "Public Key: {{key}}",
"Yes": "Yes"
}

View File

@ -31,7 +31,7 @@ i18n.use(Backend)
loadPath: basePath + "/locales/{{lng}}/{{ns}}.json",
},
load: "all",
ns: ["portal"],
ns: ["portal", "settings"],
defaultNS: "portal",
fallbackLng: {
default: ["en"],

View File

@ -10,10 +10,13 @@ import {
DialogTitle,
TextField,
} from "@mui/material";
import { useTranslation } from "react-i18next";
interface Props extends DialogProps {}
export default function AddSecurityKeyDialog(props: Props) {
const { t: translate } = useTranslation("settings");
const handleAddClick = () => {
if (props.onClose) {
props.onClose({}, "backdropClick");
@ -28,22 +31,22 @@ export default function AddSecurityKeyDialog(props: Props) {
return (
<Dialog {...props}>
<DialogTitle>Add new Security Key</DialogTitle>
<DialogTitle>{translate("Add new Security Key")}</DialogTitle>
<DialogContent>
<DialogContentText>Provide the details for the new security key.</DialogContentText>
<DialogContentText>{translate("Provide the details for the new security key")}.</DialogContentText>
<TextField
autoFocus
margin="dense"
id="description"
label="Description"
label={translate("Description")}
type="text"
fullWidth
variant="standard"
/>
</DialogContent>
<DialogActions>
<Button onClick={handleCancelClick}>Cancel</Button>
<Button onClick={handleAddClick}>Add</Button>
<Button onClick={handleCancelClick}>{translate("Cancel")}</Button>
<Button onClick={handleAddClick}>{translate("Add")}</Button>
</DialogActions>
</Dialog>
);

View File

@ -31,6 +31,7 @@ import {
Tooltip,
Typography,
} from "@mui/material";
import { useTranslation } from "react-i18next";
import { WebauthnDevice } from "@root/models/Webauthn";
import { getWebauthnDevices } from "@root/services/UserWebauthnDevices";
@ -42,6 +43,8 @@ interface Props {}
const drawerWidth = 240;
export default function SettingsView(props: Props) {
const { t: translate } = useTranslation("settings");
const [webauthnDevices, setWebauthnDevices] = useState<WebauthnDevice[] | undefined>();
const [addKeyOpen, setAddKeyOpen] = useState<boolean>(false);
const [webauthnShowDetails, setWebauthnShowDetails] = useState<number>(-1);
@ -73,7 +76,7 @@ export default function SettingsView(props: Props) {
<Box sx={{ display: "flex" }}>
<AppBar position="fixed" sx={{ zIndex: (theme) => theme.zIndex.drawer + 1 }}>
<Toolbar variant="dense">
<Typography style={{ flexGrow: 1 }}>Settings</Typography>
<Typography style={{ flexGrow: 1 }}>{translate("Settings")}</Typography>
</Toolbar>
</AppBar>
<Drawer
@ -92,7 +95,7 @@ export default function SettingsView(props: Props) {
<ListItemIcon>
<SystemSecurityUpdateGoodIcon />
</ListItemIcon>
<ListItemText primary={"Security Keys"} />
<ListItemText primary={translate("Security Keys")} />
</ListItemButton>
</ListItem>
</List>
@ -101,12 +104,12 @@ export default function SettingsView(props: Props) {
<Box component="main" sx={{ flexGrow: 1, p: 3 }}>
<Grid container spacing={2}>
<Grid item xs={12}>
<Typography>Manage your security keys</Typography>
<Typography>{translate("Manage your security keys")}</Typography>
</Grid>
<Grid item xs={12}>
<Stack spacing={1} direction="row">
<Button color="primary" variant="contained" onClick={handleAddKeyButtonClick}>
Add
{translate("Add")}
</Button>
</Stack>
</Grid>
@ -116,9 +119,9 @@ export default function SettingsView(props: Props) {
<TableHead>
<TableRow>
<TableCell />
<TableCell>Name</TableCell>
<TableCell>Enabled</TableCell>
<TableCell align="center">Actions</TableCell>
<TableCell>{translate("Name")}</TableCell>
<TableCell>{translate("Enabled")}</TableCell>
<TableCell align="center">{translate("Actions")}</TableCell>
</TableRow>
</TableHead>
<TableBody>
@ -131,7 +134,10 @@ export default function SettingsView(props: Props) {
key={x.kid.toString()}
>
<TableCell>
<Tooltip title="Show Details" placement="right">
<Tooltip
title={translate("Show Details")}
placement="right"
>
<IconButton
aria-label="expand row"
size="small"
@ -158,12 +164,15 @@ export default function SettingsView(props: Props) {
alignItems="center"
justifyContent="center"
>
<Tooltip title="Edit" placement="bottom">
<Tooltip title={translate("Edit")} placement="bottom">
<IconButton aria-label="edit">
<EditIcon />
</IconButton>
</Tooltip>
<Tooltip title="Delete" placement="bottom">
<Tooltip
title={translate("Delete")}
placement="bottom"
>
<IconButton aria-label="delete">
<DeleteIcon />
</IconButton>
@ -196,7 +205,7 @@ export default function SettingsView(props: Props) {
gutterBottom
component="div"
>
Details
{translate("Details")}
</Typography>
</Box>
</Grid>
@ -218,7 +227,14 @@ export default function SettingsView(props: Props) {
lg={12}
xl={12}
>
<Typography>Key ID: {x.kid}</Typography>
<Typography>
{translate(
"Webauthn Credential Identifier",
{
id: x.kid.toString(),
},
)}
</Typography>
</Grid>
<Grid
item
@ -230,6 +246,9 @@ export default function SettingsView(props: Props) {
>
<Typography>
Public Key: {x.public_key}
{translate("Webauthn Public Key", {
key: x.public_key.toString(),
})}
</Typography>
</Grid>
<Grid
@ -243,21 +262,29 @@ export default function SettingsView(props: Props) {
<Divider variant="middle" />
</Grid>
<Grid item xs={6} sm={6} md={4} lg={4} xl={3}>
<Typography>Relying Party ID</Typography>
<Typography>
{translate("Relying Party ID")}
</Typography>
<Typography>{x.rpid}</Typography>
</Grid>
<Grid item xs={6} sm={6} md={4} lg={4} xl={3}>
<Typography>
Authenticator Attestation GUID
{translate(
"Authenticator Attestation GUID",
)}
</Typography>
<Typography>{x.aaguid}</Typography>
</Grid>
<Grid item xs={6} sm={6} md={4} lg={4} xl={3}>
<Typography>Attestation Type</Typography>
<Typography>
{translate("Attestation Type")}
</Typography>
<Typography>{x.attestation_type}</Typography>
</Grid>
<Grid item xs={6} sm={6} md={4} lg={4} xl={3}>
<Typography>Transports</Typography>
<Typography>
{translate("Transports")}
</Typography>
<Typography>
{x.transports.length === 0
? "N/A"
@ -265,30 +292,40 @@ export default function SettingsView(props: Props) {
</Typography>
</Grid>
<Grid item xs={6} sm={6} md={4} lg={4} xl={3}>
<Typography>Clone Warning</Typography>
<Typography>
{x.clone_warning ? "Yes" : "No"}
{translate("Clone Warning")}
</Typography>
<Typography>
{x.clone_warning
? translate("Yes")
: translate("No")}
</Typography>
</Grid>
<Grid item xs={6} sm={6} md={4} lg={4} xl={3}>
<Typography>Created</Typography>
<Typography>
{translate("Created")}
</Typography>
<Typography>
{x.created_at.toString()}
</Typography>
</Grid>
<Grid item xs={6} sm={6} md={4} lg={4} xl={3}>
<Typography>Last Used</Typography>
<Typography>
{translate("Last Used")}
</Typography>
<Typography>
{x.last_used_at === undefined
? "Never"
? translate("Never")
: x.last_used_at.toString()}
</Typography>
</Grid>
<Grid item xs={6} sm={6} md={4} lg={4} xl={3}>
<Typography>Usage Count</Typography>
<Typography>
{translate("Usage Count")}
</Typography>
<Typography>
{x.sign_count === 0
? "Never"
? translate("Never")
: x.sign_count}
</Typography>
</Grid>