diff --git a/interface/src/SignIn.tsx b/interface/src/SignIn.tsx
index dc67ad09c..9d5d47c51 100644
--- a/interface/src/SignIn.tsx
+++ b/interface/src/SignIn.tsx
@@ -58,7 +58,7 @@ const SignIn: FC = () => {
enqueueSnackbar(LL.INVALID_LOGIN(), { variant: 'warning' });
}
} else {
- enqueueSnackbar(extractErrorMessage(error, 'Unexpected error, please try again'), { variant: 'error' });
+ enqueueSnackbar(extractErrorMessage(error, LL.ERROR()), { variant: 'error' });
}
setProcessing(false);
}
diff --git a/interface/src/components/layout/LayoutMenu.tsx b/interface/src/components/layout/LayoutMenu.tsx
index 502f78f0d..b2d049e71 100644
--- a/interface/src/components/layout/LayoutMenu.tsx
+++ b/interface/src/components/layout/LayoutMenu.tsx
@@ -35,8 +35,13 @@ const LayoutMenu: FC = () => {
{features.ntp && }
{features.mqtt && }
-
-
+
+
>
);
diff --git a/interface/src/components/upload/SingleUpload.tsx b/interface/src/components/upload/SingleUpload.tsx
index acdc15071..574f4dae0 100644
--- a/interface/src/components/upload/SingleUpload.tsx
+++ b/interface/src/components/upload/SingleUpload.tsx
@@ -6,6 +6,8 @@ import { Box, Button, LinearProgress, Theme, Typography, useTheme } from '@mui/m
import CloudUploadIcon from '@mui/icons-material/CloudUpload';
import CancelIcon from '@mui/icons-material/Cancel';
+import { useI18nContext } from '../../i18n/i18n-react';
+
const progressPercentage = (progress: ProgressEvent) => Math.round((progress.loaded * 100) / progress.total);
const getBorderColor = (theme: Theme, props: DropzoneState) => {
@@ -41,14 +43,16 @@ const SingleUpload: FC = ({ onDrop, onCancel, uploading, prog
const { getRootProps, getInputProps } = dropzoneState;
const theme = useTheme();
+ const { LL } = useI18nContext();
+
const progressText = () => {
if (uploading) {
if (progress?.lengthComputable) {
- return `Uploading: ${progressPercentage(progress)}%`;
+ return LL.UPLOADING() + `: ${progressPercentage(progress)}%`;
}
- return 'Uploading\u2026';
+ return LL.UPLOADING() + `\u2026`;
}
- return 'Drop file or click here';
+ return LL.UPLOAD_DROP_TEXT();
};
return (
@@ -81,7 +85,7 @@ const SingleUpload: FC = ({ onDrop, onCancel, uploading, prog
/>
} variant="outlined" color="secondary" onClick={onCancel}>
- Cancel
+ {LL.CANCEL()}
)}
diff --git a/interface/src/framework/ntp/NTPStatusForm.tsx b/interface/src/framework/ntp/NTPStatusForm.tsx
index 51182af5f..1b9855e69 100644
--- a/interface/src/framework/ntp/NTPStatusForm.tsx
+++ b/interface/src/framework/ntp/NTPStatusForm.tsx
@@ -31,6 +31,8 @@ import { ButtonRow, FormLoader, SectionContent } from '../../components';
import { extractErrorMessage, formatDateTime, formatLocalDateTime, useRest } from '../../utils';
import { AuthenticatedContext } from '../../contexts/authentication';
+import { useI18nContext } from '../../i18n/i18n-react';
+
export const isNtpActive = ({ status }: NTPStatus) => status === NTPSyncStatus.NTP_ACTIVE;
export const isNtpEnabled = ({ status }: NTPStatus) => status !== NTPSyncStatus.NTP_DISABLED;
@@ -68,6 +70,8 @@ const NTPStatusForm: FC = () => {
const { enqueueSnackbar } = useSnackbar();
const { me } = useContext(AuthenticatedContext);
+ const { LL } = useI18nContext();
+
const updateLocalTime = (event: React.ChangeEvent) => setLocalTime(event.target.value);
const openSetTime = () => {
@@ -83,11 +87,11 @@ const NTPStatusForm: FC = () => {
await NTPApi.updateTime({
local_time: formatLocalDateTime(new Date(localTime))
});
- enqueueSnackbar('Time set', { variant: 'success' });
+ enqueueSnackbar(LL.TIME_SET(), { variant: 'success' });
setSettingTime(false);
loadData();
} catch (error: unknown) {
- enqueueSnackbar(extractErrorMessage(error, 'Problem updating time'), { variant: 'error' });
+ enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_UPDATING()), { variant: 'error' });
} finally {
setProcessing(false);
}
diff --git a/interface/src/framework/system/GeneralFileUpload.tsx b/interface/src/framework/system/GeneralFileUpload.tsx
index aa41d1e91..8da4835ef 100644
--- a/interface/src/framework/system/GeneralFileUpload.tsx
+++ b/interface/src/framework/system/GeneralFileUpload.tsx
@@ -14,6 +14,8 @@ import { extractErrorMessage } from '../../utils';
import * as EMSESP from '../../project/api';
+import { useI18nContext } from '../../i18n/i18n-react';
+
interface UploadFileProps {
uploadGeneralFile: (file: File, config?: FileUploadConfig) => AxiosPromise;
}
@@ -23,6 +25,8 @@ const GeneralFileUpload: FC = ({ uploadGeneralFile }) => {
const { enqueueSnackbar } = useSnackbar();
+ const { LL } = useI18nContext();
+
const saveFile = (json: any, endpoint: string) => {
const a = document.createElement('a');
const filename = 'emsesp_' + endpoint + '.json';
@@ -35,19 +39,19 @@ const GeneralFileUpload: FC = ({ uploadGeneralFile }) => {
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
- enqueueSnackbar('File downloaded', { variant: 'info' });
+ enqueueSnackbar(LL.DOWNLOAD_SUCCESSFUL(), { variant: 'info' });
};
const downloadSettings = async () => {
try {
const response = await EMSESP.getSettings();
if (response.status !== 200) {
- enqueueSnackbar('Unable to get settings', { variant: 'error' });
+ enqueueSnackbar(LL.PROBLEM_LOADING(), { variant: 'error' });
} else {
saveFile(response.data, 'settings');
}
} catch (error: unknown) {
- enqueueSnackbar(extractErrorMessage(error, 'Problem with downloading'), { variant: 'error' });
+ enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_LOADING()), { variant: 'error' });
}
};
@@ -55,47 +59,43 @@ const GeneralFileUpload: FC = ({ uploadGeneralFile }) => {
try {
const response = await EMSESP.getCustomizations();
if (response.status !== 200) {
- enqueueSnackbar('Unable to get customizations', { variant: 'error' });
+ enqueueSnackbar(LL.PROBLEM_LOADING(), { variant: 'error' });
} else {
saveFile(response.data, 'customizations');
}
} catch (error: unknown) {
- enqueueSnackbar(extractErrorMessage(error, 'Problem with downloading'), { variant: 'error' });
+ enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_LOADING()), { variant: 'error' });
}
};
return (
<>
- Upload
+ {LL.UPLOAD()}
{!uploading && (
-
- Upload a new firmware (.bin) file, settings or customizations (.json) file below
-
+ {LL.UPLOAD_TEXT()}
)}
- Download
+ {LL.DOWNLOAD()}
{!uploading && (
<>
- Download the application settings. Be careful when sharing your settings as this file contains passwords
- and other sensitive system information
+ {LL.DOWNLOAD_SETTINGS_TEXT()}
} variant="outlined" color="primary" onClick={() => downloadSettings()}>
- settings
+ {LL.SETTINGS()}
-
- Download the entity customizations
+ {LL.DOWNLOAD_CUSTOMIZATION_TEXT()}{' '}
>
)}
diff --git a/interface/src/framework/system/OTASettingsForm.tsx b/interface/src/framework/system/OTASettingsForm.tsx
index cec00f8d2..a35ecc741 100644
--- a/interface/src/framework/system/OTASettingsForm.tsx
+++ b/interface/src/framework/system/OTASettingsForm.tsx
@@ -12,6 +12,7 @@ import {
ValidatedPasswordField,
ValidatedTextField
} from '../../components';
+
import { OTASettings } from '../../types';
import { numberValue, updateValue, useRest } from '../../utils';
@@ -19,12 +20,16 @@ import { ValidateFieldsError } from 'async-validator';
import { validate } from '../../validators';
import { OTA_SETTINGS_VALIDATOR } from '../../validators/system';
+import { useI18nContext } from '../../i18n/i18n-react';
+
const OTASettingsForm: FC = () => {
const { loadData, saving, data, setData, saveData, errorMessage } = useRest({
read: SystemApi.readOTASettings,
update: SystemApi.updateOTASettings
});
+ const { LL } = useI18nContext();
+
const updateFormValue = updateValue(setData);
const [fieldErrors, setFieldErrors] = useState();
@@ -48,7 +53,7 @@ const OTASettingsForm: FC = () => {
<>
}
- label="Enable OTA Updates"
+ label={LL.ENABLE_OTA()}
/>
{
{
type="submit"
onClick={validateAndSubmit}
>
- Save
+ {LL.SAVE()}
>
@@ -88,7 +93,7 @@ const OTASettingsForm: FC = () => {
};
return (
-
+
{content()}
);
diff --git a/interface/src/framework/system/RestartMonitor.tsx b/interface/src/framework/system/RestartMonitor.tsx
index 3e832f302..c8eb8cb42 100644
--- a/interface/src/framework/system/RestartMonitor.tsx
+++ b/interface/src/framework/system/RestartMonitor.tsx
@@ -4,6 +4,8 @@ import { FC, useRef, useState } from 'react';
import * as SystemApi from '../../api/system';
import { FormLoader } from '../../components';
+import { useI18nContext } from '../../i18n/i18n-react';
+
const RESTART_TIMEOUT = 2 * 60 * 1000;
const POLL_TIMEOUT = 2000;
const POLL_INTERVAL = 5000;
@@ -12,6 +14,8 @@ const RestartMonitor: FC = () => {
const [failed, setFailed] = useState(false);
const [timeoutId, setTimeoutId] = useState();
+ const { LL } = useI18nContext();
+
const timeoutAt = useRef(new Date().getTime() + RESTART_TIMEOUT);
const poll = useRef(async () => {
try {
@@ -32,12 +36,7 @@ const RestartMonitor: FC = () => {
useEffect(() => () => timeoutId && clearTimeout(timeoutId), [timeoutId]);
- return (
-
- );
+ return ;
};
export default RestartMonitor;
diff --git a/interface/src/framework/system/System.tsx b/interface/src/framework/system/System.tsx
index e8e38e391..3d77ebd06 100644
--- a/interface/src/framework/system/System.tsx
+++ b/interface/src/framework/system/System.tsx
@@ -12,8 +12,13 @@ import OTASettingsForm from './OTASettingsForm';
import SystemLog from './SystemLog';
+import { useI18nContext } from '../../i18n/i18n-react';
+
const System: FC = () => {
- useLayoutTitle('System');
+
+ const { LL } = useI18nContext();
+
+ useLayoutTitle(LL.SYSTEM());
const { me } = useContext(AuthenticatedContext);
const { features } = useContext(FeaturesContext);
@@ -22,11 +27,11 @@ const System: FC = () => {
return (
<>
-
-
+
+
- {features.ota && }
- {features.upload_firmware && }
+ {features.ota && }
+ {features.upload_firmware && }
} />
diff --git a/interface/src/framework/system/SystemLog.tsx b/interface/src/framework/system/SystemLog.tsx
index 9d5413aa6..dff0c3f1d 100644
--- a/interface/src/framework/system/SystemLog.tsx
+++ b/interface/src/framework/system/SystemLog.tsx
@@ -15,6 +15,9 @@ import DownloadIcon from '@mui/icons-material/GetApp';
import { useSnackbar } from 'notistack';
import { EVENT_SOURCE_ROOT } from '../../api/endpoints';
+
+import { useI18nContext } from '../../i18n/i18n-react';
+
export const LOG_EVENTSOURCE_URL = EVENT_SOURCE_ROOT + 'log';
const useWindowSize = () => {
@@ -63,6 +66,8 @@ const levelLabel = (level: LogLevel) => {
const SystemLog: FC = () => {
useWindowSize();
+ const { LL } = useI18nContext();
+
const { loadData, data, setData } = useRest({
read: SystemApi.readLogSettings
});
@@ -104,10 +109,10 @@ const SystemLog: FC = () => {
compact: data.compact
});
if (response.status !== 200) {
- enqueueSnackbar('Problem applying log settings', { variant: 'error' });
+ enqueueSnackbar(LL.PROBLEM_UPDATING(), { variant: 'error' });
}
} catch (error: unknown) {
- enqueueSnackbar(extractErrorMessage(error, 'Problem applying log settings'), { variant: 'error' });
+ enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_UPDATING()), { variant: 'error' });
}
}
};
@@ -159,9 +164,9 @@ const SystemLog: FC = () => {
try {
setLogEntries((await SystemApi.readLogEntries()).data);
} catch (error: unknown) {
- setErrorMessage(extractErrorMessage(error, 'Failed to fetch log'));
+ setErrorMessage(extractErrorMessage(error, LL.PROBLEM_LOADING()));
}
- }, []);
+ }, [LL]);
useEffect(() => {
fetchLog();
@@ -214,7 +219,7 @@ const SystemLog: FC = () => {
- Buffer size
+ {LL.BUFFER_SIZE()}
{
}
- label="Compact"
+ label={LL.COMPACT()}
/>
} variant="outlined" color="secondary" onClick={onDownload}>
- Export
+ {LL.EXPORT()}
@@ -273,7 +278,7 @@ const SystemLog: FC = () => {
};
return (
-
+
{content()}
);
diff --git a/interface/src/framework/system/SystemStatusForm.tsx b/interface/src/framework/system/SystemStatusForm.tsx
index 52c1a3a4b..ffd889fa9 100644
--- a/interface/src/framework/system/SystemStatusForm.tsx
+++ b/interface/src/framework/system/SystemStatusForm.tsx
@@ -39,6 +39,8 @@ import { AuthenticatedContext } from '../../contexts/authentication';
import axios from 'axios';
+import { useI18nContext } from '../../i18n/i18n-react';
+
export const VERSIONCHECK_ENDPOINT = 'https://api.github.com/repos/emsesp/EMS-ESP32/releases/latest';
export const VERSIONCHECK_DEV_ENDPOINT = 'https://api.github.com/repos/emsesp/EMS-ESP32/releases/tags/latest';
export const uploadURL = window.location.origin + '/system/upload';
@@ -48,6 +50,8 @@ function formatNumber(num: number) {
}
const SystemStatusForm: FC = () => {
+ const { LL } = useI18nContext();
+
const { loadData, data, errorMessage } = useRest({ read: SystemApi.readSystemStatus });
const { me } = useContext(AuthenticatedContext);
@@ -80,9 +84,9 @@ const SystemStatusForm: FC = () => {
setProcessing(true);
try {
await SystemApi.restart();
- enqueueSnackbar('EMS-ESP is restarting...', { variant: 'info' });
+ enqueueSnackbar(LL.APPLICATION_RESTARTING(), { variant: 'info' });
} catch (error: unknown) {
- enqueueSnackbar(extractErrorMessage(error, 'Problem restarting device'), { variant: 'error' });
+ enqueueSnackbar(extractErrorMessage(error, LL.PROBLEM_LOADING()), { variant: 'error' });
} finally {
setConfirmRestart(false);
setProcessing(false);
@@ -92,7 +96,7 @@ const SystemStatusForm: FC = () => {
const renderRestartDialog = () => (