Translations

This commit is contained in:
binwiederhier 2022-12-29 08:20:53 -05:00
parent 9be8be49ef
commit 66cb35b5fc
7 changed files with 87 additions and 122 deletions

View File

@ -49,10 +49,7 @@ import (
- "mute" setting
- figure out what settings are "web" or "phone"
UI:
- Translations
- aria-labels
- Home UI sign-in/login to top right
-
rate limiting:
- login/account endpoints
Tests:

View File

@ -1,4 +1,17 @@
{
"signup_title": "Create a ntfy account",
"signup_form_username": "Username",
"signup_form_password": "Password",
"signup_form_confirm_password": "Confirm password",
"signup_form_button_submit": "Sign up",
"signup_already_have_account": "Already have an account? Sign in!",
"signup_disabled": "Signup is disabled",
"signup_error_username_taken": "Username {{username}} is already taken",
"signup_error_creation_limit_reached": "Account creation limit reached",
"signup_error_unknown": "Unknown error. Check logs for details.",
"login_title": "Sign in to your ntfy account",
"login_form_button_submit": "Sign in",
"login_link_signup": "Sign up",
"action_bar_show_menu": "Show menu",
"action_bar_logo_alt": "ntfy logo",
"action_bar_settings": "Settings",

View File

@ -21,6 +21,7 @@ import IconButton from "@mui/material/IconButton";
import {useOutletContext} from "react-router-dom";
import {formatBytes} from "../app/utils";
import accountApi, {UnauthorizedError} from "../app/AccountApi";
import {Pref, PrefGroup} from "./Pref";
const Account = () => {
if (!session.exists()) {
@ -56,9 +57,11 @@ const Basics = () => {
const Username = () => {
const { t } = useTranslation();
const { account } = useOutletContext();
const labelId = "prefUsername";
return (
<Pref title={t("account_basics_username_title")} description={t("account_basics_username_description")}>
<div>
<Pref labelId={labelId} title={t("account_basics_username_title")} description={t("account_basics_username_description")}>
<div aria-labelledby={labelId}>
{session.username()}
{account?.role === "admin"
? <>{" "}<Tooltip title={t("account_basics_username_admin_tooltip")}><span style={{cursor: "default"}}>👑</span></Tooltip></>
@ -72,6 +75,7 @@ const ChangePassword = () => {
const { t } = useTranslation();
const [dialogKey, setDialogKey] = useState(0);
const [dialogOpen, setDialogOpen] = useState(false);
const labelId = "prefChangePassword";
const handleDialogOpen = () => {
setDialogKey(prev => prev+1);
@ -97,8 +101,8 @@ const ChangePassword = () => {
};
return (
<Pref title={t("account_basics_password_title")} description={t("account_basics_password_description")}>
<div>
<Pref labelId={labelId} title={t("account_basics_password_title")} description={t("account_basics_password_description")}>
<div aria-labelledby={labelId}>
<Typography color="gray" sx={{float: "left", fontSize: "0.7rem", lineHeight: "3.5"}}></Typography>
<IconButton onClick={handleDialogOpen} aria-label={t("account_basics_password_description")}>
<EditIcon/>
@ -302,55 +306,4 @@ const DeleteAccountDialog = (props) => {
);
};
// FIXME duplicate code
const PrefGroup = (props) => {
return (
<div role="table">
{props.children}
</div>
)
};
const Pref = (props) => {
return (
<div
role="row"
style={{
display: "flex",
flexDirection: "row",
marginTop: "10px",
marginBottom: "20px",
}}
>
<div
role="cell"
aria-label={props.title}
style={{
flex: '1 0 40%',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
paddingRight: '30px'
}}
>
<div><b>{props.title}</b>{props.subtitle && <em> ({props.subtitle})</em>}</div>
{props.description && <div><em>{props.description}</em></div>}
</div>
<div
role="cell"
style={{
flex: '1 0 calc(60% - 50px)',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center'
}}
>
{props.children}
</div>
</div>
);
};
export default Account;

View File

@ -46,7 +46,7 @@ const Login = () => {
return (
<AvatarBox>
<Typography sx={{ typography: 'h6' }}>
{t("Sign in to your ntfy account")}
{t("login_title")}
</Typography>
<Box component="form" onSubmit={handleSubmit} noValidate sx={{mt: 1, maxWidth: 400}}>
<TextField
@ -54,7 +54,7 @@ const Login = () => {
required
fullWidth
id="username"
label={t("Username")}
label={t("signup_form_username")}
name="username"
value={username}
onChange={ev => setUsername(ev.target.value.trim())}
@ -65,7 +65,7 @@ const Login = () => {
required
fullWidth
name="password"
label={t("Password")}
label={t("signup_form_password")}
type="password"
id="password"
value={password}
@ -79,7 +79,7 @@ const Login = () => {
disabled={username === "" || password === ""}
sx={{mt: 2, mb: 2}}
>
{t("Sign in")}
{t("login_form_button_submit")}
</Button>
{error &&
<Box sx={{
@ -94,7 +94,7 @@ const Login = () => {
}
<Box sx={{width: "100%"}}>
{config.enableResetPassword && <div style={{float: "left"}}><NavLink to={routes.resetPassword} variant="body1">{t("Reset password")}</NavLink></div>}
{config.enableSignup && <div style={{float: "right"}}><NavLink to={routes.signup} variant="body1">{t("Sign up")}</NavLink></div>}
{config.enableSignup && <div style={{float: "right"}}><NavLink to={routes.signup} variant="body1">{t("login_link_signup")}</NavLink></div>}
</Box>
</Box>
</AvatarBox>

View File

@ -0,0 +1,50 @@
import * as React from "react";
export const PrefGroup = (props) => {
return (
<div role="table">
{props.children}
</div>
)
};
export const Pref = (props) => {
return (
<div
role="row"
style={{
display: "flex",
flexDirection: "row",
marginTop: "10px",
marginBottom: "20px",
}}
>
<div
role="cell"
id={props.labelId ?? ""}
aria-label={props.title}
style={{
flex: '1 0 40%',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
paddingRight: '30px'
}}
>
<div><b>{props.title}</b>{props.subtitle && <em> ({props.subtitle})</em>}</div>
{props.description && <div><em>{props.description}</em></div>}
</div>
<div
role="cell"
style={{
flex: '1 0 calc(60% - 50px)',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center'
}}
>
{props.children}
</div>
</div>
);
};

View File

@ -37,6 +37,7 @@ import {useTranslation} from "react-i18next";
import session from "../app/Session";
import routes from "./routes";
import accountApi, {UnauthorizedError} from "../app/AccountApi";
import {Pref, PrefGroup} from "./Pref";
const Preferences = () => {
return (
@ -191,55 +192,6 @@ const DeleteAfter = () => {
)
};
const PrefGroup = (props) => {
return (
<div role="table">
{props.children}
</div>
)
};
const Pref = (props) => {
return (
<div
role="row"
style={{
display: "flex",
flexDirection: "row",
marginTop: "10px",
marginBottom: "20px",
}}
>
<div
role="cell"
id={props.labelId}
aria-label={props.title}
style={{
flex: '1 0 40%',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
paddingRight: '30px'
}}
>
<div><b>{props.title}</b></div>
{props.description && <div><em>{props.description}</em></div>}
</div>
<div
role="cell"
style={{
flex: '1 0 calc(60% - 50px)',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center'
}}
>
{props.children}
</div>
</div>
);
};
const Users = () => {
const { t } = useTranslation();
const [dialogKey, setDialogKey] = useState(0);

View File

@ -30,27 +30,27 @@ const Signup = () => {
} catch (e) {
console.log(`[Signup] Signup for user ${user.username} failed`, e);
if ((e instanceof UsernameTakenError)) {
setError(t("Username {{username}} is already taken", { username: e.username }));
setError(t("signup_error_username_taken", { username: e.username }));
} else if ((e instanceof AccountCreateLimitReachedError)) {
setError(t("Account creation limit reached"));
setError(t("signup_error_creation_limit_reached"));
} else if (e.message) {
setError(e.message);
} else {
setError(t("Unknown error. Check logs for details."))
setError(t("signup_error_unknown"))
}
}
};
if (!config.enableSignup) {
return (
<AvatarBox>
<Typography sx={{ typography: 'h6' }}>{t("Signup is disabled")}</Typography>
<Typography sx={{ typography: 'h6' }}>{t("signup_disabled")}</Typography>
</AvatarBox>
);
}
return (
<AvatarBox>
<Typography sx={{ typography: 'h6' }}>
{t("Create a ntfy account")}
{t("signup_title")}
</Typography>
<Box component="form" onSubmit={handleSubmit} noValidate sx={{mt: 1, maxWidth: 400}}>
<TextField
@ -58,7 +58,7 @@ const Signup = () => {
required
fullWidth
id="username"
label="Username"
label={t("signup_form_username")}
name="username"
value={username}
onChange={ev => setUsername(ev.target.value.trim())}
@ -69,7 +69,7 @@ const Signup = () => {
required
fullWidth
name="password"
label="Password"
label={t("signup_form_password")}
type="password"
id="password"
autoComplete="current-password"
@ -81,7 +81,7 @@ const Signup = () => {
required
fullWidth
name="confirm-password"
label="Confirm password"
label={t("signup_form_confirm_password")}
type="password"
id="confirm-password"
value={confirm}
@ -95,7 +95,7 @@ const Signup = () => {
disabled={username === "" || password === "" || password !== confirm}
sx={{mt: 2, mb: 2}}
>
{t("Sign up")}
{t("signup_form_button_submit")}
</Button>
{error &&
<Box sx={{
@ -112,7 +112,7 @@ const Signup = () => {
{config.enableLogin &&
<Typography sx={{mb: 4}}>
<NavLink to={routes.login} variant="body1">
{t("Already have an account? Sign in!")}
{t("signup_already_have_account")}
</NavLink>
</Typography>
}