mirror of
https://github.com/binwiederhier/ntfy.git
synced 2024-11-23 03:24:27 +03:00
Translations
This commit is contained in:
parent
9be8be49ef
commit
66cb35b5fc
@ -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:
|
||||
|
@ -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",
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
50
web/src/components/Pref.js
Normal file
50
web/src/components/Pref.js
Normal 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>
|
||||
);
|
||||
};
|
@ -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);
|
||||
|
@ -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>
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user