diff --git a/web/public/static/langs/de.json b/web/public/static/langs/de.json
new file mode 100644
index 0000000..ef6b3a1
--- /dev/null
+++ b/web/public/static/langs/de.json
@@ -0,0 +1,3 @@
+{
+ "nav_button_subscribe": "Thema abonnieren"
+}
diff --git a/web/src/components/App.js b/web/src/components/App.js
index 25e4983..d7a251f 100644
--- a/web/src/components/App.js
+++ b/web/src/components/App.js
@@ -22,6 +22,7 @@ import {useAutoSubscribe, useBackgroundProcesses, useConnectionListeners} from "
import SendDialog from "./SendDialog";
import Messaging from "./Messaging";
import "./i18n"; // Translations!
+import {Backdrop, CircularProgress} from "@mui/material";
// TODO races when two tabs are open
// TODO investigate service workers
@@ -48,12 +49,6 @@ const App = () => {
);
}
-const Loader = () => (
-
-);
-
const AllSubscriptions = () => {
const { subscriptions } = useOutletContext();
return ;
@@ -132,6 +127,18 @@ const Main = (props) => {
);
};
+const Loader = () => (
+ theme.palette.mode === 'light' ? theme.palette.grey[100] : theme.palette.grey[900]
+ }}
+ >
+
+
+);
+
const updateTitle = (newNotificationsCount) => {
document.title = (newNotificationsCount > 0) ? `(${newNotificationsCount}) ntfy` : "ntfy";
}
diff --git a/web/src/components/Preferences.js b/web/src/components/Preferences.js
index ef4b7fc..ab1406a 100644
--- a/web/src/components/Preferences.js
+++ b/web/src/components/Preferences.js
@@ -33,6 +33,7 @@ import DialogContent from "@mui/material/DialogContent";
import DialogActions from "@mui/material/DialogActions";
import userManager from "../app/UserManager";
import {playSound} from "../app/utils";
+import {useTranslation} from "react-i18next";
const Preferences = () => {
return (
@@ -40,6 +41,7 @@ const Preferences = () => {
+
);
@@ -60,7 +62,6 @@ const Notifications = () => {
);
};
-
const Sound = () => {
const sound = useLiveQuery(async () => prefs.sound());
const handleChange = async (ev) => {
@@ -362,4 +363,31 @@ const UserDialog = (props) => {
);
};
+const Appearance = () => {
+ return (
+
+
+ Appearance
+
+
+
+
+
+ );
+};
+
+const Language = () => {
+ const { t, i18n } = useTranslation();
+ return (
+
+
+
+
+
+ )
+};
+
export default Preferences;
diff --git a/web/src/components/i18n.js b/web/src/components/i18n.js
index 7400a99..42eb572 100644
--- a/web/src/components/i18n.js
+++ b/web/src/components/i18n.js
@@ -3,10 +3,13 @@ import Backend from 'i18next-http-backend';
import LanguageDetector from 'i18next-browser-languagedetector';
import { initReactI18next } from 'react-i18next';
-// init i18next
-// for all options read: https://www.i18next.com/overview/configuration-options
-// learn more: https://github.com/i18next/i18next-browser-languageDetector
-// learn more: https://github.com/i18next/i18next-http-backend
+// Translations using i18next
+// - Options: https://www.i18next.com/overview/configuration-options
+// - Browser Language Detector: https://github.com/i18next/i18next-browser-languageDetector
+// - HTTP Backend (load files via fetch): https://github.com/i18next/i18next-http-backend
+//
+// See example project here:
+// https://github.com/i18next/react-i18next/tree/master/example/react
i18n
.use(Backend)
diff --git a/web/src/index.js b/web/src/index.js
index e439fe5..659bcb8 100644
--- a/web/src/index.js
+++ b/web/src/index.js
@@ -1,8 +1,6 @@
import * as React from 'react';
-import ReactDOM from 'react-dom';
+import { createRoot } from 'react-dom/client';
import App from './components/App';
-ReactDOM.render(
- ,
- document.querySelector('#root')
-);
+const root = createRoot(document.querySelector('#root'));
+root.render();