diff --git a/web/src/app/utils.js b/web/src/app/utils.js index 244d3321..39e7a3b2 100644 --- a/web/src/app/utils.js +++ b/web/src/app/utils.js @@ -130,13 +130,14 @@ export const hashCode = (s) => { return hash; }; -export const formatShortDateTime = (timestamp) => - new Intl.DateTimeFormat("default", { +export const formatShortDateTime = (timestamp, language) => + new Intl.DateTimeFormat(language, { dateStyle: "short", timeStyle: "short", }).format(new Date(timestamp * 1000)); -export const formatShortDate = (timestamp) => new Intl.DateTimeFormat("default", { dateStyle: "short" }).format(new Date(timestamp * 1000)); +export const formatShortDate = (timestamp, language) => + new Intl.DateTimeFormat(language, { dateStyle: "short" }).format(new Date(timestamp * 1000)); export const formatBytes = (bytes, decimals = 2) => { if (bytes === 0) return "0 bytes"; diff --git a/web/src/components/Account.jsx b/web/src/components/Account.jsx index 541a008d..319353df 100644 --- a/web/src/components/Account.jsx +++ b/web/src/components/Account.jsx @@ -39,7 +39,6 @@ import EditIcon from "@mui/icons-material/Edit"; import { Trans, useTranslation } from "react-i18next"; import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline"; import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined"; -import i18n from "i18next"; import humanizeDuration from "humanize-duration"; import CelebrationIcon from "@mui/icons-material/Celebration"; import CloseIcon from "@mui/icons-material/Close"; @@ -224,7 +223,7 @@ const ChangePasswordDialog = (props) => { }; const AccountType = () => { - const { t } = useTranslation(); + const { t, i18n } = useTranslation(); const { account } = useContext(AccountContext); const [upgradeDialogKey, setUpgradeDialogKey] = useState(0); const [upgradeDialogOpen, setUpgradeDialogOpen] = useState(false); @@ -283,7 +282,7 @@ const AccountType = () => { {account.billing?.paid_until && !account.billing?.cancel_at && ( @@ -328,7 +327,7 @@ const AccountType = () => { {account.billing?.cancel_at > 0 && ( {t("account_basics_tier_canceled_subscription", { - date: formatShortDate(account.billing.cancel_at), + date: formatShortDate(account.billing.cancel_at, i18n.language), })} )} @@ -556,7 +555,7 @@ const AddPhoneNumberDialog = (props) => { }; const Stats = () => { - const { t } = useTranslation(); + const { t, i18n } = useTranslation(); const { account } = useContext(AccountContext); if (!account) { @@ -798,7 +797,7 @@ const Tokens = () => { }; const TokensTable = (props) => { - const { t } = useTranslation(); + const { t, i18n } = useTranslation(); const [snackOpen, setSnackOpen] = useState(false); const [upsertDialogKey, setUpsertDialogKey] = useState(0); const [upsertDialogOpen, setUpsertDialogOpen] = useState(false); @@ -872,11 +871,11 @@ const TokensTable = (props) => { {token.token !== session.token() && (token.label || "-")} - {token.expires ? formatShortDateTime(token.expires) : {t("account_tokens_table_never_expires")}} + {token.expires ? formatShortDateTime(token.expires, i18n.language) : {t("account_tokens_table_never_expires")}}
- {formatShortDateTime(token.last_access)} + {formatShortDateTime(token.last_access, i18n.language)} { }; const NotificationItem = (props) => { - const { t } = useTranslation(); + const { t, i18n } = useTranslation(); const { notification } = props; const { attachment } = notification; - const date = formatShortDateTime(notification.time); + const date = formatShortDateTime(notification.time, i18n.language); const otherTags = unmatchedTags(notification.tags); const tags = otherTags.length > 0 ? otherTags.join(", ") : null; const handleDelete = async () => { @@ -277,7 +277,7 @@ const NotificationItem = (props) => { }; const Attachment = (props) => { - const { t } = useTranslation(); + const { t, i18n } = useTranslation(); const { attachment } = props; const expired = attachment.expires && attachment.expires < Date.now() / 1000; const expires = attachment.expires && attachment.expires > Date.now() / 1000; @@ -296,7 +296,7 @@ const Attachment = (props) => { if (expires) { infos.push( t("notifications_attachment_link_expires", { - date: formatShortDateTime(attachment.expires), + date: formatShortDateTime(attachment.expires, i18n.language), }) ); } diff --git a/web/src/components/SubscriptionPopup.jsx b/web/src/components/SubscriptionPopup.jsx index 17b12504..1a6a689c 100644 --- a/web/src/components/SubscriptionPopup.jsx +++ b/web/src/components/SubscriptionPopup.jsx @@ -117,10 +117,16 @@ export const SubscriptionPopup = (props) => { ])[0]; const nowSeconds = Math.round(Date.now() / 1000); const message = shuffle([ - `Hello friend, this is a test notification from ntfy web. It's ${formatShortDateTime(nowSeconds)} right now. Is that early or late?`, + `Hello friend, this is a test notification from ntfy web. It's ${formatShortDateTime( + nowSeconds, + "en-US" + )} right now. Is that early or late?`, `So I heard you like ntfy? If that's true, go to GitHub and star it, or to the Play store and rate it. Thanks! Oh yeah, this is a test notification.`, `It's almost like you want to hear what I have to say. I'm not even a machine. I'm just a sentence that Phil typed on a random Thursday.`, - `Alright then, it's ${formatShortDateTime(nowSeconds)} already. Boy oh boy, where did the time go? I hope you're alright, friend.`, + `Alright then, it's ${formatShortDateTime( + nowSeconds, + "en-US" + )} already. Boy oh boy, where did the time go? I hope you're alright, friend.`, `There are nine million bicycles in Beijing That's a fact; It's a thing we can't deny. I wonder if that's true ...`, `I'm really excited that you're trying out ntfy. Did you know that there are a few public topics, such as ntfy.sh/stats and ntfy.sh/announcements.`, `It's interesting to hear what people use ntfy for. I've heard people talk about using it for so many cool things. What do you use it for?`, diff --git a/web/src/components/UpgradeDialog.jsx b/web/src/components/UpgradeDialog.jsx index 4bf0244d..712c47ec 100644 --- a/web/src/components/UpgradeDialog.jsx +++ b/web/src/components/UpgradeDialog.jsx @@ -62,7 +62,7 @@ const Banner = { const UpgradeDialog = (props) => { const theme = useTheme(); - const { t } = useTranslation(); + const { t, i18n } = useTranslation(); const { account } = useContext(AccountContext); // May be undefined! const [error, setError] = useState(""); const [tiers, setTiers] = useState(null); @@ -233,7 +233,7 @@ const UpgradeDialog = (props) => {