feat: localization (#196)
* Started adding locale translations :) * Added some more translations * Working on translating even more pages * More translations * Added test default locale retrieval * replace `intl.formatMessage` with custom `t` hook * add more translations * improve title syntax * add more translations * translate admin config page * translated error messages * add language selecter * minor fixes * improve language handling * add upcoming languages * add `crowdin.yml` * run formatter --------- Co-authored-by: Steve Tautonico <stautonico@gmail.com>
This commit is contained in:
@@ -14,18 +14,22 @@ import {
|
||||
import { useForm, yupResolver } from "@mantine/form";
|
||||
import { useModals } from "@mantine/modals";
|
||||
import { Tb2Fa } from "react-icons/tb";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import * as yup from "yup";
|
||||
import showEnableTotpModal from "../../components/account/showEnableTotpModal";
|
||||
import ThemeSwitcher from "../../components/account/ThemeSwitcher";
|
||||
import Meta from "../../components/Meta";
|
||||
import ThemeSwitcher from "../../components/account/ThemeSwitcher";
|
||||
import showEnableTotpModal from "../../components/account/showEnableTotpModal";
|
||||
import useTranslate from "../../hooks/useTranslate.hook";
|
||||
import useUser from "../../hooks/user.hook";
|
||||
import authService from "../../services/auth.service";
|
||||
import userService from "../../services/user.service";
|
||||
import toast from "../../utils/toast.util";
|
||||
import LanguagePicker from "../../components/account/LanguagePicker";
|
||||
|
||||
const Account = () => {
|
||||
const { user, refreshUser } = useUser();
|
||||
const modals = useModals();
|
||||
const t = useTranslate();
|
||||
|
||||
const accountForm = useForm({
|
||||
initialValues: {
|
||||
@@ -34,8 +38,10 @@ const Account = () => {
|
||||
},
|
||||
validate: yupResolver(
|
||||
yup.object().shape({
|
||||
email: yup.string().email(),
|
||||
username: yup.string().min(3),
|
||||
email: yup.string().email(t("common.error.invalid-email")),
|
||||
username: yup
|
||||
.string()
|
||||
.min(3, t("common.error.too-short", { length: 3 })),
|
||||
})
|
||||
),
|
||||
});
|
||||
@@ -47,8 +53,14 @@ const Account = () => {
|
||||
},
|
||||
validate: yupResolver(
|
||||
yup.object().shape({
|
||||
oldPassword: yup.string().min(8),
|
||||
password: yup.string().min(8),
|
||||
oldPassword: yup
|
||||
.string()
|
||||
.min(8, t("common.error.too-short", { length: 8 }))
|
||||
.required(t("common.error.field-required")),
|
||||
password: yup
|
||||
.string()
|
||||
.min(8, t("common.error.too-short", { length: 8 }))
|
||||
.required(t("common.error.field-required")),
|
||||
})
|
||||
),
|
||||
});
|
||||
@@ -59,7 +71,10 @@ const Account = () => {
|
||||
},
|
||||
validate: yupResolver(
|
||||
yup.object().shape({
|
||||
password: yup.string().min(8),
|
||||
password: yup
|
||||
.string()
|
||||
.min(8, t("common.error.too-short", { length: 8 }))
|
||||
.required(t("common.error.field-required")),
|
||||
})
|
||||
),
|
||||
});
|
||||
@@ -74,23 +89,23 @@ const Account = () => {
|
||||
password: yup.string().min(8),
|
||||
code: yup
|
||||
.string()
|
||||
.min(6)
|
||||
.max(6)
|
||||
.matches(/^[0-9]+$/, { message: "Code must be a number" }),
|
||||
.min(6, t("common.error.exact-length", { length: 6 }))
|
||||
.max(6, t("common.error.exact-length", { length: 6 }))
|
||||
.matches(/^[0-9]+$/, { message: t("common.error.invalid-number") }),
|
||||
})
|
||||
),
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<Meta title="My account" />
|
||||
<Meta title={t("account.title")} />
|
||||
<Container size="sm">
|
||||
<Title order={3} mb="xs">
|
||||
My account
|
||||
<FormattedMessage id="account.title" />
|
||||
</Title>
|
||||
<Paper withBorder p="xl">
|
||||
<Title order={5} mb="xs">
|
||||
Account Info
|
||||
<FormattedMessage id="account.card.info.title" />
|
||||
</Title>
|
||||
<form
|
||||
onSubmit={accountForm.onSubmit((values) =>
|
||||
@@ -99,35 +114,37 @@ const Account = () => {
|
||||
username: values.username,
|
||||
email: values.email,
|
||||
})
|
||||
.then(() => toast.success("User updated successfully"))
|
||||
.then(() => toast.success(t("account.notify.info.success")))
|
||||
.catch(toast.axiosError)
|
||||
)}
|
||||
>
|
||||
<Stack>
|
||||
<TextInput
|
||||
label="Username"
|
||||
label={t("account.card.info.username")}
|
||||
{...accountForm.getInputProps("username")}
|
||||
/>
|
||||
<TextInput
|
||||
label="Email"
|
||||
label={t("account.card.info.email")}
|
||||
{...accountForm.getInputProps("email")}
|
||||
/>
|
||||
<Group position="right">
|
||||
<Button type="submit">Save</Button>
|
||||
<Button type="submit">
|
||||
<FormattedMessage id="common.button.save" />
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</form>
|
||||
</Paper>
|
||||
<Paper withBorder p="xl" mt="lg">
|
||||
<Title order={5} mb="xs">
|
||||
Password
|
||||
<FormattedMessage id="account.card.password.title" />
|
||||
</Title>
|
||||
<form
|
||||
onSubmit={passwordForm.onSubmit((values) =>
|
||||
authService
|
||||
.updatePassword(values.oldPassword, values.password)
|
||||
.then(() => {
|
||||
toast.success("Password updated successfully");
|
||||
toast.success(t("account.notify.password.success"));
|
||||
passwordForm.reset();
|
||||
})
|
||||
.catch(toast.axiosError)
|
||||
@@ -135,15 +152,17 @@ const Account = () => {
|
||||
>
|
||||
<Stack>
|
||||
<PasswordInput
|
||||
label="Old password"
|
||||
label={t("account.card.password.old")}
|
||||
{...passwordForm.getInputProps("oldPassword")}
|
||||
/>
|
||||
<PasswordInput
|
||||
label="New password"
|
||||
label={t("account.card.password.new")}
|
||||
{...passwordForm.getInputProps("password")}
|
||||
/>
|
||||
<Group position="right">
|
||||
<Button type="submit">Save</Button>
|
||||
<Button type="submit">
|
||||
<FormattedMessage id="common.button.save" />
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</form>
|
||||
@@ -151,7 +170,7 @@ const Account = () => {
|
||||
|
||||
<Paper withBorder p="xl" mt="lg">
|
||||
<Title order={5} mb="xs">
|
||||
Security
|
||||
<FormattedMessage id="account.card.security.title" />
|
||||
</Title>
|
||||
|
||||
<Tabs defaultValue="totp">
|
||||
@@ -169,7 +188,7 @@ const Account = () => {
|
||||
authService
|
||||
.disableTOTP(values.code, values.password)
|
||||
.then(() => {
|
||||
toast.success("Successfully disabled TOTP");
|
||||
toast.success(t("account.notify.totp.disable"));
|
||||
values.password = "";
|
||||
values.code = "";
|
||||
refreshUser();
|
||||
@@ -179,21 +198,23 @@ const Account = () => {
|
||||
>
|
||||
<Stack>
|
||||
<PasswordInput
|
||||
description="Enter your current password to disable TOTP"
|
||||
label="Password"
|
||||
description={t(
|
||||
"account.card.security.totp.disable.description"
|
||||
)}
|
||||
label={t("account.card.password.title")}
|
||||
{...disableTotpForm.getInputProps("password")}
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
variant="filled"
|
||||
label="Code"
|
||||
label={t("account.modal.totp.code")}
|
||||
placeholder="******"
|
||||
{...disableTotpForm.getInputProps("code")}
|
||||
/>
|
||||
|
||||
<Group position="right">
|
||||
<Button color="red" type="submit">
|
||||
Disable
|
||||
<FormattedMessage id="common.button.disable" />
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
@@ -218,12 +239,16 @@ const Account = () => {
|
||||
>
|
||||
<Stack>
|
||||
<PasswordInput
|
||||
label="Password"
|
||||
description="Enter your current password to start enabling TOTP"
|
||||
label={t("account.card.password.title")}
|
||||
description={t(
|
||||
"account.card.security.totp.enable.description"
|
||||
)}
|
||||
{...enableTotpForm.getInputProps("password")}
|
||||
/>
|
||||
<Group position="right">
|
||||
<Button type="submit">Start</Button>
|
||||
<Button type="submit">
|
||||
<FormattedMessage id="account.card.security.totp.button.start" />
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</form>
|
||||
@@ -234,7 +259,13 @@ const Account = () => {
|
||||
</Paper>
|
||||
<Paper withBorder p="xl" mt="lg">
|
||||
<Title order={5} mb="xs">
|
||||
Color scheme
|
||||
<FormattedMessage id="account.card.language.title" />
|
||||
</Title>
|
||||
<LanguagePicker />
|
||||
</Paper>
|
||||
<Paper withBorder p="xl" mt="lg">
|
||||
<Title order={5} mb="xs">
|
||||
<FormattedMessage id="account.card.color.title" />
|
||||
</Title>
|
||||
<ThemeSwitcher />
|
||||
</Paper>
|
||||
@@ -245,15 +276,17 @@ const Account = () => {
|
||||
color="red"
|
||||
onClick={() =>
|
||||
modals.openConfirmModal({
|
||||
title: "Account deletion",
|
||||
title: t("account.modal.delete.title"),
|
||||
children: (
|
||||
<Text size="sm">
|
||||
Do you really want to delete your account including all
|
||||
your active shares?
|
||||
<FormattedMessage id="account.modal.delete.description" />
|
||||
</Text>
|
||||
),
|
||||
|
||||
labels: { confirm: "Delete", cancel: "Cancel" },
|
||||
labels: {
|
||||
confirm: t("common.button.delete"),
|
||||
cancel: t("common.button.cancel"),
|
||||
},
|
||||
confirmProps: { color: "red" },
|
||||
onConfirm: async () => {
|
||||
await userService.removeCurrentUser();
|
||||
@@ -262,7 +295,7 @@ const Account = () => {
|
||||
})
|
||||
}
|
||||
>
|
||||
Delete Account
|
||||
<FormattedMessage id="account.button.delete" />
|
||||
</Button>
|
||||
</Stack>
|
||||
</Center>
|
||||
|
||||
Reference in New Issue
Block a user