diff --git a/web/public/static/langs/en.json b/web/public/static/langs/en.json
index 78c9c00b..ba226435 100644
--- a/web/public/static/langs/en.json
+++ b/web/public/static/langs/en.json
@@ -1,4 +1,10 @@
{
+ "action_bar_settings": "Settings",
+ "action_bar_send_test_notification": "Send test notification",
+ "action_bar_clear_notifications": "Clear all notifications",
+ "action_bar_unsubscribe": "Unsubscribe",
+ "message_bar_type_message": "Type a message here",
+ "message_bar_error_publishing": "Error publishing message",
"nav_topics_title": "Subscribed topics",
"nav_button_all_notifications": "All notifications",
"nav_button_settings": "Settings",
@@ -31,5 +37,103 @@
"notifications_example": "Example",
"notifications_more_details": "For more information, check out the website or documentation.",
"notifications_loading": "Loading notifications ...",
- "emoji_picker_search_placeholder": "Search emoji"
+ "publish_dialog_title_topic": "Publish to {{topic}}",
+ "publish_dialog_title_no_topic": "Publish message",
+ "publish_dialog_progress_uploading": "Uploading ...",
+ "publish_dialog_progress_uploading_detail": "Uploading {{loaded}}/{{total}} ({{percent}}%) ...",
+ "publish_dialog_message_published": "Message published",
+ "publish_dialog_attachment_limits_file_and_quota_reached": "exceeds {{fileSizeLimit}} file limit and quota, {{remainingBytes}} remaining",
+ "publish_dialog_attachment_limits_file_reached": "exceeds {{fileSizeLimit}} file limit",
+ "publish_dialog_attachment_limits_quota_reached": "exceeds quota,{{remainingBytes}} remaining",
+ "publish_dialog_priority_min": "Min. priority",
+ "publish_dialog_priority_low": "Low priority",
+ "publish_dialog_priority_default": "Default priority",
+ "publish_dialog_priority_high": "High priority",
+ "publish_dialog_priority_max": "Max. priority",
+ "publish_dialog_base_url_label": "Server URL",
+ "publish_dialog_base_url_placeholder": "Server URL, e.g. https://example.com",
+ "publish_dialog_topic_label": "Topic name",
+ "publish_dialog_topic_placeholder": "Topic name, e.g. phil_alerts",
+ "publish_dialog_title_label": "Title",
+ "publish_dialog_title_placeholder": "Notification title, e.g. Disk space alert",
+ "publish_dialog_message_label": "Message",
+ "publish_dialog_message_placeholder": "Type a message here",
+ "publish_dialog_tags_label": "Tags",
+ "publish_dialog_tags_placeholder": "Comma-separated list of tags, e.g. warning, srv1-backup",
+ "publish_dialog_priority_label": "Priority",
+ "publish_dialog_click_label": "Click URL",
+ "publish_dialog_click_placeholder": "URL that is opened when notification is clicked",
+ "publish_dialog_email_label": "Email",
+ "publish_dialog_email_placeholder": "Address to forward the message to, e.g. phil@example.com",
+ "publish_dialog_attach_label": "Attachment URL",
+ "publish_dialog_attach_placeholder": "Attach file by URL, e.g. https://f-droid.org/F-Droid.apk",
+ "publish_dialog_filename_label": "Filename",
+ "publish_dialog_filename_placeholder": "Attachment filename",
+ "publish_dialog_delay_label": "Delay",
+ "publish_dialog_delay_placeholder": "Delay delivery, e.g. 1649029748, 30m, or tomorrow, 9am",
+ "publish_dialog_other_features": "Other features:",
+ "publish_dialog_chip_click_label": "Click URL",
+ "publish_dialog_chip_email_label": "Forward to email",
+ "publish_dialog_chip_attach_url_label": "Attach file by URL",
+ "publish_dialog_chip_attach_file_label": "Attach local file",
+ "publish_dialog_chip_delay_label": "Delay delivery",
+ "publish_dialog_chip_topic_label": "Change topic",
+ "publish_dialog_details_examples_description": "For examples and a detailed description of all send features, please refer to the documentation.",
+ "publish_dialog_button_cancel_sending": "Cancel sending",
+ "publish_dialog_button_cancel": "Cancel",
+ "publish_dialog_button_send": "Send",
+ "publish_dialog_checkbox_publish_another": "Publish another",
+ "publish_dialog_attached_file_title": "Attached file:",
+ "publish_dialog_attached_file_filename_placeholder": "Attachment filename",
+ "publish_dialog_drop_file_here": "Drop file here",
+ "emoji_picker_search_placeholder": "Search emoji",
+ "subscribe_dialog_subscribe_title": "Subscribe to topic",
+ "subscribe_dialog_subscribe_description": "Topics may not be password-protected, so choose a name that's not easy to guess. Once subscribed, you can PUT/POST notifications.",
+ "subscribe_dialog_subscribe_topic_placeholder": "Topic name, e.g. phil_alerts",
+ "subscribe_dialog_subscribe_use_another_label": "Use another server",
+ "subscribe_dialog_subscribe_button_cancel": "Cancel",
+ "subscribe_dialog_subscribe_button_subscribe": "Subscribe",
+ "subscribe_dialog_login_title": "Login required",
+ "subscribe_dialog_login_description": "This topic is password-protected. Please enter username and password to subscribe.",
+ "subscribe_dialog_login_username_label": "Username, e.g. phil",
+ "subscribe_dialog_login_password_label": "Password",
+ "subscribe_dialog_login_button_back": "Back",
+ "subscribe_dialog_login_button_login": "Login",
+ "subscribe_dialog_error_user_not_authorized": "User {{username}} not authorized",
+ "subscribe_dialog_error_user_anonymous": "anonymous",
+ "prefs_notifications_title": "Notifications",
+ "prefs_notifications_sound_title": "Notification sound",
+ "prefs_notifications_sound_no_sound": "No sound",
+ "prefs_notifications_min_priority_title": "Minimum priority",
+ "prefs_notifications_min_priority_any": "Any priority",
+ "prefs_notifications_min_priority_low_and_higher": "Low priority and higher",
+ "prefs_notifications_min_priority_default_and_higher": "Default priority and higher",
+ "prefs_notifications_min_priority_high_and_higher": "High priority and higher",
+ "prefs_notifications_min_priority_max_only": "Only max priority",
+ "prefs_notifications_delete_after_title": "Delete notifications",
+ "prefs_notifications_delete_after_never": "Never",
+ "prefs_notifications_delete_after_three_hours": "After three hours",
+ "prefs_notifications_delete_after_one_day": "After one day",
+ "prefs_notifications_delete_after_one_week": "After one week",
+ "prefs_notifications_delete_after_one_month": "After one month",
+ "prefs_users_title": "Manage users",
+ "prefs_users_description": "Add/remove users for your protected topics here. Please note that username and password are stored in the browser's local storage.",
+ "prefs_users_add_button": "Add user",
+ "prefs_users_table_user_header": "User",
+ "prefs_users_table_base_url_header": "Service URL",
+ "prefs_users_dialog_title_add": "Add user",
+ "prefs_users_dialog_title_edit": "Edit user",
+ "prefs_users_dialog_base_url_label": "Service URL, e.g. https://ntfy.sh",
+ "prefs_users_dialog_username_label": "Username, e.g. phil",
+ "prefs_users_dialog_password_label": "Password",
+ "prefs_users_dialog_button_cancel": "Cancel",
+ "prefs_users_dialog_button_add": "Add",
+ "prefs_users_dialog_button_save": "Save",
+ "prefs_appearance_title": "Appearance",
+ "prefs_appearance_language_title": "Language",
+ "error_boundary_title": "Oh no, ntfy crashed",
+ "error_boundary_description": "This should obviously not happen. Very sorry about this. If you have a minute, please report this on GitHub, or let us know via Discord or Matrix.",
+ "error_boundary_button_copy_stack_trace": "Copy stack trace",
+ "error_boundary_stack_trace": "Stack trace",
+ "error_boundary_gathering_info": "Gather more info ..."
}
diff --git a/web/src/app/Connection.js b/web/src/app/Connection.js
index 55778023..fbc54cc0 100644
--- a/web/src/app/Connection.js
+++ b/web/src/app/Connection.js
@@ -71,7 +71,7 @@ class Connection {
this.onStateChanged(this.subscriptionId, ConnectionState.Connecting);
}
};
- this.ws.onerror = (event) => {
+ this.ws.onerrgoogle.ccor = (event) => {
console.log(`[Connection, ${this.shortUrl}, ${this.connectionId}] Error occurred: ${event}`, event);
};
}
diff --git a/web/src/components/ActionBar.js b/web/src/components/ActionBar.js
index bb10b1c4..7e3ce44b 100644
--- a/web/src/components/ActionBar.js
+++ b/web/src/components/ActionBar.js
@@ -22,14 +22,16 @@ import api from "../app/Api";
import routes from "./routes";
import subscriptionManager from "../app/SubscriptionManager";
import logo from "../img/ntfy.svg";
+import {useTranslation} from "react-i18next";
const ActionBar = (props) => {
+ const { t } = useTranslation();
const location = useLocation();
let title = "ntfy";
if (props.selected) {
title = topicShortUrl(props.selected.baseUrl, props.selected.topic);
} else if (location.pathname === "/settings") {
- title = "Settings";
+ title = t("action_bar_settings");
}
return (
{
// Originally from https://mui.com/components/menus/#MenuListComposition.js
const SettingsIcons = (props) => {
+ const { t } = useTranslation();
const navigate = useNavigate();
const [open, setOpen] = useState(false);
const anchorRef = useRef(null);
@@ -189,9 +192,9 @@ const SettingsIcons = (props) => {
-
-
-
+
+
+
diff --git a/web/src/components/App.js b/web/src/components/App.js
index d7a251f1..1ecf878c 100644
--- a/web/src/components/App.js
+++ b/web/src/components/App.js
@@ -19,7 +19,7 @@ import {expandUrl} from "../app/utils";
import ErrorBoundary from "./ErrorBoundary";
import routes from "./routes";
import {useAutoSubscribe, useBackgroundProcesses, useConnectionListeners} from "./hooks";
-import SendDialog from "./SendDialog";
+import PublishDialog from "./PublishDialog";
import Messaging from "./Messaging";
import "./i18n"; // Translations!
import {Backdrop, CircularProgress} from "@mui/material";
@@ -91,7 +91,7 @@ const Layout = () => {
mobileDrawerOpen={mobileDrawerOpen}
onMobileDrawerToggle={() => setMobileDrawerOpen(!mobileDrawerOpen)}
onNotificationGranted={setNotificationsGranted}
- onPublishMessageClick={() => setSendDialogOpenMode(SendDialog.OPEN_MODE_DEFAULT)}
+ onPublishMessageClick={() => setSendDialogOpenMode(PublishDialog.OPEN_MODE_DEFAULT)}
/>
diff --git a/web/src/components/ErrorBoundary.js b/web/src/components/ErrorBoundary.js
index d309f4b0..0a3393c5 100644
--- a/web/src/components/ErrorBoundary.js
+++ b/web/src/components/ErrorBoundary.js
@@ -1,9 +1,10 @@
import * as React from "react";
import StackTrace from "stacktrace-js";
-import {CircularProgress} from "@mui/material";
+import {CircularProgress, Link} from "@mui/material";
import Button from "@mui/material/Button";
+import {Trans, withTranslation} from "react-i18next";
-class ErrorBoundary extends React.Component {
+class ErrorBoundaryImpl extends React.Component {
constructor(props) {
super(props);
this.state = {
@@ -45,22 +46,28 @@ class ErrorBoundary extends React.Component {
}
render() {
+ const { t } = this.props;
if (this.state.error) {
return (
-
Oh no, ntfy crashed 😮
+
{t("error_boundary_title")} 😮
- This should obviously not happen. Very sorry about this.
- If you have a minute, please report this on GitHub, or let us
- know via Discord or Matrix.
+ ,
+ discordLink: ,
+ matrixLink:
+ }}
+ />
-
+
-
Stack trace
+
{t("error_boundary_stack_trace")}
{this.state.niceStack
?
{this.state.niceStack}
- : <> Gather more info ...>}
+ : <> {t("error_boundary_gathering_info")}>}