aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAnton Tananaev <anton@traccar.org>2024-04-06 09:22:10 -0700
committerAnton Tananaev <anton@traccar.org>2024-04-06 09:22:10 -0700
commitf418231b6b2f5e030a0d2dcc390c314602b1f740 (patch)
tree10326adf3792bc2697e06bb5f2b8ef2a8f7e55fe /src
parentb392a4af78e01c8e0f50aad5468e9583675b24be (diff)
downloadtrackermap-web-f418231b6b2f5e030a0d2dcc390c314602b1f740.tar.gz
trackermap-web-f418231b6b2f5e030a0d2dcc390c314602b1f740.tar.bz2
trackermap-web-f418231b6b2f5e030a0d2dcc390c314602b1f740.zip
Move modern to the top
Diffstat (limited to 'src')
-rw-r--r--src/App.jsx65
-rw-r--r--src/AppThemeProvider.jsx22
-rw-r--r--src/CachingController.js70
-rw-r--r--src/ErrorBoundary.jsx35
-rw-r--r--src/Navigation.jsx172
-rw-r--r--src/ServerProvider.jsx51
-rw-r--r--src/SocketController.jsx140
-rw-r--r--src/UpdateController.jsx58
-rw-r--r--src/common/attributes/useCommandAttributes.js213
-rw-r--r--src/common/attributes/useCommonDeviceAttributes.js21
-rw-r--r--src/common/attributes/useCommonUserAttributes.js136
-rw-r--r--src/common/attributes/useDeviceAttributes.js33
-rw-r--r--src/common/attributes/useGeofenceAttributes.js18
-rw-r--r--src/common/attributes/useGroupAttributes.js12
-rw-r--r--src/common/attributes/usePositionAttributes.js380
-rw-r--r--src/common/attributes/useServerAttributes.js62
-rw-r--r--src/common/attributes/useUserAttributes.js60
-rw-r--r--src/common/components/AddressValue.jsx37
-rw-r--r--src/common/components/BottomMenu.jsx135
-rw-r--r--src/common/components/DriverValue.js9
-rw-r--r--src/common/components/ErrorHandler.jsx27
-rw-r--r--src/common/components/GeofencesValue.js9
-rw-r--r--src/common/components/LinkField.jsx93
-rw-r--r--src/common/components/LocalizationProvider.jsx187
-rw-r--r--src/common/components/NativeInterface.js72
-rw-r--r--src/common/components/NavBar.jsx25
-rw-r--r--src/common/components/PageLayout.jsx118
-rw-r--r--src/common/components/PositionValue.jsx133
-rw-r--r--src/common/components/RemoveDialog.jsx54
-rw-r--r--src/common/components/SelectField.jsx77
-rw-r--r--src/common/components/SideNav.jsx33
-rw-r--r--src/common/components/SplitButton.jsx48
-rw-r--r--src/common/components/StatusCard.jsx288
-rw-r--r--src/common/components/TableShimmer.jsx17
-rw-r--r--src/common/theme/components.js40
-rw-r--r--src/common/theme/dimensions.js14
-rw-r--r--src/common/theme/index.js11
-rw-r--r--src/common/theme/palette.js22
-rw-r--r--src/common/util/converter.js107
-rw-r--r--src/common/util/deviceCategories.js24
-rw-r--r--src/common/util/duration.js2
-rw-r--r--src/common/util/formatter.js143
-rw-r--r--src/common/util/permissions.js28
-rw-r--r--src/common/util/preferences.js41
-rw-r--r--src/common/util/stringUtils.js3
-rw-r--r--src/common/util/useFeatures.js44
-rw-r--r--src/common/util/usePersistedState.js22
-rw-r--r--src/common/util/useQuery.js7
-rw-r--r--src/index.jsx42
-rw-r--r--src/login/ChangeServerPage.jsx83
-rw-r--r--src/login/LoginLayout.jsx62
-rw-r--r--src/login/LoginPage.jsx263
-rw-r--r--src/login/LogoImage.jsx36
-rw-r--r--src/login/RegisterPage.jsx148
-rw-r--r--src/login/ResetPasswordPage.jsx118
-rw-r--r--src/main/DeviceList.jsx66
-rw-r--r--src/main/DeviceRow.jsx145
-rw-r--r--src/main/EventsDrawer.jsx81
-rw-r--r--src/main/MainMap.jsx66
-rw-r--r--src/main/MainPage.jsx160
-rw-r--r--src/main/MainToolbar.jsx178
-rw-r--r--src/main/useFilter.js46
-rw-r--r--src/map/MapCamera.js32
-rw-r--r--src/map/MapCurrentLocation.js21
-rw-r--r--src/map/MapGeofence.js94
-rw-r--r--src/map/MapMarkers.js89
-rw-r--r--src/map/MapPadding.js20
-rw-r--r--src/map/MapPositions.js216
-rw-r--r--src/map/MapRoutePath.js100
-rw-r--r--src/map/MapRoutePoints.js77
-rw-r--r--src/map/MapScale.js34
-rw-r--r--src/map/core/MapView.jsx123
-rw-r--r--src/map/core/mapUtil.js105
-rw-r--r--src/map/core/preloadImages.js78
-rw-r--r--src/map/core/useMapStyles.js259
-rw-r--r--src/map/draw/MapGeofenceEdit.js161
-rw-r--r--src/map/draw/theme.js230
-rw-r--r--src/map/geocoder/MapGeocoder.js56
-rw-r--r--src/map/geocoder/geocoder.css223
-rw-r--r--src/map/main/MapAccuracy.js56
-rw-r--r--src/map/main/MapDefaultCamera.js52
-rw-r--r--src/map/main/MapLiveRoutes.js83
-rw-r--r--src/map/main/MapSelectedDevice.js31
-rw-r--r--src/map/main/PoiMap.js87
-rw-r--r--src/map/notification/MapNotification.js49
-rw-r--r--src/map/notification/notification.css13
-rw-r--r--src/map/overlay/MapOverlay.js39
-rw-r--r--src/map/overlay/useMapOverlays.js103
-rw-r--r--src/map/switcher/switcher.css34
-rw-r--r--src/map/switcher/switcher.js123
-rw-r--r--src/other/EventPage.jsx108
-rw-r--r--src/other/GeofencesList.jsx54
-rw-r--r--src/other/GeofencesPage.jsx142
-rw-r--r--src/other/NetworkPage.jsx122
-rw-r--r--src/other/PositionPage.jsx110
-rw-r--r--src/other/ReplayPage.jsx233
-rw-r--r--src/reactHelper.js40
-rw-r--r--src/reports/ChartReportPage.jsx152
-rw-r--r--src/reports/CombinedReportPage.jsx105
-rw-r--r--src/reports/EventReportPage.jsx232
-rw-r--r--src/reports/LogsPage.jsx84
-rw-r--r--src/reports/RouteReportPage.jsx173
-rw-r--r--src/reports/ScheduledPage.jsx106
-rw-r--r--src/reports/StatisticsPage.jsx85
-rw-r--r--src/reports/StopReportPage.jsx172
-rw-r--r--src/reports/SummaryReportPage.jsx152
-rw-r--r--src/reports/TripReportPage.jsx216
-rw-r--r--src/reports/common/scheduleReport.js26
-rw-r--r--src/reports/common/useReportStyles.js49
-rw-r--r--src/reports/components/ColumnSelect.jsx34
-rw-r--r--src/reports/components/ReportFilter.jsx215
-rw-r--r--src/reports/components/ReportsMenu.jsx116
-rw-r--r--src/resources/alarm.mp3bin0 -> 4642 bytes
-rw-r--r--src/resources/images/arrow.svg4
-rw-r--r--src/resources/images/background.svg10
-rw-r--r--src/resources/images/data/engine.svg4
-rw-r--r--src/resources/images/direction.svg4
-rw-r--r--src/resources/images/icon/animal.svg2
-rw-r--r--src/resources/images/icon/bicycle.svg2
-rw-r--r--src/resources/images/icon/boat.svg2
-rw-r--r--src/resources/images/icon/bus.svg2
-rw-r--r--src/resources/images/icon/camper.svg2
-rw-r--r--src/resources/images/icon/car.svg2
-rw-r--r--src/resources/images/icon/crane.svg2
-rw-r--r--src/resources/images/icon/default.svg2
-rw-r--r--src/resources/images/icon/helicopter.svg2
-rw-r--r--src/resources/images/icon/motorcycle.svg2
-rw-r--r--src/resources/images/icon/offroad.svg2
-rw-r--r--src/resources/images/icon/person.svg2
-rw-r--r--src/resources/images/icon/pickup.svg2
-rw-r--r--src/resources/images/icon/plane.svg2
-rw-r--r--src/resources/images/icon/scooter.svg2
-rw-r--r--src/resources/images/icon/ship.svg2
-rw-r--r--src/resources/images/icon/tractor.svg2
-rw-r--r--src/resources/images/icon/train.svg2
-rw-r--r--src/resources/images/icon/tram.svg2
-rw-r--r--src/resources/images/icon/trolleybus.svg2
-rw-r--r--src/resources/images/icon/truck.svg2
-rw-r--r--src/resources/images/icon/van.svg2
-rw-r--r--src/resources/images/logo.svg23
-rw-r--r--src/resources/l10n/af.json600
-rw-r--r--src/resources/l10n/ar.json600
-rw-r--r--src/resources/l10n/az.json600
-rw-r--r--src/resources/l10n/bg.json600
-rw-r--r--src/resources/l10n/bn.json600
-rw-r--r--src/resources/l10n/ca.json600
-rw-r--r--src/resources/l10n/cs.json600
-rw-r--r--src/resources/l10n/da.json600
-rw-r--r--src/resources/l10n/de.json600
-rw-r--r--src/resources/l10n/el.json600
-rw-r--r--src/resources/l10n/en.json600
-rw-r--r--src/resources/l10n/es.json600
-rw-r--r--src/resources/l10n/fa.json600
-rw-r--r--src/resources/l10n/fi.json600
-rw-r--r--src/resources/l10n/fr.json600
-rw-r--r--src/resources/l10n/gl.json600
-rw-r--r--src/resources/l10n/he.json600
-rw-r--r--src/resources/l10n/hi.json600
-rw-r--r--src/resources/l10n/hr.json600
-rw-r--r--src/resources/l10n/hu.json600
-rw-r--r--src/resources/l10n/id.json600
-rw-r--r--src/resources/l10n/it.json600
-rw-r--r--src/resources/l10n/ja.json600
-rw-r--r--src/resources/l10n/ka.json600
-rw-r--r--src/resources/l10n/kk.json600
-rw-r--r--src/resources/l10n/km.json600
-rw-r--r--src/resources/l10n/ko.json600
-rw-r--r--src/resources/l10n/lo.json600
-rw-r--r--src/resources/l10n/lt.json600
-rw-r--r--src/resources/l10n/lv.json600
-rw-r--r--src/resources/l10n/mk.json600
-rw-r--r--src/resources/l10n/ml.json600
-rw-r--r--src/resources/l10n/mn.json600
-rw-r--r--src/resources/l10n/ms.json600
-rw-r--r--src/resources/l10n/nb.json600
-rw-r--r--src/resources/l10n/ne.json600
-rw-r--r--src/resources/l10n/nl.json600
-rw-r--r--src/resources/l10n/nn.json600
-rw-r--r--src/resources/l10n/pl.json600
-rw-r--r--src/resources/l10n/pt.json600
-rw-r--r--src/resources/l10n/pt_BR.json600
-rw-r--r--src/resources/l10n/ro.json600
-rw-r--r--src/resources/l10n/ru.json600
-rw-r--r--src/resources/l10n/si.json600
-rw-r--r--src/resources/l10n/sk.json600
-rw-r--r--src/resources/l10n/sl.json600
-rw-r--r--src/resources/l10n/sq.json600
-rw-r--r--src/resources/l10n/sr.json600
-rw-r--r--src/resources/l10n/sv.json600
-rw-r--r--src/resources/l10n/ta.json600
-rw-r--r--src/resources/l10n/th.json600
-rw-r--r--src/resources/l10n/tr.json600
-rw-r--r--src/resources/l10n/uk.json600
-rw-r--r--src/resources/l10n/uz.json600
-rw-r--r--src/resources/l10n/vi.json600
-rw-r--r--src/resources/l10n/zh.json600
-rw-r--r--src/resources/l10n/zh_TW.json600
-rw-r--r--src/settings/AccumulatorsPage.jsx107
-rw-r--r--src/settings/AnnouncementPage.jsx106
-rw-r--r--src/settings/CalendarPage.jsx208
-rw-r--r--src/settings/CalendarsPage.jsx64
-rw-r--r--src/settings/CommandDevicePage.jsx111
-rw-r--r--src/settings/CommandGroupPage.jsx105
-rw-r--r--src/settings/CommandPage.jsx50
-rw-r--r--src/settings/CommandsPage.jsx74
-rw-r--r--src/settings/ComputedAttributePage.jsx177
-rw-r--r--src/settings/ComputedAttributesPage.jsx74
-rw-r--r--src/settings/DeviceConnectionsPage.jsx107
-rw-r--r--src/settings/DevicePage.jsx176
-rw-r--r--src/settings/DevicesPage.jsx114
-rw-r--r--src/settings/DriverPage.jsx62
-rw-r--r--src/settings/DriversPage.jsx66
-rw-r--r--src/settings/GeofencePage.jsx88
-rw-r--r--src/settings/GroupConnectionsPage.jsx107
-rw-r--r--src/settings/GroupPage.jsx93
-rw-r--r--src/settings/GroupsPage.jsx91
-rw-r--r--src/settings/MaintenancePage.jsx174
-rw-r--r--src/settings/MaintenancesPage.jsx100
-rw-r--r--src/settings/NotificationPage.jsx144
-rw-r--r--src/settings/NotificationsPage.jsx83
-rw-r--r--src/settings/PreferencesPage.jsx375
-rw-r--r--src/settings/ServerPage.jsx316
-rw-r--r--src/settings/SharePage.jsx109
-rw-r--r--src/settings/UserConnectionsPage.jsx129
-rw-r--r--src/settings/UserPage.jsx428
-rw-r--r--src/settings/UsersPage.jsx130
-rw-r--r--src/settings/common/useSettingsStyles.js33
-rw-r--r--src/settings/components/AddAttributeDialog.jsx104
-rw-r--r--src/settings/components/BaseCommandView.jsx79
-rw-r--r--src/settings/components/CollectionActions.jsx104
-rw-r--r--src/settings/components/CollectionFab.jsx35
-rw-r--r--src/settings/components/EditAttributesAccordion.jsx217
-rw-r--r--src/settings/components/EditItemView.jsx101
-rw-r--r--src/settings/components/SearchHeader.jsx38
-rw-r--r--src/settings/components/SettingsMenu.jsx173
-rw-r--r--src/store/calendars.js17
-rw-r--r--src/store/devices.js36
-rw-r--r--src/store/drivers.js17
-rw-r--r--src/store/errors.js21
-rw-r--r--src/store/events.js23
-rw-r--r--src/store/geofences.js20
-rw-r--r--src/store/groups.js17
-rw-r--r--src/store/index.js42
-rw-r--r--src/store/maintenances.js17
-rw-r--r--src/store/reports.js29
-rw-r--r--src/store/session.js53
-rw-r--r--src/store/throttleMiddleware.js36
247 files changed, 49695 insertions, 0 deletions
diff --git a/src/App.jsx b/src/App.jsx
new file mode 100644
index 00000000..4fe34f64
--- /dev/null
+++ b/src/App.jsx
@@ -0,0 +1,65 @@
+import React from 'react';
+import { Outlet, useNavigate } from 'react-router-dom';
+import { useDispatch, useSelector } from 'react-redux';
+import { LinearProgress, useMediaQuery, useTheme } from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+import BottomMenu from './common/components/BottomMenu';
+import SocketController from './SocketController';
+import CachingController from './CachingController';
+import { useEffectAsync } from './reactHelper';
+import { sessionActions } from './store';
+import UpdateController from './UpdateController';
+
+const useStyles = makeStyles(() => ({
+ page: {
+ flexGrow: 1,
+ overflow: 'auto',
+ },
+ menu: {
+ zIndex: 4,
+ },
+}));
+
+const App = () => {
+ const classes = useStyles();
+ const theme = useTheme();
+ const dispatch = useDispatch();
+ const navigate = useNavigate();
+
+ const desktop = useMediaQuery(theme.breakpoints.up('md'));
+
+ const newServer = useSelector((state) => state.session.server.newServer);
+ const initialized = useSelector((state) => !!state.session.user);
+
+ useEffectAsync(async () => {
+ if (!initialized) {
+ const response = await fetch('/api/session');
+ if (response.ok) {
+ dispatch(sessionActions.updateUser(await response.json()));
+ } else if (newServer) {
+ navigate('/register');
+ } else {
+ navigate('/login');
+ }
+ }
+ return null;
+ }, [initialized]);
+
+ return !initialized ? (<LinearProgress />) : (
+ <>
+ <SocketController />
+ <CachingController />
+ <UpdateController />
+ <div className={classes.page}>
+ <Outlet />
+ </div>
+ {!desktop && (
+ <div className={classes.menu}>
+ <BottomMenu />
+ </div>
+ )}
+ </>
+ );
+};
+
+export default App;
diff --git a/src/AppThemeProvider.jsx b/src/AppThemeProvider.jsx
new file mode 100644
index 00000000..109d25c4
--- /dev/null
+++ b/src/AppThemeProvider.jsx
@@ -0,0 +1,22 @@
+import React from 'react';
+import { useSelector } from 'react-redux';
+import { ThemeProvider, useMediaQuery } from '@mui/material';
+import theme from './common/theme';
+
+const AppThemeProvider = ({ children }) => {
+ const server = useSelector((state) => state.session.server);
+
+ const serverDarkMode = server?.attributes?.darkMode;
+ const preferDarkMode = useMediaQuery('(prefers-color-scheme: dark)');
+ const darkMode = serverDarkMode !== undefined ? serverDarkMode : preferDarkMode;
+
+ const themeInstance = theme(server, darkMode);
+
+ return (
+ <ThemeProvider theme={themeInstance}>
+ {children}
+ </ThemeProvider>
+ );
+};
+
+export default AppThemeProvider;
diff --git a/src/CachingController.js b/src/CachingController.js
new file mode 100644
index 00000000..b8e5fd90
--- /dev/null
+++ b/src/CachingController.js
@@ -0,0 +1,70 @@
+import { useDispatch, useSelector, connect } from 'react-redux';
+
+import {
+ geofencesActions, groupsActions, driversActions, maintenancesActions, calendarsActions,
+} from './store';
+import { useEffectAsync } from './reactHelper';
+
+const CachingController = () => {
+ const authenticated = useSelector((state) => !!state.session.user);
+ const dispatch = useDispatch();
+
+ useEffectAsync(async () => {
+ if (authenticated) {
+ const response = await fetch('/api/geofences');
+ if (response.ok) {
+ dispatch(geofencesActions.refresh(await response.json()));
+ } else {
+ throw Error(await response.text());
+ }
+ }
+ }, [authenticated]);
+
+ useEffectAsync(async () => {
+ if (authenticated) {
+ const response = await fetch('/api/groups');
+ if (response.ok) {
+ dispatch(groupsActions.refresh(await response.json()));
+ } else {
+ throw Error(await response.text());
+ }
+ }
+ }, [authenticated]);
+
+ useEffectAsync(async () => {
+ if (authenticated) {
+ const response = await fetch('/api/drivers');
+ if (response.ok) {
+ dispatch(driversActions.refresh(await response.json()));
+ } else {
+ throw Error(await response.text());
+ }
+ }
+ }, [authenticated]);
+
+ useEffectAsync(async () => {
+ if (authenticated) {
+ const response = await fetch('/api/maintenance');
+ if (response.ok) {
+ dispatch(maintenancesActions.refresh(await response.json()));
+ } else {
+ throw Error(await response.text());
+ }
+ }
+ }, [authenticated]);
+
+ useEffectAsync(async () => {
+ if (authenticated) {
+ const response = await fetch('/api/calendars');
+ if (response.ok) {
+ dispatch(calendarsActions.refresh(await response.json()));
+ } else {
+ throw Error(await response.text());
+ }
+ }
+ }, [authenticated]);
+
+ return null;
+};
+
+export default connect()(CachingController);
diff --git a/src/ErrorBoundary.jsx b/src/ErrorBoundary.jsx
new file mode 100644
index 00000000..93a6fe0f
--- /dev/null
+++ b/src/ErrorBoundary.jsx
@@ -0,0 +1,35 @@
+import React from 'react';
+import { Alert } from '@mui/material';
+
+class ErrorBoundary extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ error: null,
+ };
+ }
+
+ static getDerivedStateFromError(error) {
+ return { error };
+ }
+
+ /* eslint-disable react/no-danger */
+ render() {
+ const { error } = this.state;
+ if (error) {
+ return (
+ <Alert severity="error">
+ <code
+ dangerouslySetInnerHTML={{
+ __html: error.stack.replaceAll('\n', '<br>').replaceAll(' ', '&nbsp;'),
+ }}
+ />
+ </Alert>
+ );
+ }
+ const { children } = this.props;
+ return children;
+ }
+}
+
+export default ErrorBoundary;
diff --git a/src/Navigation.jsx b/src/Navigation.jsx
new file mode 100644
index 00000000..37a6ddb5
--- /dev/null
+++ b/src/Navigation.jsx
@@ -0,0 +1,172 @@
+import React, { useState } from 'react';
+import {
+ Route, Routes, useLocation, useNavigate,
+} from 'react-router-dom';
+import { useDispatch } from 'react-redux';
+import { LinearProgress } from '@mui/material';
+import MainPage from './main/MainPage';
+import CombinedReportPage from './reports/CombinedReportPage';
+import RouteReportPage from './reports/RouteReportPage';
+import ServerPage from './settings/ServerPage';
+import UsersPage from './settings/UsersPage';
+import DevicePage from './settings/DevicePage';
+import UserPage from './settings/UserPage';
+import NotificationsPage from './settings/NotificationsPage';
+import NotificationPage from './settings/NotificationPage';
+import GroupsPage from './settings/GroupsPage';
+import GroupPage from './settings/GroupPage';
+import PositionPage from './other/PositionPage';
+import NetworkPage from './other/NetworkPage';
+import EventReportPage from './reports/EventReportPage';
+import ReplayPage from './other/ReplayPage';
+import TripReportPage from './reports/TripReportPage';
+import StopReportPage from './reports/StopReportPage';
+import SummaryReportPage from './reports/SummaryReportPage';
+import ChartReportPage from './reports/ChartReportPage';
+import DriversPage from './settings/DriversPage';
+import DriverPage from './settings/DriverPage';
+import CalendarsPage from './settings/CalendarsPage';
+import CalendarPage from './settings/CalendarPage';
+import ComputedAttributesPage from './settings/ComputedAttributesPage';
+import ComputedAttributePage from './settings/ComputedAttributePage';
+import MaintenancesPage from './settings/MaintenancesPage';
+import MaintenancePage from './settings/MaintenancePage';
+import CommandsPage from './settings/CommandsPage';
+import CommandPage from './settings/CommandPage';
+import StatisticsPage from './reports/StatisticsPage';
+import LoginPage from './login/LoginPage';
+import RegisterPage from './login/RegisterPage';
+import ResetPasswordPage from './login/ResetPasswordPage';
+import GeofencesPage from './other/GeofencesPage';
+import GeofencePage from './settings/GeofencePage';
+import useQuery from './common/util/useQuery';
+import { useEffectAsync } from './reactHelper';
+import { devicesActions } from './store';
+import EventPage from './other/EventPage';
+import PreferencesPage from './settings/PreferencesPage';
+import AccumulatorsPage from './settings/AccumulatorsPage';
+import CommandDevicePage from './settings/CommandDevicePage';
+import CommandGroupPage from './settings/CommandGroupPage';
+import App from './App';
+import ChangeServerPage from './login/ChangeServerPage';
+import DevicesPage from './settings/DevicesPage';
+import ScheduledPage from './reports/ScheduledPage';
+import DeviceConnectionsPage from './settings/DeviceConnectionsPage';
+import GroupConnectionsPage from './settings/GroupConnectionsPage';
+import UserConnectionsPage from './settings/UserConnectionsPage';
+import LogsPage from './reports/LogsPage';
+import SharePage from './settings/SharePage';
+import AnnouncementPage from './settings/AnnouncementPage';
+
+const Navigation = () => {
+ const navigate = useNavigate();
+ const dispatch = useDispatch();
+
+ const [redirectsHandled, setRedirectsHandled] = useState(false);
+
+ const { pathname } = useLocation();
+ const query = useQuery();
+
+ useEffectAsync(async () => {
+ if (query.get('token')) {
+ const token = query.get('token');
+ await fetch(`/api/session?token=${encodeURIComponent(token)}`);
+ navigate(pathname);
+ } else if (query.get('deviceId')) {
+ const deviceId = query.get('deviceId');
+ const response = await fetch(`/api/devices?uniqueId=${deviceId}`);
+ if (response.ok) {
+ const items = await response.json();
+ if (items.length > 0) {
+ dispatch(devicesActions.selectId(items[0].id));
+ }
+ } else {
+ throw Error(await response.text());
+ }
+ navigate('/');
+ } else if (query.get('eventId')) {
+ const eventId = parseInt(query.get('eventId'), 10);
+ navigate(`/event/${eventId}`);
+ } else {
+ setRedirectsHandled(true);
+ }
+ }, [query]);
+
+ if (!redirectsHandled) {
+ return (<LinearProgress />);
+ }
+ return (
+ <Routes>
+ <Route path="/login" element={<LoginPage />} />
+ <Route path="/register" element={<RegisterPage />} />
+ <Route path="/reset-password" element={<ResetPasswordPage />} />
+ <Route path="/change-server" element={<ChangeServerPage />} />
+ <Route path="/" element={<App />}>
+ <Route index element={<MainPage />} />
+
+ <Route path="position/:id" element={<PositionPage />} />
+ <Route path="network/:positionId" element={<NetworkPage />} />
+ <Route path="event/:id" element={<EventPage />} />
+ <Route path="replay" element={<ReplayPage />} />
+ <Route path="geofences" element={<GeofencesPage />} />
+
+ <Route path="settings">
+ <Route path="accumulators/:deviceId" element={<AccumulatorsPage />} />
+ <Route path="announcement" element={<AnnouncementPage />} />
+ <Route path="calendars" element={<CalendarsPage />} />
+ <Route path="calendar/:id" element={<CalendarPage />} />
+ <Route path="calendar" element={<CalendarPage />} />
+ <Route path="commands" element={<CommandsPage />} />
+ <Route path="command/:id" element={<CommandPage />} />
+ <Route path="command" element={<CommandPage />} />
+ <Route path="attributes" element={<ComputedAttributesPage />} />
+ <Route path="attribute/:id" element={<ComputedAttributePage />} />
+ <Route path="attribute" element={<ComputedAttributePage />} />
+ <Route path="devices" element={<DevicesPage />} />
+ <Route path="device/:id/connections" element={<DeviceConnectionsPage />} />
+ <Route path="device/:id/command" element={<CommandDevicePage />} />
+ <Route path="device/:id/share" element={<SharePage />} />
+ <Route path="device/:id" element={<DevicePage />} />
+ <Route path="device" element={<DevicePage />} />
+ <Route path="drivers" element={<DriversPage />} />
+ <Route path="driver/:id" element={<DriverPage />} />
+ <Route path="driver" element={<DriverPage />} />
+ <Route path="geofence/:id" element={<GeofencePage />} />
+ <Route path="geofence" element={<GeofencePage />} />
+ <Route path="groups" element={<GroupsPage />} />
+ <Route path="group/:id/connections" element={<GroupConnectionsPage />} />
+ <Route path="group/:id/command" element={<CommandGroupPage />} />
+ <Route path="group/:id" element={<GroupPage />} />
+ <Route path="group" element={<GroupPage />} />
+ <Route path="maintenances" element={<MaintenancesPage />} />
+ <Route path="maintenance/:id" element={<MaintenancePage />} />
+ <Route path="maintenance" element={<MaintenancePage />} />
+ <Route path="notifications" element={<NotificationsPage />} />
+ <Route path="notification/:id" element={<NotificationPage />} />
+ <Route path="notification" element={<NotificationPage />} />
+ <Route path="preferences" element={<PreferencesPage />} />
+ <Route path="server" element={<ServerPage />} />
+ <Route path="users" element={<UsersPage />} />
+ <Route path="user/:id/connections" element={<UserConnectionsPage />} />
+ <Route path="user/:id" element={<UserPage />} />
+ <Route path="user" element={<UserPage />} />
+ </Route>
+
+ <Route path="reports">
+ <Route path="combined" element={<CombinedReportPage />} />
+ <Route path="chart" element={<ChartReportPage />} />
+ <Route path="event" element={<EventReportPage />} />
+ <Route path="route" element={<RouteReportPage />} />
+ <Route path="stop" element={<StopReportPage />} />
+ <Route path="summary" element={<SummaryReportPage />} />
+ <Route path="trip" element={<TripReportPage />} />
+ <Route path="scheduled" element={<ScheduledPage />} />
+ <Route path="statistics" element={<StatisticsPage />} />
+ <Route path="logs" element={<LogsPage />} />
+ </Route>
+ </Route>
+ </Routes>
+ );
+};
+
+export default Navigation;
diff --git a/src/ServerProvider.jsx b/src/ServerProvider.jsx
new file mode 100644
index 00000000..720d0418
--- /dev/null
+++ b/src/ServerProvider.jsx
@@ -0,0 +1,51 @@
+import React, { useState } from 'react';
+import { Alert, IconButton, LinearProgress } from '@mui/material';
+import ReplayIcon from '@mui/icons-material/Replay';
+import { useDispatch, useSelector } from 'react-redux';
+import { useEffectAsync } from './reactHelper';
+import { sessionActions } from './store';
+
+const ServerProvider = ({
+ children,
+}) => {
+ const dispatch = useDispatch();
+
+ const initialized = useSelector((state) => !!state.session.server);
+ const [error, setError] = useState(null);
+
+ useEffectAsync(async () => {
+ if (!error) {
+ try {
+ const response = await fetch('/api/server');
+ if (response.ok) {
+ dispatch(sessionActions.updateServer(await response.json()));
+ } else {
+ throw Error(await response.text());
+ }
+ } catch (error) {
+ setError(error.message);
+ }
+ }
+ }, [error]);
+
+ if (error) {
+ return (
+ <Alert
+ severity="error"
+ action={(
+ <IconButton color="inherit" size="small" onClick={() => setError(null)}>
+ <ReplayIcon fontSize="inherit" />
+ </IconButton>
+ )}
+ >
+ {error}
+ </Alert>
+ );
+ }
+ if (!initialized) {
+ return (<LinearProgress />);
+ }
+ return children;
+};
+
+export default ServerProvider;
diff --git a/src/SocketController.jsx b/src/SocketController.jsx
new file mode 100644
index 00000000..fe39d2b7
--- /dev/null
+++ b/src/SocketController.jsx
@@ -0,0 +1,140 @@
+import React, { useEffect, useRef, useState } from 'react';
+import { useDispatch, useSelector, connect } from 'react-redux';
+import { useNavigate } from 'react-router-dom';
+import { Snackbar } from '@mui/material';
+import { devicesActions, sessionActions } from './store';
+import { useEffectAsync } from './reactHelper';
+import { useTranslation } from './common/components/LocalizationProvider';
+import { snackBarDurationLongMs } from './common/util/duration';
+import alarm from './resources/alarm.mp3';
+import { eventsActions } from './store/events';
+import useFeatures from './common/util/useFeatures';
+import { useAttributePreference } from './common/util/preferences';
+
+const logoutCode = 4000;
+
+const SocketController = () => {
+ const dispatch = useDispatch();
+ const navigate = useNavigate();
+ const t = useTranslation();
+
+ const authenticated = useSelector((state) => !!state.session.user);
+ const devices = useSelector((state) => state.devices.items);
+ const includeLogs = useSelector((state) => state.session.includeLogs);
+
+ const socketRef = useRef();
+
+ const [events, setEvents] = useState([]);
+ const [notifications, setNotifications] = useState([]);
+
+ const soundEvents = useAttributePreference('soundEvents', '');
+ const soundAlarms = useAttributePreference('soundAlarms', 'sos');
+
+ const features = useFeatures();
+
+ const connectSocket = () => {
+ const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
+ const socket = new WebSocket(`${protocol}//${window.location.host}/api/socket`);
+ socketRef.current = socket;
+
+ socket.onopen = () => {
+ dispatch(sessionActions.updateSocket(true));
+ };
+
+ socket.onclose = async (event) => {
+ dispatch(sessionActions.updateSocket(false));
+ if (event.code !== logoutCode) {
+ try {
+ const devicesResponse = await fetch('/api/devices');
+ if (devicesResponse.ok) {
+ dispatch(devicesActions.update(await devicesResponse.json()));
+ }
+ const positionsResponse = await fetch('/api/positions');
+ if (positionsResponse.ok) {
+ dispatch(sessionActions.updatePositions(await positionsResponse.json()));
+ }
+ if (devicesResponse.status === 401 || positionsResponse.status === 401) {
+ navigate('/login');
+ }
+ } catch (error) {
+ // ignore errors
+ }
+ setTimeout(() => connectSocket(), 60000);
+ }
+ };
+
+ socket.onmessage = (event) => {
+ const data = JSON.parse(event.data);
+ if (data.devices) {
+ dispatch(devicesActions.update(data.devices));
+ }
+ if (data.positions) {
+ dispatch(sessionActions.updatePositions(data.positions));
+ }
+ if (data.events) {
+ if (!features.disableEvents) {
+ dispatch(eventsActions.add(data.events));
+ }
+ setEvents(data.events);
+ }
+ if (data.logs) {
+ dispatch(sessionActions.updateLogs(data.logs));
+ }
+ };
+ };
+
+ useEffect(() => {
+ socketRef.current?.send(JSON.stringify({ logs: includeLogs }));
+ }, [socketRef, includeLogs]);
+
+ useEffectAsync(async () => {
+ if (authenticated) {
+ const response = await fetch('/api/devices');
+ if (response.ok) {
+ dispatch(devicesActions.refresh(await response.json()));
+ } else {
+ throw Error(await response.text());
+ }
+ connectSocket();
+ return () => {
+ const socket = socketRef.current;
+ if (socket) {
+ socket.close(logoutCode);
+ }
+ };
+ }
+ return null;
+ }, [authenticated]);
+
+ useEffect(() => {
+ setNotifications(events.map((event) => ({
+ id: event.id,
+ message: event.attributes.message,
+ show: true,
+ })));
+ }, [events, devices, t]);
+
+ useEffect(() => {
+ events.forEach((event) => {
+ if (soundEvents.includes(event.type) || (event.type === 'alarm' && soundAlarms.includes(event.attributes.alarm))) {
+ new Audio(alarm).play();
+ }
+ });
+ }, [events, soundEvents, soundAlarms]);
+
+ return (
+ <>
+ {notifications.map((notification) => (
+ <Snackbar
+ key={notification.id}
+ open={notification.show}
+ message={notification.message}
+ autoHideDuration={snackBarDurationLongMs}
+ onClose={() => setEvents(events.filter((e) => e.id !== notification.id))}
+ />
+ ))}
+ </>
+ );
+};
+
+export default connect()(SocketController);
diff --git a/src/UpdateController.jsx b/src/UpdateController.jsx
new file mode 100644
index 00000000..80ca6dc2
--- /dev/null
+++ b/src/UpdateController.jsx
@@ -0,0 +1,58 @@
+import { Snackbar, IconButton } from '@mui/material';
+import RefreshIcon from '@mui/icons-material/Refresh';
+import React from 'react';
+import { useSelector } from 'react-redux';
+import { useRegisterSW } from 'virtual:pwa-register/react';
+import { useTranslation } from './common/components/LocalizationProvider';
+
+// Based on https://vite-pwa-org.netlify.app/frameworks/react.html
+const UpdateController = () => {
+ const t = useTranslation();
+
+ const swUpdateInterval = useSelector((state) => state.session.server.attributes.serviceWorkerUpdateInterval || 3600000);
+
+ const {
+ needRefresh: [needRefresh],
+ updateServiceWorker,
+ } = useRegisterSW({
+ onRegisteredSW(swUrl, swRegistration) {
+ if (swUpdateInterval > 0 && swRegistration) {
+ setInterval(async () => {
+ if (!(!swRegistration.installing && navigator)) {
+ return;
+ }
+
+ if (('connection' in navigator) && !navigator.onLine) {
+ return;
+ }
+
+ const newSW = await fetch(swUrl, {
+ cache: 'no-store',
+ headers: {
+ cache: 'no-store',
+ 'cache-control': 'no-cache',
+ },
+ });
+
+ if (newSW?.status === 200) {
+ await swRegistration.update();
+ }
+ }, swUpdateInterval);
+ }
+ },
+ });
+
+ return (
+ <Snackbar
+ open={needRefresh}
+ message={t('settingsUpdateAvailable')}
+ action={(
+ <IconButton color="inherit" onClick={() => updateServiceWorker(true)}>
+ <RefreshIcon />
+ </IconButton>
+ )}
+ />
+ );
+};
+
+export default UpdateController;
diff --git a/src/common/attributes/useCommandAttributes.js b/src/common/attributes/useCommandAttributes.js
new file mode 100644
index 00000000..189a0e2e
--- /dev/null
+++ b/src/common/attributes/useCommandAttributes.js
@@ -0,0 +1,213 @@
+import { useMemo } from 'react';
+
+export default (t) => useMemo(() => ({
+ custom: [
+ {
+ key: 'data',
+ name: t('commandData'),
+ type: 'string',
+ },
+ ],
+ positionPeriodic: [
+ {
+ key: 'frequency',
+ name: t('commandFrequency'),
+ type: 'number',
+ },
+ ],
+ setTimezone: [
+ {
+ key: 'timezone',
+ name: t('commandTimezone'),
+ type: 'string',
+ },
+ ],
+ sendSms: [
+ {
+ key: 'phone',
+ name: t('commandPhone'),
+ type: 'string',
+ },
+ {
+ key: 'message',
+ name: t('commandMessage'),
+ type: 'string',
+ },
+ ],
+ message: [
+ {
+ key: 'message',
+ name: t('commandMessage'),
+ type: 'string',
+ },
+ ],
+ sendUssd: [
+ {
+ key: 'phone',
+ name: t('commandPhone'),
+ type: 'string',
+ },
+ ],
+ sosNumber: [
+ {
+ key: 'index',
+ name: t('commandIndex'),
+ type: 'number',
+ },
+ {
+ key: 'phone',
+ name: t('commandPhone'),
+ type: 'string',
+ },
+ ],
+ silenceTime: [
+ {
+ key: 'data',
+ name: t('commandData'),
+ type: 'string',
+ },
+ ],
+ setPhonebook: [
+ {
+ key: 'data',
+ name: t('commandData'),
+ type: 'string',
+ },
+ ],
+ voiceMessage: [
+ {
+ key: 'data',
+ name: t('commandData'),
+ type: 'string',
+ },
+ ],
+ outputControl: [
+ {
+ key: 'index',
+ name: t('commandIndex'),
+ type: 'number',
+ },
+ {
+ key: 'data',
+ name: t('commandData'),
+ type: 'string',
+ },
+ ],
+ voiceMonitoring: [
+ {
+ key: 'enable',
+ name: t('commandEnable'),
+ type: 'boolean',
+ },
+ ],
+ setAgps: [
+ {
+ key: 'enable',
+ name: t('commandEnable'),
+ type: 'boolean',
+ },
+ ],
+ setIndicator: [
+ {
+ key: 'data',
+ name: t('commandData'),
+ type: 'string',
+ },
+ ],
+ configuration: [
+ {
+ key: 'data',
+ name: t('commandData'),
+ type: 'string',
+ },
+ ],
+ setConnection: [
+ {
+ key: 'server',
+ name: t('commandServer'),
+ type: 'string',
+ },
+ {
+ key: 'port',
+ name: t('commandPort'),
+ type: 'number',
+ },
+ ],
+ setOdometer: [
+ {
+ key: 'data',
+ name: t('commandData'),
+ type: 'string',
+ },
+ ],
+ modePowerSaving: [
+ {
+ key: 'enable',
+ name: t('commandEnable'),
+ type: 'boolean',
+ },
+ ],
+ modeDeepSleep: [
+ {
+ key: 'enable',
+ name: t('commandEnable'),
+ type: 'boolean',
+ },
+ ],
+ alarmGeofence: [
+ {
+ key: 'radius',
+ name: t('commandRadius'),
+ type: 'number',
+ },
+ ],
+ alarmBattery: [
+ {
+ key: 'enable',
+ name: t('commandEnable'),
+ type: 'boolean',
+ },
+ ],
+ alarmSos: [
+ {
+ key: 'enable',
+ name: t('commandEnable'),
+ type: 'boolean',
+ },
+ ],
+ alarmRemove: [
+ {
+ key: 'enable',
+ name: t('commandEnable'),
+ type: 'boolean',
+ },
+ ],
+ alarmClock: [
+ {
+ key: 'data',
+ name: t('commandData'),
+ type: 'string',
+ },
+ ],
+ alarmSpeed: [
+ {
+ key: 'data',
+ name: t('commandData'),
+ type: 'string',
+ },
+ ],
+ alarmFall: [
+ {
+ key: 'enable',
+ name: t('commandEnable'),
+ type: 'boolean',
+ },
+ ],
+ alarmVibration: [
+ {
+ key: 'data',
+ name: t('commandData'),
+ type: 'string',
+ },
+ ],
+}), [t]);
diff --git a/src/common/attributes/useCommonDeviceAttributes.js b/src/common/attributes/useCommonDeviceAttributes.js
new file mode 100644
index 00000000..f9214818
--- /dev/null
+++ b/src/common/attributes/useCommonDeviceAttributes.js
@@ -0,0 +1,21 @@
+import { useMemo } from 'react';
+
+export default (t) => useMemo(() => ({
+ speedLimit: {
+ name: t('attributeSpeedLimit'),
+ type: 'number',
+ subtype: 'speed',
+ },
+ fuelDropThreshold: {
+ name: t('attributeFuelDropThreshold'),
+ type: 'number',
+ },
+ fuelIncreaseThreshold: {
+ name: t('attributeFuelIncreaseThreshold'),
+ type: 'number',
+ },
+ 'report.ignoreOdometer': {
+ name: t('attributeReportIgnoreOdometer'),
+ type: 'boolean',
+ },
+}), [t]);
diff --git a/src/common/attributes/useCommonUserAttributes.js b/src/common/attributes/useCommonUserAttributes.js
new file mode 100644
index 00000000..294ddea8
--- /dev/null
+++ b/src/common/attributes/useCommonUserAttributes.js
@@ -0,0 +1,136 @@
+import { useMemo } from 'react';
+
+export default (t) => useMemo(() => ({
+ mapGeofences: {
+ name: t('attributeShowGeofences'),
+ type: 'boolean',
+ },
+ mapLiveRoutes: {
+ name: t('mapLiveRoutes'),
+ type: 'string',
+ },
+ mapDirection: {
+ name: t('mapDirection'),
+ type: 'string',
+ },
+ mapFollow: {
+ name: t('deviceFollow'),
+ type: 'boolean',
+ },
+ mapCluster: {
+ name: t('mapClustering'),
+ type: 'boolean',
+ },
+ mapOnSelect: {
+ name: t('mapOnSelect'),
+ type: 'boolean',
+ },
+ activeMapStyles: {
+ name: t('mapActive'),
+ type: 'string',
+ },
+ devicePrimary: {
+ name: t('devicePrimaryInfo'),
+ type: 'string',
+ },
+ deviceSecondary: {
+ name: t('deviceSecondaryInfo'),
+ type: 'string',
+ },
+ soundEvents: {
+ name: t('eventsSoundEvents'),
+ type: 'string',
+ },
+ soundAlarms: {
+ name: t('eventsSoundAlarms'),
+ type: 'string',
+ },
+ positionItems: {
+ name: t('attributePopupInfo'),
+ type: 'string',
+ },
+ locationIqKey: {
+ name: t('mapLocationIqKey'),
+ type: 'string',
+ },
+ mapboxAccessToken: {
+ name: t('mapMapboxKey'),
+ type: 'string',
+ },
+ mapTilerKey: {
+ name: t('mapMapTilerKey'),
+ type: 'string',
+ },
+ bingMapsKey: {
+ name: t('mapBingKey'),
+ type: 'string',
+ },
+ openWeatherKey: {
+ name: t('mapOpenWeatherKey'),
+ type: 'string',
+ },
+ tomTomKey: {
+ name: t('mapTomTomKey'),
+ type: 'string',
+ },
+ hereKey: {
+ name: t('mapHereKey'),
+ type: 'string',
+ },
+ notificationTokens: {
+ name: t('attributeNotificationTokens'),
+ type: 'string',
+ },
+ 'ui.disableSavedCommands': {
+ name: t('attributeUiDisableSavedCommands'),
+ type: 'boolean',
+ },
+ 'ui.disableGroups': {
+ name: t('attributeUiDisableGroups'),
+ type: 'boolean',
+ },
+ 'ui.disableAttributes': {
+ name: t('attributeUiDisableAttributes'),
+ type: 'boolean',
+ },
+ 'ui.disableEvents': {
+ name: t('attributeUiDisableEvents'),
+ type: 'boolean',
+ },
+ 'ui.disableVehicleFeatures': {
+ name: t('attributeUiDisableVehicleFeatures'),
+ type: 'boolean',
+ },
+ 'ui.disableDrivers': {
+ name: t('attributeUiDisableDrivers'),
+ type: 'boolean',
+ },
+ 'ui.disableComputedAttributes': {
+ name: t('attributeUiDisableComputedAttributes'),
+ type: 'boolean',
+ },
+ 'ui.disableCalendars': {
+ name: t('attributeUiDisableCalendars'),
+ type: 'boolean',
+ },
+ 'ui.disableMaintenance': {
+ name: t('attributeUiDisableMaintenance'),
+ type: 'boolean',
+ },
+ 'web.liveRouteLength': {
+ name: t('attributeWebLiveRouteLength'),
+ type: 'number',
+ },
+ 'web.selectZoom': {
+ name: t('attributeWebSelectZoom'),
+ type: 'number',
+ },
+ 'web.maxZoom': {
+ name: t('attributeWebMaxZoom'),
+ type: 'number',
+ },
+ iconScale: {
+ name: t('sharedIconScale'),
+ type: 'number',
+ },
+}), [t]);
diff --git a/src/common/attributes/useDeviceAttributes.js b/src/common/attributes/useDeviceAttributes.js
new file mode 100644
index 00000000..eab9b8f6
--- /dev/null
+++ b/src/common/attributes/useDeviceAttributes.js
@@ -0,0 +1,33 @@
+import { useMemo } from 'react';
+
+export default (t) => useMemo(() => ({
+ 'web.reportColor': {
+ name: t('attributeWebReportColor'),
+ type: 'string',
+ subtype: 'color',
+ },
+ devicePassword: {
+ name: t('attributeDevicePassword'),
+ type: 'string',
+ },
+ deviceImage: {
+ name: t('attributeDeviceImage'),
+ type: 'string',
+ },
+ 'processing.copyAttributes': {
+ name: t('attributeProcessingCopyAttributes'),
+ type: 'string',
+ },
+ 'decoder.timezone': {
+ name: t('sharedTimezone'),
+ type: 'string',
+ },
+ deviceInactivityStart: {
+ name: t('attributeDeviceInactivityStart'),
+ type: 'number',
+ },
+ deviceInactivityPeriod: {
+ name: t('attributeDeviceInactivityPeriod'),
+ type: 'number',
+ },
+}), [t]);
diff --git a/src/common/attributes/useGeofenceAttributes.js b/src/common/attributes/useGeofenceAttributes.js
new file mode 100644
index 00000000..a5cd068b
--- /dev/null
+++ b/src/common/attributes/useGeofenceAttributes.js
@@ -0,0 +1,18 @@
+import { useMemo } from 'react';
+
+export default (t) => useMemo(() => ({
+ color: {
+ name: t('attributeColor'),
+ type: 'string',
+ },
+ speedLimit: {
+ name: t('attributeSpeedLimit'),
+ type: 'number',
+ subtype: 'speed',
+ },
+ polylineDistance: {
+ name: t('attributePolylineDistance'),
+ type: 'number',
+ subtype: 'distance',
+ },
+}), [t]);
diff --git a/src/common/attributes/useGroupAttributes.js b/src/common/attributes/useGroupAttributes.js
new file mode 100644
index 00000000..53a299e1
--- /dev/null
+++ b/src/common/attributes/useGroupAttributes.js
@@ -0,0 +1,12 @@
+import { useMemo } from 'react';
+
+export default (t) => useMemo(() => ({
+ 'processing.copyAttributes': {
+ name: t('attributeProcessingCopyAttributes'),
+ type: 'string',
+ },
+ 'decoder.timezone': {
+ name: t('sharedTimezone'),
+ type: 'string',
+ },
+}), [t]);
diff --git a/src/common/attributes/usePositionAttributes.js b/src/common/attributes/usePositionAttributes.js
new file mode 100644
index 00000000..0b191ebc
--- /dev/null
+++ b/src/common/attributes/usePositionAttributes.js
@@ -0,0 +1,380 @@
+import { useMemo } from 'react';
+
+export default (t) => useMemo(() => ({
+ id: {
+ name: t('deviceIdentifier'),
+ type: 'number',
+ property: true,
+ },
+ latitude: {
+ name: t('positionLatitude'),
+ type: 'number',
+ property: true,
+ },
+ longitude: {
+ name: t('positionLongitude'),
+ type: 'number',
+ property: true,
+ },
+ speed: {
+ name: t('positionSpeed'),
+ type: 'number',
+ dataType: 'speed',
+ property: true,
+ },
+ course: {
+ name: t('positionCourse'),
+ type: 'number',
+ property: true,
+ },
+ altitude: {
+ name: t('positionAltitude'),
+ type: 'number',
+ property: true,
+ },
+ accuracy: {
+ name: t('positionAccuracy'),
+ type: 'number',
+ dataType: 'distance',
+ property: true,
+ },
+ valid: {
+ name: t('positionValid'),
+ type: 'boolean',
+ property: true,
+ },
+ protocol: {
+ name: t('positionProtocol'),
+ type: 'string',
+ property: true,
+ },
+ address: {
+ name: t('positionAddress'),
+ type: 'string',
+ property: true,
+ },
+ deviceTime: {
+ name: t('positionDeviceTime'),
+ type: 'string',
+ property: true,
+ },
+ fixTime: {
+ name: t('positionFixTime'),
+ type: 'string',
+ property: true,
+ },
+ serverTime: {
+ name: t('positionServerTime'),
+ type: 'string',
+ property: true,
+ },
+ geofenceIds: {
+ name: t('sharedGeofences'),
+ property: true,
+ },
+ raw: {
+ name: t('positionRaw'),
+ type: 'string',
+ },
+ index: {
+ name: t('positionIndex'),
+ type: 'number',
+ },
+ hdop: {
+ name: t('positionHdop'),
+ type: 'number',
+ },
+ vdop: {
+ name: t('positionVdop'),
+ type: 'number',
+ },
+ pdop: {
+ name: t('positionPdop'),
+ type: 'number',
+ },
+ sat: {
+ name: t('positionSat'),
+ type: 'number',
+ },
+ satVisible: {
+ name: t('positionSatVisible'),
+ type: 'number',
+ },
+ rssi: {
+ name: t('positionRssi'),
+ type: 'number',
+ },
+ coolantTemp: {
+ name: t('positionCoolantTemp'),
+ type: 'number',
+ },
+ gps: {
+ name: t('positionGps'),
+ type: 'number',
+ },
+ roaming: {
+ name: t('positionRoaming'),
+ type: 'boolean',
+ },
+ event: {
+ name: t('positionEvent'),
+ type: 'string',
+ },
+ alarm: {
+ name: t('positionAlarm'),
+ type: 'string',
+ },
+ status: {
+ name: t('positionStatus'),
+ type: 'string',
+ },
+ odometer: {
+ name: t('positionOdometer'),
+ type: 'number',
+ dataType: 'distance',
+ },
+ serviceOdometer: {
+ name: t('positionServiceOdometer'),
+ type: 'number',
+ dataType: 'distance',
+ },
+ tripOdometer: {
+ name: t('positionTripOdometer'),
+ type: 'number',
+ dataType: 'distance',
+ },
+ hours: {
+ name: t('positionHours'),
+ type: 'number',
+ dataType: 'hours',
+ },
+ steps: {
+ name: t('positionSteps'),
+ type: 'number',
+ },
+ heartRate: {
+ name: t('positionHeartRate'),
+ type: 'number',
+ },
+ input: {
+ name: t('positionInput'),
+ type: 'number',
+ },
+ in1: {
+ name: `${t('positionInput')} 1`,
+ type: 'boolean',
+ },
+ in2: {
+ name: `${t('positionInput')} 2`,
+ type: 'boolean',
+ },
+ in3: {
+ name: `${t('positionInput')} 3`,
+ type: 'boolean',
+ },
+ in4: {
+ name: `${t('positionInput')} 4`,
+ type: 'boolean',
+ },
+ output: {
+ name: t('positionOutput'),
+ type: 'number',
+ },
+ out1: {
+ name: `${t('positionOutput')} 1`,
+ type: 'boolean',
+ },
+ out2: {
+ name: `${t('positionOutput')} 2`,
+ type: 'boolean',
+ },
+ out3: {
+ name: `${t('positionOutput')} 3`,
+ type: 'boolean',
+ },
+ out4: {
+ name: `${t('positionOutput')} 4`,
+ type: 'boolean',
+ },
+ power: {
+ name: t('positionPower'),
+ type: 'number',
+ dataType: 'voltage',
+ },
+ battery: {
+ name: t('positionBattery'),
+ type: 'number',
+ dataType: 'voltage',
+ },
+ batteryLevel: {
+ name: t('positionBatteryLevel'),
+ type: 'number',
+ dataType: 'percentage',
+ },
+ fuel: {
+ name: t('positionFuel'),
+ type: 'number',
+ dataType: 'volume',
+ },
+ fuelConsumption: {
+ name: t('positionFuelConsumption'),
+ type: 'number',
+ },
+ versionFw: {
+ name: t('positionVersionFw'),
+ type: 'string',
+ },
+ versionHw: {
+ name: t('positionVersionHw'),
+ type: 'string',
+ },
+ type: {
+ name: t('sharedType'),
+ type: 'string',
+ },
+ ignition: {
+ name: t('positionIgnition'),
+ type: 'boolean',
+ },
+ flags: {
+ name: t('positionFlags'),
+ type: 'string',
+ },
+ charge: {
+ name: t('positionCharge'),
+ type: 'boolean',
+ },
+ ip: {
+ name: t('positionIp'),
+ type: 'string',
+ },
+ archive: {
+ name: t('positionArchive'),
+ type: 'boolean',
+ },
+ distance: {
+ name: t('positionDistance'),
+ type: 'number',
+ dataType: 'distance',
+ },
+ totalDistance: {
+ name: t('deviceTotalDistance'),
+ type: 'number',
+ dataType: 'distance',
+ },
+ rpm: {
+ name: t('positionRpm'),
+ type: 'number',
+ },
+ vin: {
+ name: t('positionVin'),
+ type: 'string',
+ },
+ approximate: {
+ name: t('positionApproximate'),
+ type: 'boolean',
+ },
+ throttle: {
+ name: t('positionThrottle'),
+ type: 'number',
+ },
+ motion: {
+ name: t('positionMotion'),
+ type: 'boolean',
+ },
+ armed: {
+ name: t('positionArmed'),
+ type: 'boolean',
+ },
+ geofence: {
+ name: t('sharedGeofence'),
+ type: 'string',
+ },
+ acceleration: {
+ name: t('positionAcceleration'),
+ type: 'number',
+ },
+ deviceTemp: {
+ name: t('positionDeviceTemp'),
+ type: 'number',
+ },
+ temp1: {
+ name: `${t('positionTemp')} 1`,
+ type: 'number',
+ },
+ temp2: {
+ name: `${t('positionTemp')} 2`,
+ type: 'number',
+ },
+ temp3: {
+ name: `${t('positionTemp')} 3`,
+ type: 'number',
+ },
+ temp4: {
+ name: `${t('positionTemp')} 4`,
+ type: 'number',
+ },
+ operator: {
+ name: t('positionOperator'),
+ type: 'string',
+ },
+ command: {
+ name: t('deviceCommand'),
+ type: 'string',
+ },
+ blocked: {
+ name: t('positionBlocked'),
+ type: 'boolean',
+ },
+ lock: {
+ name: t('alarmLock'),
+ type: 'boolean',
+ },
+ dtcs: {
+ name: t('positionDtcs'),
+ type: 'string',
+ },
+ obdSpeed: {
+ name: t('positionObdSpeed'),
+ type: 'number',
+ dataType: 'speed',
+ },
+ obdOdometer: {
+ name: t('positionObdOdometer'),
+ type: 'number',
+ dataType: 'distance',
+ },
+ result: {
+ name: t('eventCommandResult'),
+ type: 'string',
+ },
+ driverUniqueId: {
+ name: t('sharedDriver'),
+ type: 'string',
+ },
+ card: {
+ name: t('positionCard'),
+ type: 'string',
+ },
+ drivingTime: {
+ name: t('positionDrivingTime'),
+ type: 'number',
+ dataType: 'hours',
+ },
+ color: {
+ name: t('attributeColor'),
+ type: 'string',
+ },
+ image: {
+ name: t('positionImage'),
+ type: 'string',
+ },
+ video: {
+ name: t('positionVideo'),
+ type: 'string',
+ },
+ audio: {
+ name: t('positionAudio'),
+ type: 'string',
+ },
+}), [t]);
diff --git a/src/common/attributes/useServerAttributes.js b/src/common/attributes/useServerAttributes.js
new file mode 100644
index 00000000..80ac3c7d
--- /dev/null
+++ b/src/common/attributes/useServerAttributes.js
@@ -0,0 +1,62 @@
+import { useMemo } from 'react';
+
+export default (t) => useMemo(() => ({
+ support: {
+ name: t('settingsSupport'),
+ type: 'string',
+ },
+ title: {
+ name: t('serverName'),
+ type: 'string',
+ },
+ description: {
+ name: t('serverDescription'),
+ type: 'string',
+ },
+ logo: {
+ name: t('serverLogo'),
+ type: 'string',
+ },
+ logoInverted: {
+ name: t('serverLogoInverted'),
+ type: 'string',
+ },
+ colorPrimary: {
+ name: t('serverColorPrimary'),
+ type: 'string',
+ subtype: 'color',
+ },
+ colorSecondary: {
+ name: t('serverColorSecondary'),
+ type: 'string',
+ subtype: 'color',
+ },
+ disableChange: {
+ name: t('serverChangeDisable'),
+ type: 'boolean',
+ },
+ darkMode: {
+ name: t('settingsDarkMode'),
+ type: 'boolean',
+ },
+ totpEnable: {
+ name: t('settingsTotpEnable'),
+ type: 'boolean',
+ },
+ totpForce: {
+ name: t('settingsTotpForce'),
+ type: 'boolean',
+ },
+ serviceWorkerUpdateInterval: {
+ name: t('settingsServiceWorkerUpdateInterval'),
+ type: 'number',
+ },
+ 'ui.disableLoginLanguage': {
+ name: t('attributeUiDisableLoginLanguage'),
+ type: 'boolean',
+ },
+ disableShare: {
+ name: t('serverDisableShare'),
+ type: 'boolean',
+ },
+}), [t]);
diff --git a/src/common/attributes/useUserAttributes.js b/src/common/attributes/useUserAttributes.js
new file mode 100644
index 00000000..81230884
--- /dev/null
+++ b/src/common/attributes/useUserAttributes.js
@@ -0,0 +1,60 @@
+import { useMemo } from 'react';
+
+export default (t) => useMemo(() => ({
+ telegramChatId: {
+ name: t('attributeTelegramChatId'),
+ type: 'string',
+ },
+ pushoverUserKey: {
+ name: t('attributePushoverUserKey'),
+ type: 'string',
+ },
+ pushoverDeviceNames: {
+ name: t('attributePushoverDeviceNames'),
+ type: 'string',
+ },
+ 'mail.smtp.host': {
+ name: t('attributeMailSmtpHost'),
+ type: 'string',
+ },
+ 'mail.smtp.port': {
+ name: t('attributeMailSmtpPort'),
+ type: 'number',
+ },
+ 'mail.smtp.starttls.enable': {
+ name: t('attributeMailSmtpStarttlsEnable'),
+ type: 'boolean',
+ },
+ 'mail.smtp.starttls.required': {
+ name: t('attributeMailSmtpStarttlsRequired'),
+ type: 'boolean',
+ },
+ 'mail.smtp.ssl.enable': {
+ name: t('attributeMailSmtpSslEnable'),
+ type: 'boolean',
+ },
+ 'mail.smtp.ssl.trust': {
+ name: t('attributeMailSmtpSslTrust'),
+ type: 'string',
+ },
+ 'mail.smtp.ssl.protocols': {
+ name: t('attributeMailSmtpSslProtocols'),
+ type: 'string',
+ },
+ 'mail.smtp.from': {
+ name: t('attributeMailSmtpFrom'),
+ type: 'string',
+ },
+ 'mail.smtp.auth': {
+ name: t('attributeMailSmtpAuth'),
+ type: 'boolean',
+ },
+ 'mail.smtp.username': {
+ name: t('attributeMailSmtpUsername'),
+ type: 'string',
+ },
+ 'mail.smtp.password': {
+ name: t('attributeMailSmtpPassword'),
+ type: 'string',
+ },
+}), [t]);
diff --git a/src/common/components/AddressValue.jsx b/src/common/components/AddressValue.jsx
new file mode 100644
index 00000000..827a71de
--- /dev/null
+++ b/src/common/components/AddressValue.jsx
@@ -0,0 +1,37 @@
+import React, { useEffect, useState } from 'react';
+import { useSelector } from 'react-redux';
+import { Link } from '@mui/material';
+import { useTranslation } from './LocalizationProvider';
+import { useCatch } from '../../reactHelper';
+
+const AddressValue = ({ latitude, longitude, originalAddress }) => {
+ const t = useTranslation();
+
+ const addressEnabled = useSelector((state) => state.session.server.geocoderEnabled);
+
+ const [address, setAddress] = useState();
+
+ useEffect(() => {
+ setAddress(originalAddress);
+ }, [latitude, longitude, originalAddress]);
+
+ const showAddress = useCatch(async () => {
+ const query = new URLSearchParams({ latitude, longitude });
+ const response = await fetch(`/api/server/geocode?${query.toString()}`);
+ if (response.ok) {
+ setAddress(await response.text());
+ } else {
+ throw Error(await response.text());
+ }
+ });
+
+ if (address) {
+ return address;
+ }
+ if (addressEnabled) {
+ return (<Link href="#" onClick={showAddress}>{t('sharedShowAddress')}</Link>);
+ }
+ return '';
+};
+
+export default AddressValue;
diff --git a/src/common/components/BottomMenu.jsx b/src/common/components/BottomMenu.jsx
new file mode 100644
index 00000000..07fa2e11
--- /dev/null
+++ b/src/common/components/BottomMenu.jsx
@@ -0,0 +1,135 @@
+import React, { useState } from 'react';
+import { useDispatch, useSelector } from 'react-redux';
+import { useNavigate, useLocation } from 'react-router-dom';
+import {
+ Paper, BottomNavigation, BottomNavigationAction, Menu, MenuItem, Typography, Badge,
+} from '@mui/material';
+
+import DescriptionIcon from '@mui/icons-material/Description';
+import SettingsIcon from '@mui/icons-material/Settings';
+import MapIcon from '@mui/icons-material/Map';
+import PersonIcon from '@mui/icons-material/Person';
+import ExitToAppIcon from '@mui/icons-material/ExitToApp';
+
+import { sessionActions } from '../../store';
+import { useTranslation } from './LocalizationProvider';
+import { useRestriction } from '../util/permissions';
+import { nativePostMessage } from './NativeInterface';
+
+const BottomMenu = () => {
+ const navigate = useNavigate();
+ const location = useLocation();
+ const dispatch = useDispatch();
+ const t = useTranslation();
+
+ const readonly = useRestriction('readonly');
+ const disableReports = useRestriction('disableReports');
+ const user = useSelector((state) => state.session.user);
+ const socket = useSelector((state) => state.session.socket);
+
+ const [anchorEl, setAnchorEl] = useState(null);
+
+ const currentSelection = () => {
+ if (location.pathname === `/settings/user/${user.id}`) {
+ return 'account';
+ } if (location.pathname.startsWith('/settings')) {
+ return 'settings';
+ } if (location.pathname.startsWith('/reports')) {
+ return 'reports';
+ } if (location.pathname === '/') {
+ return 'map';
+ }
+ return null;
+ };
+
+ const handleAccount = () => {
+ setAnchorEl(null);
+ navigate(`/settings/user/${user.id}`);
+ };
+
+ const handleLogout = async () => {
+ setAnchorEl(null);
+
+ const notificationToken = window.localStorage.getItem('notificationToken');
+ if (notificationToken && !user.readonly) {
+ window.localStorage.removeItem('notificationToken');
+ const tokens = user.attributes.notificationTokens?.split(',') || [];
+ if (tokens.includes(notificationToken)) {
+ const updatedUser = {
+ ...user,
+ attributes: {
+ ...user.attributes,
+ notificationTokens: tokens.length > 1 ? tokens.filter((it) => it !== notificationToken).join(',') : undefined,
+ },
+ };
+ await fetch(`/api/users/${user.id}`, {
+ method: 'PUT',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(updatedUser),
+ });
+ }
+ }
+
+ await fetch('/api/session', { method: 'DELETE' });
+ nativePostMessage('logout');
+ navigate('/login');
+ dispatch(sessionActions.updateUser(null));
+ };
+
+ const handleSelection = (event, value) => {
+ switch (value) {
+ case 'map':
+ navigate('/');
+ break;
+ case 'reports':
+ navigate('/reports/combined');
+ break;
+ case 'settings':
+ navigate('/settings/preferences');
+ break;
+ case 'account':
+ setAnchorEl(event.currentTarget);
+ break;
+ case 'logout':
+ handleLogout();
+ break;
+ default:
+ break;
+ }
+ };
+
+ return (
+ <Paper square elevation={3}>
+ <BottomNavigation value={currentSelection()} onChange={handleSelection} showLabels>
+ <BottomNavigationAction
+ label={t('mapTitle')}
+ icon={(
+ <Badge color="error" variant="dot" overlap="circular" invisible={socket !== false}>
+ <MapIcon />
+ </Badge>
+ )}
+ value="map"
+ />
+ {!disableReports && (
+ <BottomNavigationAction label={t('reportTitle')} icon={<DescriptionIcon />} value="reports" />
+ )}
+ <BottomNavigationAction label={t('settingsTitle')} icon={<SettingsIcon />} value="settings" />
+ {readonly ? (
+ <BottomNavigationAction label={t('loginLogout')} icon={<ExitToAppIcon />} value="logout" />
+ ) : (
+ <BottomNavigationAction label={t('settingsUser')} icon={<PersonIcon />} value="account" />
+ )}
+ </BottomNavigation>
+ <Menu anchorEl={anchorEl} open={Boolean(anchorEl)} onClose={() => setAnchorEl(null)}>
+ <MenuItem onClick={handleAccount}>
+ <Typography color="textPrimary">{t('settingsUser')}</Typography>
+ </MenuItem>
+ <MenuItem onClick={handleLogout}>
+ <Typography color="error">{t('loginLogout')}</Typography>
+ </MenuItem>
+ </Menu>
+ </Paper>
+ );
+};
+
+export default BottomMenu;
diff --git a/src/common/components/DriverValue.js b/src/common/components/DriverValue.js
new file mode 100644
index 00000000..6148b418
--- /dev/null
+++ b/src/common/components/DriverValue.js
@@ -0,0 +1,9 @@
+import { useSelector } from 'react-redux';
+
+const DriverValue = ({ driverUniqueId }) => {
+ const driver = useSelector((state) => state.drivers.items[driverUniqueId]);
+
+ return driver?.name || driverUniqueId;
+};
+
+export default DriverValue;
diff --git a/src/common/components/ErrorHandler.jsx b/src/common/components/ErrorHandler.jsx
new file mode 100644
index 00000000..5c9c26d9
--- /dev/null
+++ b/src/common/components/ErrorHandler.jsx
@@ -0,0 +1,27 @@
+import { Snackbar, Alert } from '@mui/material';
+import React from 'react';
+import { useDispatch, useSelector } from 'react-redux';
+import { usePrevious } from '../../reactHelper';
+import { errorsActions } from '../../store';
+
+const ErrorHandler = () => {
+ const dispatch = useDispatch();
+
+ const error = useSelector((state) => state.errors.errors.find(() => true));
+ const previousError = usePrevious(error);
+
+ return (
+ <Snackbar open={!!error}>
+ <Alert
+ elevation={6}
+ onClose={() => dispatch(errorsActions.pop())}
+ severity="error"
+ variant="filled"
+ >
+ {error || previousError}
+ </Alert>
+ </Snackbar>
+ );
+};
+
+export default ErrorHandler;
diff --git a/src/common/components/GeofencesValue.js b/src/common/components/GeofencesValue.js
new file mode 100644
index 00000000..4808a8a2
--- /dev/null
+++ b/src/common/components/GeofencesValue.js
@@ -0,0 +1,9 @@
+import { useSelector } from 'react-redux';
+
+const GeofencesValue = ({ geofenceIds }) => {
+ const geofences = useSelector((state) => state.geofences.items);
+
+ return geofenceIds.map((id) => geofences[id]?.name).join(', ');
+};
+
+export default GeofencesValue;
diff --git a/src/common/components/LinkField.jsx b/src/common/components/LinkField.jsx
new file mode 100644
index 00000000..08c6213a
--- /dev/null
+++ b/src/common/components/LinkField.jsx
@@ -0,0 +1,93 @@
+import { Autocomplete, TextField } from '@mui/material';
+import React, { useState } from 'react';
+import { useEffectAsync } from '../../reactHelper';
+
+const LinkField = ({
+ label,
+ endpointAll,
+ endpointLinked,
+ baseId,
+ keyBase,
+ keyLink,
+ keyGetter = (item) => item.id,
+ titleGetter = (item) => item.name,
+}) => {
+ const [active, setActive] = useState(false);
+ const [open, setOpen] = useState(false);
+ const [items, setItems] = useState();
+ const [linked, setLinked] = useState();
+
+ useEffectAsync(async () => {
+ if (active) {
+ const response = await fetch(endpointAll);
+ if (response.ok) {
+ setItems(await response.json());
+ } else {
+ throw Error(await response.text());
+ }
+ }
+ }, [active]);
+
+ useEffectAsync(async () => {
+ if (active) {
+ const response = await fetch(endpointLinked);
+ if (response.ok) {
+ setLinked(await response.json());
+ } else {
+ throw Error(await response.text());
+ }
+ }
+ }, [active]);
+
+ const createBody = (linkId) => {
+ const body = {};
+ body[keyBase] = baseId;
+ body[keyLink] = linkId;
+ return body;
+ };
+
+ const onChange = async (value) => {
+ const oldValue = linked.map((it) => keyGetter(it));
+ const newValue = value.map((it) => keyGetter(it));
+ if (!newValue.find((it) => it < 0)) {
+ const results = [];
+ newValue.filter((it) => !oldValue.includes(it)).forEach((added) => {
+ results.push(fetch('/api/permissions', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(createBody(added)),
+ }));
+ });
+ oldValue.filter((it) => !newValue.includes(it)).forEach((removed) => {
+ results.push(fetch('/api/permissions', {
+ method: 'DELETE',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(createBody(removed)),
+ }));
+ });
+ await Promise.all(results);
+ setLinked(value);
+ }
+ };
+
+ return (
+ <Autocomplete
+ loading={active && !items}
+ isOptionEqualToValue={(i1, i2) => keyGetter(i1) === keyGetter(i2)}
+ options={items || []}
+ getOptionLabel={(item) => titleGetter(item)}
+ renderInput={(params) => <TextField {...params} label={label} />}
+ value={(items && linked) || []}
+ onChange={(_, value) => onChange(value)}
+ open={open}
+ onOpen={() => {
+ setOpen(true);
+ setActive(true);
+ }}
+ onClose={() => setOpen(false)}
+ multiple
+ />
+ );
+};
+
+export default LinkField;
diff --git a/src/common/components/LocalizationProvider.jsx b/src/common/components/LocalizationProvider.jsx
new file mode 100644
index 00000000..4104c773
--- /dev/null
+++ b/src/common/components/LocalizationProvider.jsx
@@ -0,0 +1,187 @@
+/* eslint-disable import/no-relative-packages */
+import React, {
+ createContext, useContext, useEffect, useMemo,
+} from 'react';
+import dayjs from 'dayjs';
+import usePersistedState from '../util/usePersistedState';
+
+import af from '../../resources/l10n/af.json'; import 'dayjs/locale/af';
+import ar from '../../resources/l10n/ar.json'; import 'dayjs/locale/ar';
+import az from '../../resources/l10n/az.json'; import 'dayjs/locale/az';
+import bg from '../../resources/l10n/bg.json'; import 'dayjs/locale/bg';
+import bn from '../../resources/l10n/bn.json'; import 'dayjs/locale/bn';
+import ca from '../../resources/l10n/ca.json'; import 'dayjs/locale/ca';
+import cs from '../../resources/l10n/cs.json'; import 'dayjs/locale/cs';
+import da from '../../resources/l10n/da.json'; import 'dayjs/locale/da';
+import de from '../../resources/l10n/de.json'; import 'dayjs/locale/de';
+import el from '../../resources/l10n/el.json'; import 'dayjs/locale/el';
+import en from '../../resources/l10n/en.json'; import 'dayjs/locale/en';
+import es from '../../resources/l10n/es.json'; import 'dayjs/locale/es';
+import fa from '../../resources/l10n/fa.json'; import 'dayjs/locale/fa';
+import fi from '../../resources/l10n/fi.json'; import 'dayjs/locale/fi';
+import fr from '../../resources/l10n/fr.json'; import 'dayjs/locale/fr';
+import gl from '../../resources/l10n/gl.json'; import 'dayjs/locale/gl';
+import he from '../../resources/l10n/he.json'; import 'dayjs/locale/he';
+import hi from '../../resources/l10n/hi.json'; import 'dayjs/locale/hi';
+import hr from '../../resources/l10n/hr.json'; import 'dayjs/locale/hr';
+import hu from '../../resources/l10n/hu.json'; import 'dayjs/locale/hu';
+import id from '../../resources/l10n/id.json'; import 'dayjs/locale/id';
+import it from '../../resources/l10n/it.json'; import 'dayjs/locale/it';
+import ja from '../../resources/l10n/ja.json'; import 'dayjs/locale/ja';
+import ka from '../../resources/l10n/ka.json'; import 'dayjs/locale/ka';
+import kk from '../../resources/l10n/kk.json'; import 'dayjs/locale/kk';
+import km from '../../resources/l10n/km.json'; import 'dayjs/locale/km';
+import ko from '../../resources/l10n/ko.json'; import 'dayjs/locale/ko';
+import lo from '../../resources/l10n/lo.json'; import 'dayjs/locale/lo';
+import lt from '../../resources/l10n/lt.json'; import 'dayjs/locale/lt';
+import lv from '../../resources/l10n/lv.json'; import 'dayjs/locale/lv';
+import mk from '../../resources/l10n/mk.json'; import 'dayjs/locale/mk';
+import ml from '../../resources/l10n/ml.json'; import 'dayjs/locale/ml';
+import mn from '../../resources/l10n/mn.json'; import 'dayjs/locale/mn';
+import ms from '../../resources/l10n/ms.json'; import 'dayjs/locale/ms';
+import nb from '../../resources/l10n/nb.json'; import 'dayjs/locale/nb';
+import ne from '../../resources/l10n/ne.json'; import 'dayjs/locale/ne';
+import nl from '../../resources/l10n/nl.json'; import 'dayjs/locale/nl';
+import nn from '../../resources/l10n/nn.json'; import 'dayjs/locale/nn';
+import pl from '../../resources/l10n/pl.json'; import 'dayjs/locale/pl';
+import pt from '../../resources/l10n/pt.json'; import 'dayjs/locale/pt';
+import ptBR from '../../resources/l10n/pt_BR.json'; import 'dayjs/locale/pt-br';
+import ro from '../../resources/l10n/ro.json'; import 'dayjs/locale/ro';
+import ru from '../../resources/l10n/ru.json'; import 'dayjs/locale/ru';
+import si from '../../resources/l10n/si.json'; import 'dayjs/locale/si';
+import sk from '../../resources/l10n/sk.json'; import 'dayjs/locale/sk';
+import sl from '../../resources/l10n/sl.json'; import 'dayjs/locale/sl';
+import sq from '../../resources/l10n/sq.json'; import 'dayjs/locale/sq';
+import sr from '../../resources/l10n/sr.json'; import 'dayjs/locale/sr';
+import sv from '../../resources/l10n/sv.json'; import 'dayjs/locale/sv';
+import ta from '../../resources/l10n/ta.json'; import 'dayjs/locale/ta';
+import th from '../../resources/l10n/th.json'; import 'dayjs/locale/th';
+import tr from '../../resources/l10n/tr.json'; import 'dayjs/locale/tr';
+import uk from '../../resources/l10n/uk.json'; import 'dayjs/locale/uk';
+import uz from '../../resources/l10n/uz.json'; import 'dayjs/locale/uz';
+import vi from '../../resources/l10n/vi.json'; import 'dayjs/locale/vi';
+import zh from '../../resources/l10n/zh.json'; import 'dayjs/locale/zh';
+import zhTW from '../../resources/l10n/zh_TW.json'; import 'dayjs/locale/zh-tw';
+
+const languages = {
+ af: { data: af, country: 'ZA', name: 'Afrikaans' },
+ ar: { data: ar, country: 'AE', name: 'العربية' },
+ az: { data: az, country: 'AZ', name: 'Azərbaycanca' },
+ bg: { data: bg, country: 'BG', name: 'Български' },
+ bn: { data: bn, country: 'IN', name: 'বাংলা' },
+ ca: { data: ca, country: 'ES', name: 'Català' },
+ cs: { data: cs, country: 'CZ', name: 'Čeština' },
+ de: { data: de, country: 'DE', name: 'Deutsch' },
+ da: { data: da, country: 'DK', name: 'Dansk' },
+ el: { data: el, country: 'GR', name: 'Ελληνικά' },
+ en: { data: en, country: 'US', name: 'English' },
+ es: { data: es, country: 'ES', name: 'Español' },
+ fa: { data: fa, country: 'IR', name: 'فارسی' },
+ fi: { data: fi, country: 'FI', name: 'Suomi' },
+ fr: { data: fr, country: 'FR', name: 'Français' },
+ gl: { data: gl, country: 'ES', name: 'Galego' },
+ he: { data: he, country: 'IL', name: 'עברית' },
+ hi: { data: hi, country: 'IN', name: 'हिन्दी' },
+ hr: { data: hr, country: 'HR', name: 'Hrvatski' },
+ hu: { data: hu, country: 'HU', name: 'Magyar' },
+ id: { data: id, country: 'ID', name: 'Bahasa Indonesia' },
+ it: { data: it, country: 'IT', name: 'Italiano' },
+ ja: { data: ja, country: 'JP', name: '日本語' },
+ ka: { data: ka, country: 'GE', name: 'ქართული' },
+ kk: { data: kk, country: 'KZ', name: 'Қазақша' },
+ ko: { data: ko, country: 'KR', name: '한국어' },
+ km: { data: km, country: 'KH', name: 'ភាសាខ្មែរ' },
+ lo: { data: lo, country: 'LA', name: 'ລາວ' },
+ lt: { data: lt, country: 'LT', name: 'Lietuvių' },
+ lv: { data: lv, country: 'LV', name: 'Latviešu' },
+ mk: { data: mk, country: 'MK', name: 'Mакедонски' },
+ ml: { data: ml, country: 'IN', name: 'മലയാളം' },
+ mn: { data: mn, country: 'MN', name: 'Монгол хэл' },
+ ms: { data: ms, country: 'MY', name: 'بهاس ملايو' },
+ nb: { data: nb, country: 'NO', name: 'Norsk bokmål' },
+ ne: { data: ne, country: 'NP', name: 'नेपाली' },
+ nl: { data: nl, country: 'NL', name: 'Nederlands' },
+ nn: { data: nn, country: 'NO', name: 'Norsk nynorsk' },
+ pl: { data: pl, country: 'PL', name: 'Polski' },
+ pt: { data: pt, country: 'PT', name: 'Português' },
+ ptBR: { data: ptBR, country: 'BR', name: 'Português (Brasil)' },
+ ro: { data: ro, country: 'RO', name: 'Română' },
+ ru: { data: ru, country: 'RU', name: 'Русский' },
+ si: { data: si, country: 'LK', name: 'සිංහල' },
+ sk: { data: sk, country: 'SK', name: 'Slovenčina' },
+ sl: { data: sl, country: 'SI', name: 'Slovenščina' },
+ sq: { data: sq, country: 'AL', name: 'Shqipëria' },
+ sr: { data: sr, country: 'RS', name: 'Srpski' },
+ sv: { data: sv, country: 'SE', name: 'Svenska' },
+ ta: { data: ta, country: 'IN', name: 'தமிழ்' },
+ th: { data: th, country: 'TH', name: 'ไทย' },
+ tr: { data: tr, country: 'TR', name: 'Türkçe' },
+ uk: { data: uk, country: 'UA', name: 'Українська' },
+ uz: { data: uz, country: 'UZ', name: 'Oʻzbekcha' },
+ vi: { data: vi, country: 'VN', name: 'Tiếng Việt' },
+ zh: { data: zh, country: 'CN', name: '中文' },
+ zhTW: { data: zhTW, country: 'TW', name: '中文 (Taiwan)' },
+};
+
+const getDefaultLanguage = () => {
+ const browserLanguages = window.navigator.languages ? window.navigator.languages.slice() : [];
+ const browserLanguage = window.navigator.userLanguage || window.navigator.language;
+ browserLanguages.push(browserLanguage);
+ browserLanguages.push(browserLanguage.substring(0, 2));
+
+ for (let i = 0; i < browserLanguages.length; i += 1) {
+ let language = browserLanguages[i].replace('-', '');
+ if (language in languages) {
+ return language;
+ }
+ if (language.length > 2) {
+ language = language.substring(0, 2);
+ if (language in languages) {
+ return language;
+ }
+ }
+ }
+ return 'en';
+};
+
+const LocalizationContext = createContext({
+ languages,
+ language: 'en',
+ setLanguage: () => {},
+});
+
+export const LocalizationProvider = ({ children }) => {
+ const [language, setLanguage] = usePersistedState('language', getDefaultLanguage());
+
+ const value = useMemo(() => ({ languages, language, setLanguage }), [languages, language, setLanguage]);
+
+ useEffect(() => {
+ let selected;
+ if (language.length > 2) {
+ selected = `${language.slice(0, 2)}-${language.slice(-2).toLowerCase()}`;
+ } else {
+ selected = language;
+ }
+ dayjs.locale(selected);
+ }, [language]);
+
+ return (
+ <LocalizationContext.Provider value={value}>
+ {children}
+ </LocalizationContext.Provider>
+ );
+};
+
+export const useLocalization = () => useContext(LocalizationContext);
+
+export const useTranslation = () => {
+ const context = useContext(LocalizationContext);
+ const { data } = context.languages[context.language];
+ return useMemo(() => (key) => data[key], [data]);
+};
+
+export const useTranslationKeys = (predicate) => {
+ const context = useContext(LocalizationContext);
+ const { data } = context.languages[context.language];
+ return Object.keys(data).filter(predicate);
+};
diff --git a/src/common/components/NativeInterface.js b/src/common/components/NativeInterface.js
new file mode 100644
index 00000000..b088de0e
--- /dev/null
+++ b/src/common/components/NativeInterface.js
@@ -0,0 +1,72 @@
+import { useEffect, useState } from 'react';
+import { useDispatch, useSelector } from 'react-redux';
+import { useEffectAsync } from '../../reactHelper';
+import { sessionActions } from '../../store';
+
+export const nativeEnvironment = window.appInterface || (window.webkit && window.webkit.messageHandlers.appInterface);
+
+export const nativePostMessage = (message) => {
+ if (window.webkit && window.webkit.messageHandlers.appInterface) {
+ window.webkit.messageHandlers.appInterface.postMessage(message);
+ }
+ if (window.appInterface) {
+ window.appInterface.postMessage(message);
+ }
+};
+
+export const handleLoginTokenListeners = new Set();
+window.handleLoginToken = (token) => {
+ handleLoginTokenListeners.forEach((listener) => listener(token));
+};
+
+const updateNotificationTokenListeners = new Set();
+window.updateNotificationToken = (token) => {
+ updateNotificationTokenListeners.forEach((listener) => listener(token));
+};
+
+const NativeInterface = () => {
+ const dispatch = useDispatch();
+
+ const user = useSelector((state) => state.session.user);
+ const [notificationToken, setNotificationToken] = useState(null);
+
+ useEffect(() => {
+ const listener = (token) => setNotificationToken(token);
+ updateNotificationTokenListeners.add(listener);
+ return () => updateNotificationTokenListeners.delete(listener);
+ }, [setNotificationToken]);
+
+ useEffectAsync(async () => {
+ if (user && !user.readonly && notificationToken) {
+ window.localStorage.setItem('notificationToken', notificationToken);
+ setNotificationToken(null);
+
+ const tokens = user.attributes.notificationTokens?.split(',') || [];
+ if (!tokens.includes(notificationToken)) {
+ const updatedUser = {
+ ...user,
+ attributes: {
+ ...user.attributes,
+ notificationTokens: [...tokens.slice(-2), notificationToken].join(','),
+ },
+ };
+
+ const response = await fetch(`/api/users/${user.id}`, {
+ method: 'PUT',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(updatedUser),
+ });
+
+ if (response.ok) {
+ dispatch(sessionActions.updateUser(await response.json()));
+ } else {
+ throw Error(await response.text());
+ }
+ }
+ }
+ }, [user, notificationToken, setNotificationToken]);
+
+ return null;
+};
+
+export default NativeInterface;
diff --git a/src/common/components/NavBar.jsx b/src/common/components/NavBar.jsx
new file mode 100644
index 00000000..a53960fd
--- /dev/null
+++ b/src/common/components/NavBar.jsx
@@ -0,0 +1,25 @@
+import React from 'react';
+import {
+ AppBar, Toolbar, Typography, IconButton,
+} from '@mui/material';
+import MenuIcon from '@mui/icons-material/Menu';
+
+const Navbar = ({ setOpenDrawer, title }) => (
+ <AppBar position="sticky" color="inherit">
+ <Toolbar>
+ <IconButton
+ color="inherit"
+ edge="start"
+ sx={{ mr: 2 }}
+ onClick={() => setOpenDrawer(true)}
+ >
+ <MenuIcon />
+ </IconButton>
+ <Typography variant="h6" noWrap>
+ {title}
+ </Typography>
+ </Toolbar>
+ </AppBar>
+);
+
+export default Navbar;
diff --git a/src/common/components/PageLayout.jsx b/src/common/components/PageLayout.jsx
new file mode 100644
index 00000000..e81c9754
--- /dev/null
+++ b/src/common/components/PageLayout.jsx
@@ -0,0 +1,118 @@
+import React, { useState } from 'react';
+import {
+ AppBar,
+ Breadcrumbs,
+ Divider,
+ Drawer,
+ IconButton,
+ Toolbar,
+ Typography,
+ useMediaQuery,
+ useTheme,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+import ArrowBackIcon from '@mui/icons-material/ArrowBack';
+import MenuIcon from '@mui/icons-material/Menu';
+import { useNavigate } from 'react-router-dom';
+import { useTranslation } from './LocalizationProvider';
+
+const useStyles = makeStyles((theme) => ({
+ desktopRoot: {
+ height: '100%',
+ display: 'flex',
+ },
+ mobileRoot: {
+ height: '100%',
+ display: 'flex',
+ flexDirection: 'column',
+ },
+ desktopDrawer: {
+ width: theme.dimensions.drawerWidthDesktop,
+ },
+ mobileDrawer: {
+ width: theme.dimensions.drawerWidthTablet,
+ },
+ mobileToolbar: {
+ zIndex: 1,
+ },
+ content: {
+ flexGrow: 1,
+ alignItems: 'stretch',
+ display: 'flex',
+ flexDirection: 'column',
+ overflowY: 'auto',
+ },
+}));
+
+const PageTitle = ({ breadcrumbs }) => {
+ const theme = useTheme();
+ const t = useTranslation();
+
+ const desktop = useMediaQuery(theme.breakpoints.up('md'));
+
+ if (desktop) {
+ return (
+ <Typography variant="h6" noWrap>{t(breadcrumbs[0])}</Typography>
+ );
+ }
+ return (
+ <Breadcrumbs>
+ {breadcrumbs.slice(0, -1).map((breadcrumb) => (
+ <Typography variant="h6" color="inherit" key={breadcrumb}>{t(breadcrumb)}</Typography>
+ ))}
+ <Typography variant="h6" color="textPrimary">{t(breadcrumbs[breadcrumbs.length - 1])}</Typography>
+ </Breadcrumbs>
+ );
+};
+
+const PageLayout = ({ menu, breadcrumbs, children }) => {
+ const classes = useStyles();
+ const theme = useTheme();
+ const navigate = useNavigate();
+
+ const desktop = useMediaQuery(theme.breakpoints.up('md'));
+
+ const [openDrawer, setOpenDrawer] = useState(false);
+
+ return desktop ? (
+ <div className={classes.desktopRoot}>
+ <Drawer
+ variant="permanent"
+ className={classes.desktopDrawer}
+ classes={{ paper: classes.desktopDrawer }}
+ >
+ <Toolbar>
+ <IconButton color="inherit" edge="start" sx={{ mr: 2 }} onClick={() => navigate('/')}>
+ <ArrowBackIcon />
+ </IconButton>
+ <PageTitle breadcrumbs={breadcrumbs} />
+ </Toolbar>
+ <Divider />
+ {menu}
+ </Drawer>
+ <div className={classes.content}>{children}</div>
+ </div>
+ ) : (
+ <div className={classes.mobileRoot}>
+ <Drawer
+ variant="temporary"
+ open={openDrawer}
+ onClose={() => setOpenDrawer(false)}
+ classes={{ paper: classes.mobileDrawer }}
+ >
+ {menu}
+ </Drawer>
+ <AppBar className={classes.mobileToolbar} position="static" color="inherit">
+ <Toolbar>
+ <IconButton color="inherit" edge="start" sx={{ mr: 2 }} onClick={() => setOpenDrawer(true)}>
+ <MenuIcon />
+ </IconButton>
+ <PageTitle breadcrumbs={breadcrumbs} />
+ </Toolbar>
+ </AppBar>
+ <div className={classes.content}>{children}</div>
+ </div>
+ );
+};
+
+export default PageLayout;
diff --git a/src/common/components/PositionValue.jsx b/src/common/components/PositionValue.jsx
new file mode 100644
index 00000000..b1f8f656
--- /dev/null
+++ b/src/common/components/PositionValue.jsx
@@ -0,0 +1,133 @@
+import React from 'react';
+import { useSelector } from 'react-redux';
+import { Link } from '@mui/material';
+import { Link as RouterLink } from 'react-router-dom';
+import {
+ formatAlarm,
+ formatAltitude,
+ formatBoolean,
+ formatCoordinate,
+ formatCourse,
+ formatDistance,
+ formatNumber,
+ formatNumericHours,
+ formatPercentage,
+ formatSpeed,
+ formatTime,
+ formatTemperature,
+ formatVoltage,
+ formatVolume,
+ formatConsumption,
+} from '../util/formatter';
+import { speedToKnots } from '../util/converter';
+import { useAttributePreference, usePreference } from '../util/preferences';
+import { useTranslation } from './LocalizationProvider';
+import { useAdministrator } from '../util/permissions';
+import AddressValue from './AddressValue';
+import GeofencesValue from './GeofencesValue';
+import DriverValue from './DriverValue';
+
+const PositionValue = ({ position, property, attribute }) => {
+ const t = useTranslation();
+
+ const admin = useAdministrator();
+
+ const device = useSelector((state) => state.devices.items[position.deviceId]);
+
+ const key = property || attribute;
+ const value = property ? position[property] : position.attributes[attribute];
+
+ const distanceUnit = useAttributePreference('distanceUnit');
+ const altitudeUnit = useAttributePreference('altitudeUnit');
+ const speedUnit = useAttributePreference('speedUnit');
+ const volumeUnit = useAttributePreference('volumeUnit');
+ const coordinateFormat = usePreference('coordinateFormat');
+ const hours12 = usePreference('twelveHourFormat');
+
+ const formatValue = () => {
+ switch (key) {
+ case 'fixTime':
+ case 'deviceTime':
+ case 'serverTime':
+ return formatTime(value, 'seconds', hours12);
+ case 'latitude':
+ return formatCoordinate('latitude', value, coordinateFormat);
+ case 'longitude':
+ return formatCoordinate('longitude', value, coordinateFormat);
+ case 'speed':
+ return value != null ? formatSpeed(value, speedUnit, t) : '';
+ case 'obdSpeed':
+ return value != null ? formatSpeed(speedToKnots(value, 'kmh'), speedUnit, t) : '';
+ case 'course':
+ return formatCourse(value);
+ case 'altitude':
+ return formatAltitude(value, altitudeUnit, t);
+ case 'power':
+ case 'battery':
+ return formatVoltage(value, t);
+ case 'batteryLevel':
+ return value != null ? formatPercentage(value, t) : '';
+ case 'volume':
+ return value != null ? formatVolume(value, volumeUnit, t) : '';
+ case 'fuelConsumption':
+ return value != null ? formatConsumption(value, t) : '';
+ case 'coolantTemp':
+ return formatTemperature(value);
+ case 'alarm':
+ return formatAlarm(value, t);
+ case 'odometer':
+ case 'serviceOdometer':
+ case 'tripOdometer':
+ case 'obdOdometer':
+ case 'distance':
+ case 'totalDistance':
+ return value != null ? formatDistance(value, distanceUnit, t) : '';
+ case 'hours':
+ return value != null ? formatNumericHours(value, t) : '';
+ default:
+ if (typeof value === 'number') {
+ return formatNumber(value);
+ } if (typeof value === 'boolean') {
+ return formatBoolean(value, t);
+ }
+ return value || '';
+ }
+ };
+
+ switch (key) {
+ case 'image':
+ case 'video':
+ case 'audio':
+ return <Link href={`/api/media/${device.uniqueId}/${value}`} target="_blank">{value}</Link>;
+ case 'totalDistance':
+ case 'hours':
+ return (
+ <>
+ {formatValue(value)}
+ &nbsp;&nbsp;
+ {admin && <Link component={RouterLink} underline="none" to={`/settings/accumulators/${position.deviceId}`}>&#9881;</Link>}
+ </>
+ );
+ case 'address':
+ return <AddressValue latitude={position.latitude} longitude={position.longitude} originalAddress={value} />;
+ case 'network':
+ if (value) {
+ return <Link component={RouterLink} underline="none" to={`/network/${position.id}`}>{t('sharedInfoTitle')}</Link>;
+ }
+ return '';
+ case 'geofenceIds':
+ if (value) {
+ return <GeofencesValue geofenceIds={value} />;
+ }
+ return '';
+ case 'driverUniqueId':
+ if (value) {
+ return <DriverValue driverUniqueId={value} />;
+ }
+ return '';
+ default:
+ return formatValue(value);
+ }
+};
+
+export default PositionValue;
diff --git a/src/common/components/RemoveDialog.jsx b/src/common/components/RemoveDialog.jsx
new file mode 100644
index 00000000..0f4254a8
--- /dev/null
+++ b/src/common/components/RemoveDialog.jsx
@@ -0,0 +1,54 @@
+import React from 'react';
+import Button from '@mui/material/Button';
+import { Snackbar } from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+import { useTranslation } from './LocalizationProvider';
+import { useCatch } from '../../reactHelper';
+import { snackBarDurationLongMs } from '../util/duration';
+
+const useStyles = makeStyles((theme) => ({
+ root: {
+ [theme.breakpoints.down('md')]: {
+ bottom: `calc(${theme.dimensions.bottomBarHeight}px + ${theme.spacing(1)})`,
+ },
+ },
+ button: {
+ height: 'auto',
+ marginTop: 0,
+ marginBottom: 0,
+ color: theme.palette.error.main,
+ },
+}));
+
+const RemoveDialog = ({
+ open, endpoint, itemId, onResult,
+}) => {
+ const classes = useStyles();
+ const t = useTranslation();
+
+ const handleRemove = useCatch(async () => {
+ const response = await fetch(`/api/${endpoint}/${itemId}`, { method: 'DELETE' });
+ if (response.ok) {
+ onResult(true);
+ } else {
+ throw Error(await response.text());
+ }
+ });
+
+ return (
+ <Snackbar
+ className={classes.root}
+ open={open}
+ autoHideDuration={snackBarDurationLongMs}
+ onClose={() => onResult(false)}
+ message={t('sharedRemoveConfirm')}
+ action={(
+ <Button size="small" className={classes.button} onClick={handleRemove}>
+ {t('sharedRemove')}
+ </Button>
+ )}
+ />
+ );
+};
+
+export default RemoveDialog;
diff --git a/src/common/components/SelectField.jsx b/src/common/components/SelectField.jsx
new file mode 100644
index 00000000..db8c30b0
--- /dev/null
+++ b/src/common/components/SelectField.jsx
@@ -0,0 +1,77 @@
+import {
+ FormControl, InputLabel, MenuItem, Select, Autocomplete, TextField,
+} from '@mui/material';
+import React, { useState } from 'react';
+import { useEffectAsync } from '../../reactHelper';
+
+const SelectField = ({
+ label,
+ fullWidth,
+ multiple,
+ value = null,
+ emptyValue = null,
+ emptyTitle = '',
+ onChange,
+ endpoint,
+ data,
+ keyGetter = (item) => item.id,
+ titleGetter = (item) => item.name,
+}) => {
+ const [items, setItems] = useState(data);
+
+ const getOptionLabel = (option) => {
+ if (typeof option !== 'object') {
+ option = items.find((obj) => keyGetter(obj) === option);
+ }
+ return option ? titleGetter(option) : emptyTitle;
+ };
+
+ useEffectAsync(async () => {
+ if (endpoint) {
+ const response = await fetch(endpoint);
+ if (response.ok) {
+ setItems(await response.json());
+ } else {
+ throw Error(await response.text());
+ }
+ }
+ }, []);
+
+ if (items) {
+ return (
+ <FormControl fullWidth={fullWidth}>
+ {multiple ? (
+ <>
+ <InputLabel>{label}</InputLabel>
+ <Select
+ label={label}
+ multiple
+ value={value}
+ onChange={onChange}
+ >
+ {items.map((item) => (
+ <MenuItem key={keyGetter(item)} value={keyGetter(item)}>{titleGetter(item)}</MenuItem>
+ ))}
+ </Select>
+ </>
+ ) : (
+ <Autocomplete
+ size="small"
+ options={items}
+ getOptionLabel={getOptionLabel}
+ renderOption={(props, option) => (
+ <MenuItem {...props} key={keyGetter(option)} value={keyGetter(option)}>{titleGetter(option)}</MenuItem>
+ )}
+ isOptionEqualToValue={(option, value) => keyGetter(option) === value}
+ value={value}
+ onChange={(_, value) => onChange({ target: { value: value ? keyGetter(value) : emptyValue } })}
+ renderInput={(params) => <TextField {...params} label={label} />}
+ />
+ )}
+ </FormControl>
+ );
+ }
+ return null;
+};
+
+export default SelectField;
diff --git a/src/common/components/SideNav.jsx b/src/common/components/SideNav.jsx
new file mode 100644
index 00000000..97968bd1
--- /dev/null
+++ b/src/common/components/SideNav.jsx
@@ -0,0 +1,33 @@
+import React, { Fragment } from 'react';
+import {
+ List, ListItemText, ListItemIcon, Divider, ListSubheader, ListItemButton,
+} from '@mui/material';
+import { Link, useLocation } from 'react-router-dom';
+
+const SideNav = ({ routes }) => {
+ const location = useLocation();
+
+ return (
+ <List disablePadding style={{ paddingTop: '16px' }}>
+ {routes.map((route) => (route.subheader ? (
+ <Fragment key={route.subheader}>
+ <Divider />
+ <ListSubheader>{route.subheader}</ListSubheader>
+ </Fragment>
+ ) : (
+ <ListItemButton
+ disableRipple
+ component={Link}
+ key={route.href}
+ to={route.href}
+ selected={location.pathname.match(route.match || route.href) !== null}
+ >
+ <ListItemIcon>{route.icon}</ListItemIcon>
+ <ListItemText primary={route.name} />
+ </ListItemButton>
+ )))}
+ </List>
+ );
+};
+
+export default SideNav;
diff --git a/src/common/components/SplitButton.jsx b/src/common/components/SplitButton.jsx
new file mode 100644
index 00000000..84876f15
--- /dev/null
+++ b/src/common/components/SplitButton.jsx
@@ -0,0 +1,48 @@
+import React, { useRef, useState } from 'react';
+import {
+ Button, ButtonGroup, Menu, MenuItem, Typography,
+} from '@mui/material';
+import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
+
+const SplitButton = ({
+ fullWidth, variant, color, disabled, onClick, options, selected, setSelected,
+}) => {
+ const anchorRef = useRef();
+ const [menuAnchorEl, setMenuAnchorEl] = useState(null);
+
+ return (
+ <>
+ <ButtonGroup fullWidth={fullWidth} variant={variant} color={color} ref={anchorRef}>
+ <Button disabled={disabled} onClick={() => onClick(selected)}>
+ <Typography variant="button" noWrap>{options[selected]}</Typography>
+ </Button>
+ <Button fullWidth={false} size="small" onClick={() => setMenuAnchorEl(anchorRef.current)}>
+ <ArrowDropDownIcon />
+ </Button>
+ </ButtonGroup>
+ <Menu
+ open={!!menuAnchorEl}
+ anchorEl={menuAnchorEl}
+ onClose={() => setMenuAnchorEl(null)}
+ anchorOrigin={{
+ vertical: 'bottom',
+ horizontal: 'right',
+ }}
+ >
+ {Object.entries(options).map(([key, value]) => (
+ <MenuItem
+ key={key}
+ onClick={() => {
+ setSelected(key);
+ setMenuAnchorEl(null);
+ }}
+ >
+ {value}
+ </MenuItem>
+ ))}
+ </Menu>
+ </>
+ );
+};
+
+export default SplitButton;
diff --git a/src/common/components/StatusCard.jsx b/src/common/components/StatusCard.jsx
new file mode 100644
index 00000000..a63d0f80
--- /dev/null
+++ b/src/common/components/StatusCard.jsx
@@ -0,0 +1,288 @@
+import React, { useState } from 'react';
+import { useDispatch, useSelector } from 'react-redux';
+import { useNavigate } from 'react-router-dom';
+import Draggable from 'react-draggable';
+import {
+ Card,
+ CardContent,
+ Typography,
+ CardActions,
+ IconButton,
+ Table,
+ TableBody,
+ TableRow,
+ TableCell,
+ Menu,
+ MenuItem,
+ CardMedia,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+import CloseIcon from '@mui/icons-material/Close';
+import ReplayIcon from '@mui/icons-material/Replay';
+import PublishIcon from '@mui/icons-material/Publish';
+import EditIcon from '@mui/icons-material/Edit';
+import DeleteIcon from '@mui/icons-material/Delete';
+import PendingIcon from '@mui/icons-material/Pending';
+
+import { useTranslation } from './LocalizationProvider';
+import RemoveDialog from './RemoveDialog';
+import PositionValue from './PositionValue';
+import { useDeviceReadonly } from '../util/permissions';
+import usePositionAttributes from '../attributes/usePositionAttributes';
+import { devicesActions } from '../../store';
+import { useCatch, useCatchCallback } from '../../reactHelper';
+import { useAttributePreference } from '../util/preferences';
+
+const useStyles = makeStyles((theme) => ({
+ card: {
+ pointerEvents: 'auto',
+ width: theme.dimensions.popupMaxWidth,
+ },
+ media: {
+ height: theme.dimensions.popupImageHeight,
+ display: 'flex',
+ justifyContent: 'flex-end',
+ alignItems: 'flex-start',
+ },
+ mediaButton: {
+ color: theme.palette.primary.contrastText,
+ mixBlendMode: 'difference',
+ },
+ header: {
+ display: 'flex',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ padding: theme.spacing(1, 1, 0, 2),
+ },
+ content: {
+ paddingTop: theme.spacing(1),
+ paddingBottom: theme.spacing(1),
+ maxHeight: theme.dimensions.cardContentMaxHeight,
+ overflow: 'auto',
+ },
+ delete: {
+ color: theme.palette.error.main,
+ },
+ icon: {
+ width: '25px',
+ height: '25px',
+ filter: 'brightness(0) invert(1)',
+ },
+ table: {
+ '& .MuiTableCell-sizeSmall': {
+ paddingLeft: 0,
+ paddingRight: 0,
+ },
+ },
+ cell: {
+ borderBottom: 'none',
+ },
+ actions: {
+ justifyContent: 'space-between',
+ },
+ root: ({ desktopPadding }) => ({
+ pointerEvents: 'none',
+ position: 'fixed',
+ zIndex: 5,
+ left: '50%',
+ [theme.breakpoints.up('md')]: {
+ left: `calc(50% + ${desktopPadding} / 2)`,
+ bottom: theme.spacing(3),
+ },
+ [theme.breakpoints.down('md')]: {
+ left: '50%',
+ bottom: `calc(${theme.spacing(3)} + ${theme.dimensions.bottomBarHeight}px)`,
+ },
+ transform: 'translateX(-50%)',
+ }),
+}));
+
+const StatusRow = ({ name, content }) => {
+ const classes = useStyles();
+
+ return (
+ <TableRow>
+ <TableCell className={classes.cell}>
+ <Typography variant="body2">{name}</Typography>
+ </TableCell>
+ <TableCell className={classes.cell}>
+ <Typography variant="body2" color="textSecondary">{content}</Typography>
+ </TableCell>
+ </TableRow>
+ );
+};
+
+const StatusCard = ({ deviceId, position, onClose, disableActions, desktopPadding = 0 }) => {
+ const classes = useStyles({ desktopPadding });
+ const navigate = useNavigate();
+ const dispatch = useDispatch();
+ const t = useTranslation();
+
+ const deviceReadonly = useDeviceReadonly();
+
+ const shareDisabled = useSelector((state) => state.session.server.attributes.disableShare);
+ const user = useSelector((state) => state.session.user);
+ const device = useSelector((state) => state.devices.items[deviceId]);
+
+ const deviceImage = device?.attributes?.deviceImage;
+
+ const positionAttributes = usePositionAttributes(t);
+ const positionItems = useAttributePreference('positionItems', 'speed,address,totalDistance,course');
+
+ const [anchorEl, setAnchorEl] = useState(null);
+
+ const [removing, setRemoving] = useState(false);
+
+ const handleRemove = useCatch(async (removed) => {
+ if (removed) {
+ const response = await fetch('/api/devices');
+ if (response.ok) {
+ dispatch(devicesActions.refresh(await response.json()));
+ } else {
+ throw Error(await response.text());
+ }
+ }
+ setRemoving(false);
+ });
+
+ const handleGeofence = useCatchCallback(async () => {
+ const newItem = {
+ name: t('sharedGeofence'),
+ area: `CIRCLE (${position.latitude} ${position.longitude}, 50)`,
+ };
+ const response = await fetch('/api/geofences', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(newItem),
+ });
+ if (response.ok) {
+ const item = await response.json();
+ const permissionResponse = await fetch('/api/permissions', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ deviceId: position.deviceId, geofenceId: item.id }),
+ });
+ if (!permissionResponse.ok) {
+ throw Error(await permissionResponse.text());
+ }
+ navigate(`/settings/geofence/${item.id}`);
+ } else {
+ throw Error(await response.text());
+ }
+ }, [navigate, position]);
+
+ return (
+ <>
+ <div className={classes.root}>
+ {device && (
+ <Draggable
+ handle={`.${classes.media}, .${classes.header}`}
+ >
+ <Card elevation={3} className={classes.card}>
+ {deviceImage ? (
+ <CardMedia
+ className={classes.media}
+ image={`/api/media/${device.uniqueId}/${deviceImage}`}
+ >
+ <IconButton
+ size="small"
+ onClick={onClose}
+ onTouchStart={onClose}
+ >
+ <CloseIcon fontSize="small" className={classes.mediaButton} />
+ </IconButton>
+ </CardMedia>
+ ) : (
+ <div className={classes.header}>
+ <Typography variant="body2" color="textSecondary">
+ {device.name}
+ </Typography>
+ <IconButton
+ size="small"
+ onClick={onClose}
+ onTouchStart={onClose}
+ >
+ <CloseIcon fontSize="small" />
+ </IconButton>
+ </div>
+ )}
+ {position && (
+ <CardContent className={classes.content}>
+ <Table size="small" classes={{ root: classes.table }}>
+ <TableBody>
+ {positionItems.split(',').filter((key) => position.hasOwnProperty(key) || position.attributes.hasOwnProperty(key)).map((key) => (
+ <StatusRow
+ key={key}
+ name={positionAttributes[key]?.name || key}
+ content={(
+ <PositionValue
+ position={position}
+ property={position.hasOwnProperty(key) ? key : null}
+ attribute={position.hasOwnProperty(key) ? null : key}
+ />
+ )}
+ />
+ ))}
+ </TableBody>
+ </Table>
+ </CardContent>
+ )}
+ <CardActions classes={{ root: classes.actions }} disableSpacing>
+ <IconButton
+ color="secondary"
+ onClick={(e) => setAnchorEl(e.currentTarget)}
+ disabled={!position}
+ >
+ <PendingIcon />
+ </IconButton>
+ <IconButton
+ onClick={() => navigate('/replay')}
+ disabled={disableActions || !position}
+ >
+ <ReplayIcon />
+ </IconButton>
+ <IconButton
+ onClick={() => navigate(`/settings/device/${deviceId}/command`)}
+ disabled={disableActions}
+ >
+ <PublishIcon />
+ </IconButton>
+ <IconButton
+ onClick={() => navigate(`/settings/device/${deviceId}`)}
+ disabled={disableActions || deviceReadonly}
+ >
+ <EditIcon />
+ </IconButton>
+ <IconButton
+ onClick={() => setRemoving(true)}
+ disabled={disableActions || deviceReadonly}
+ className={classes.delete}
+ >
+ <DeleteIcon />
+ </IconButton>
+ </CardActions>
+ </Card>
+ </Draggable>
+ )}
+ </div>
+ {position && (
+ <Menu anchorEl={anchorEl} open={Boolean(anchorEl)} onClose={() => setAnchorEl(null)}>
+ <MenuItem onClick={() => navigate(`/position/${position.id}`)}><Typography color="secondary">{t('sharedShowDetails')}</Typography></MenuItem>
+ <MenuItem onClick={handleGeofence}>{t('sharedCreateGeofence')}</MenuItem>
+ <MenuItem component="a" target="_blank" href={`https://www.google.com/maps/search/?api=1&query=${position.latitude}%2C${position.longitude}`}>{t('linkGoogleMaps')}</MenuItem>
+ <MenuItem component="a" target="_blank" href={`http://maps.apple.com/?ll=${position.latitude},${position.longitude}`}>{t('linkAppleMaps')}</MenuItem>
+ <MenuItem component="a" target="_blank" href={`https://www.google.com/maps/@?api=1&map_action=pano&viewpoint=${position.latitude}%2C${position.longitude}&heading=${position.course}`}>{t('linkStreetView')}</MenuItem>
+ {!shareDisabled && !user.temporary && <MenuItem onClick={() => navigate(`/settings/device/${deviceId}/share`)}>{t('deviceShare')}</MenuItem>}
+ </Menu>
+ )}
+ <RemoveDialog
+ open={removing}
+ endpoint="devices"
+ itemId={deviceId}
+ onResult={(removed) => handleRemove(removed)}
+ />
+ </>
+ );
+};
+
+export default StatusCard;
diff --git a/src/common/components/TableShimmer.jsx b/src/common/components/TableShimmer.jsx
new file mode 100644
index 00000000..08a984a4
--- /dev/null
+++ b/src/common/components/TableShimmer.jsx
@@ -0,0 +1,17 @@
+import React from 'react';
+import { Skeleton, TableCell, TableRow } from '@mui/material';
+
+const TableShimmer = ({ columns, startAction, endAction }) => [...Array(3)].map((_, i) => (
+ <TableRow key={-i}>
+ {[...Array(columns)].map((_, j) => {
+ const action = (startAction && j === 0) || (endAction && j === columns - 1);
+ return (
+ <TableCell key={-j} padding={action ? 'none' : 'normal'}>
+ {!action && <Skeleton />}
+ </TableCell>
+ );
+ })}
+ </TableRow>
+));
+
+export default TableShimmer;
diff --git a/src/common/theme/components.js b/src/common/theme/components.js
new file mode 100644
index 00000000..56a2ac75
--- /dev/null
+++ b/src/common/theme/components.js
@@ -0,0 +1,40 @@
+export default {
+ MuiUseMediaQuery: {
+ defaultProps: {
+ noSsr: true,
+ },
+ },
+ MuiOutlinedInput: {
+ styleOverrides: {
+ root: ({ theme }) => ({
+ backgroundColor: theme.palette.background.default,
+ }),
+ },
+ },
+ MuiButton: {
+ styleOverrides: {
+ sizeMedium: {
+ height: '40px',
+ },
+ },
+ },
+ MuiFormControl: {
+ defaultProps: {
+ size: 'small',
+ },
+ },
+ MuiSnackbar: {
+ defaultProps: {
+ anchorOrigin: {
+ vertical: 'bottom',
+ horizontal: 'center',
+ },
+ },
+ },
+ MuiTooltip: {
+ defaultProps: {
+ enterDelay: 500,
+ enterNextDelay: 500,
+ },
+ },
+};
diff --git a/src/common/theme/dimensions.js b/src/common/theme/dimensions.js
new file mode 100644
index 00000000..4930803a
--- /dev/null
+++ b/src/common/theme/dimensions.js
@@ -0,0 +1,14 @@
+export default {
+ sidebarWidth: '28%',
+ sidebarWidthTablet: '52px',
+ drawerWidthDesktop: '360px',
+ drawerWidthTablet: '320px',
+ drawerHeightPhone: '250px',
+ filterFormWidth: '160px',
+ eventsDrawerWidth: '320px',
+ bottomBarHeight: 56,
+ popupMapOffset: 300,
+ popupMaxWidth: 288,
+ popupImageHeight: 144,
+ cardContentMaxHeight: '40vh',
+};
diff --git a/src/common/theme/index.js b/src/common/theme/index.js
new file mode 100644
index 00000000..e8ce698b
--- /dev/null
+++ b/src/common/theme/index.js
@@ -0,0 +1,11 @@
+import { useMemo } from 'react';
+import { createTheme } from '@mui/material/styles';
+import palette from './palette';
+import dimensions from './dimensions';
+import components from './components';
+
+export default (server, darkMode) => useMemo(() => createTheme({
+ palette: palette(server, darkMode),
+ dimensions,
+ components,
+}), [server, darkMode]);
diff --git a/src/common/theme/palette.js b/src/common/theme/palette.js
new file mode 100644
index 00000000..f32ed93e
--- /dev/null
+++ b/src/common/theme/palette.js
@@ -0,0 +1,22 @@
+import { grey, green, indigo } from '@mui/material/colors';
+
+const validatedColor = (color) => (/^#([0-9A-Fa-f]{3}){1,2}$/.test(color) ? color : null);
+
+export default (server, darkMode) => ({
+ mode: darkMode ? 'dark' : 'light',
+ background: {
+ default: darkMode ? grey[900] : grey[50],
+ },
+ primary: {
+ main: validatedColor(server?.attributes?.colorPrimary) || (darkMode ? indigo[200] : indigo[900]),
+ },
+ secondary: {
+ main: validatedColor(server?.attributes?.colorSecondary) || (darkMode ? green[200] : green[800]),
+ },
+ neutral: {
+ main: grey[500],
+ },
+ geometry: {
+ main: '#3bb2d0',
+ },
+});
diff --git a/src/common/util/converter.js b/src/common/util/converter.js
new file mode 100644
index 00000000..cb21566b
--- /dev/null
+++ b/src/common/util/converter.js
@@ -0,0 +1,107 @@
+const speedConverter = (unit) => {
+ switch (unit) {
+ case 'kmh':
+ return 1.852;
+ case 'mph':
+ return 1.15078;
+ case 'kn':
+ default:
+ return 1;
+ }
+};
+
+export const speedUnitString = (unit, t) => {
+ switch (unit) {
+ case 'kmh':
+ return t('sharedKmh');
+ case 'mph':
+ return t('sharedMph');
+ case 'kn':
+ default:
+ return t('sharedKn');
+ }
+};
+
+export const speedFromKnots = (value, unit) => value * speedConverter(unit);
+
+export const speedToKnots = (value, unit) => value / speedConverter(unit);
+
+const distanceConverter = (unit) => {
+ switch (unit) {
+ case 'mi':
+ return 0.000621371;
+ case 'nmi':
+ return 0.000539957;
+ case 'km':
+ default:
+ return 0.001;
+ }
+};
+
+export const distanceUnitString = (unit, t) => {
+ switch (unit) {
+ case 'mi':
+ return t('sharedMi');
+ case 'nmi':
+ return t('sharedNmi');
+ case 'km':
+ default:
+ return t('sharedKm');
+ }
+};
+
+export const distanceFromMeters = (value, unit) => value * distanceConverter(unit);
+
+export const distanceToMeters = (value, unit) => value / distanceConverter(unit);
+
+const altitudeConverter = (unit) => {
+ switch (unit) {
+ case 'ft':
+ return 3.28084;
+ case 'm':
+ default:
+ return 1;
+ }
+};
+
+export const altitudeUnitString = (unit, t) => {
+ switch (unit) {
+ case 'ft':
+ return t('sharedFeet');
+ case 'm':
+ default:
+ return t('sharedMeters');
+ }
+};
+
+export const altitudeFromMeters = (value, unit) => value * altitudeConverter(unit);
+
+export const altitudeToMeters = (value, unit) => value / altitudeConverter(unit);
+
+const volumeConverter = (unit) => {
+ switch (unit) {
+ case 'impGal':
+ return 4.546;
+ case 'usGal':
+ return 3.785;
+ case 'ltr':
+ default:
+ return 1;
+ }
+};
+
+export const volumeUnitString = (unit, t) => {
+ switch (unit) {
+ case 'impGal':
+ return t('sharedGallonAbbreviation');
+ case 'usGal':
+ return t('sharedGallonAbbreviation');
+ case 'ltr':
+ default:
+ return t('sharedLiterAbbreviation');
+ }
+};
+
+export const volumeFromLiters = (value, unit) => value / volumeConverter(unit);
+
+export const volumeToLiters = (value, unit) => value * volumeConverter(unit);
diff --git a/src/common/util/deviceCategories.js b/src/common/util/deviceCategories.js
new file mode 100644
index 00000000..a991e505
--- /dev/null
+++ b/src/common/util/deviceCategories.js
@@ -0,0 +1,24 @@
+export default [
+ 'default',
+ 'animal',
+ 'bicycle',
+ 'boat',
+ 'bus',
+ 'car',
+ 'camper',
+ 'crane',
+ 'helicopter',
+ 'motorcycle',
+ 'offroad',
+ 'person',
+ 'pickup',
+ 'plane',
+ 'ship',
+ 'tractor',
+ 'train',
+ 'tram',
+ 'trolleybus',
+ 'truck',
+ 'van',
+ 'scooter',
+];
diff --git a/src/common/util/duration.js b/src/common/util/duration.js
new file mode 100644
index 00000000..aae74868
--- /dev/null
+++ b/src/common/util/duration.js
@@ -0,0 +1,2 @@
+export const snackBarDurationShortMs = 1500;
+export const snackBarDurationLongMs = 2750;
diff --git a/src/common/util/formatter.js b/src/common/util/formatter.js
new file mode 100644
index 00000000..7b7fc96d
--- /dev/null
+++ b/src/common/util/formatter.js
@@ -0,0 +1,143 @@
+import dayjs from 'dayjs';
+import duration from 'dayjs/plugin/duration';
+import relativeTime from 'dayjs/plugin/relativeTime';
+
+import {
+ altitudeFromMeters,
+ altitudeUnitString,
+ distanceFromMeters,
+ distanceUnitString,
+ speedFromKnots,
+ speedUnitString,
+ volumeFromLiters,
+ volumeUnitString,
+} from './converter';
+import { prefixString } from './stringUtils';
+
+dayjs.extend(duration);
+dayjs.extend(relativeTime);
+
+export const formatBoolean = (value, t) => (value ? t('sharedYes') : t('sharedNo'));
+
+export const formatNumber = (value, precision = 1) => Number(value.toFixed(precision));
+
+export const formatPercentage = (value) => `${value}%`;
+
+export const formatTemperature = (value) => `${value}°C`;
+
+export const formatVoltage = (value, t) => `${value} ${t('sharedVoltAbbreviation')}`;
+
+export const formatConsumption = (value, t) => `${value} ${t('sharedLiterPerHourAbbreviation')}`;
+
+export const formatTime = (value, format, hours12) => {
+ if (value) {
+ const d = dayjs(value);
+ switch (format) {
+ case 'date':
+ return d.format('YYYY-MM-DD');
+ case 'time':
+ return d.format(hours12 ? 'hh:mm:ss A' : 'HH:mm:ss');
+ case 'minutes':
+ return d.format(hours12 ? 'YYYY-MM-DD hh:mm A' : 'YYYY-MM-DD HH:mm');
+ default:
+ return d.format(hours12 ? 'YYYY-MM-DD hh:mm:ss A' : 'YYYY-MM-DD HH:mm:ss');
+ }
+ }
+ return '';
+};
+
+export const formatStatus = (value, t) => t(prefixString('deviceStatus', value));
+export const formatAlarm = (value, t) => (value ? t(prefixString('alarm', value)) : '');
+
+export const formatCourse = (value) => {
+ const courseValues = ['\u2191', '\u2197', '\u2192', '\u2198', '\u2193', '\u2199', '\u2190', '\u2196'];
+ let normalizedValue = (value + 45 / 2) % 360;
+ if (normalizedValue < 0) {
+ normalizedValue += 360;
+ }
+ return courseValues[Math.floor(normalizedValue / 45)];
+};
+
+export const formatDistance = (value, unit, t) => `${distanceFromMeters(value, unit).toFixed(2)} ${distanceUnitString(unit, t)}`;
+
+export const formatAltitude = (value, unit, t) => `${altitudeFromMeters(value, unit).toFixed(2)} ${altitudeUnitString(unit, t)}`;
+
+export const formatSpeed = (value, unit, t) => `${speedFromKnots(value, unit).toFixed(2)} ${speedUnitString(unit, t)}`;
+
+export const formatVolume = (value, unit, t) => `${volumeFromLiters(value, unit).toFixed(2)} ${volumeUnitString(unit, t)}`;
+
+export const formatNumericHours = (value, t) => {
+ const hours = Math.floor(value / 3600000);
+ const minutes = Math.floor((value % 3600000) / 60000);
+ return `${hours} ${t('sharedHourAbbreviation')} ${minutes} ${t('sharedMinuteAbbreviation')}`;
+};
+
+export const formatCoordinate = (key, value, unit) => {
+ let hemisphere;
+ let degrees;
+ let minutes;
+ let seconds;
+
+ if (key === 'latitude') {
+ hemisphere = value >= 0 ? 'N' : 'S';
+ } else {
+ hemisphere = value >= 0 ? 'E' : 'W';
+ }
+
+ switch (unit) {
+ case 'ddm':
+ value = Math.abs(value);
+ degrees = Math.floor(value);
+ minutes = (value - degrees) * 60;
+ return `${degrees}° ${minutes.toFixed(6)}' ${hemisphere}`;
+ case 'dms':
+ value = Math.abs(value);
+ degrees = Math.floor(value);
+ minutes = Math.floor((value - degrees) * 60);
+ seconds = Math.round((value - degrees - minutes / 60) * 3600);
+ return `${degrees}° ${minutes}' ${seconds}" ${hemisphere}`;
+ default:
+ return `${value.toFixed(6)}°`;
+ }
+};
+
+export const getStatusColor = (status) => {
+ switch (status) {
+ case 'online':
+ return 'success';
+ case 'offline':
+ return 'error';
+ case 'unknown':
+ default:
+ return 'neutral';
+ }
+};
+
+export const getBatteryStatus = (batteryLevel) => {
+ if (batteryLevel >= 70) {
+ return 'success';
+ }
+ if (batteryLevel > 30) {
+ return 'warning';
+ }
+ return 'error';
+};
+
+export const formatNotificationTitle = (t, notification, includeId) => {
+ let title = t(prefixString('event', notification.type));
+ if (notification.type === 'alarm') {
+ const alarmString = notification.attributes.alarms;
+ if (alarmString) {
+ const alarms = alarmString.split(',');
+ if (alarms.length > 1) {
+ title += ` (${alarms.length})`;
+ } else {
+ title += ` ${formatAlarm(alarms[0], t)}`;
+ }
+ }
+ }
+ if (includeId) {
+ title += ` [${notification.id}]`;
+ }
+ return title;
+};
diff --git a/src/common/util/permissions.js b/src/common/util/permissions.js
new file mode 100644
index 00000000..8a63b5a1
--- /dev/null
+++ b/src/common/util/permissions.js
@@ -0,0 +1,28 @@
+import { useSelector } from 'react-redux';
+
+export const useAdministrator = () => useSelector((state) => {
+ const admin = state.session.user.administrator;
+ return admin;
+});
+
+export const useManager = () => useSelector((state) => {
+ const admin = state.session.user.administrator;
+ const manager = (state.session.user.userLimit || 0) !== 0;
+ return admin || manager;
+});
+
+export const useDeviceReadonly = () => useSelector((state) => {
+ const admin = state.session.user.administrator;
+ const serverReadonly = state.session.server.readonly;
+ const userReadonly = state.session.user.readonly;
+ const serverDeviceReadonly = state.session.server.deviceReadonly;
+ const userDeviceReadonly = state.session.user.deviceReadonly;
+ return !admin && (serverReadonly || userReadonly || serverDeviceReadonly || userDeviceReadonly);
+});
+
+export const useRestriction = (key) => useSelector((state) => {
+ const admin = state.session.user.administrator;
+ const serverValue = state.session.server[key];
+ const userValue = state.session.user[key];
+ return !admin && (serverValue || userValue);
+});
diff --git a/src/common/util/preferences.js b/src/common/util/preferences.js
new file mode 100644
index 00000000..229b6f17
--- /dev/null
+++ b/src/common/util/preferences.js
@@ -0,0 +1,41 @@
+import { useSelector } from 'react-redux';
+
+const containsProperty = (object, key) => object.hasOwnProperty(key) && object[key] !== null;
+
+export const usePreference = (key, defaultValue) => useSelector((state) => {
+ if (state.session.server.forceSettings) {
+ if (containsProperty(state.session.server, key)) {
+ return state.session.server[key];
+ }
+ if (containsProperty(state.session.user, key)) {
+ return state.session.user[key];
+ }
+ return defaultValue;
+ }
+ if (containsProperty(state.session.user, key)) {
+ return state.session.user[key];
+ }
+ if (containsProperty(state.session.server, key)) {
+ return state.session.server[key];
+ }
+ return defaultValue;
+});
+
+export const useAttributePreference = (key, defaultValue) => useSelector((state) => {
+ if (state.session.server.forceSettings) {
+ if (containsProperty(state.session.server.attributes, key)) {
+ return state.session.server.attributes[key];
+ }
+ if (containsProperty(state.session.user.attributes, key)) {
+ return state.session.user.attributes[key];
+ }
+ return defaultValue;
+ }
+ if (containsProperty(state.session.user.attributes, key)) {
+ return state.session.user.attributes[key];
+ }
+ if (containsProperty(state.session.server.attributes, key)) {
+ return state.session.server.attributes[key];
+ }
+ return defaultValue;
+});
diff --git a/src/common/util/stringUtils.js b/src/common/util/stringUtils.js
new file mode 100644
index 00000000..fc997fe0
--- /dev/null
+++ b/src/common/util/stringUtils.js
@@ -0,0 +1,3 @@
+export const prefixString = (prefix, value) => prefix + value.charAt(0).toUpperCase() + value.slice(1);
+
+export const unprefixString = (prefix, value) => value.charAt(prefix.length).toLowerCase() + value.slice(prefix.length + 1);
diff --git a/src/common/util/useFeatures.js b/src/common/util/useFeatures.js
new file mode 100644
index 00000000..30361589
--- /dev/null
+++ b/src/common/util/useFeatures.js
@@ -0,0 +1,44 @@
+import { createSelector } from '@reduxjs/toolkit';
+import { useSelector } from 'react-redux';
+
+const get = (server, user, key) => {
+ if (server && user) {
+ if (user.administrator) {
+ return false;
+ }
+ if (server.forceSettings) {
+ return server.attributes[key] || user.attributes[key] || false;
+ }
+ return user.attributes[key] || server.attributes[key] || false;
+ }
+ return false;
+};
+
+const featureSelector = createSelector(
+ (state) => state.session.server,
+ (state) => state.session.user,
+ (server, user) => {
+ const disableSavedCommands = get(server, user, 'ui.disableSavedCommands');
+ const disableAttributes = get(server, user, 'ui.disableAttributes');
+ const disableVehicleFeatures = get(server, user, 'ui.disableVehicleFeatures');
+ const disableDrivers = disableVehicleFeatures || get(server, user, 'ui.disableDrivers');
+ const disableMaintenance = disableVehicleFeatures || get(server, user, 'ui.disableMaintenance');
+ const disableGroups = get(server, user, 'ui.disableGroups');
+ const disableEvents = get(server, user, 'ui.disableEvents');
+ const disableComputedAttributes = get(server, user, 'ui.disableComputedAttributes');
+ const disableCalendars = get(server, user, 'ui.disableCalendars');
+
+ return {
+ disableSavedCommands,
+ disableAttributes,
+ disableDrivers,
+ disableMaintenance,
+ disableGroups,
+ disableEvents,
+ disableComputedAttributes,
+ disableCalendars,
+ };
+ },
+);
+
+export default () => useSelector(featureSelector);
diff --git a/src/common/util/usePersistedState.js b/src/common/util/usePersistedState.js
new file mode 100644
index 00000000..70a652ad
--- /dev/null
+++ b/src/common/util/usePersistedState.js
@@ -0,0 +1,22 @@
+import { useEffect, useState } from 'react';
+
+export const savePersistedState = (key, value) => {
+ window.localStorage.setItem(key, JSON.stringify(value));
+};
+
+export default (key, defaultValue) => {
+ const [value, setValue] = useState(() => {
+ const stickyValue = window.localStorage.getItem(key);
+ return stickyValue ? JSON.parse(stickyValue) : defaultValue;
+ });
+
+ useEffect(() => {
+ if (value !== defaultValue) {
+ savePersistedState(key, value);
+ } else {
+ window.localStorage.removeItem(key);
+ }
+ }, [key, value]);
+
+ return [value, setValue];
+};
diff --git a/src/common/util/useQuery.js b/src/common/util/useQuery.js
new file mode 100644
index 00000000..f246df7c
--- /dev/null
+++ b/src/common/util/useQuery.js
@@ -0,0 +1,7 @@
+import { useMemo } from 'react';
+import { useLocation } from 'react-router-dom';
+
+export default () => {
+ const { search } = useLocation();
+ return useMemo(() => new URLSearchParams(search), [search]);
+};
diff --git a/src/index.jsx b/src/index.jsx
new file mode 100644
index 00000000..3eb96d2f
--- /dev/null
+++ b/src/index.jsx
@@ -0,0 +1,42 @@
+import '@fontsource/roboto/300.css';
+import '@fontsource/roboto/400.css';
+import '@fontsource/roboto/500.css';
+import '@fontsource/roboto/700.css';
+import React from 'react';
+import { createRoot } from 'react-dom/client';
+import { BrowserRouter } from 'react-router-dom';
+import { Provider } from 'react-redux';
+import { CssBaseline, StyledEngineProvider } from '@mui/material';
+import store from './store';
+import { LocalizationProvider } from './common/components/LocalizationProvider';
+import ErrorHandler from './common/components/ErrorHandler';
+import Navigation from './Navigation';
+import preloadImages from './map/core/preloadImages';
+import NativeInterface from './common/components/NativeInterface';
+import ServerProvider from './ServerProvider';
+import ErrorBoundary from './ErrorBoundary';
+import AppThemeProvider from './AppThemeProvider';
+
+preloadImages();
+
+const root = createRoot(document.getElementById('root'));
+root.render(
+ <ErrorBoundary>
+ <Provider store={store}>
+ <LocalizationProvider>
+ <StyledEngineProvider injectFirst>
+ <AppThemeProvider>
+ <CssBaseline />
+ <ServerProvider>
+ <BrowserRouter>
+ <Navigation />
+ </BrowserRouter>
+ <ErrorHandler />
+ <NativeInterface />
+ </ServerProvider>
+ </AppThemeProvider>
+ </StyledEngineProvider>
+ </LocalizationProvider>
+ </Provider>
+ </ErrorBoundary>,
+);
diff --git a/src/login/ChangeServerPage.jsx b/src/login/ChangeServerPage.jsx
new file mode 100644
index 00000000..1ffc1257
--- /dev/null
+++ b/src/login/ChangeServerPage.jsx
@@ -0,0 +1,83 @@
+import React from 'react';
+import ElectricalServicesIcon from '@mui/icons-material/ElectricalServices';
+import { makeStyles } from '@mui/styles';
+import {
+ Autocomplete, Button, Container, createFilterOptions, TextField,
+} from '@mui/material';
+import { useNavigate } from 'react-router-dom';
+import { useTranslation } from '../common/components/LocalizationProvider';
+
+const currentServer = `${window.location.protocol}//${window.location.host}`;
+
+const officialServers = [
+ currentServer,
+ 'https://demo.traccar.org',
+ 'https://demo2.traccar.org',
+ 'https://demo3.traccar.org',
+ 'https://demo4.traccar.org',
+ 'https://server.traccar.org',
+ 'http://localhost:8082',
+ 'http://localhost:3000',
+];
+
+const useStyles = makeStyles((theme) => ({
+ icon: {
+ textAlign: 'center',
+ fontSize: '128px',
+ color: theme.palette.neutral.main,
+ },
+ container: {
+ textAlign: 'center',
+ padding: theme.spacing(5, 3),
+ },
+ field: {
+ margin: theme.spacing(3, 0),
+ },
+}));
+
+const ChangeServerPage = () => {
+ const classes = useStyles();
+ const navigate = useNavigate();
+ const t = useTranslation();
+
+ const filter = createFilterOptions();
+
+ const handleSubmit = (url) => {
+ if (window.webkit && window.webkit.messageHandlers.appInterface) {
+ window.webkit.messageHandlers.appInterface.postMessage(`server|${url}`);
+ } else if (window.appInterface) {
+ window.appInterface.postMessage(`server|${url}`);
+ } else {
+ window.location.replace(url);
+ }
+ };
+
+ return (
+ <Container maxWidth="xs" className={classes.container}>
+ <ElectricalServicesIcon className={classes.icon} />
+ <Autocomplete
+ freeSolo
+ className={classes.field}
+ options={officialServers}
+ renderInput={(params) => <TextField {...params} label={t('settingsServer')} />}
+ value={currentServer}
+ onChange={(_, value) => value && handleSubmit(value)}
+ filterOptions={(options, params) => {
+ const filtered = filter(options, params);
+ if (params.inputValue && !filtered.includes(params.inputValue)) {
+ filtered.push(params.inputValue);
+ }
+ return filtered;
+ }}
+ />
+ <Button
+ onClick={() => navigate(-1)}
+ color="secondary"
+ >
+ {t('sharedCancel')}
+ </Button>
+ </Container>
+ );
+};
+
+export default ChangeServerPage;
diff --git a/src/login/LoginLayout.jsx b/src/login/LoginLayout.jsx
new file mode 100644
index 00000000..8f2ee6ca
--- /dev/null
+++ b/src/login/LoginLayout.jsx
@@ -0,0 +1,62 @@
+import React from 'react';
+import { useMediaQuery, Paper } from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+import { useTheme } from '@mui/material/styles';
+import LogoImage from './LogoImage';
+
+const useStyles = makeStyles((theme) => ({
+ root: {
+ display: 'flex',
+ height: '100%',
+ },
+ sidebar: {
+ display: 'flex',
+ justifyContent: 'center',
+ alignItems: 'center',
+ background: theme.palette.primary.main,
+ paddingBottom: theme.spacing(5),
+ width: theme.dimensions.sidebarWidth,
+ [theme.breakpoints.down('lg')]: {
+ width: theme.dimensions.sidebarWidthTablet,
+ },
+ [theme.breakpoints.down('sm')]: {
+ width: '0px',
+ },
+ },
+ paper: {
+ display: 'flex',
+ flexDirection: 'column',
+ justifyContent: 'center',
+ alignItems: 'center',
+ flex: 1,
+ boxShadow: '-2px 0px 16px rgba(0, 0, 0, 0.25)',
+ [theme.breakpoints.up('lg')]: {
+ padding: theme.spacing(0, 25, 0, 0),
+ },
+ },
+ form: {
+ maxWidth: theme.spacing(52),
+ padding: theme.spacing(5),
+ width: '100%',
+ },
+}));
+
+const LoginLayout = ({ children }) => {
+ const classes = useStyles();
+ const theme = useTheme();
+
+ return (
+ <main className={classes.root}>
+ <div className={classes.sidebar}>
+ {!useMediaQuery(theme.breakpoints.down('lg')) && <LogoImage color={theme.palette.secondary.contrastText} />}
+ </div>
+ <Paper className={classes.paper}>
+ <form className={classes.form}>
+ {children}
+ </form>
+ </Paper>
+ </main>
+ );
+};
+
+export default LoginLayout;
diff --git a/src/login/LoginPage.jsx b/src/login/LoginPage.jsx
new file mode 100644
index 00000000..27aad7c9
--- /dev/null
+++ b/src/login/LoginPage.jsx
@@ -0,0 +1,263 @@
+import React, { useEffect, useState } from 'react';
+import dayjs from 'dayjs';
+import {
+ useMediaQuery, Select, MenuItem, FormControl, Button, TextField, Link, Snackbar, IconButton, Tooltip, LinearProgress, Box,
+} from '@mui/material';
+import ReactCountryFlag from 'react-country-flag';
+import makeStyles from '@mui/styles/makeStyles';
+import CloseIcon from '@mui/icons-material/Close';
+import LockOpenIcon from '@mui/icons-material/LockOpen';
+import { useTheme } from '@mui/material/styles';
+import { useDispatch, useSelector } from 'react-redux';
+import { useNavigate } from 'react-router-dom';
+import { sessionActions } from '../store';
+import { useLocalization, useTranslation } from '../common/components/LocalizationProvider';
+import LoginLayout from './LoginLayout';
+import usePersistedState from '../common/util/usePersistedState';
+import { handleLoginTokenListeners, nativeEnvironment, nativePostMessage } from '../common/components/NativeInterface';
+import LogoImage from './LogoImage';
+import { useCatch } from '../reactHelper';
+
+const useStyles = makeStyles((theme) => ({
+ options: {
+ position: 'fixed',
+ top: theme.spacing(2),
+ right: theme.spacing(2),
+ display: 'flex',
+ flexDirection: 'row',
+ gap: theme.spacing(1),
+ },
+ container: {
+ display: 'flex',
+ flexDirection: 'column',
+ gap: theme.spacing(2),
+ },
+ extraContainer: {
+ display: 'flex',
+ flexDirection: 'row',
+ justifyContent: 'center',
+ gap: theme.spacing(4),
+ marginTop: theme.spacing(2),
+ },
+ registerButton: {
+ minWidth: 'unset',
+ },
+ link: {
+ cursor: 'pointer',
+ },
+}));
+
+const LoginPage = () => {
+ const classes = useStyles();
+ const dispatch = useDispatch();
+ const navigate = useNavigate();
+ const theme = useTheme();
+ const t = useTranslation();
+
+ const { languages, language, setLanguage } = useLocalization();
+ const languageList = Object.entries(languages).map((values) => ({ code: values[0], country: values[1].country, name: values[1].name }));
+
+ const [failed, setFailed] = useState(false);
+
+ const [email, setEmail] = usePersistedState('loginEmail', '');
+ const [password, setPassword] = useState('');
+ const [code, setCode] = useState('');
+
+ const registrationEnabled = useSelector((state) => state.session.server.registration);
+ const languageEnabled = useSelector((state) => !state.session.server.attributes['ui.disableLoginLanguage']);
+ const changeEnabled = useSelector((state) => !state.session.server.attributes.disableChange);
+ const emailEnabled = useSelector((state) => state.session.server.emailEnabled);
+ const openIdEnabled = useSelector((state) => state.session.server.openIdEnabled);
+ const openIdForced = useSelector((state) => state.session.server.openIdEnabled && state.session.server.openIdForce);
+ const [codeEnabled, setCodeEnabled] = useState(false);
+
+ const [announcementShown, setAnnouncementShown] = useState(false);
+ const announcement = useSelector((state) => state.session.server.announcement);
+
+ const generateLoginToken = async () => {
+ if (nativeEnvironment) {
+ let token = '';
+ try {
+ const expiration = dayjs().add(6, 'months').toISOString();
+ const response = await fetch('/api/session/token', {
+ method: 'POST',
+ body: new URLSearchParams(`expiration=${expiration}`),
+ });
+ if (response.ok) {
+ token = await response.text();
+ }
+ } catch (error) {
+ token = '';
+ }
+ nativePostMessage(`login|${token}`);
+ }
+ };
+
+ const handlePasswordLogin = async (event) => {
+ event.preventDefault();
+ setFailed(false);
+ try {
+ const query = `email=${encodeURIComponent(email)}&password=${encodeURIComponent(password)}`;
+ const response = await fetch('/api/session', {
+ method: 'POST',
+ body: new URLSearchParams(code.length ? `${query}&code=${code}` : query),
+ });
+ if (response.ok) {
+ const user = await response.json();
+ generateLoginToken();
+ dispatch(sessionActions.updateUser(user));
+ navigate('/');
+ } else if (response.status === 401 && response.headers.get('WWW-Authenticate') === 'TOTP') {
+ setCodeEnabled(true);
+ } else {
+ throw Error(await response.text());
+ }
+ } catch (error) {
+ setFailed(true);
+ setPassword('');
+ }
+ };
+
+ const handleTokenLogin = useCatch(async (token) => {
+ const response = await fetch(`/api/session?token=${encodeURIComponent(token)}`);
+ if (response.ok) {
+ const user = await response.json();
+ dispatch(sessionActions.updateUser(user));
+ navigate('/');
+ } else {
+ throw Error(await response.text());
+ }
+ });
+
+ const handleOpenIdLogin = () => {
+ document.location = '/api/session/openid/auth';
+ };
+
+ useEffect(() => nativePostMessage('authentication'), []);
+
+ useEffect(() => {
+ const listener = (token) => handleTokenLogin(token);
+ handleLoginTokenListeners.add(listener);
+ return () => handleLoginTokenListeners.delete(listener);
+ }, []);
+
+ if (openIdForced) {
+ handleOpenIdLogin();
+ return (<LinearProgress />);
+ }
+
+ return (
+ <LoginLayout>
+ <div className={classes.options}>
+ {nativeEnvironment && changeEnabled && (
+ <Tooltip title={t('settingsServer')}>
+ <IconButton onClick={() => navigate('/change-server')}>
+ <LockOpenIcon />
+ </IconButton>
+ </Tooltip>
+ )}
+ {languageEnabled && (
+ <FormControl>
+ <Select value={language} onChange={(e) => setLanguage(e.target.value)}>
+ {languageList.map((it) => (
+ <MenuItem key={it.code} value={it.code}>
+ <Box component="span" sx={{ mr: 1 }}>
+ <ReactCountryFlag countryCode={it.country} svg />
+ </Box>
+ {it.name}
+ </MenuItem>
+ ))}
+ </Select>
+ </FormControl>
+ )}
+ </div>
+ <div className={classes.container}>
+ {useMediaQuery(theme.breakpoints.down('lg')) && <LogoImage color={theme.palette.primary.main} />}
+ <TextField
+ required
+ error={failed}
+ label={t('userEmail')}
+ name="email"
+ value={email}
+ autoComplete="email"
+ autoFocus={!email}
+ onChange={(e) => setEmail(e.target.value)}
+ helperText={failed && 'Invalid username or password'}
+ />
+ <TextField
+ required
+ error={failed}
+ label={t('userPassword')}
+ name="password"
+ value={password}
+ type="password"
+ autoComplete="current-password"
+ autoFocus={!!email}
+ onChange={(e) => setPassword(e.target.value)}
+ />
+ {codeEnabled && (
+ <TextField
+ required
+ error={failed}
+ label={t('loginTotpCode')}
+ name="code"
+ value={code}
+ type="number"
+ onChange={(e) => setCode(e.target.value)}
+ />
+ )}
+ <Button
+ onClick={handlePasswordLogin}
+ type="submit"
+ variant="contained"
+ color="secondary"
+ disabled={!email || !password || (codeEnabled && !code)}
+ >
+ {t('loginLogin')}
+ </Button>
+ {openIdEnabled && (
+ <Button
+ onClick={() => handleOpenIdLogin()}
+ variant="contained"
+ color="secondary"
+ >
+ {t('loginOpenId')}
+ </Button>
+ )}
+ <div className={classes.extraContainer}>
+ {registrationEnabled && (
+ <Link
+ onClick={() => navigate('/register')}
+ className={classes.link}
+ underline="none"
+ variant="caption"
+ >
+ {t('loginRegister')}
+ </Link>
+ )}
+ {emailEnabled && (
+ <Link
+ onClick={() => navigate('/reset-password')}
+ className={classes.link}
+ underline="none"
+ variant="caption"
+ >
+ {t('loginReset')}
+ </Link>
+ )}
+ </div>
+ </div>
+ <Snackbar
+ open={!!announcement && !announcementShown}
+ message={announcement}
+ action={(
+ <IconButton size="small" color="inherit" onClick={() => setAnnouncementShown(true)}>
+ <CloseIcon fontSize="small" />
+ </IconButton>
+ )}
+ />
+ </LoginLayout>
+ );
+};
+
+export default LoginPage;
diff --git a/src/login/LogoImage.jsx b/src/login/LogoImage.jsx
new file mode 100644
index 00000000..e92403ef
--- /dev/null
+++ b/src/login/LogoImage.jsx
@@ -0,0 +1,36 @@
+import React from 'react';
+import { useTheme, useMediaQuery } from '@mui/material';
+import { useSelector } from 'react-redux';
+import { makeStyles } from '@mui/styles';
+import Logo from '../resources/images/logo.svg?react';
+
+const useStyles = makeStyles((theme) => ({
+ image: {
+ alignSelf: 'center',
+ maxWidth: '240px',
+ maxHeight: '120px',
+ width: 'auto',
+ height: 'auto',
+ margin: theme.spacing(2),
+ },
+}));
+
+const LogoImage = ({ color }) => {
+ const theme = useTheme();
+ const classes = useStyles();
+
+ const expanded = !useMediaQuery(theme.breakpoints.down('lg'));
+
+ const logo = useSelector((state) => state.session.server.attributes?.logo);
+ const logoInverted = useSelector((state) => state.session.server.attributes?.logoInverted);
+
+ if (logo) {
+ if (expanded && logoInverted) {
+ return <img className={classes.image} src={logoInverted} alt="" />;
+ }
+ return <img className={classes.image} src={logo} alt="" />;
+ }
+ return <Logo className={classes.image} style={{ color }} />;
+};
+
+export default LogoImage;
diff --git a/src/login/RegisterPage.jsx b/src/login/RegisterPage.jsx
new file mode 100644
index 00000000..4e2a05e1
--- /dev/null
+++ b/src/login/RegisterPage.jsx
@@ -0,0 +1,148 @@
+import React, { useState } from 'react';
+import { useDispatch, useSelector } from 'react-redux';
+import {
+ Button, TextField, Typography, Snackbar, IconButton,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+import { useNavigate } from 'react-router-dom';
+import ArrowBackIcon from '@mui/icons-material/ArrowBack';
+import LoginLayout from './LoginLayout';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import { snackBarDurationShortMs } from '../common/util/duration';
+import { useCatch, useEffectAsync } from '../reactHelper';
+import { sessionActions } from '../store';
+
+const useStyles = makeStyles((theme) => ({
+ container: {
+ display: 'flex',
+ flexDirection: 'column',
+ gap: theme.spacing(2),
+ },
+ header: {
+ display: 'flex',
+ alignItems: 'center',
+ },
+ title: {
+ fontSize: theme.spacing(3),
+ fontWeight: 500,
+ marginLeft: theme.spacing(1),
+ textTransform: 'uppercase',
+ },
+}));
+
+const RegisterPage = () => {
+ const classes = useStyles();
+ const dispatch = useDispatch();
+ const navigate = useNavigate();
+ const t = useTranslation();
+
+ const server = useSelector((state) => state.session.server);
+ const totpForce = useSelector((state) => state.session.server.attributes.totpForce);
+
+ const [name, setName] = useState('');
+ const [email, setEmail] = useState('');
+ const [password, setPassword] = useState('');
+ const [totpKey, setTotpKey] = useState(null);
+ const [snackbarOpen, setSnackbarOpen] = useState(false);
+
+ useEffectAsync(async () => {
+ if (totpForce) {
+ const response = await fetch('/api/users/totp', { method: 'POST' });
+ if (response.ok) {
+ setTotpKey(await response.text());
+ } else {
+ throw Error(await response.text());
+ }
+ }
+ }, [totpForce, setTotpKey]);
+
+ const handleSubmit = useCatch(async (event) => {
+ event.preventDefault();
+ const response = await fetch('/api/users', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ name, email, password, totpKey }),
+ });
+ if (response.ok) {
+ setSnackbarOpen(true);
+ } else {
+ throw Error(await response.text());
+ }
+ });
+
+ return (
+ <LoginLayout>
+ <div className={classes.container}>
+ <div className={classes.header}>
+ {!server.newServer && (
+ <IconButton color="primary" onClick={() => navigate('/login')}>
+ <ArrowBackIcon />
+ </IconButton>
+ )}
+ <Typography className={classes.title} color="primary">
+ {t('loginRegister')}
+ </Typography>
+ </div>
+ <TextField
+ required
+ label={t('sharedName')}
+ name="name"
+ value={name}
+ autoComplete="name"
+ autoFocus
+ onChange={(event) => setName(event.target.value)}
+ />
+ <TextField
+ required
+ type="email"
+ label={t('userEmail')}
+ name="email"
+ value={email}
+ autoComplete="email"
+ onChange={(event) => setEmail(event.target.value)}
+ />
+ <TextField
+ required
+ label={t('userPassword')}
+ name="password"
+ value={password}
+ type="password"
+ autoComplete="current-password"
+ onChange={(event) => setPassword(event.target.value)}
+ />
+ {totpForce && (
+ <TextField
+ required
+ label={t('loginTotpKey')}
+ name="totpKey"
+ value={totpKey || ''}
+ InputProps={{
+ readOnly: true,
+ }}
+ />
+ )}
+ <Button
+ variant="contained"
+ color="secondary"
+ onClick={handleSubmit}
+ type="submit"
+ disabled={!name || !password || !(server.newServer || /(.+)@(.+)\.(.{2,})/.test(email))}
+ fullWidth
+ >
+ {t('loginRegister')}
+ </Button>
+ </div>
+ <Snackbar
+ open={snackbarOpen}
+ onClose={() => {
+ dispatch(sessionActions.updateServer({ ...server, newServer: false }));
+ navigate('/login');
+ }}
+ autoHideDuration={snackBarDurationShortMs}
+ message={t('loginCreated')}
+ />
+ </LoginLayout>
+ );
+};
+
+export default RegisterPage;
diff --git a/src/login/ResetPasswordPage.jsx b/src/login/ResetPasswordPage.jsx
new file mode 100644
index 00000000..d10299ca
--- /dev/null
+++ b/src/login/ResetPasswordPage.jsx
@@ -0,0 +1,118 @@
+import React, { useState } from 'react';
+import {
+ Button, TextField, Typography, Snackbar, IconButton,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+import { useNavigate } from 'react-router-dom';
+import ArrowBackIcon from '@mui/icons-material/ArrowBack';
+import LoginLayout from './LoginLayout';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import useQuery from '../common/util/useQuery';
+import { snackBarDurationShortMs } from '../common/util/duration';
+import { useCatch } from '../reactHelper';
+
+const useStyles = makeStyles((theme) => ({
+ container: {
+ display: 'flex',
+ flexDirection: 'column',
+ gap: theme.spacing(2),
+ },
+ header: {
+ display: 'flex',
+ alignItems: 'center',
+ },
+ title: {
+ fontSize: theme.spacing(3),
+ fontWeight: 500,
+ marginLeft: theme.spacing(1),
+ textTransform: 'uppercase',
+ },
+}));
+
+const ResetPasswordPage = () => {
+ const classes = useStyles();
+ const navigate = useNavigate();
+ const t = useTranslation();
+ const query = useQuery();
+
+ const token = query.get('passwordReset');
+
+ const [email, setEmail] = useState('');
+ const [password, setPassword] = useState('');
+ const [snackbarOpen, setSnackbarOpen] = useState(false);
+
+ const handleSubmit = useCatch(async (event) => {
+ event.preventDefault();
+ let response;
+ if (!token) {
+ response = await fetch('/api/password/reset', {
+ method: 'POST',
+ body: new URLSearchParams(`email=${encodeURIComponent(email)}`),
+ });
+ } else {
+ response = await fetch('/api/password/update', {
+ method: 'POST',
+ body: new URLSearchParams(`token=${encodeURIComponent(token)}&password=${encodeURIComponent(password)}`),
+ });
+ }
+ if (response.ok) {
+ setSnackbarOpen(true);
+ } else {
+ throw Error(await response.text());
+ }
+ });
+
+ return (
+ <LoginLayout>
+ <div className={classes.container}>
+ <div className={classes.header}>
+ <IconButton color="primary" onClick={() => navigate('/login')}>
+ <ArrowBackIcon />
+ </IconButton>
+ <Typography className={classes.title} color="primary">
+ {t('loginReset')}
+ </Typography>
+ </div>
+ {!token ? (
+ <TextField
+ required
+ type="email"
+ label={t('userEmail')}
+ name="email"
+ value={email}
+ autoComplete="email"
+ onChange={(event) => setEmail(event.target.value)}
+ />
+ ) : (
+ <TextField
+ required
+ label={t('userPassword')}
+ name="password"
+ value={password}
+ type="password"
+ autoComplete="current-password"
+ onChange={(event) => setPassword(event.target.value)}
+ />
+ )}
+ <Button
+ variant="contained"
+ color="secondary"
+ type="submit"
+ onClick={handleSubmit}
+ disabled={!/(.+)@(.+)\.(.{2,})/.test(email) && !password}
+ fullWidth
+ >
+ {t('loginReset')}
+ </Button>
+ </div>
+ <Snackbar
+ open={snackbarOpen}
+ onClose={() => navigate('/login')}
+ autoHideDuration={snackBarDurationShortMs}
+ message={!token ? t('loginResetSuccess') : t('loginUpdateSuccess')}
+ />
+ </LoginLayout>
+ );
+};
+
+export default ResetPasswordPage;
diff --git a/src/main/DeviceList.jsx b/src/main/DeviceList.jsx
new file mode 100644
index 00000000..eb51232f
--- /dev/null
+++ b/src/main/DeviceList.jsx
@@ -0,0 +1,66 @@
+import React, { useEffect, useRef, useState } from 'react';
+import { useDispatch } from 'react-redux';
+import makeStyles from '@mui/styles/makeStyles';
+import { FixedSizeList } from 'react-window';
+import AutoSizer from 'react-virtualized-auto-sizer';
+import { devicesActions } from '../store';
+import { useEffectAsync } from '../reactHelper';
+import DeviceRow from './DeviceRow';
+
+const useStyles = makeStyles((theme) => ({
+ list: {
+ maxHeight: '100%',
+ },
+ listInner: {
+ position: 'relative',
+ margin: theme.spacing(1.5, 0),
+ },
+}));
+
+const DeviceList = ({ devices }) => {
+ const classes = useStyles();
+ const dispatch = useDispatch();
+ const listInnerEl = useRef(null);
+
+ if (listInnerEl.current) {
+ listInnerEl.current.className = classes.listInner;
+ }
+
+ const [, setTime] = useState(Date.now());
+
+ useEffect(() => {
+ const interval = setInterval(() => setTime(Date.now()), 60000);
+ return () => {
+ clearInterval(interval);
+ };
+ }, []);
+
+ useEffectAsync(async () => {
+ const response = await fetch('/api/devices');
+ if (response.ok) {
+ dispatch(devicesActions.refresh(await response.json()));
+ } else {
+ throw Error(await response.text());
+ }
+ }, []);
+
+ return (
+ <AutoSizer className={classes.list}>
+ {({ height, width }) => (
+ <FixedSizeList
+ width={width}
+ height={height}
+ itemCount={devices.length}
+ itemData={devices}
+ itemSize={72}
+ overscanCount={10}
+ innerRef={listInnerEl}
+ >
+ {DeviceRow}
+ </FixedSizeList>
+ )}
+ </AutoSizer>
+ );
+};
+
+export default DeviceList;
diff --git a/src/main/DeviceRow.jsx b/src/main/DeviceRow.jsx
new file mode 100644
index 00000000..d9c1a189
--- /dev/null
+++ b/src/main/DeviceRow.jsx
@@ -0,0 +1,145 @@
+import React from 'react';
+import { useDispatch, useSelector } from 'react-redux';
+import makeStyles from '@mui/styles/makeStyles';
+import {
+ IconButton, Tooltip, Avatar, ListItemAvatar, ListItemText, ListItemButton,
+} from '@mui/material';
+import BatteryFullIcon from '@mui/icons-material/BatteryFull';
+import BatteryChargingFullIcon from '@mui/icons-material/BatteryChargingFull';
+import Battery60Icon from '@mui/icons-material/Battery60';
+import BatteryCharging60Icon from '@mui/icons-material/BatteryCharging60';
+import Battery20Icon from '@mui/icons-material/Battery20';
+import BatteryCharging20Icon from '@mui/icons-material/BatteryCharging20';
+import ErrorIcon from '@mui/icons-material/Error';
+import dayjs from 'dayjs';
+import relativeTime from 'dayjs/plugin/relativeTime';
+import { devicesActions } from '../store';
+import {
+ formatAlarm, formatBoolean, formatPercentage, formatStatus, getStatusColor,
+} from '../common/util/formatter';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import { mapIconKey, mapIcons } from '../map/core/preloadImages';
+import { useAdministrator } from '../common/util/permissions';
+import EngineIcon from '../resources/images/data/engine.svg?react';
+import { useAttributePreference } from '../common/util/preferences';
+
+dayjs.extend(relativeTime);
+
+const useStyles = makeStyles((theme) => ({
+ icon: {
+ width: '25px',
+ height: '25px',
+ filter: 'brightness(0) invert(1)',
+ },
+ batteryText: {
+ fontSize: '0.75rem',
+ fontWeight: 'normal',
+ lineHeight: '0.875rem',
+ },
+ success: {
+ color: theme.palette.success.main,
+ },
+ warning: {
+ color: theme.palette.warning.main,
+ },
+ error: {
+ color: theme.palette.error.main,
+ },
+ neutral: {
+ color: theme.palette.neutral.main,
+ },
+}));
+
+const DeviceRow = ({ data, index, style }) => {
+ const classes = useStyles();
+ const dispatch = useDispatch();
+ const t = useTranslation();
+
+ const admin = useAdministrator();
+
+ const item = data[index];
+ const position = useSelector((state) => state.session.positions[item.id]);
+
+ const devicePrimary = useAttributePreference('devicePrimary', 'name');
+ const deviceSecondary = useAttributePreference('deviceSecondary', '');
+
+ const secondaryText = () => {
+ let status;
+ if (item.status === 'online' || !item.lastUpdate) {
+ status = formatStatus(item.status, t);
+ } else {
+ status = dayjs(item.lastUpdate).fromNow();
+ }
+ return (
+ <>
+ {deviceSecondary && item[deviceSecondary] && `${item[deviceSecondary]} • `}
+ <span className={classes[getStatusColor(item.status)]}>{status}</span>
+ </>
+ );
+ };
+
+ return (
+ <div style={style}>
+ <ListItemButton
+ key={item.id}
+ onClick={() => dispatch(devicesActions.selectId(item.id))}
+ disabled={!admin && item.disabled}
+ >
+ <ListItemAvatar>
+ <Avatar>
+ <img className={classes.icon} src={mapIcons[mapIconKey(item.category)]} alt="" />
+ </Avatar>
+ </ListItemAvatar>
+ <ListItemText
+ primary={item[devicePrimary]}
+ primaryTypographyProps={{ noWrap: true }}
+ secondary={secondaryText()}
+ secondaryTypographyProps={{ noWrap: true }}
+ />
+ {position && (
+ <>
+ {position.attributes.hasOwnProperty('alarm') && (
+ <Tooltip title={`${t('eventAlarm')}: ${formatAlarm(position.attributes.alarm, t)}`}>
+ <IconButton size="small">
+ <ErrorIcon fontSize="small" className={classes.error} />
+ </IconButton>
+ </Tooltip>
+ )}
+ {position.attributes.hasOwnProperty('ignition') && (
+ <Tooltip title={`${t('positionIgnition')}: ${formatBoolean(position.attributes.ignition, t)}`}>
+ <IconButton size="small">
+ {position.attributes.ignition ? (
+ <EngineIcon width={20} height={20} className={classes.success} />
+ ) : (
+ <EngineIcon width={20} height={20} className={classes.neutral} />
+ )}
+ </IconButton>
+ </Tooltip>
+ )}
+ {position.attributes.hasOwnProperty('batteryLevel') && (
+ <Tooltip title={`${t('positionBatteryLevel')}: ${formatPercentage(position.attributes.batteryLevel)}`}>
+ <IconButton size="small">
+ {(position.attributes.batteryLevel > 70 && (
+ position.attributes.charge
+ ? (<BatteryChargingFullIcon fontSize="small" className={classes.success} />)
+ : (<BatteryFullIcon fontSize="small" className={classes.success} />)
+ )) || (position.attributes.batteryLevel > 30 && (
+ position.attributes.charge
+ ? (<BatteryCharging60Icon fontSize="small" className={classes.warning} />)
+ : (<Battery60Icon fontSize="small" className={classes.warning} />)
+ )) || (
+ position.attributes.charge
+ ? (<BatteryCharging20Icon fontSize="small" className={classes.error} />)
+ : (<Battery20Icon fontSize="small" className={classes.error} />)
+ )}
+ </IconButton>
+ </Tooltip>
+ )}
+ </>
+ )}
+ </ListItemButton>
+ </div>
+ );
+};
+
+export default DeviceRow;
diff --git a/src/main/EventsDrawer.jsx b/src/main/EventsDrawer.jsx
new file mode 100644
index 00000000..f9602e95
--- /dev/null
+++ b/src/main/EventsDrawer.jsx
@@ -0,0 +1,81 @@
+import React from 'react';
+import { useDispatch, useSelector } from 'react-redux';
+import { useNavigate } from 'react-router-dom';
+import {
+ Drawer, IconButton, List, ListItemButton, ListItemText, Toolbar, Typography,
+} from '@mui/material';
+import { makeStyles } from '@mui/styles';
+import DeleteIcon from '@mui/icons-material/Delete';
+import { formatNotificationTitle, formatTime } from '../common/util/formatter';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import { eventsActions } from '../store';
+import { usePreference } from '../common/util/preferences';
+
+const useStyles = makeStyles((theme) => ({
+ drawer: {
+ width: theme.dimensions.eventsDrawerWidth,
+ },
+ toolbar: {
+ paddingLeft: theme.spacing(2),
+ paddingRight: theme.spacing(2),
+ },
+ title: {
+ flexGrow: 1,
+ },
+}));
+
+const EventsDrawer = ({ open, onClose }) => {
+ const classes = useStyles();
+ const navigate = useNavigate();
+ const dispatch = useDispatch();
+ const t = useTranslation();
+
+ const hours12 = usePreference('twelveHourFormat');
+
+ const devices = useSelector((state) => state.devices.items);
+
+ const events = useSelector((state) => state.events.items);
+
+ const formatType = (event) => formatNotificationTitle(t, {
+ type: event.type,
+ attributes: {
+ alarms: event.attributes.alarm,
+ },
+ });
+
+ return (
+ <Drawer
+ anchor="right"
+ open={open}
+ onClose={onClose}
+ >
+ <Toolbar className={classes.toolbar} disableGutters>
+ <Typography variant="h6" className={classes.title}>
+ {t('reportEvents')}
+ </Typography>
+ <IconButton size="small" color="inherit" onClick={() => dispatch(eventsActions.deleteAll())}>
+ <DeleteIcon fontSize="small" />
+ </IconButton>
+ </Toolbar>
+ <List className={classes.drawer} dense>
+ {events.map((event) => (
+ <ListItemButton
+ key={event.id}
+ onClick={() => navigate(`/event/${event.id}`)}
+ disabled={!event.id}
+ >
+ <ListItemText
+ primary={`${devices[event.deviceId]?.name} • ${formatType(event)}`}
+ secondary={formatTime(event.eventTime, 'seconds', hours12)}
+ />
+ <IconButton size="small" onClick={() => dispatch(eventsActions.delete(event))}>
+ <DeleteIcon fontSize="small" className={classes.delete} />
+ </IconButton>
+ </ListItemButton>
+ ))}
+ </List>
+ </Drawer>
+ );
+};
+
+export default EventsDrawer;
diff --git a/src/main/MainMap.jsx b/src/main/MainMap.jsx
new file mode 100644
index 00000000..3b57c745
--- /dev/null
+++ b/src/main/MainMap.jsx
@@ -0,0 +1,66 @@
+import React, { useCallback } from 'react';
+import { useTheme } from '@mui/material/styles';
+import useMediaQuery from '@mui/material/useMediaQuery';
+import { useDispatch, useSelector } from 'react-redux';
+import MapView from '../map/core/MapView';
+import MapSelectedDevice from '../map/main/MapSelectedDevice';
+import MapAccuracy from '../map/main/MapAccuracy';
+import MapGeofence from '../map/MapGeofence';
+import MapCurrentLocation from '../map/MapCurrentLocation';
+import PoiMap from '../map/main/PoiMap';
+import MapPadding from '../map/MapPadding';
+import { devicesActions } from '../store';
+import MapDefaultCamera from '../map/main/MapDefaultCamera';
+import MapLiveRoutes from '../map/main/MapLiveRoutes';
+import MapPositions from '../map/MapPositions';
+import MapOverlay from '../map/overlay/MapOverlay';
+import MapGeocoder from '../map/geocoder/MapGeocoder';
+import MapScale from '../map/MapScale';
+import MapNotification from '../map/notification/MapNotification';
+import useFeatures from '../common/util/useFeatures';
+
+const MainMap = ({ filteredPositions, selectedPosition, onEventsClick }) => {
+ const theme = useTheme();
+ const dispatch = useDispatch();
+
+ const desktop = useMediaQuery(theme.breakpoints.up('md'));
+
+ const eventsAvailable = useSelector((state) => !!state.events.items.length);
+
+ const features = useFeatures();
+
+ const onMarkerClick = useCallback((_, deviceId) => {
+ dispatch(devicesActions.selectId(deviceId));
+ }, [dispatch]);
+
+ return (
+ <>
+ <MapView>
+ <MapOverlay />
+ <MapGeofence />
+ <MapAccuracy positions={filteredPositions} />
+ <MapLiveRoutes />
+ <MapPositions
+ positions={filteredPositions}
+ onClick={onMarkerClick}
+ selectedPosition={selectedPosition}
+ showStatus
+ />
+ <MapDefaultCamera />
+ <MapSelectedDevice />
+ <PoiMap />
+ </MapView>
+ <MapScale />
+ <MapCurrentLocation />
+ <MapGeocoder />
+ {!features.disableEvents && (
+ <MapNotification enabled={eventsAvailable} onClick={onEventsClick} />
+ )}
+ {desktop && (
+ <MapPadding left={parseInt(theme.dimensions.drawerWidthDesktop, 10)} />
+ )}
+ </>
+ );
+};
+
+export default MainMap;
diff --git a/src/main/MainPage.jsx b/src/main/MainPage.jsx
new file mode 100644
index 00000000..8369ba97
--- /dev/null
+++ b/src/main/MainPage.jsx
@@ -0,0 +1,160 @@
+import React, {
+ useState, useCallback, useEffect,
+} from 'react';
+import { Paper } from '@mui/material';
+import { makeStyles } from '@mui/styles';
+import { useTheme } from '@mui/material/styles';
+import useMediaQuery from '@mui/material/useMediaQuery';
+import { useDispatch, useSelector } from 'react-redux';
+import DeviceList from './DeviceList';
+import BottomMenu from '../common/components/BottomMenu';
+import StatusCard from '../common/components/StatusCard';
+import { devicesActions } from '../store';
+import usePersistedState from '../common/util/usePersistedState';
+import EventsDrawer from './EventsDrawer';
+import useFilter from './useFilter';
+import MainToolbar from './MainToolbar';
+import MainMap from './MainMap';
+import { useAttributePreference } from '../common/util/preferences';
+
+const useStyles = makeStyles((theme) => ({
+ root: {
+ height: '100%',
+ },
+ sidebar: {
+ pointerEvents: 'none',
+ display: 'flex',
+ flexDirection: 'column',
+ [theme.breakpoints.up('md')]: {
+ position: 'fixed',
+ left: 0,
+ top: 0,
+ height: `calc(100% - ${theme.spacing(3)})`,
+ width: theme.dimensions.drawerWidthDesktop,
+ margin: theme.spacing(1.5),
+ zIndex: 3,
+ },
+ [theme.breakpoints.down('md')]: {
+ height: '100%',
+ width: '100%',
+ },
+ },
+ header: {
+ pointerEvents: 'auto',
+ zIndex: 6,
+ },
+ footer: {
+ pointerEvents: 'auto',
+ zIndex: 5,
+ },
+ middle: {
+ flex: 1,
+ display: 'grid',
+ },
+ contentMap: {
+ pointerEvents: 'auto',
+ gridArea: '1 / 1',
+ },
+ contentList: {
+ pointerEvents: 'auto',
+ gridArea: '1 / 1',
+ zIndex: 4,
+ },
+}));
+
+const MainPage = () => {
+ const classes = useStyles();
+ const dispatch = useDispatch();
+ const theme = useTheme();
+
+ const desktop = useMediaQuery(theme.breakpoints.up('md'));
+
+ const mapOnSelect = useAttributePreference('mapOnSelect', true);
+
+ const selectedDeviceId = useSelector((state) => state.devices.selectedId);
+ const positions = useSelector((state) => state.session.positions);
+ const [filteredPositions, setFilteredPositions] = useState([]);
+ const selectedPosition = filteredPositions.find((position) => selectedDeviceId && position.deviceId === selectedDeviceId);
+
+ const [filteredDevices, setFilteredDevices] = useState([]);
+
+ const [keyword, setKeyword] = useState('');
+ const [filter, setFilter] = usePersistedState('filter', {
+ statuses: [],
+ groups: [],
+ });
+ const [filterSort, setFilterSort] = usePersistedState('filterSort', '');
+ const [filterMap, setFilterMap] = usePersistedState('filterMap', false);
+
+ const [devicesOpen, setDevicesOpen] = useState(desktop);
+ const [eventsOpen, setEventsOpen] = useState(false);
+
+ const onEventsClick = useCallback(() => setEventsOpen(true), [setEventsOpen]);
+
+ useEffect(() => {
+ if (!desktop && mapOnSelect && selectedDeviceId) {
+ setDevicesOpen(false);
+ }
+ }, [desktop, mapOnSelect, selectedDeviceId]);
+
+ useFilter(keyword, filter, filterSort, filterMap, positions, setFilteredDevices, setFilteredPositions);
+
+ return (
+ <div className={classes.root}>
+ {desktop && (
+ <MainMap
+ filteredPositions={filteredPositions}
+ selectedPosition={selectedPosition}
+ onEventsClick={onEventsClick}
+ />
+ )}
+ <div className={classes.sidebar}>
+ <Paper square elevation={3} className={classes.header}>
+ <MainToolbar
+ filteredDevices={filteredDevices}
+ devicesOpen={devicesOpen}
+ setDevicesOpen={setDevicesOpen}
+ keyword={keyword}
+ setKeyword={setKeyword}
+ filter={filter}
+ setFilter={setFilter}
+ filterSort={filterSort}
+ setFilterSort={setFilterSort}
+ filterMap={filterMap}
+ setFilterMap={setFilterMap}
+ />
+ </Paper>
+ <div className={classes.middle}>
+ {!desktop && (
+ <div className={classes.contentMap}>
+ <MainMap
+ filteredPositions={filteredPositions}
+ selectedPosition={selectedPosition}
+ onEventsClick={onEventsClick}
+ />
+ </div>
+ )}
+ <Paper square className={classes.contentList} style={devicesOpen ? {} : { visibility: 'hidden' }}>
+ <DeviceList devices={filteredDevices} />
+ </Paper>
+ </div>
+ {desktop && (
+ <div className={classes.footer}>
+ <BottomMenu />
+ </div>
+ )}
+ </div>
+ <EventsDrawer open={eventsOpen} onClose={() => setEventsOpen(false)} />
+ {selectedDeviceId && (
+ <StatusCard
+ deviceId={selectedDeviceId}
+ position={selectedPosition}
+ onClose={() => dispatch(devicesActions.selectId(null))}
+ desktopPadding={theme.dimensions.drawerWidthDesktop}
+ />
+ )}
+ </div>
+ );
+};
+
+export default MainPage;
diff --git a/src/main/MainToolbar.jsx b/src/main/MainToolbar.jsx
new file mode 100644
index 00000000..b029529c
--- /dev/null
+++ b/src/main/MainToolbar.jsx
@@ -0,0 +1,178 @@
+import React, { useState, useRef } from 'react';
+import { useSelector } from 'react-redux';
+import { useNavigate } from 'react-router-dom';
+import {
+ Toolbar, IconButton, OutlinedInput, InputAdornment, Popover, FormControl, InputLabel, Select, MenuItem, FormGroup, FormControlLabel, Checkbox, Badge, ListItemButton, ListItemText, Tooltip,
+} from '@mui/material';
+import { makeStyles, useTheme } from '@mui/styles';
+import MapIcon from '@mui/icons-material/Map';
+import ViewListIcon from '@mui/icons-material/ViewList';
+import AddIcon from '@mui/icons-material/Add';
+import TuneIcon from '@mui/icons-material/Tune';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import { useDeviceReadonly } from '../common/util/permissions';
+import DeviceRow from './DeviceRow';
+
+const useStyles = makeStyles((theme) => ({
+ toolbar: {
+ display: 'flex',
+ gap: theme.spacing(1),
+ },
+ filterPanel: {
+ display: 'flex',
+ flexDirection: 'column',
+ padding: theme.spacing(2),
+ gap: theme.spacing(2),
+ width: theme.dimensions.drawerWidthTablet,
+ },
+}));
+
+const MainToolbar = ({
+ filteredDevices,
+ devicesOpen,
+ setDevicesOpen,
+ keyword,
+ setKeyword,
+ filter,
+ setFilter,
+ filterSort,
+ setFilterSort,
+ filterMap,
+ setFilterMap,
+}) => {
+ const classes = useStyles();
+ const theme = useTheme();
+ const navigate = useNavigate();
+ const t = useTranslation();
+
+ const deviceReadonly = useDeviceReadonly();
+
+ const groups = useSelector((state) => state.groups.items);
+ const devices = useSelector((state) => state.devices.items);
+
+ const toolbarRef = useRef();
+ const inputRef = useRef();
+ const [filterAnchorEl, setFilterAnchorEl] = useState(null);
+ const [devicesAnchorEl, setDevicesAnchorEl] = useState(null);
+
+ const deviceStatusCount = (status) => Object.values(devices).filter((d) => d.status === status).length;
+
+ return (
+ <Toolbar ref={toolbarRef} className={classes.toolbar}>
+ <IconButton edge="start" onClick={() => setDevicesOpen(!devicesOpen)}>
+ {devicesOpen ? <MapIcon /> : <ViewListIcon />}
+ </IconButton>
+ <OutlinedInput
+ ref={inputRef}
+ placeholder={t('sharedSearchDevices')}
+ value={keyword}
+ onChange={(e) => setKeyword(e.target.value)}
+ onFocus={() => setDevicesAnchorEl(toolbarRef.current)}
+ onBlur={() => setDevicesAnchorEl(null)}
+ endAdornment={(
+ <InputAdornment position="end">
+ <IconButton size="small" edge="end" onClick={() => setFilterAnchorEl(inputRef.current)}>
+ <Badge color="info" variant="dot" invisible={!filter.statuses.length && !filter.groups.length}>
+ <TuneIcon fontSize="small" />
+ </Badge>
+ </IconButton>
+ </InputAdornment>
+ )}
+ size="small"
+ fullWidth
+ />
+ <Popover
+ open={!!devicesAnchorEl && !devicesOpen}
+ anchorEl={devicesAnchorEl}
+ onClose={() => setDevicesAnchorEl(null)}
+ anchorOrigin={{
+ vertical: 'bottom',
+ horizontal: Number(theme.spacing(2).slice(0, -2)),
+ }}
+ marginThreshold={0}
+ PaperProps={{
+ style: { width: `calc(${toolbarRef.current?.clientWidth}px - ${theme.spacing(4)})` },
+ }}
+ elevation={1}
+ disableAutoFocus
+ disableEnforceFocus
+ >
+ {filteredDevices.slice(0, 3).map((_, index) => (
+ <DeviceRow key={filteredDevices[index].id} data={filteredDevices} index={index} />
+ ))}
+ {filteredDevices.length > 3 && (
+ <ListItemButton alignItems="center" onClick={() => setDevicesOpen(true)}>
+ <ListItemText
+ primary={t('notificationAlways')}
+ style={{ textAlign: 'center' }}
+ />
+ </ListItemButton>
+ )}
+ </Popover>
+ <Popover
+ open={!!filterAnchorEl}
+ anchorEl={filterAnchorEl}
+ onClose={() => setFilterAnchorEl(null)}
+ anchorOrigin={{
+ vertical: 'bottom',
+ horizontal: 'left',
+ }}
+ >
+ <div className={classes.filterPanel}>
+ <FormControl>
+ <InputLabel>{t('deviceStatus')}</InputLabel>
+ <Select
+ label={t('deviceStatus')}
+ value={filter.statuses}
+ onChange={(e) => setFilter({ ...filter, statuses: e.target.value })}
+ multiple
+ >
+ <MenuItem value="online">{`${t('deviceStatusOnline')} (${deviceStatusCount('online')})`}</MenuItem>
+ <MenuItem value="offline">{`${t('deviceStatusOffline')} (${deviceStatusCount('offline')})`}</MenuItem>
+ <MenuItem value="unknown">{`${t('deviceStatusUnknown')} (${deviceStatusCount('unknown')})`}</MenuItem>
+ </Select>
+ </FormControl>
+ <FormControl>
+ <InputLabel>{t('settingsGroups')}</InputLabel>
+ <Select
+ label={t('settingsGroups')}
+ value={filter.groups}
+ onChange={(e) => setFilter({ ...filter, groups: e.target.value })}
+ multiple
+ >
+ {Object.values(groups).sort((a, b) => a.name.localeCompare(b.name)).map((group) => (
+ <MenuItem key={group.id} value={group.id}>{group.name}</MenuItem>
+ ))}
+ </Select>
+ </FormControl>
+ <FormControl>
+ <InputLabel>{t('sharedSortBy')}</InputLabel>
+ <Select
+ label={t('sharedSortBy')}
+ value={filterSort}
+ onChange={(e) => setFilterSort(e.target.value)}
+ displayEmpty
+ >
+ <MenuItem value="">{'\u00a0'}</MenuItem>
+ <MenuItem value="name">{t('sharedName')}</MenuItem>
+ <MenuItem value="lastUpdate">{t('deviceLastUpdate')}</MenuItem>
+ </Select>
+ </FormControl>
+ <FormGroup>
+ <FormControlLabel
+ control={<Checkbox checked={filterMap} onChange={(e) => setFilterMap(e.target.checked)} />}
+ label={t('sharedFilterMap')}
+ />
+ </FormGroup>
+ </div>
+ </Popover>
+ <IconButton edge="end" onClick={() => navigate('/settings/device')} disabled={deviceReadonly}>
+ <Tooltip open={!deviceReadonly && Object.keys(devices).length === 0} title={t('deviceRegisterFirst')} arrow>
+ <AddIcon />
+ </Tooltip>
+ </IconButton>
+ </Toolbar>
+ );
+};
+
+export default MainToolbar;
diff --git a/src/main/useFilter.js b/src/main/useFilter.js
new file mode 100644
index 00000000..ccda6e14
--- /dev/null
+++ b/src/main/useFilter.js
@@ -0,0 +1,46 @@
+import { useEffect } from 'react';
+import { useSelector } from 'react-redux';
+import dayjs from 'dayjs';
+
+export default (keyword, filter, filterSort, filterMap, positions, setFilteredDevices, setFilteredPositions) => {
+ const groups = useSelector((state) => state.groups.items);
+ const devices = useSelector((state) => state.devices.items);
+
+ useEffect(() => {
+ const deviceGroups = (device) => {
+ const groupIds = [];
+ let { groupId } = device;
+ while (groupId) {
+ groupIds.push(groupId);
+ groupId = groups[groupId]?.groupId || 0;
+ }
+ return groupIds;
+ };
+
+ const filtered = Object.values(devices)
+ .filter((device) => !filter.statuses.length || filter.statuses.includes(device.status))
+ .filter((device) => !filter.groups.length || deviceGroups(device).some((id) => filter.groups.includes(id)))
+ .filter((device) => {
+ const lowerCaseKeyword = keyword.toLowerCase();
+ return [device.name, device.uniqueId, device.phone, device.model, device.contact].some((s) => s && s.toLowerCase().includes(lowerCaseKeyword));
+ });
+ switch (filterSort) {
+ case 'name':
+ filtered.sort((device1, device2) => device1.name.localeCompare(device2.name));
+ break;
+ case 'lastUpdate':
+ filtered.sort((device1, device2) => {
+ const time1 = device1.lastUpdate ? dayjs(device1.lastUpdate).valueOf() : 0;
+ const time2 = device2.lastUpdate ? dayjs(device2.lastUpdate).valueOf() : 0;
+ return time2 - time1;
+ });
+ break;
+ default:
+ break;
+ }
+ setFilteredDevices(filtered);
+ setFilteredPositions(filterMap
+ ? filtered.map((device) => positions[device.id]).filter(Boolean)
+ : Object.values(positions));
+ }, [keyword, filter, filterSort, filterMap, groups, devices, positions, setFilteredDevices, setFilteredPositions]);
+};
diff --git a/src/map/MapCamera.js b/src/map/MapCamera.js
new file mode 100644
index 00000000..afb52f89
--- /dev/null
+++ b/src/map/MapCamera.js
@@ -0,0 +1,32 @@
+import { useEffect } from 'react';
+import maplibregl from 'maplibre-gl';
+import { map } from './core/MapView';
+
+const MapCamera = ({
+ latitude, longitude, positions, coordinates,
+}) => {
+ useEffect(() => {
+ if (coordinates || positions) {
+ if (!coordinates) {
+ coordinates = positions.map((item) => [item.longitude, item.latitude]);
+ }
+ if (coordinates.length) {
+ const bounds = coordinates.reduce((bounds, item) => bounds.extend(item), new maplibregl.LngLatBounds(coordinates[0], coordinates[0]));
+ const canvas = map.getCanvas();
+ map.fitBounds(bounds, {
+ padding: Math.min(canvas.width, canvas.height) * 0.1,
+ duration: 0,
+ });
+ }
+ } else {
+ map.jumpTo({
+ center: [longitude, latitude],
+ zoom: Math.max(map.getZoom(), 10),
+ });
+ }
+ }, [latitude, longitude, positions, coordinates]);
+
+ return null;
+};
+
+export default MapCamera;
diff --git a/src/map/MapCurrentLocation.js b/src/map/MapCurrentLocation.js
new file mode 100644
index 00000000..66fc46ec
--- /dev/null
+++ b/src/map/MapCurrentLocation.js
@@ -0,0 +1,21 @@
+import maplibregl from 'maplibre-gl';
+import { useEffect } from 'react';
+import { map } from './core/MapView';
+
+const MapCurrentLocation = () => {
+ useEffect(() => {
+ const control = new maplibregl.GeolocateControl({
+ positionOptions: {
+ enableHighAccuracy: true,
+ timeout: 5000,
+ },
+ trackUserLocation: true,
+ });
+ map.addControl(control);
+ return () => map.removeControl(control);
+ }, []);
+
+ return null;
+};
+
+export default MapCurrentLocation;
diff --git a/src/map/MapGeofence.js b/src/map/MapGeofence.js
new file mode 100644
index 00000000..73b32724
--- /dev/null
+++ b/src/map/MapGeofence.js
@@ -0,0 +1,94 @@
+import { useId, useEffect } from 'react';
+import { useSelector } from 'react-redux';
+import { useTheme } from '@mui/styles';
+import { map } from './core/MapView';
+import { findFonts, geofenceToFeature } from './core/mapUtil';
+import { useAttributePreference } from '../common/util/preferences';
+
+const MapGeofence = () => {
+ const id = useId();
+
+ const theme = useTheme();
+
+ const mapGeofences = useAttributePreference('mapGeofences', true);
+
+ const geofences = useSelector((state) => state.geofences.items);
+
+ useEffect(() => {
+ if (mapGeofences) {
+ map.addSource(id, {
+ type: 'geojson',
+ data: {
+ type: 'FeatureCollection',
+ features: [],
+ },
+ });
+ map.addLayer({
+ source: id,
+ id: 'geofences-fill',
+ type: 'fill',
+ filter: [
+ 'all',
+ ['==', '$type', 'Polygon'],
+ ],
+ paint: {
+ 'fill-color': ['get', 'color'],
+ 'fill-outline-color': ['get', 'color'],
+ 'fill-opacity': 0.1,
+ },
+ });
+ map.addLayer({
+ source: id,
+ id: 'geofences-line',
+ type: 'line',
+ paint: {
+ 'line-color': ['get', 'color'],
+ 'line-width': 2,
+ },
+ });
+ map.addLayer({
+ source: id,
+ id: 'geofences-title',
+ type: 'symbol',
+ layout: {
+ 'text-field': '{name}',
+ 'text-font': findFonts(map),
+ 'text-size': 12,
+ },
+ paint: {
+ 'text-halo-color': 'white',
+ 'text-halo-width': 1,
+ },
+ });
+
+ return () => {
+ if (map.getLayer('geofences-fill')) {
+ map.removeLayer('geofences-fill');
+ }
+ if (map.getLayer('geofences-line')) {
+ map.removeLayer('geofences-line');
+ }
+ if (map.getLayer('geofences-title')) {
+ map.removeLayer('geofences-title');
+ }
+ if (map.getSource(id)) {
+ map.removeSource(id);
+ }
+ };
+ }
+ return () => {};
+ }, [mapGeofences]);
+
+ useEffect(() => {
+ if (mapGeofences) {
+ map.getSource(id)?.setData({
+ type: 'FeatureCollection',
+ features: Object.values(geofences).map((geofence) => geofenceToFeature(theme, geofence)),
+ });
+ }
+ }, [mapGeofences, geofences]);
+
+ return null;
+};
+
+export default MapGeofence;
diff --git a/src/map/MapMarkers.js b/src/map/MapMarkers.js
new file mode 100644
index 00000000..8fbe92b6
--- /dev/null
+++ b/src/map/MapMarkers.js
@@ -0,0 +1,89 @@
+import { useId, useEffect } from 'react';
+import { useTheme } from '@mui/styles';
+import { useMediaQuery } from '@mui/material';
+import { map } from './core/MapView';
+import { useAttributePreference } from '../common/util/preferences';
+import { findFonts } from './core/mapUtil';
+
+const MapMarkers = ({ markers, showTitles }) => {
+ const id = useId();
+
+ const theme = useTheme();
+ const desktop = useMediaQuery(theme.breakpoints.up('md'));
+ const iconScale = useAttributePreference('iconScale', desktop ? 0.75 : 1);
+
+ useEffect(() => {
+ map.addSource(id, {
+ type: 'geojson',
+ data: {
+ type: 'FeatureCollection',
+ features: [],
+ },
+ });
+
+ if (showTitles) {
+ map.addLayer({
+ id,
+ type: 'symbol',
+ source: id,
+ filter: ['!has', 'point_count'],
+ layout: {
+ 'icon-image': '{image}',
+ 'icon-size': iconScale,
+ 'icon-allow-overlap': true,
+ 'text-field': '{title}',
+ 'text-allow-overlap': true,
+ 'text-anchor': 'bottom',
+ 'text-offset': [0, -2 * iconScale],
+ 'text-font': findFonts(map),
+ 'text-size': 12,
+ },
+ paint: {
+ 'text-halo-color': 'white',
+ 'text-halo-width': 1,
+ },
+ });
+ } else {
+ map.addLayer({
+ id,
+ type: 'symbol',
+ source: id,
+ layout: {
+ 'icon-image': '{image}',
+ 'icon-size': iconScale,
+ 'icon-allow-overlap': true,
+ },
+ });
+ }
+
+ return () => {
+ if (map.getLayer(id)) {
+ map.removeLayer(id);
+ }
+ if (map.getSource(id)) {
+ map.removeSource(id);
+ }
+ };
+ }, [showTitles]);
+
+ useEffect(() => {
+ map.getSource(id)?.setData({
+ type: 'FeatureCollection',
+ features: markers.map(({ latitude, longitude, image, title }) => ({
+ type: 'Feature',
+ geometry: {
+ type: 'Point',
+ coordinates: [longitude, latitude],
+ },
+ properties: {
+ image: image || 'default-neutral',
+ title: title || '',
+ },
+ })),
+ });
+ }, [showTitles, markers]);
+
+ return null;
+};
+
+export default MapMarkers;
diff --git a/src/map/MapPadding.js b/src/map/MapPadding.js
new file mode 100644
index 00000000..b1927a1f
--- /dev/null
+++ b/src/map/MapPadding.js
@@ -0,0 +1,20 @@
+import { useEffect } from 'react';
+
+import { map } from './core/MapView';
+
+const MapPadding = ({
+ top, right, bottom, left,
+}) => {
+ useEffect(() => {
+ map.setPadding({
+ top, right, bottom, left,
+ });
+ return () => map.setPadding({
+ top: 0, right: 0, bottom: 0, left: 0,
+ });
+ }, [top, right, bottom, left]);
+
+ return null;
+};
+
+export default MapPadding;
diff --git a/src/map/MapPositions.js b/src/map/MapPositions.js
new file mode 100644
index 00000000..751c61b9
--- /dev/null
+++ b/src/map/MapPositions.js
@@ -0,0 +1,216 @@
+import { useId, useCallback, useEffect } from 'react';
+import { useSelector } from 'react-redux';
+import { useMediaQuery } from '@mui/material';
+import { useTheme } from '@mui/styles';
+import { map } from './core/MapView';
+import { formatTime, getStatusColor } from '../common/util/formatter';
+import { mapIconKey } from './core/preloadImages';
+import { findFonts } from './core/mapUtil';
+import { useAttributePreference, usePreference } from '../common/util/preferences';
+
+const MapPositions = ({ positions, onClick, showStatus, selectedPosition, titleField }) => {
+ const id = useId();
+ const clusters = `${id}-clusters`;
+ const selected = `${id}-selected`;
+
+ const theme = useTheme();
+ const desktop = useMediaQuery(theme.breakpoints.up('md'));
+ const iconScale = useAttributePreference('iconScale', desktop ? 0.75 : 1);
+
+ const devices = useSelector((state) => state.devices.items);
+ const selectedDeviceId = useSelector((state) => state.devices.selectedId);
+
+ const mapCluster = useAttributePreference('mapCluster', true);
+ const hours12 = usePreference('twelveHourFormat');
+ const directionType = useAttributePreference('mapDirection', 'selected');
+
+ const createFeature = (devices, position, selectedPositionId) => {
+ const device = devices[position.deviceId];
+ let showDirection;
+ switch (directionType) {
+ case 'none':
+ showDirection = false;
+ break;
+ case 'all':
+ showDirection = true;
+ break;
+ default:
+ showDirection = selectedPositionId === position.id;
+ break;
+ }
+ return {
+ id: position.id,
+ deviceId: position.deviceId,
+ name: device.name,
+ fixTime: formatTime(position.fixTime, 'seconds', hours12),
+ category: mapIconKey(device.category),
+ color: showStatus ? position.attributes.color || getStatusColor(device.status) : 'neutral',
+ rotation: position.course,
+ direction: showDirection,
+ };
+ };
+
+ const onMouseEnter = () => map.getCanvas().style.cursor = 'pointer';
+ const onMouseLeave = () => map.getCanvas().style.cursor = '';
+
+ const onMapClick = useCallback((event) => {
+ if (!event.defaultPrevented && onClick) {
+ onClick();
+ }
+ }, [onClick]);
+
+ const onMarkerClick = useCallback((event) => {
+ event.preventDefault();
+ const feature = event.features[0];
+ if (onClick) {
+ onClick(feature.properties.id, feature.properties.deviceId);
+ }
+ }, [onClick]);
+
+ const onClusterClick = useCallback((event) => {
+ event.preventDefault();
+ const features = map.queryRenderedFeatures(event.point, {
+ layers: [clusters],
+ });
+ const clusterId = features[0].properties.cluster_id;
+ map.getSource(id).getClusterExpansionZoom(clusterId, (error, zoom) => {
+ if (!error) {
+ map.easeTo({
+ center: features[0].geometry.coordinates,
+ zoom,
+ });
+ }
+ });
+ }, [clusters]);
+
+ useEffect(() => {
+ map.addSource(id, {
+ type: 'geojson',
+ data: {
+ type: 'FeatureCollection',
+ features: [],
+ },
+ cluster: mapCluster,
+ clusterMaxZoom: 14,
+ clusterRadius: 50,
+ });
+ map.addSource(selected, {
+ type: 'geojson',
+ data: {
+ type: 'FeatureCollection',
+ features: [],
+ },
+ });
+ [id, selected].forEach((source) => {
+ map.addLayer({
+ id: source,
+ type: 'symbol',
+ source,
+ filter: ['!has', 'point_count'],
+ layout: {
+ 'icon-image': '{category}-{color}',
+ 'icon-size': iconScale,
+ 'icon-allow-overlap': true,
+ 'text-field': `{${titleField || 'name'}}`,
+ 'text-allow-overlap': true,
+ 'text-anchor': 'bottom',
+ 'text-offset': [0, -2 * iconScale],
+ 'text-font': findFonts(map),
+ 'text-size': 12,
+ },
+ paint: {
+ 'text-halo-color': 'white',
+ 'text-halo-width': 1,
+ },
+ });
+ map.addLayer({
+ id: `direction-${source}`,
+ type: 'symbol',
+ source,
+ filter: [
+ 'all',
+ ['!has', 'point_count'],
+ ['==', 'direction', true],
+ ],
+ layout: {
+ 'icon-image': 'direction',
+ 'icon-size': iconScale,
+ 'icon-allow-overlap': true,
+ 'icon-rotate': ['get', 'rotation'],
+ 'icon-rotation-alignment': 'map',
+ },
+ });
+
+ map.on('mouseenter', source, onMouseEnter);
+ map.on('mouseleave', source, onMouseLeave);
+ map.on('click', source, onMarkerClick);
+ });
+ map.addLayer({
+ id: clusters,
+ type: 'symbol',
+ source: id,
+ filter: ['has', 'point_count'],
+ layout: {
+ 'icon-image': 'background',
+ 'icon-size': iconScale,
+ 'text-field': '{point_count_abbreviated}',
+ 'text-font': findFonts(map),
+ 'text-size': 14,
+ },
+ });
+
+ map.on('mouseenter', clusters, onMouseEnter);
+ map.on('mouseleave', clusters, onMouseLeave);
+ map.on('click', clusters, onClusterClick);
+ map.on('click', onMapClick);
+
+ return () => {
+ map.off('mouseenter', clusters, onMouseEnter);
+ map.off('mouseleave', clusters, onMouseLeave);
+ map.off('click', clusters, onClusterClick);
+ map.off('click', onMapClick);
+
+ if (map.getLayer(clusters)) {
+ map.removeLayer(clusters);
+ }
+
+ [id, selected].forEach((source) => {
+ map.off('mouseenter', source, onMouseEnter);
+ map.off('mouseleave', source, onMouseLeave);
+ map.off('click', source, onMarkerClick);
+
+ if (map.getLayer(source)) {
+ map.removeLayer(source);
+ }
+ if (map.getLayer(`direction-${source}`)) {
+ map.removeLayer(`direction-${source}`);
+ }
+ if (map.getSource(source)) {
+ map.removeSource(source);
+ }
+ });
+ };
+ }, [mapCluster, clusters, onMarkerClick, onClusterClick]);
+
+ useEffect(() => {
+ [id, selected].forEach((source) => {
+ map.getSource(source)?.setData({
+ type: 'FeatureCollection',
+ features: positions.filter((it) => devices.hasOwnProperty(it.deviceId))
+ .filter((it) => (source === id ? it.deviceId !== selectedDeviceId : it.deviceId === selectedDeviceId))
+ .map((position) => ({
+ type: 'Feature',
+ geometry: {
+ type: 'Point',
+ coordinates: [position.longitude, position.latitude],
+ },
+ properties: createFeature(devices, position, selectedPosition && selectedPosition.id),
+ })),
+ });
+ });
+ }, [mapCluster, clusters, onMarkerClick, onClusterClick, devices, positions, selectedPosition]);
+
+ return null;
+};
+
+export default MapPositions;
diff --git a/src/map/MapRoutePath.js b/src/map/MapRoutePath.js
new file mode 100644
index 00000000..18069a71
--- /dev/null
+++ b/src/map/MapRoutePath.js
@@ -0,0 +1,100 @@
+import { useTheme } from '@mui/styles';
+import { useId, useEffect } from 'react';
+import { useSelector } from 'react-redux';
+import { map } from './core/MapView';
+import { findFonts } from './core/mapUtil';
+
+const MapRoutePath = ({ name, positions, coordinates }) => {
+ const id = useId();
+
+ const theme = useTheme();
+
+ const reportColor = useSelector((state) => {
+ const position = positions?.find(() => true);
+ if (position) {
+ const attributes = state.devices.items[position.deviceId]?.attributes;
+ if (attributes) {
+ const color = attributes['web.reportColor'];
+ if (color) {
+ return color;
+ }
+ }
+ }
+ return theme.palette.geometry.main;
+ });
+
+ useEffect(() => {
+ map.addSource(id, {
+ type: 'geojson',
+ data: {
+ type: 'Feature',
+ geometry: {
+ type: 'LineString',
+ coordinates: [],
+ },
+ },
+ });
+ map.addLayer({
+ source: id,
+ id: `${id}-line`,
+ type: 'line',
+ layout: {
+ 'line-join': 'round',
+ 'line-cap': 'round',
+ },
+ paint: {
+ 'line-color': ['get', 'color'],
+ 'line-width': 2,
+ },
+ });
+ if (name) {
+ map.addLayer({
+ source: id,
+ id: `${id}-title`,
+ type: 'symbol',
+ layout: {
+ 'text-field': '{name}',
+ 'text-font': findFonts(map),
+ 'text-size': 12,
+ },
+ paint: {
+ 'text-halo-color': 'white',
+ 'text-halo-width': 1,
+ },
+ });
+ }
+
+ return () => {
+ if (map.getLayer(`${id}-title`)) {
+ map.removeLayer(`${id}-title`);
+ }
+ if (map.getLayer(`${id}-line`)) {
+ map.removeLayer(`${id}-line`);
+ }
+ if (map.getSource(id)) {
+ map.removeSource(id);
+ }
+ };
+ }, []);
+
+ useEffect(() => {
+ if (!coordinates) {
+ coordinates = positions.map((item) => [item.longitude, item.latitude]);
+ }
+ map.getSource(id)?.setData({
+ type: 'Feature',
+ geometry: {
+ type: 'LineString',
+ coordinates,
+ },
+ properties: {
+ name,
+ color: reportColor,
+ },
+ });
+ }, [theme, positions, coordinates, reportColor]);
+
+ return null;
+};
+
+export default MapRoutePath;
diff --git a/src/map/MapRoutePoints.js b/src/map/MapRoutePoints.js
new file mode 100644
index 00000000..e329da81
--- /dev/null
+++ b/src/map/MapRoutePoints.js
@@ -0,0 +1,77 @@
+import { useId, useCallback, useEffect } from 'react';
+import { map } from './core/MapView';
+
+const MapRoutePoints = ({ positions, onClick }) => {
+ const id = useId();
+
+ const onMouseEnter = () => map.getCanvas().style.cursor = 'pointer';
+ const onMouseLeave = () => map.getCanvas().style.cursor = '';
+
+ const onMarkerClick = useCallback((event) => {
+ event.preventDefault();
+ const feature = event.features[0];
+ if (onClick) {
+ onClick(feature.properties.id, feature.properties.index);
+ }
+ }, [onClick]);
+
+ useEffect(() => {
+ map.addSource(id, {
+ type: 'geojson',
+ data: {
+ type: 'FeatureCollection',
+ features: [],
+ },
+ });
+ map.addLayer({
+ id,
+ type: 'symbol',
+ source: id,
+ layout: {
+ 'icon-image': 'arrow',
+ 'icon-allow-overlap': true,
+ 'icon-rotate': ['get', 'rotation'],
+ 'icon-rotation-alignment': 'map',
+ },
+ });
+
+ map.on('mouseenter', id, onMouseEnter);
+ map.on('mouseleave', id, onMouseLeave);
+ map.on('click', id, onMarkerClick);
+
+ return () => {
+ map.off('mouseenter', id, onMouseEnter);
+ map.off('mouseleave', id, onMouseLeave);
+ map.off('click', id, onMarkerClick);
+
+ if (map.getLayer(id)) {
+ map.removeLayer(id);
+ }
+ if (map.getSource(id)) {
+ map.removeSource(id);
+ }
+ };
+ }, [onMarkerClick]);
+
+ useEffect(() => {
+ map.getSource(id)?.setData({
+ type: 'FeatureCollection',
+ features: positions.map((position, index) => ({
+ type: 'Feature',
+ geometry: {
+ type: 'Point',
+ coordinates: [position.longitude, position.latitude],
+ },
+ properties: {
+ index,
+ id: position.id,
+ rotation: position.course,
+ },
+ })),
+ });
+ }, [onMarkerClick, positions]);
+
+ return null;
+};
+
+export default MapRoutePoints;
diff --git a/src/map/MapScale.js b/src/map/MapScale.js
new file mode 100644
index 00000000..c8a724c9
--- /dev/null
+++ b/src/map/MapScale.js
@@ -0,0 +1,34 @@
+import maplibregl from 'maplibre-gl';
+import { useEffect, useMemo } from 'react';
+import { useAttributePreference } from '../common/util/preferences';
+import { map } from './core/MapView';
+
+const MapScale = () => {
+ const distanceUnit = useAttributePreference('distanceUnit');
+
+ const control = useMemo(() => new maplibregl.ScaleControl(), []);
+
+ useEffect(() => {
+ map.addControl(control, 'bottom-right');
+ return () => map.removeControl(control);
+ }, [control]);
+
+ useEffect(() => {
+ switch (distanceUnit) {
+ case 'mi':
+ control.setUnit('imperial');
+ break;
+ case 'nmi':
+ control.setUnit('nautical');
+ break;
+ case 'km':
+ default:
+ control.setUnit('metric');
+ break;
+ }
+ }, [control, distanceUnit]);
+
+ return null;
+};
+
+export default MapScale;
diff --git a/src/map/core/MapView.jsx b/src/map/core/MapView.jsx
new file mode 100644
index 00000000..35b3a65a
--- /dev/null
+++ b/src/map/core/MapView.jsx
@@ -0,0 +1,123 @@
+import 'maplibre-gl/dist/maplibre-gl.css';
+import maplibregl from 'maplibre-gl';
+import React, {
+ useRef, useLayoutEffect, useEffect, useState,
+} from 'react';
+import { SwitcherControl } from '../switcher/switcher';
+import { useAttributePreference, usePreference } from '../../common/util/preferences';
+import usePersistedState, { savePersistedState } from '../../common/util/usePersistedState';
+import { mapImages } from './preloadImages';
+import useMapStyles from './useMapStyles';
+
+const element = document.createElement('div');
+element.style.width = '100%';
+element.style.height = '100%';
+element.style.boxSizing = 'initial';
+
+export const map = new maplibregl.Map({
+ container: element,
+ attributionControl: false,
+});
+
+let ready = false;
+const readyListeners = new Set();
+
+const addReadyListener = (listener) => {
+ readyListeners.add(listener);
+ listener(ready);
+};
+
+const removeReadyListener = (listener) => {
+ readyListeners.delete(listener);
+};
+
+const updateReadyValue = (value) => {
+ ready = value;
+ readyListeners.forEach((listener) => listener(value));
+};
+
+const initMap = async () => {
+ if (ready) return;
+ if (!map.hasImage('background')) {
+ Object.entries(mapImages).forEach(([key, value]) => {
+ map.addImage(key, value, {
+ pixelRatio: window.devicePixelRatio,
+ });
+ });
+ }
+ updateReadyValue(true);
+};
+
+map.addControl(new maplibregl.NavigationControl());
+
+const switcher = new SwitcherControl(
+ () => updateReadyValue(false),
+ (styleId) => savePersistedState('selectedMapStyle', styleId),
+ () => {
+ map.once('styledata', () => {
+ const waiting = () => {
+ if (!map.loaded()) {
+ setTimeout(waiting, 33);
+ } else {
+ initMap();
+ }
+ };
+ waiting();
+ });
+ },
+);
+
+map.addControl(switcher);
+
+const MapView = ({ children }) => {
+ const containerEl = useRef(null);
+
+ const [mapReady, setMapReady] = useState(false);
+
+ const mapStyles = useMapStyles();
+ const activeMapStyles = useAttributePreference('activeMapStyles', 'locationIqStreets,osm,carto');
+ const [defaultMapStyle] = usePersistedState('selectedMapStyle', usePreference('map', 'locationIqStreets'));
+ const mapboxAccessToken = useAttributePreference('mapboxAccessToken');
+ const maxZoom = useAttributePreference('web.maxZoom');
+
+ useEffect(() => {
+ if (maxZoom) {
+ map.setMaxZoom(maxZoom);
+ }
+ }, [maxZoom]);
+
+ useEffect(() => {
+ maplibregl.accessToken = mapboxAccessToken;
+ }, [mapboxAccessToken]);
+
+ useEffect(() => {
+ const filteredStyles = mapStyles.filter((s) => s.available && activeMapStyles.includes(s.id));
+ const styles = filteredStyles.length ? filteredStyles : mapStyles.filter((s) => s.id === 'osm');
+ switcher.updateStyles(styles, defaultMapStyle);
+ }, [mapStyles, defaultMapStyle]);
+
+ useEffect(() => {
+ const listener = (ready) => setMapReady(ready);
+ addReadyListener(listener);
+ return () => {
+ removeReadyListener(listener);
+ };
+ }, []);
+
+ useLayoutEffect(() => {
+ const currentEl = containerEl.current;
+ currentEl.appendChild(element);
+ map.resize();
+ return () => {
+ currentEl.removeChild(element);
+ };
+ }, [containerEl]);
+
+ return (
+ <div style={{ width: '100%', height: '100%' }} ref={containerEl}>
+ {mapReady && children}
+ </div>
+ );
+};
+
+export default MapView;
diff --git a/src/map/core/mapUtil.js b/src/map/core/mapUtil.js
new file mode 100644
index 00000000..8dcded2c
--- /dev/null
+++ b/src/map/core/mapUtil.js
@@ -0,0 +1,105 @@
+import { parse, stringify } from 'wellknown';
+import circle from '@turf/circle';
+
+export const loadImage = (url) => new Promise((imageLoaded) => {
+ const image = new Image();
+ image.onload = () => imageLoaded(image);
+ image.src = url;
+});
+
+const canvasTintImage = (image, color) => {
+ const canvas = document.createElement('canvas');
+ canvas.width = image.width * devicePixelRatio;
+ canvas.height = image.height * devicePixelRatio;
+ canvas.style.width = `${image.width}px`;
+ canvas.style.height = `${image.height}px`;
+
+ const context = canvas.getContext('2d');
+
+ context.save();
+ context.fillStyle = color;
+ context.globalAlpha = 1;
+ context.fillRect(0, 0, canvas.width, canvas.height);
+ context.globalCompositeOperation = 'destination-atop';
+ context.globalAlpha = 1;
+ context.drawImage(image, 0, 0, canvas.width, canvas.height);
+ context.restore();
+
+ return canvas;
+};
+
+export const prepareIcon = (background, icon, color) => {
+ const canvas = document.createElement('canvas');
+ canvas.width = background.width * devicePixelRatio;
+ canvas.height = background.height * devicePixelRatio;
+ canvas.style.width = `${background.width}px`;
+ canvas.style.height = `${background.height}px`;
+
+ const context = canvas.getContext('2d');
+ context.drawImage(background, 0, 0, canvas.width, canvas.height);
+
+ if (icon) {
+ const iconRatio = 0.5;
+ const imageWidth = canvas.width * iconRatio;
+ const imageHeight = canvas.height * iconRatio;
+ context.drawImage(canvasTintImage(icon, color), (canvas.width - imageWidth) / 2, (canvas.height - imageHeight) / 2, imageWidth, imageHeight);
+ }
+
+ return context.getImageData(0, 0, canvas.width, canvas.height);
+};
+
+export const reverseCoordinates = (it) => {
+ if (!it) {
+ return it;
+ } if (Array.isArray(it)) {
+ if (it.length === 2 && typeof it[0] === 'number' && typeof it[1] === 'number') {
+ return [it[1], it[0]];
+ }
+ return it.map((it) => reverseCoordinates(it));
+ }
+ return {
+ ...it,
+ coordinates: reverseCoordinates(it.coordinates),
+ };
+};
+
+export const geofenceToFeature = (theme, item) => {
+ let geometry;
+ if (item.area.indexOf('CIRCLE') > -1) {
+ const coordinates = item.area.replace(/CIRCLE|\(|\)|,/g, ' ').trim().split(/ +/);
+ const options = { steps: 32, units: 'meters' };
+ const polygon = circle([Number(coordinates[1]), Number(coordinates[0])], Number(coordinates[2]), options);
+ geometry = polygon.geometry;
+ } else {
+ geometry = reverseCoordinates(parse(item.area));
+ }
+ return {
+ id: item.id,
+ type: 'Feature',
+ geometry,
+ properties: {
+ name: item.name,
+ color: item.attributes.color || theme.palette.geometry.main,
+ },
+ };
+};
+
+export const geometryToArea = (geometry) => stringify(reverseCoordinates(geometry));
+
+export const findFonts = (map) => {
+ const fontSet = new Set();
+ const { layers } = map.getStyle();
+ layers?.forEach?.((layer) => {
+ layer.layout?.['text-font']?.forEach?.(fontSet.add, fontSet);
+ });
+ const availableFonts = [...fontSet];
+ const regularFont = availableFonts.find((it) => it.includes('Regular'));
+ if (regularFont) {
+ return [regularFont];
+ }
+ const anyFont = availableFonts.find(Boolean);
+ if (anyFont) {
+ return [anyFont];
+ }
+ return ['Roboto Regular'];
+};
diff --git a/src/map/core/preloadImages.js b/src/map/core/preloadImages.js
new file mode 100644
index 00000000..a0056d4c
--- /dev/null
+++ b/src/map/core/preloadImages.js
@@ -0,0 +1,78 @@
+import { grey } from '@mui/material/colors';
+import createPalette from '@mui/material/styles/createPalette';
+import { loadImage, prepareIcon } from './mapUtil';
+
+import arrowSvg from '../../resources/images/arrow.svg';
+import directionSvg from '../../resources/images/direction.svg';
+import backgroundSvg from '../../resources/images/background.svg';
+import animalSvg from '../../resources/images/icon/animal.svg';
+import bicycleSvg from '../../resources/images/icon/bicycle.svg';
+import boatSvg from '../../resources/images/icon/boat.svg';
+import busSvg from '../../resources/images/icon/bus.svg';
+import carSvg from '../../resources/images/icon/car.svg';
+import camperSvg from '../../resources/images/icon/camper.svg';
+import craneSvg from '../../resources/images/icon/crane.svg';
+import defaultSvg from '../../resources/images/icon/default.svg';
+import helicopterSvg from '../../resources/images/icon/helicopter.svg';
+import motorcycleSvg from '../../resources/images/icon/motorcycle.svg';
+import offroadSvg from '../../resources/images/icon/offroad.svg';
+import personSvg from '../../resources/images/icon/person.svg';
+import pickupSvg from '../../resources/images/icon/pickup.svg';
+import planeSvg from '../../resources/images/icon/plane.svg';
+import scooterSvg from '../../resources/images/icon/scooter.svg';
+import shipSvg from '../../resources/images/icon/ship.svg';
+import tractorSvg from '../../resources/images/icon/tractor.svg';
+import trainSvg from '../../resources/images/icon/train.svg';
+import tramSvg from '../../resources/images/icon/tram.svg';
+import trolleybusSvg from '../../resources/images/icon/trolleybus.svg';
+import truckSvg from '../../resources/images/icon/truck.svg';
+import vanSvg from '../../resources/images/icon/van.svg';
+
+export const mapIcons = {
+ animal: animalSvg,
+ bicycle: bicycleSvg,
+ boat: boatSvg,
+ bus: busSvg,
+ car: carSvg,
+ camper: camperSvg,
+ crane: craneSvg,
+ default: defaultSvg,
+ helicopter: helicopterSvg,
+ motorcycle: motorcycleSvg,
+ offroad: offroadSvg,
+ person: personSvg,
+ pickup: pickupSvg,
+ plane: planeSvg,
+ scooter: scooterSvg,
+ ship: shipSvg,
+ tractor: tractorSvg,
+ train: trainSvg,
+ tram: tramSvg,
+ trolleybus: trolleybusSvg,
+ truck: truckSvg,
+ van: vanSvg,
+};
+
+export const mapIconKey = (category) => (mapIcons.hasOwnProperty(category) ? category : 'default');
+
+export const mapImages = {};
+
+const mapPalette = createPalette({
+ neutral: { main: grey[500] },
+});
+
+export default async () => {
+ const background = await loadImage(backgroundSvg);
+ mapImages.background = await prepareIcon(background);
+ mapImages.direction = await prepareIcon(await loadImage(directionSvg));
+ mapImages.arrow = await prepareIcon(await loadImage(arrowSvg));
+ await Promise.all(Object.keys(mapIcons).map(async (category) => {
+ const results = [];
+ ['info', 'success', 'error', 'neutral'].forEach((color) => {
+ results.push(loadImage(mapIcons[category]).then((icon) => {
+ mapImages[`${category}-${color}`] = prepareIcon(background, icon, mapPalette[color].main);
+ }));
+ });
+ await Promise.all(results);
+ }));
+};
diff --git a/src/map/core/useMapStyles.js b/src/map/core/useMapStyles.js
new file mode 100644
index 00000000..7c3412b5
--- /dev/null
+++ b/src/map/core/useMapStyles.js
@@ -0,0 +1,259 @@
+import { useMemo } from 'react';
+import { useSelector } from 'react-redux';
+import { useTranslation } from '../../common/components/LocalizationProvider';
+import { useAttributePreference } from '../../common/util/preferences';
+
+const styleCustom = ({ tiles, minZoom, maxZoom, attribution }) => {
+ const source = {
+ type: 'raster',
+ tiles,
+ attribution,
+ tileSize: 256,
+ minzoom: minZoom,
+ maxzoom: maxZoom,
+ };
+ Object.keys(source).forEach((key) => source[key] === undefined && delete source[key]);
+ return {
+ version: 8,
+ sources: {
+ custom: source,
+ },
+ glyphs: 'https://cdn.traccar.com/map/fonts/{fontstack}/{range}.pbf',
+ layers: [{
+ id: 'custom',
+ type: 'raster',
+ source: 'custom',
+ }],
+ };
+};
+
+export default () => {
+ const t = useTranslation();
+
+ const mapTilerKey = useAttributePreference('mapTilerKey');
+ const locationIqKey = useAttributePreference('locationIqKey') || 'pk.0f147952a41c555a5b70614039fd148b';
+ const bingMapsKey = useAttributePreference('bingMapsKey');
+ const tomTomKey = useAttributePreference('tomTomKey');
+ const hereKey = useAttributePreference('hereKey');
+ const mapboxAccessToken = useAttributePreference('mapboxAccessToken');
+ const customMapUrl = useSelector((state) => state.session.server.mapUrl);
+
+ return useMemo(() => [
+ {
+ id: 'locationIqStreets',
+ title: t('mapLocationIqStreets'),
+ style: `https://tiles.locationiq.com/v3/streets/vector.json?key=${locationIqKey}`,
+ available: true,
+ },
+ {
+ id: 'locationIqDark',
+ title: t('mapLocationIqDark'),
+ style: `https://tiles.locationiq.com/v3/dark/vector.json?key=${locationIqKey}`,
+ available: true,
+ },
+ {
+ id: 'osm',
+ title: t('mapOsm'),
+ style: styleCustom({
+ tiles: ['https://tile.openstreetmap.org/{z}/{x}/{y}.png'],
+ maxZoom: 19,
+ attribution: '© <a target="_top" rel="noopener" href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
+ }),
+ available: true,
+ },
+ {
+ id: 'openTopoMap',
+ title: t('mapOpenTopoMap'),
+ style: styleCustom({
+ tiles: ['a', 'b', 'c'].map((i) => `https://${i}.tile.opentopomap.org/{z}/{x}/{y}.png`),
+ maxZoom: 17,
+ }),
+ available: true,
+ },
+ {
+ id: 'carto',
+ title: t('mapCarto'),
+ style: styleCustom({
+ tiles: ['a', 'b', 'c', 'd'].map((i) => `https://${i}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}@2x.png`),
+ maxZoom: 22,
+ attribution: '© <a target="_top" rel="noopener" href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, © <a target="_top" rel="noopener" href="https://carto.com/attribution">CARTO</a>',
+ }),
+ available: true,
+ },
+ {
+ id: 'googleRoad',
+ title: t('mapGoogleRoad'),
+ style: styleCustom({
+ tiles: [0, 1, 2, 3].map((i) => `https://mt${i}.google.com/vt/lyrs=m&hl=en&x={x}&y={y}&z={z}&s=Ga`),
+ maxZoom: 20,
+ attribution: '© Google',
+ }),
+ available: true,
+ },
+ {
+ id: 'googleSatellite',
+ title: t('mapGoogleSatellite'),
+ style: styleCustom({
+ tiles: [0, 1, 2, 3].map((i) => `https://mt${i}.google.com/vt/lyrs=s&hl=en&x={x}&y={y}&z={z}&s=Ga`),
+ maxZoom: 20,
+ attribution: '© Google',
+ }),
+ available: true,
+ },
+ {
+ id: 'googleHybrid',
+ title: t('mapGoogleHybrid'),
+ style: styleCustom({
+ tiles: [0, 1, 2, 3].map((i) => `https://mt${i}.google.com/vt/lyrs=y&hl=en&x={x}&y={y}&z={z}&s=Ga`),
+ maxZoom: 20,
+ attribution: '© Google',
+ }),
+ available: true,
+ },
+ {
+ id: 'mapTilerBasic',
+ title: t('mapMapTilerBasic'),
+ style: `https://api.maptiler.com/maps/basic/style.json?key=${mapTilerKey}`,
+ available: !!mapTilerKey,
+ attribute: 'mapTilerKey',
+ },
+ {
+ id: 'mapTilerHybrid',
+ title: t('mapMapTilerHybrid'),
+ style: `https://api.maptiler.com/maps/hybrid/style.json?key=${mapTilerKey}`,
+ available: !!mapTilerKey,
+ attribute: 'mapTilerKey',
+ },
+ {
+ id: 'bingRoad',
+ title: t('mapBingRoad'),
+ style: styleCustom({
+ tiles: [0, 1, 2, 3].map((i) => `https://t${i}.ssl.ak.dynamic.tiles.virtualearth.net/comp/ch/{quadkey}?mkt=en-US&it=G,L&shading=hill&og=1885&n=z`),
+ maxZoom: 21,
+ }),
+ available: !!bingMapsKey,
+ attribute: 'bingMapsKey',
+ },
+ {
+ id: 'bingAerial',
+ title: t('mapBingAerial'),
+ style: styleCustom({
+ tiles: [0, 1, 2, 3].map((i) => `https://ecn.t${i}.tiles.virtualearth.net/tiles/a{quadkey}.jpeg?g=12327`),
+ maxZoom: 19,
+ }),
+ available: !!bingMapsKey,
+ attribute: 'bingMapsKey',
+ },
+ {
+ id: 'bingHybrid',
+ title: t('mapBingHybrid'),
+ style: styleCustom({
+ tiles: [0, 1, 2, 3].map((i) => `https://t${i}.ssl.ak.dynamic.tiles.virtualearth.net/comp/ch/{quadkey}?mkt=en-US&it=A,G,L&og=1885&n=z`),
+ maxZoom: 19,
+ }),
+ available: !!bingMapsKey,
+ attribute: 'bingMapsKey',
+ },
+ {
+ id: 'tomTomBasic',
+ title: t('mapTomTomBasic'),
+ style: `https://api.tomtom.com/map/1/style/20.0.0-8/basic_main.json?key=${tomTomKey}`,
+ available: !!tomTomKey,
+ attribute: 'tomTomKey',
+ },
+ {
+ id: 'hereBasic',
+ title: t('mapHereBasic'),
+ style: `https://assets.vector.hereapi.com/styles/berlin/base/mapbox/tilezen?apikey=${hereKey}`,
+ available: !!hereKey,
+ attribute: 'hereKey',
+ },
+ {
+ id: 'hereHybrid',
+ title: t('mapHereHybrid'),
+ style: styleCustom({
+ tiles: [1, 2, 3, 4].map((i) => `https://${i}.aerial.maps.ls.hereapi.com/maptile/2.1/maptile/newest/hybrid.day/{z}/{x}/{y}/256/png8?apiKey=${hereKey}`),
+ maxZoom: 20,
+ }),
+ available: !!hereKey,
+ attribute: 'hereKey',
+ },
+ {
+ id: 'hereSatellite',
+ title: t('mapHereSatellite'),
+ style: styleCustom({
+ tiles: [1, 2, 3, 4].map((i) => `https://${i}.aerial.maps.ls.hereapi.com/maptile/2.1/maptile/newest/satellite.day/{z}/{x}/{y}/256/png8?apiKey=${hereKey}`),
+ maxZoom: 19,
+ }),
+ available: !!hereKey,
+ attribute: 'hereKey',
+ },
+ {
+ id: 'autoNavi',
+ title: t('mapAutoNavi'),
+ style: styleCustom({
+ tiles: [1, 2, 3, 4].map((i) => `https://webrd0${i}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}`),
+ minZoom: 3,
+ maxZoom: 18,
+ }),
+ available: true,
+ },
+ {
+ id: 'ordnanceSurvey',
+ title: t('mapOrdnanceSurvey'),
+ style: 'https://api.os.uk/maps/vector/v1/vts/resources/styles?key=EAZ8p83u72FTGiLjLC2MsTAl1ko6XQHC',
+ transformRequest: (url) => ({
+ url: `${url}&srs=3857`,
+ }),
+ available: true,
+ },
+ {
+ id: 'mapboxStreets',
+ title: t('mapMapboxStreets'),
+ style: styleCustom({
+ tiles: [`https://api.mapbox.com/styles/v1/mapbox/streets-v11/tiles/{z}/{x}/{y}?access_token=${mapboxAccessToken}`],
+ maxZoom: 22,
+ }),
+ available: !!mapboxAccessToken,
+ attribute: 'mapboxAccessToken',
+ },
+ {
+ id: 'mapboxStreetsDark',
+ title: t('mapMapboxStreetsDark'),
+ style: styleCustom({
+ tiles: [`https://api.mapbox.com/styles/v1/mapbox/dark-v11/tiles/{z}/{x}/{y}?access_token=${mapboxAccessToken}`],
+ maxZoom: 22,
+ }),
+ available: !!mapboxAccessToken,
+ attribute: 'mapboxAccessToken',
+ },
+ {
+ id: 'mapboxOutdoors',
+ title: t('mapMapboxOutdoors'),
+ style: styleCustom({
+ tiles: [`https://api.mapbox.com/styles/v1/mapbox/outdoors-v11/tiles/{z}/{x}/{y}?access_token=${mapboxAccessToken}`],
+ maxZoom: 22,
+ }),
+ available: !!mapboxAccessToken,
+ attribute: 'mapboxAccessToken',
+ },
+ {
+ id: 'mapboxSatelliteStreet',
+ title: t('mapMapboxSatellite'),
+ style: styleCustom({
+ tiles: [`https://api.mapbox.com/styles/v1/mapbox/satellite-streets-v11/tiles/{z}/{x}/{y}?access_token=${mapboxAccessToken}`],
+ maxZoom: 22,
+ }),
+ available: !!mapboxAccessToken,
+ attribute: 'mapboxAccessToken',
+ },
+ {
+ id: 'custom',
+ title: t('mapCustom'),
+ style: styleCustom({
+ tiles: [customMapUrl],
+ }),
+ available: !!customMapUrl,
+ },
+ ], [t, mapTilerKey, locationIqKey, bingMapsKey, tomTomKey, hereKey, mapboxAccessToken, customMapUrl]);
+};
diff --git a/src/map/draw/MapGeofenceEdit.js b/src/map/draw/MapGeofenceEdit.js
new file mode 100644
index 00000000..e547ea05
--- /dev/null
+++ b/src/map/draw/MapGeofenceEdit.js
@@ -0,0 +1,161 @@
+import 'mapbox-gl/dist/mapbox-gl.css';
+import '@mapbox/mapbox-gl-draw/dist/mapbox-gl-draw.css';
+import maplibregl from 'maplibre-gl';
+import MapboxDraw from '@mapbox/mapbox-gl-draw';
+import { useEffect } from 'react';
+
+import { useDispatch, useSelector } from 'react-redux';
+import { useNavigate } from 'react-router-dom';
+import { useTheme } from '@mui/styles';
+import { map } from '../core/MapView';
+import { geofenceToFeature, geometryToArea } from '../core/mapUtil';
+import { errorsActions, geofencesActions } from '../../store';
+import { useCatchCallback } from '../../reactHelper';
+import theme from './theme';
+
+const draw = new MapboxDraw({
+ displayControlsDefault: false,
+ controls: {
+ polygon: true,
+ line_string: true,
+ trash: true,
+ },
+ userProperties: true,
+ styles: [...theme, {
+ id: 'gl-draw-title',
+ type: 'symbol',
+ filter: ['all'],
+ layout: {
+ 'text-field': '{user_name}',
+ 'text-font': ['Roboto Regular'],
+ 'text-size': 12,
+ },
+ paint: {
+ 'text-halo-color': 'white',
+ 'text-halo-width': 1,
+ },
+ }],
+});
+
+const MapGeofenceEdit = ({ selectedGeofenceId }) => {
+ const theme = useTheme();
+ const dispatch = useDispatch();
+ const navigate = useNavigate();
+
+ const geofences = useSelector((state) => state.geofences.items);
+
+ const refreshGeofences = useCatchCallback(async () => {
+ const response = await fetch('/api/geofences');
+ if (response.ok) {
+ dispatch(geofencesActions.refresh(await response.json()));
+ } else {
+ throw Error(await response.text());
+ }
+ }, [dispatch]);
+
+ useEffect(() => {
+ refreshGeofences();
+
+ map.addControl(draw, 'top-left');
+ return () => map.removeControl(draw);
+ }, [refreshGeofences]);
+
+ useEffect(() => {
+ const listener = async (event) => {
+ const feature = event.features[0];
+ const newItem = { name: '', area: geometryToArea(feature.geometry) };
+ draw.delete(feature.id);
+ try {
+ const response = await fetch('/api/geofences', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(newItem),
+ });
+ if (response.ok) {
+ const item = await response.json();
+ navigate(`/settings/geofence/${item.id}`);
+ } else {
+ throw Error(await response.text());
+ }
+ } catch (error) {
+ dispatch(errorsActions.push(error.message));
+ }
+ };
+
+ map.on('draw.create', listener);
+ return () => map.off('draw.create', listener);
+ }, [dispatch, navigate]);
+
+ useEffect(() => {
+ const listener = async (event) => {
+ const feature = event.features[0];
+ try {
+ const response = await fetch(`/api/geofences/${feature.id}`, { method: 'DELETE' });
+ if (response.ok) {
+ refreshGeofences();
+ } else {
+ throw Error(await response.text());
+ }
+ } catch (error) {
+ dispatch(errorsActions.push(error.message));
+ }
+ };
+
+ map.on('draw.delete', listener);
+ return () => map.off('draw.delete', listener);
+ }, [dispatch, refreshGeofences]);
+
+ useEffect(() => {
+ const listener = async (event) => {
+ const feature = event.features[0];
+ const item = Object.values(geofences).find((i) => i.id === feature.id);
+ if (item) {
+ const updatedItem = { ...item, area: geometryToArea(feature.geometry) };
+ try {
+ const response = await fetch(`/api/geofences/${feature.id}`, {
+ method: 'PUT',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(updatedItem),
+ });
+ if (response.ok) {
+ refreshGeofences();
+ } else {
+ throw Error(await response.text());
+ }
+ } catch (error) {
+ dispatch(errorsActions.push(error.message));
+ }
+ }
+ };
+
+ map.on('draw.update', listener);
+ return () => map.off('draw.update', listener);
+ }, [dispatch, geofences, refreshGeofences]);
+
+ useEffect(() => {
+ draw.deleteAll();
+ Object.values(geofences).forEach((geofence) => {
+ draw.add(geofenceToFeature(theme, geofence));
+ });
+ }, [geofences]);
+
+ useEffect(() => {
+ if (selectedGeofenceId) {
+ const feature = draw.get(selectedGeofenceId);
+ let { coordinates } = feature.geometry;
+ if (Array.isArray(coordinates[0][0])) {
+ [coordinates] = coordinates;
+ }
+ const bounds = coordinates.reduce(
+ (bounds, coordinate) => bounds.extend(coordinate),
+ new maplibregl.LngLatBounds(coordinates[0], coordinates[1]),
+ );
+ const canvas = map.getCanvas();
+ map.fitBounds(bounds, { padding: Math.min(canvas.width, canvas.height) * 0.1 });
+ }
+ }, [selectedGeofenceId]);
+
+ return null;
+};
+
+export default MapGeofenceEdit;
diff --git a/src/map/draw/theme.js b/src/map/draw/theme.js
new file mode 100644
index 00000000..c9864e2f
--- /dev/null
+++ b/src/map/draw/theme.js
@@ -0,0 +1,230 @@
+// Copy of the original theme
+// https://github.com/mapbox/mapbox-gl-draw/blob/v1.4.0/src/lib/theme.js
+
+export default [
+ {
+ 'id': 'gl-draw-polygon-fill-inactive',
+ 'type': 'fill',
+ 'filter': ['all',
+ ['==', 'active', 'false'],
+ ['==', '$type', 'Polygon'],
+ ['!=', 'mode', 'static']
+ ],
+ 'paint': {
+ 'fill-color': '#3bb2d0',
+ 'fill-outline-color': '#3bb2d0',
+ 'fill-opacity': 0.1
+ }
+ },
+ {
+ 'id': 'gl-draw-polygon-fill-active',
+ 'type': 'fill',
+ 'filter': ['all', ['==', 'active', 'true'], ['==', '$type', 'Polygon']],
+ 'paint': {
+ 'fill-color': '#fbb03b',
+ 'fill-outline-color': '#fbb03b',
+ 'fill-opacity': 0.1
+ }
+ },
+ {
+ 'id': 'gl-draw-polygon-midpoint',
+ 'type': 'circle',
+ 'filter': ['all',
+ ['==', '$type', 'Point'],
+ ['==', 'meta', 'midpoint']],
+ 'paint': {
+ 'circle-radius': 3,
+ 'circle-color': '#fbb03b'
+ }
+ },
+ {
+ 'id': 'gl-draw-polygon-stroke-inactive',
+ 'type': 'line',
+ 'filter': ['all',
+ ['==', 'active', 'false'],
+ ['==', '$type', 'Polygon'],
+ ['!=', 'mode', 'static']
+ ],
+ 'layout': {
+ 'line-cap': 'round',
+ 'line-join': 'round'
+ },
+ 'paint': {
+ 'line-color': '#3bb2d0',
+ 'line-width': 2
+ }
+ },
+ {
+ 'id': 'gl-draw-polygon-stroke-active',
+ 'type': 'line',
+ 'filter': ['all', ['==', 'active', 'true'], ['==', '$type', 'Polygon']],
+ 'layout': {
+ 'line-cap': 'round',
+ 'line-join': 'round'
+ },
+ 'paint': {
+ 'line-color': '#fbb03b',
+ 'line-dasharray': [0.2, 2],
+ 'line-width': 2
+ }
+ },
+ {
+ 'id': 'gl-draw-line-inactive',
+ 'type': 'line',
+ 'filter': ['all',
+ ['==', 'active', 'false'],
+ ['==', '$type', 'LineString'],
+ ['!=', 'mode', 'static']
+ ],
+ 'layout': {
+ 'line-cap': 'round',
+ 'line-join': 'round'
+ },
+ 'paint': {
+ 'line-color': '#3bb2d0',
+ 'line-width': 2
+ }
+ },
+ {
+ 'id': 'gl-draw-line-active',
+ 'type': 'line',
+ 'filter': ['all',
+ ['==', '$type', 'LineString'],
+ ['==', 'active', 'true']
+ ],
+ 'layout': {
+ 'line-cap': 'round',
+ 'line-join': 'round'
+ },
+ 'paint': {
+ 'line-color': '#fbb03b',
+ 'line-dasharray': [0.2, 2],
+ 'line-width': 2
+ }
+ },
+ {
+ 'id': 'gl-draw-polygon-and-line-vertex-stroke-inactive',
+ 'type': 'circle',
+ 'filter': ['all',
+ ['==', 'meta', 'vertex'],
+ ['==', '$type', 'Point'],
+ ['!=', 'mode', 'static']
+ ],
+ 'paint': {
+ 'circle-radius': 5,
+ 'circle-color': '#fff'
+ }
+ },
+ {
+ 'id': 'gl-draw-polygon-and-line-vertex-inactive',
+ 'type': 'circle',
+ 'filter': ['all',
+ ['==', 'meta', 'vertex'],
+ ['==', '$type', 'Point'],
+ ['!=', 'mode', 'static']
+ ],
+ 'paint': {
+ 'circle-radius': 3,
+ 'circle-color': '#fbb03b'
+ }
+ },
+ {
+ 'id': 'gl-draw-point-point-stroke-inactive',
+ 'type': 'circle',
+ 'filter': ['all',
+ ['==', 'active', 'false'],
+ ['==', '$type', 'Point'],
+ ['==', 'meta', 'feature'],
+ ['!=', 'mode', 'static']
+ ],
+ 'paint': {
+ 'circle-radius': 5,
+ 'circle-opacity': 1,
+ 'circle-color': '#fff'
+ }
+ },
+ {
+ 'id': 'gl-draw-point-inactive',
+ 'type': 'circle',
+ 'filter': ['all',
+ ['==', 'active', 'false'],
+ ['==', '$type', 'Point'],
+ ['==', 'meta', 'feature'],
+ ['!=', 'mode', 'static']
+ ],
+ 'paint': {
+ 'circle-radius': 3,
+ 'circle-color': '#3bb2d0'
+ }
+ },
+ {
+ 'id': 'gl-draw-point-stroke-active',
+ 'type': 'circle',
+ 'filter': ['all',
+ ['==', '$type', 'Point'],
+ ['==', 'active', 'true'],
+ ['!=', 'meta', 'midpoint']
+ ],
+ 'paint': {
+ 'circle-radius': 7,
+ 'circle-color': '#fff'
+ }
+ },
+ {
+ 'id': 'gl-draw-point-active',
+ 'type': 'circle',
+ 'filter': ['all',
+ ['==', '$type', 'Point'],
+ ['!=', 'meta', 'midpoint'],
+ ['==', 'active', 'true']],
+ 'paint': {
+ 'circle-radius': 5,
+ 'circle-color': '#fbb03b'
+ }
+ },
+ {
+ 'id': 'gl-draw-polygon-fill-static',
+ 'type': 'fill',
+ 'filter': ['all', ['==', 'mode', 'static'], ['==', '$type', 'Polygon']],
+ 'paint': {
+ 'fill-color': '#404040',
+ 'fill-outline-color': '#404040',
+ 'fill-opacity': 0.1
+ }
+ },
+ {
+ 'id': 'gl-draw-polygon-stroke-static',
+ 'type': 'line',
+ 'filter': ['all', ['==', 'mode', 'static'], ['==', '$type', 'Polygon']],
+ 'layout': {
+ 'line-cap': 'round',
+ 'line-join': 'round'
+ },
+ 'paint': {
+ 'line-color': '#404040',
+ 'line-width': 2
+ }
+ },
+ {
+ 'id': 'gl-draw-line-static',
+ 'type': 'line',
+ 'filter': ['all', ['==', 'mode', 'static'], ['==', '$type', 'LineString']],
+ 'layout': {
+ 'line-cap': 'round',
+ 'line-join': 'round'
+ },
+ 'paint': {
+ 'line-color': '#404040',
+ 'line-width': 2
+ }
+ },
+ {
+ 'id': 'gl-draw-point-static',
+ 'type': 'circle',
+ 'filter': ['all', ['==', 'mode', 'static'], ['==', '$type', 'Point']],
+ 'paint': {
+ 'circle-radius': 5,
+ 'circle-color': '#404040'
+ }
+ }
+];
diff --git a/src/map/geocoder/MapGeocoder.js b/src/map/geocoder/MapGeocoder.js
new file mode 100644
index 00000000..de1791e6
--- /dev/null
+++ b/src/map/geocoder/MapGeocoder.js
@@ -0,0 +1,56 @@
+import './geocoder.css';
+import maplibregl from 'maplibre-gl';
+import MaplibreGeocoder from '@maplibre/maplibre-gl-geocoder';
+import { useEffect } from 'react';
+import { useDispatch } from 'react-redux';
+import { map } from '../core/MapView';
+import { errorsActions } from '../../store';
+
+const MapGeocoder = () => {
+ const dispatch = useDispatch();
+
+ useEffect(() => {
+ const geocoder = {
+ forwardGeocode: async (config) => {
+ const features = [];
+ try {
+ const request = `https://nominatim.openstreetmap.org/search?q=${config.query}&format=geojson&polygon_geojson=1&addressdetails=1`;
+ const response = await fetch(request);
+ const geojson = await response.json();
+ geojson.features.forEach((feature) => {
+ const center = [
+ feature.bbox[0] + (feature.bbox[2] - feature.bbox[0]) / 2,
+ feature.bbox[1] + (feature.bbox[3] - feature.bbox[1]) / 2,
+ ];
+ features.push({
+ type: 'Feature',
+ geometry: {
+ type: 'Point',
+ coordinates: center,
+ },
+ place_name: feature.properties.display_name,
+ properties: feature.properties,
+ text: feature.properties.display_name,
+ place_type: ['place'],
+ center,
+ });
+ });
+ } catch (e) {
+ dispatch(errorsActions.push(e.message));
+ }
+ return { features };
+ },
+ };
+
+ const control = new MaplibreGeocoder(geocoder, {
+ maplibregl,
+ collapsed: true,
+ });
+ map.addControl(control);
+ return () => map.removeControl(control);
+ }, [dispatch]);
+
+ return null;
+};
+
+export default MapGeocoder;
diff --git a/src/map/geocoder/geocoder.css b/src/map/geocoder/geocoder.css
new file mode 100644
index 00000000..86ebf6e5
--- /dev/null
+++ b/src/map/geocoder/geocoder.css
@@ -0,0 +1,223 @@
+/* Basics */
+.maplibregl-ctrl-geocoder,
+.maplibregl-ctrl-geocoder *,
+.maplibregl-ctrl-geocoder *:after,
+.maplibregl-ctrl-geocoder *:before {
+ box-sizing: border-box;
+}
+
+.maplibregl-ctrl-geocoder {
+ font-size: 15px;
+ line-height: 20px;
+ font-family: "Open Sans", "Helvetica Neue", Arial, Helvetica, sans-serif;
+ position: relative;
+ background-color: #fff;
+ width: 100%;
+ min-width: 240px;
+ max-width: 360px;
+ z-index: 1;
+ border-radius: 4px;
+ transition: width 0.25s, min-width 0.25s;
+}
+
+.maplibregl-ctrl-geocoder--input {
+ font: inherit;
+ width: 100%;
+ border: 0;
+ background-color: transparent;
+ margin: 0;
+ height: 29px;
+ color: #404040; /* fallback */
+ color: rgba(0, 0, 0, 0.75);
+ padding: 6px 30px;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+}
+
+.maplibregl-ctrl-geocoder--input:focus {
+ color: #404040; /* fallback */
+ color: rgba(0, 0, 0, 0.75);
+ outline: 0;
+ box-shadow: none;
+ outline: thin dotted;
+}
+
+.maplibregl-ctrl-geocoder .maplibregl-ctrl-geocoder--pin-right > * {
+ z-index: 2;
+ position: absolute;
+ right: 5px;
+ top: 5px;
+ display: none;
+}
+
+.maplibregl-ctrl-geocoder,
+.maplibregl-ctrl-geocoder .suggestions {
+ box-shadow: 0 0 0 2px rgb(0 0 0 / 10%);
+}
+
+/* Collapsed */
+.maplibregl-ctrl-geocoder.maplibregl-ctrl-geocoder--collapsed {
+ width: 29px;
+ min-width: 29px;
+ transition: width 0.25s, min-width 0.25s;
+}
+
+/* Suggestions */
+.maplibregl-ctrl-geocoder .suggestions {
+ background-color: #fff;
+ border-radius: 4px;
+ left: 0;
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ position: absolute;
+ width: 100%;
+ top: 110%; /* fallback */
+ top: calc(100% + 6px);
+ z-index: 1000;
+ overflow: hidden;
+ font-size: 13px;
+}
+
+.maplibregl-ctrl-bottom-left .suggestions,
+.maplibregl-ctrl-bottom-right .suggestions {
+ top: auto;
+ bottom: 100%;
+}
+
+.maplibregl-ctrl-geocoder .suggestions > li > a {
+ cursor: default;
+ display: block;
+ padding: 6px 12px;
+ color: #404040;
+}
+
+.maplibregl-ctrl-geocoder .suggestions > .active > a,
+.maplibregl-ctrl-geocoder .suggestions > li > a:hover {
+ color: #404040;
+ background-color: #f3f3f3;
+ text-decoration: none;
+ cursor: pointer;
+}
+
+.maplibregl-ctrl-geocoder--suggestion {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+}
+
+.maplibre-ctrl-geocoder--suggestion-icon {
+ min-width: 30px;
+ min-height: 24px;
+ max-width: 30px;
+ max-height: 24px;
+ padding-right: 12px;
+}
+
+.maplibregl-ctrl-geocoder--suggestion-info {
+ display: flex;
+ flex-direction: column;
+}
+
+.maplibregl-ctrl-geocoder--suggestion-match {
+ font-weight: bold;
+}
+
+.maplibregl-ctrl-geocoder--suggestion-title,
+.maplibregl-ctrl-geocoder--suggestion-address {
+ text-overflow: ellipsis;
+ overflow: hidden;
+ white-space: nowrap;
+}
+
+.maplibregl-ctrl-geocoder--result {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+}
+
+.maplibre-ctrl-geocoder--result-icon {
+ min-width: 30px;
+ min-height: 24px;
+ max-width: 30px;
+ max-height: 24px;
+ padding-right: 12px;
+}
+
+.maplibregl-ctrl-geocoder--result-title {
+ font-weight: bold;
+}
+
+.maplibregl-ctrl-geocoder--result-title,
+.maplibregl-ctrl-geocoder--result-address {
+ text-overflow: ellipsis;
+ overflow: hidden;
+ white-space: nowrap;
+}
+
+/* Icons */
+.maplibregl-ctrl-geocoder--icon {
+ display: inline-block;
+ vertical-align: middle;
+}
+
+.maplibregl-ctrl-geocoder--icon-search {
+ position: absolute;
+ top: 3px;
+ left: 3px;
+ width: 23px;
+ height: 23px;
+}
+
+.maplibregl-ctrl-geocoder--button {
+ padding: 0;
+ margin: 0;
+ border: none;
+ cursor: pointer;
+ background: #fff;
+ line-height: 1;
+}
+
+.maplibregl-ctrl-geocoder--icon-close {
+ width: 20px;
+ height: 20px;
+}
+
+.maplibregl-ctrl-geocoder--icon-loading {
+ width: 20px;
+ height: 20px;
+ -moz-animation: rotate 0.8s infinite cubic-bezier(0.45, 0.05, 0.55, 0.95);
+ -webkit-animation: rotate 0.8s infinite cubic-bezier(0.45, 0.05, 0.55, 0.95);
+ animation: rotate 0.8s infinite cubic-bezier(0.45, 0.05, 0.55, 0.95);
+}
+
+/* Animation */
+@-webkit-keyframes rotate {
+ from {
+ -webkit-transform: rotate(0);
+ transform: rotate(0);
+ }
+ to {
+ -webkit-transform: rotate(360deg);
+ transform: rotate(360deg);
+ }
+}
+
+@keyframes rotate {
+ from {
+ -webkit-transform: rotate(0);
+ transform: rotate(0);
+ }
+ to {
+ -webkit-transform: rotate(360deg);
+ transform: rotate(360deg);
+ }
+}
+
+.maplibre-gl-geocoder--error {
+ color: #909090;
+ padding: 6px 12px;
+ font-size: 16px;
+ text-align: center;
+}
diff --git a/src/map/main/MapAccuracy.js b/src/map/main/MapAccuracy.js
new file mode 100644
index 00000000..4f025b08
--- /dev/null
+++ b/src/map/main/MapAccuracy.js
@@ -0,0 +1,56 @@
+import { useId, useEffect } from 'react';
+import circle from '@turf/circle';
+import { useTheme } from '@mui/styles';
+import { map } from '../core/MapView';
+
+const MapAccuracy = ({ positions }) => {
+ const id = useId();
+
+ const theme = useTheme();
+
+ useEffect(() => {
+ map.addSource(id, {
+ type: 'geojson',
+ data: {
+ type: 'FeatureCollection',
+ features: [],
+ },
+ });
+ map.addLayer({
+ source: id,
+ id,
+ type: 'fill',
+ filter: [
+ 'all',
+ ['==', '$type', 'Polygon'],
+ ],
+ paint: {
+ 'fill-color': theme.palette.geometry.main,
+ 'fill-outline-color': theme.palette.geometry.main,
+ 'fill-opacity': 0.25,
+ },
+ });
+
+ return () => {
+ if (map.getLayer(id)) {
+ map.removeLayer(id);
+ }
+ if (map.getSource(id)) {
+ map.removeSource(id);
+ }
+ };
+ }, []);
+
+ useEffect(() => {
+ map.getSource(id)?.setData({
+ type: 'FeatureCollection',
+ features: positions
+ .filter((position) => position.accuracy > 0)
+ .map((position) => circle([position.longitude, position.latitude], position.accuracy * 0.001)),
+ });
+ }, [positions]);
+
+ return null;
+};
+
+export default MapAccuracy;
diff --git a/src/map/main/MapDefaultCamera.js b/src/map/main/MapDefaultCamera.js
new file mode 100644
index 00000000..90b3061b
--- /dev/null
+++ b/src/map/main/MapDefaultCamera.js
@@ -0,0 +1,52 @@
+import maplibregl from 'maplibre-gl';
+import { useEffect, useState } from 'react';
+import { useSelector } from 'react-redux';
+import { usePreference } from '../../common/util/preferences';
+import { map } from '../core/MapView';
+
+const MapDefaultCamera = () => {
+ const selectedDeviceId = useSelector((state) => state.devices.selectedId);
+ const positions = useSelector((state) => state.session.positions);
+
+ const defaultLatitude = usePreference('latitude');
+ const defaultLongitude = usePreference('longitude');
+ const defaultZoom = usePreference('zoom', 0);
+
+ const [initialized, setInitialized] = useState(false);
+
+ useEffect(() => {
+ if (selectedDeviceId) {
+ setInitialized(true);
+ } else if (!initialized) {
+ if (defaultLatitude && defaultLongitude) {
+ map.jumpTo({
+ center: [defaultLongitude, defaultLatitude],
+ zoom: defaultZoom,
+ });
+ setInitialized(true);
+ } else {
+ const coordinates = Object.values(positions).map((item) => [item.longitude, item.latitude]);
+ if (coordinates.length > 1) {
+ const bounds = coordinates.reduce((bounds, item) => bounds.extend(item), new maplibregl.LngLatBounds(coordinates[0], coordinates[1]));
+ const canvas = map.getCanvas();
+ map.fitBounds(bounds, {
+ duration: 0,
+ padding: Math.min(canvas.width, canvas.height) * 0.1,
+ });
+ setInitialized(true);
+ } else if (coordinates.length) {
+ const [individual] = coordinates;
+ map.jumpTo({
+ center: individual,
+ zoom: Math.max(map.getZoom(), 10),
+ });
+ setInitialized(true);
+ }
+ }
+ }
+ }, [selectedDeviceId, initialized, defaultLatitude, defaultLongitude, defaultZoom, positions]);
+
+ return null;
+};
+
+export default MapDefaultCamera;
diff --git a/src/map/main/MapLiveRoutes.js b/src/map/main/MapLiveRoutes.js
new file mode 100644
index 00000000..44cdc6ca
--- /dev/null
+++ b/src/map/main/MapLiveRoutes.js
@@ -0,0 +1,83 @@
+import { useId, useEffect } from 'react';
+import { useSelector } from 'react-redux';
+import { useTheme } from '@mui/styles';
+import { map } from '../core/MapView';
+import { useAttributePreference } from '../../common/util/preferences';
+
+const MapLiveRoutes = () => {
+ const id = useId();
+
+ const theme = useTheme();
+
+ const type = useAttributePreference('mapLiveRoutes', 'none');
+
+ const devices = useSelector((state) => state.devices.items);
+ const selectedDeviceId = useSelector((state) => state.devices.selectedId);
+
+ const history = useSelector((state) => state.session.history);
+
+ useEffect(() => {
+ if (type !== 'none') {
+ map.addSource(id, {
+ type: 'geojson',
+ data: {
+ type: 'Feature',
+ geometry: {
+ type: 'LineString',
+ coordinates: [],
+ },
+ },
+ });
+ map.addLayer({
+ source: id,
+ id,
+ type: 'line',
+ layout: {
+ 'line-join': 'round',
+ 'line-cap': 'round',
+ },
+ paint: {
+ 'line-color': ['get', 'color'],
+ 'line-width': 2,
+ },
+ });
+
+ return () => {
+ if (map.getLayer(id)) {
+ map.removeLayer(id);
+ }
+ if (map.getSource(id)) {
+ map.removeSource(id);
+ }
+ };
+ }
+ return () => {};
+ }, [type]);
+
+ useEffect(() => {
+ if (type !== 'none') {
+ const deviceIds = Object.values(devices)
+ .map((device) => device.id)
+ .filter((id) => (type === 'selected' ? id === selectedDeviceId : true))
+ .filter((id) => history.hasOwnProperty(id));
+
+ map.getSource(id)?.setData({
+ type: 'FeatureCollection',
+ features: deviceIds.map((deviceId) => ({
+ type: 'Feature',
+ geometry: {
+ type: 'LineString',
+ coordinates: history[deviceId],
+ },
+ properties: {
+ color: devices[deviceId].attributes['web.reportColor'] || theme.palette.geometry.main,
+ },
+ })),
+ });
+ }
+ }, [theme, type, devices, selectedDeviceId, history]);
+
+ return null;
+};
+
+export default MapLiveRoutes;
diff --git a/src/map/main/MapSelectedDevice.js b/src/map/main/MapSelectedDevice.js
new file mode 100644
index 00000000..caf40cf8
--- /dev/null
+++ b/src/map/main/MapSelectedDevice.js
@@ -0,0 +1,31 @@
+import { useEffect } from 'react';
+
+import { useSelector } from 'react-redux';
+import dimensions from '../../common/theme/dimensions';
+import { map } from '../core/MapView';
+import { usePrevious } from '../../reactHelper';
+import { useAttributePreference } from '../../common/util/preferences';
+
+const MapSelectedDevice = () => {
+ const selectedDeviceId = useSelector((state) => state.devices.selectedId);
+ const previousDeviceId = usePrevious(selectedDeviceId);
+
+ const selectZoom = useAttributePreference('web.selectZoom', 10);
+ const mapFollow = useAttributePreference('mapFollow', false);
+
+ const position = useSelector((state) => state.session.positions[selectedDeviceId]);
+
+ useEffect(() => {
+ if ((selectedDeviceId !== previousDeviceId || mapFollow) && position) {
+ map.easeTo({
+ center: [position.longitude, position.latitude],
+ zoom: Math.max(map.getZoom(), selectZoom),
+ offset: [0, -dimensions.popupMapOffset / 2],
+ });
+ }
+ });
+
+ return null;
+};
+
+export default MapSelectedDevice;
diff --git a/src/map/main/PoiMap.js b/src/map/main/PoiMap.js
new file mode 100644
index 00000000..07341183
--- /dev/null
+++ b/src/map/main/PoiMap.js
@@ -0,0 +1,87 @@
+import { useId, useEffect, useState } from 'react';
+import { kml } from '@tmcw/togeojson';
+import { useTheme } from '@mui/styles';
+import { map } from '../core/MapView';
+import { useEffectAsync } from '../../reactHelper';
+import { usePreference } from '../../common/util/preferences';
+import { findFonts } from '../core/mapUtil';
+
+const PoiMap = () => {
+ const id = useId();
+
+ const theme = useTheme();
+
+ const poiLayer = usePreference('poiLayer');
+
+ const [data, setData] = useState(null);
+
+ useEffectAsync(async () => {
+ if (poiLayer) {
+ const file = await fetch(poiLayer);
+ const dom = new DOMParser().parseFromString(await file.text(), 'text/xml');
+ setData(kml(dom));
+ }
+ }, [poiLayer]);
+
+ useEffect(() => {
+ if (data) {
+ map.addSource(id, {
+ type: 'geojson',
+ data,
+ });
+ map.addLayer({
+ source: id,
+ id: 'poi-point',
+ type: 'circle',
+ paint: {
+ 'circle-radius': 5,
+ 'circle-color': theme.palette.geometry.main,
+ },
+ });
+ map.addLayer({
+ source: id,
+ id: 'poi-line',
+ type: 'line',
+ paint: {
+ 'line-color': theme.palette.geometry.main,
+ 'line-width': 2,
+ },
+ });
+ map.addLayer({
+ source: id,
+ id: 'poi-title',
+ type: 'symbol',
+ layout: {
+ 'text-field': '{name}',
+ 'text-anchor': 'bottom',
+ 'text-offset': [0, -0.5],
+ 'text-font': findFonts(map),
+ 'text-size': 12,
+ },
+ paint: {
+ 'text-halo-color': 'white',
+ 'text-halo-width': 1,
+ },
+ });
+ return () => {
+ if (map.getLayer('poi-point')) {
+ map.removeLayer('poi-point');
+ }
+ if (map.getLayer('poi-line')) {
+ map.removeLayer('poi-line');
+ }
+ if (map.getLayer('poi-title')) {
+ map.removeLayer('poi-title');
+ }
+ if (map.getSource(id)) {
+ map.removeSource(id);
+ }
+ };
+ }
+ return () => {};
+ }, [data]);
+
+ return null;
+};
+
+export default PoiMap;
diff --git a/src/map/notification/MapNotification.js b/src/map/notification/MapNotification.js
new file mode 100644
index 00000000..81038f95
--- /dev/null
+++ b/src/map/notification/MapNotification.js
@@ -0,0 +1,49 @@
+import { useEffect, useMemo } from 'react';
+import { map } from '../core/MapView';
+import './notification.css';
+
+const statusClass = (status) => `maplibregl-ctrl-icon maplibre-ctrl-notification maplibre-ctrl-notification-${status}`;
+
+class NotificationControl {
+ constructor(eventHandler) {
+ this.eventHandler = eventHandler;
+ }
+
+ onAdd() {
+ this.button = document.createElement('button');
+ this.button.className = statusClass('off');
+ this.button.type = 'button';
+ this.button.onclick = () => this.eventHandler(this);
+
+ this.container = document.createElement('div');
+ this.container.className = 'maplibregl-ctrl-group maplibregl-ctrl';
+ this.container.appendChild(this.button);
+
+ return this.container;
+ }
+
+ onRemove() {
+ this.container.parentNode.removeChild(this.container);
+ }
+
+ setEnabled(enabled) {
+ this.button.className = statusClass(enabled ? 'on' : 'off');
+ }
+}
+
+const MapNotification = ({ enabled, onClick }) => {
+ const control = useMemo(() => new NotificationControl(onClick), [onClick]);
+
+ useEffect(() => {
+ map.addControl(control);
+ return () => map.removeControl(control);
+ }, [onClick]);
+
+ useEffect(() => {
+ control.setEnabled(enabled);
+ }, [enabled]);
+
+ return null;
+};
+
+export default MapNotification;
diff --git a/src/map/notification/notification.css b/src/map/notification/notification.css
new file mode 100644
index 00000000..73db13bb
--- /dev/null
+++ b/src/map/notification/notification.css
@@ -0,0 +1,13 @@
+.maplibre-ctrl-notification {
+ background-repeat: no-repeat;
+ background-position: center;
+ pointer-events: auto;
+}
+
+.maplibre-ctrl-notification-on {
+ background-image: url("data:image/svg+xml,%3Csvg width='20' height='20' version='1.1' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='%23f44336' d='m6.4728 3.4802-1.1412-1.1412c-1.9152 1.4604-3.1761 3.7108-3.2878 6.2645h1.596c0.1197-2.1148 1.205-3.9662 2.833-5.1233zm9.8875 5.1233h1.596c-0.1197-2.5537-1.3806-4.8041-3.2878-6.2645l-1.1332 1.1412c1.612 1.1571 2.7053 3.0085 2.825 5.1233zm-1.5721 0.39901c0-2.4499-1.3088-4.5008-3.5911-5.0435v-0.54265c0-0.66236-0.53467-1.197-1.197-1.197-0.66236 0-1.197 0.53467-1.197 1.197v0.54265c-2.2903 0.54265-3.5911 2.5856-3.5911 5.0435v3.9901l-1.596 1.596v0.79802h12.768v-0.79802l-1.596-1.596zm-4.7881 8.7782c0.11172 0 0.21546-8e-3 0.31921-0.03192 0.51871-0.11172 0.94166-0.46285 1.1491-0.94166 0.0798-0.19152 0.1197-0.39901 0.1197-0.62246h-3.1921c0.00798 0.87782 0.71822 1.596 1.604 1.596z' stroke-width='.79802'/%3E%3C/svg%3E");
+}
+
+.maplibre-ctrl-notification-off {
+ background-image: url("data:image/svg+xml,%3Csvg width='20' height='20' version='1.1' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='m6.4728 3.4802-1.1412-1.1412c-1.9152 1.4604-3.1761 3.7108-3.2878 6.2645h1.596c0.1197-2.1148 1.205-3.9662 2.833-5.1233zm9.8875 5.1233h1.596c-0.1197-2.5537-1.3806-4.8041-3.2878-6.2645l-1.1332 1.1412c1.612 1.1571 2.7053 3.0085 2.825 5.1233zm-1.5721 0.39901c0-2.4499-1.3088-4.5008-3.5911-5.0435v-0.54265c0-0.66236-0.53467-1.197-1.197-1.197-0.66236 0-1.197 0.53467-1.197 1.197v0.54265c-2.2903 0.54265-3.5911 2.5856-3.5911 5.0435v3.9901l-1.596 1.596v0.79802h12.768v-0.79802l-1.596-1.596zm-4.7881 8.7782c0.11172 0 0.21546-8e-3 0.31921-0.03192 0.51871-0.11172 0.94166-0.46285 1.1491-0.94166 0.0798-0.19152 0.1197-0.39901 0.1197-0.62246h-3.1921c0.00798 0.87782 0.71822 1.596 1.604 1.596z' stroke-width='.79802'/%3E%3C/svg%3E");
+}
diff --git a/src/map/overlay/MapOverlay.js b/src/map/overlay/MapOverlay.js
new file mode 100644
index 00000000..e436ea8d
--- /dev/null
+++ b/src/map/overlay/MapOverlay.js
@@ -0,0 +1,39 @@
+import { useId, useEffect } from 'react';
+import { useAttributePreference } from '../../common/util/preferences';
+import { map } from '../core/MapView';
+import useMapOverlays from './useMapOverlays';
+
+const MapOverlay = () => {
+ const id = useId();
+
+ const mapOverlays = useMapOverlays();
+ const selectedMapOverlay = useAttributePreference('selectedMapOverlay');
+
+ const activeOverlay = mapOverlays.filter((overlay) => overlay.available).find((overlay) => overlay.id === selectedMapOverlay);
+
+ useEffect(() => {
+ if (activeOverlay) {
+ map.addSource(id, activeOverlay.source);
+ map.addLayer({
+ id,
+ type: 'raster',
+ source: id,
+ layout: {
+ visibility: 'visible',
+ },
+ });
+ }
+ return () => {
+ if (map.getLayer(id)) {
+ map.removeLayer(id);
+ }
+ if (map.getSource(id)) {
+ map.removeSource(id);
+ }
+ };
+ }, [id, activeOverlay]);
+
+ return null;
+};
+
+export default MapOverlay;
diff --git a/src/map/overlay/useMapOverlays.js b/src/map/overlay/useMapOverlays.js
new file mode 100644
index 00000000..dafb5f83
--- /dev/null
+++ b/src/map/overlay/useMapOverlays.js
@@ -0,0 +1,103 @@
+import { useMemo } from 'react';
+import { useSelector } from 'react-redux';
+import { useTranslation } from '../../common/components/LocalizationProvider';
+import { useAttributePreference } from '../../common/util/preferences';
+
+const sourceCustom = (urls) => ({
+ type: 'raster',
+ tiles: urls,
+ tileSize: 256,
+ maxzoom: 18,
+});
+
+const sourceOpenWeather = (style, key) => sourceCustom([
+ `https://tile.openweathermap.org/map/${style}/{z}/{x}/{y}.png?appid=${key}`,
+]);
+
+export default () => {
+ const t = useTranslation();
+
+ const openWeatherKey = useAttributePreference('openWeatherKey');
+ const tomTomKey = useAttributePreference('tomTomKey');
+ const hereKey = useAttributePreference('hereKey');
+ const customMapOverlay = useSelector((state) => state.session.server.overlayUrl);
+
+ return useMemo(() => [
+ {
+ id: 'openSeaMap',
+ title: t('mapOpenSeaMap'),
+ source: sourceCustom(['https://tiles.openseamap.org/seamark/{z}/{x}/{y}.png']),
+ available: true,
+ },
+ {
+ id: 'openRailwayMap',
+ title: t('mapOpenRailwayMap'),
+ source: sourceCustom(['https://tiles.openrailwaymap.org/standard/{z}/{x}/{y}.png']),
+ available: true,
+ },
+ {
+ id: 'openWeatherClouds',
+ title: t('mapOpenWeatherClouds'),
+ source: sourceOpenWeather('clouds_new', openWeatherKey),
+ available: !!openWeatherKey,
+ attribute: 'openWeatherKey',
+ },
+ {
+ id: 'openWeatherPrecipitation',
+ title: t('mapOpenWeatherPrecipitation'),
+ source: sourceOpenWeather('precipitation_new', openWeatherKey),
+ available: !!openWeatherKey,
+ attribute: 'openWeatherKey',
+ },
+ {
+ id: 'openWeatherPressure',
+ title: t('mapOpenWeatherPressure'),
+ source: sourceOpenWeather('pressure_new', openWeatherKey),
+ available: !!openWeatherKey,
+ attribute: 'openWeatherKey',
+ },
+ {
+ id: 'openWeatherWind',
+ title: t('mapOpenWeatherWind'),
+ source: sourceOpenWeather('wind_new', openWeatherKey),
+ available: !!openWeatherKey,
+ attribute: 'openWeatherKey',
+ },
+ {
+ id: 'openWeatherTemperature',
+ title: t('mapOpenWeatherTemperature'),
+ source: sourceOpenWeather('temp_new', openWeatherKey),
+ available: !!openWeatherKey,
+ attribute: 'openWeatherKey',
+ },
+ {
+ id: 'tomTomFlow',
+ title: t('mapTomTomFlow'),
+ source: sourceCustom([`https://api.tomtom.com/traffic/map/4/tile/flow/absolute/{z}/{x}/{y}.png?key=${tomTomKey}`]),
+ available: !!tomTomKey,
+ attribute: 'tomTomKey',
+ },
+ {
+ id: 'tomTomIncidents',
+ title: t('mapTomTomIncidents'),
+ source: sourceCustom([`https://api.tomtom.com/traffic/map/4/tile/incidents/s3/{z}/{x}/{y}.png?key=${tomTomKey}`]),
+ available: !!tomTomKey,
+ attribute: 'tomTomKey',
+ },
+ {
+ id: 'hereFlow',
+ title: t('mapHereFlow'),
+ source: sourceCustom(
+ [1, 2, 3, 4].map((i) => `https://${i}.traffic.maps.ls.hereapi.com/maptile/2.1/flowtile/newest/normal.day/{z}/{x}/{y}/256/png8?apiKey=${hereKey}`),
+ ),
+ available: !!hereKey,
+ attribute: 'hereKey',
+ },
+ {
+ id: 'custom',
+ title: t('mapOverlayCustom'),
+ source: sourceCustom(customMapOverlay),
+ available: !!customMapOverlay,
+ },
+ ], [t, openWeatherKey, tomTomKey, hereKey, customMapOverlay]);
+};
diff --git a/src/map/switcher/switcher.css b/src/map/switcher/switcher.css
new file mode 100644
index 00000000..afba84d6
--- /dev/null
+++ b/src/map/switcher/switcher.css
@@ -0,0 +1,34 @@
+.maplibregl-style-list {
+ display: none;
+}
+
+.maplibregl-ctrl-group .maplibregl-style-list button {
+ background: none;
+ border: none;
+ cursor: pointer;
+ display: block;
+ font-size: 14px;
+ padding: 8px 8px 6px;
+ text-align: right;
+ width: 100%;
+ height: auto;
+}
+
+.maplibregl-style-list button.active {
+ font-weight: bold;
+}
+
+.maplibregl-style-list button:hover {
+ background-color: rgba(0, 0, 0, 0.05);
+}
+
+.maplibregl-style-list button + button {
+ border-top: 1px solid #ddd;
+}
+
+.maplibregl-style-switcher {
+ background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJDYXBhXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB3aWR0aD0iNTQuODQ5cHgiIGhlaWdodD0iNTQuODQ5cHgiIHZpZXdCb3g9IjAgMCA1NC44NDkgNTQuODQ5IiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1NC44NDkgNTQuODQ5OyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PGc+PGc+PGc+PHBhdGggZD0iTTU0LjQ5NywzOS42MTRsLTEwLjM2My00LjQ5bC0xNC45MTcsNS45NjhjLTAuNTM3LDAuMjE0LTEuMTY1LDAuMzE5LTEuNzkzLDAuMzE5Yy0wLjYyNywwLTEuMjU0LTAuMTA0LTEuNzktMC4zMThsLTE0LjkyMS01Ljk2OEwwLjM1MSwzOS42MTRjLTAuNDcyLDAuMjAzLTAuNDY3LDAuNTI0LDAuMDEsMC43MTZMMjYuNTYsNTAuODFjMC40NzcsMC4xOTEsMS4yNTEsMC4xOTEsMS43MjksMEw1NC40ODgsNDAuMzNDNTQuOTY0LDQwLjEzOSw1NC45NjksMzkuODE3LDU0LjQ5NywzOS42MTR6Ii8+PHBhdGggZD0iTTU0LjQ5NywyNy41MTJsLTEwLjM2NC00LjQ5MWwtMTQuOTE2LDUuOTY2Yy0wLjUzNiwwLjIxNS0xLjE2NSwwLjMyMS0xLjc5MiwwLjMyMWMtMC42MjgsMC0xLjI1Ni0wLjEwNi0xLjc5My0wLjMyMWwtMTQuOTE4LTUuOTY2TDAuMzUxLDI3LjUxMmMtMC40NzIsMC4yMDMtMC40NjcsMC41MjMsMC4wMSwwLjcxNkwyNi41NiwzOC43MDZjMC40NzcsMC4xOSwxLjI1MSwwLjE5LDEuNzI5LDBsMjYuMTk5LTEwLjQ3OUM1NC45NjQsMjguMDM2LDU0Ljk2OSwyNy43MTYsNTQuNDk3LDI3LjUxMnoiLz48cGF0aCBkPSJNMC4zNjEsMTYuMTI1bDEzLjY2Miw1LjQ2NWwxMi41MzcsNS4wMTVjMC40NzcsMC4xOTEsMS4yNTEsMC4xOTEsMS43MjksMGwxMi41NDEtNS4wMTZsMTMuNjU4LTUuNDYzYzAuNDc3LTAuMTkxLDAuNDgtMC41MTEsMC4wMS0wLjcxNkwyOC4yNzcsNC4wNDhjLTAuNDcxLTAuMjA0LTEuMjM2LTAuMjA0LTEuNzA4LDBMMC4zNTEsMTUuNDFDLTAuMTIxLDE1LjYxNC0wLjExNiwxNS45MzUsMC4zNjEsMTYuMTI1eiIvPjwvZz48L2c+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjwvc3ZnPg==);
+ background-position: center;
+ background-repeat: no-repeat;
+ background-size: 70%;
+}
diff --git a/src/map/switcher/switcher.js b/src/map/switcher/switcher.js
new file mode 100644
index 00000000..b6c62a6e
--- /dev/null
+++ b/src/map/switcher/switcher.js
@@ -0,0 +1,123 @@
+import './switcher.css';
+
+export class SwitcherControl {
+
+ constructor(onBeforeSwitch, onSwitch, onAfterSwitch) {
+ this.onBeforeSwitch = onBeforeSwitch;
+ this.onSwitch = onSwitch;
+ this.onAfterSwitch = onAfterSwitch;
+ this.onDocumentClick = this.onDocumentClick.bind(this);
+ this.styles = [];
+ this.currentStyle = null;
+ }
+
+ getDefaultPosition() {
+ return 'top-right';
+ }
+
+ updateStyles(updatedStyles, defaultStyle) {
+ this.styles = updatedStyles;
+
+ let selectedStyle = null;
+ for (const style of this.styles) {
+ if (style.id === (this.currentStyle || defaultStyle)) {
+ selectedStyle = style.id;
+ break;
+ }
+ }
+ if (!selectedStyle) {
+ selectedStyle = this.styles[0].id;
+ }
+
+ while (this.mapStyleContainer.firstChild) {
+ this.mapStyleContainer.removeChild(this.mapStyleContainer.firstChild);
+ }
+
+ let selectedStyleElement;
+
+ for (const style of this.styles) {
+ const styleElement = document.createElement('button');
+ styleElement.type = 'button';
+ styleElement.innerText = style.title;
+ styleElement.dataset.id = style.id;
+ styleElement.dataset.style = JSON.stringify(style.style);
+ styleElement.addEventListener('click', (event) => {
+ const { target } = event;
+ if (!target.classList.contains('active')) {
+ this.onSelectStyle(target);
+ }
+ });
+ if (style.id === selectedStyle) {
+ selectedStyleElement = styleElement;
+ styleElement.classList.add('active');
+ }
+ this.mapStyleContainer.appendChild(styleElement);
+ }
+
+ if (this.currentStyle !== selectedStyle) {
+ this.onSelectStyle(selectedStyleElement);
+ this.currentStyle = selectedStyle;
+ }
+ }
+
+ onSelectStyle(target) {
+ this.onBeforeSwitch();
+
+ const style = this.styles.find((it) => it.id === target.dataset.id);
+ this.map.setStyle(style.style, { diff: false });
+ this.map.setTransformRequest(style.transformRequest);
+
+ this.onSwitch(target.dataset.id);
+
+ this.mapStyleContainer.style.display = 'none';
+ this.styleButton.style.display = 'block';
+
+ const elements = this.mapStyleContainer.getElementsByClassName('active');
+ while (elements[0]) {
+ elements[0].classList.remove('active');
+ }
+ target.classList.add('active');
+
+ this.currentStyle = target.dataset.id;
+
+ this.onAfterSwitch();
+ }
+
+ onAdd(map) {
+ this.map = map;
+ this.controlContainer = document.createElement('div');
+ this.controlContainer.classList.add('maplibregl-ctrl');
+ this.controlContainer.classList.add('maplibregl-ctrl-group');
+ this.mapStyleContainer = document.createElement('div');
+ this.styleButton = document.createElement('button');
+ this.styleButton.type = 'button';
+ this.mapStyleContainer.classList.add('maplibregl-style-list');
+ this.styleButton.classList.add('maplibregl-ctrl-icon');
+ this.styleButton.classList.add('maplibregl-style-switcher');
+ this.styleButton.addEventListener('click', () => {
+ this.styleButton.style.display = 'none';
+ this.mapStyleContainer.style.display = 'block';
+ });
+ document.addEventListener('click', this.onDocumentClick);
+ this.controlContainer.appendChild(this.styleButton);
+ this.controlContainer.appendChild(this.mapStyleContainer);
+ return this.controlContainer;
+ }
+
+ onRemove() {
+ if (!this.controlContainer || !this.controlContainer.parentNode || !this.map || !this.styleButton) {
+ return;
+ }
+ this.styleButton.removeEventListener('click', this.onDocumentClick);
+ this.controlContainer.parentNode.removeChild(this.controlContainer);
+ document.removeEventListener('click', this.onDocumentClick);
+ this.map = undefined;
+ }
+
+ onDocumentClick(event) {
+ if (this.controlContainer && !this.controlContainer.contains(event.target) && this.mapStyleContainer && this.styleButton) {
+ this.mapStyleContainer.style.display = 'none';
+ this.styleButton.style.display = 'block';
+ }
+ }
+}
diff --git a/src/other/EventPage.jsx b/src/other/EventPage.jsx
new file mode 100644
index 00000000..c8d84d5e
--- /dev/null
+++ b/src/other/EventPage.jsx
@@ -0,0 +1,108 @@
+import React, { useCallback, useState } from 'react';
+
+import {
+ Typography, AppBar, Toolbar, IconButton,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+import ArrowBackIcon from '@mui/icons-material/ArrowBack';
+import { useNavigate, useParams } from 'react-router-dom';
+import { useEffectAsync } from '../reactHelper';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import MapView from '../map/core/MapView';
+import MapCamera from '../map/MapCamera';
+import MapPositions from '../map/MapPositions';
+import MapGeofence from '../map/MapGeofence';
+import StatusCard from '../common/components/StatusCard';
+import { formatNotificationTitle } from '../common/util/formatter';
+
+const useStyles = makeStyles(() => ({
+ root: {
+ height: '100%',
+ display: 'flex',
+ flexDirection: 'column',
+ },
+ toolbar: {
+ zIndex: 1,
+ },
+ mapContainer: {
+ flexGrow: 1,
+ },
+}));
+
+const EventPage = () => {
+ const classes = useStyles();
+ const navigate = useNavigate();
+ const t = useTranslation();
+
+ const { id } = useParams();
+
+ const [event, setEvent] = useState();
+ const [position, setPosition] = useState();
+ const [showCard, setShowCard] = useState(false);
+
+ const formatType = (event) => formatNotificationTitle(t, {
+ type: event.type,
+ attributes: {
+ alarms: event.attributes.alarm,
+ },
+ });
+
+ const onMarkerClick = useCallback((positionId) => {
+ setShowCard(!!positionId);
+ }, [setShowCard]);
+
+ useEffectAsync(async () => {
+ if (id) {
+ const response = await fetch(`/api/events/${id}`);
+ if (response.ok) {
+ setEvent(await response.json());
+ } else {
+ throw Error(await response.text());
+ }
+ }
+ }, [id]);
+
+ useEffectAsync(async () => {
+ if (event && event.positionId) {
+ const response = await fetch(`/api/positions?id=${event.positionId}`);
+ if (response.ok) {
+ const positions = await response.json();
+ if (positions.length > 0) {
+ setPosition(positions[0]);
+ }
+ } else {
+ throw Error(await response.text());
+ }
+ }
+ }, [event]);
+
+ return (
+ <div className={classes.root}>
+ <AppBar color="inherit" position="static" className={classes.toolbar}>
+ <Toolbar>
+ <IconButton color="inherit" edge="start" sx={{ mr: 2 }} onClick={() => navigate('/')}>
+ <ArrowBackIcon />
+ </IconButton>
+ <Typography variant="h6">{event && formatType(event)}</Typography>
+ </Toolbar>
+ </AppBar>
+ <div className={classes.mapContainer}>
+ <MapView>
+ <MapGeofence />
+ {position && <MapPositions positions={[position]} onClick={onMarkerClick} titleField="fixTime" />}
+ </MapView>
+ {position && <MapCamera latitude={position.latitude} longitude={position.longitude} />}
+ {position && showCard && (
+ <StatusCard
+ deviceId={position.deviceId}
+ position={position}
+ onClose={() => setShowCard(false)}
+ disableActions
+ />
+ )}
+ </div>
+ </div>
+ );
+};
+
+export default EventPage;
diff --git a/src/other/GeofencesList.jsx b/src/other/GeofencesList.jsx
new file mode 100644
index 00000000..d26eff09
--- /dev/null
+++ b/src/other/GeofencesList.jsx
@@ -0,0 +1,54 @@
+import React, { Fragment } from 'react';
+import { useDispatch, useSelector } from 'react-redux';
+import makeStyles from '@mui/styles/makeStyles';
+import {
+ Divider, List, ListItemButton, ListItemText,
+} from '@mui/material';
+
+import { geofencesActions } from '../store';
+import CollectionActions from '../settings/components/CollectionActions';
+import { useCatchCallback } from '../reactHelper';
+
+const useStyles = makeStyles(() => ({
+ list: {
+ maxHeight: '100%',
+ overflow: 'auto',
+ },
+ icon: {
+ width: '25px',
+ height: '25px',
+ filter: 'brightness(0) invert(1)',
+ },
+}));
+
+const GeofencesList = ({ onGeofenceSelected }) => {
+ const classes = useStyles();
+ const dispatch = useDispatch();
+
+ const items = useSelector((state) => state.geofences.items);
+
+ const refreshGeofences = useCatchCallback(async () => {
+ const response = await fetch('/api/geofences');
+ if (response.ok) {
+ dispatch(geofencesActions.refresh(await response.json()));
+ } else {
+ throw Error(await response.text());
+ }
+ }, [dispatch]);
+
+ return (
+ <List className={classes.list}>
+ {Object.values(items).map((item, index, list) => (
+ <Fragment key={item.id}>
+ <ListItemButton key={item.id} onClick={() => onGeofenceSelected(item.id)}>
+ <ListItemText primary={item.name} />
+ <CollectionActions itemId={item.id} editPath="/settings/geofence" endpoint="geofences" setTimestamp={refreshGeofences} />
+ </ListItemButton>
+ {index < list.length - 1 ? <Divider /> : null}
+ </Fragment>
+ ))}
+ </List>
+ );
+};
+
+export default GeofencesList;
diff --git a/src/other/GeofencesPage.jsx b/src/other/GeofencesPage.jsx
new file mode 100644
index 00000000..a27a6dca
--- /dev/null
+++ b/src/other/GeofencesPage.jsx
@@ -0,0 +1,142 @@
+import React, { useState } from 'react';
+import { useDispatch } from 'react-redux';
+import {
+ Divider, Typography, IconButton, useMediaQuery, Toolbar,
+} from '@mui/material';
+import Tooltip from '@mui/material/Tooltip';
+import makeStyles from '@mui/styles/makeStyles';
+import { useTheme } from '@mui/material/styles';
+import Drawer from '@mui/material/Drawer';
+import ArrowBackIcon from '@mui/icons-material/ArrowBack';
+import UploadFileIcon from '@mui/icons-material/UploadFile';
+import { useNavigate } from 'react-router-dom';
+import MapView from '../map/core/MapView';
+import MapCurrentLocation from '../map/MapCurrentLocation';
+import MapGeofenceEdit from '../map/draw/MapGeofenceEdit';
+import GeofencesList from './GeofencesList';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import MapGeocoder from '../map/geocoder/MapGeocoder';
+import { errorsActions } from '../store';
+
+const useStyles = makeStyles((theme) => ({
+ root: {
+ height: '100%',
+ display: 'flex',
+ flexDirection: 'column',
+ },
+ content: {
+ flexGrow: 1,
+ overflow: 'hidden',
+ display: 'flex',
+ flexDirection: 'row',
+ [theme.breakpoints.down('sm')]: {
+ flexDirection: 'column-reverse',
+ },
+ },
+ drawer: {
+ zIndex: 1,
+ },
+ drawerPaper: {
+ position: 'relative',
+ [theme.breakpoints.up('sm')]: {
+ width: theme.dimensions.drawerWidthTablet,
+ },
+ [theme.breakpoints.down('sm')]: {
+ height: theme.dimensions.drawerHeightPhone,
+ },
+ },
+ mapContainer: {
+ flexGrow: 1,
+ },
+ title: {
+ flexGrow: 1,
+ },
+ fileInput: {
+ display: 'none',
+ },
+}));
+
+const GeofencesPage = () => {
+ const theme = useTheme();
+ const classes = useStyles();
+ const dispatch = useDispatch();
+ const navigate = useNavigate();
+ const t = useTranslation();
+
+ const isPhone = useMediaQuery(theme.breakpoints.down('sm'));
+
+ const [selectedGeofenceId, setSelectedGeofenceId] = useState();
+
+ const handleFile = (event) => {
+ const files = Array.from(event.target.files);
+ const [file] = files;
+ const reader = new FileReader();
+ reader.onload = async () => {
+ const xml = new DOMParser().parseFromString(reader.result, 'text/xml');
+ const segment = xml.getElementsByTagName('trkseg')[0];
+ const coordinates = Array.from(segment.getElementsByTagName('trkpt'))
+ .map((point) => `${point.getAttribute('lat')} ${point.getAttribute('lon')}`)
+ .join(', ');
+ const area = `LINESTRING (${coordinates})`;
+ const newItem = { name: t('sharedGeofence'), area };
+ try {
+ const response = await fetch('/api/geofences', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(newItem),
+ });
+ if (response.ok) {
+ const item = await response.json();
+ navigate(`/settings/geofence/${item.id}`);
+ } else {
+ throw Error(await response.text());
+ }
+ } catch (error) {
+ dispatch(errorsActions.push(error.message));
+ }
+ };
+ reader.onerror = (event) => {
+ dispatch(errorsActions.push(event.target.error));
+ };
+ reader.readAsText(file);
+ };
+
+ return (
+ <div className={classes.root}>
+ <div className={classes.content}>
+ <Drawer
+ className={classes.drawer}
+ anchor={isPhone ? 'bottom' : 'left'}
+ variant="permanent"
+ classes={{ paper: classes.drawerPaper }}
+ >
+ <Toolbar>
+ <IconButton edge="start" sx={{ mr: 2 }} onClick={() => navigate(-1)}>
+ <ArrowBackIcon />
+ </IconButton>
+ <Typography variant="h6" className={classes.title}>{t('sharedGeofences')}</Typography>
+ <label htmlFor="upload-gpx">
+ <input accept=".gpx" id="upload-gpx" type="file" className={classes.fileInput} onChange={handleFile} />
+ <IconButton edge="end" component="span" onClick={() => {}}>
+ <Tooltip title={t('sharedUpload')}>
+ <UploadFileIcon />
+ </Tooltip>
+ </IconButton>
+ </label>
+ </Toolbar>
+ <Divider />
+ <GeofencesList onGeofenceSelected={setSelectedGeofenceId} />
+ </Drawer>
+ <div className={classes.mapContainer}>
+ <MapView>
+ <MapGeofenceEdit selectedGeofenceId={selectedGeofenceId} />
+ </MapView>
+ <MapCurrentLocation />
+ <MapGeocoder />
+ </div>
+ </div>
+ </div>
+ );
+};
+
+export default GeofencesPage;
diff --git a/src/other/NetworkPage.jsx b/src/other/NetworkPage.jsx
new file mode 100644
index 00000000..9dc00c61
--- /dev/null
+++ b/src/other/NetworkPage.jsx
@@ -0,0 +1,122 @@
+import React, { useState } from 'react';
+import { useSelector } from 'react-redux';
+
+import {
+ Typography, Container, Paper, AppBar, Toolbar, IconButton, Table, TableHead, TableRow, TableCell, TableBody,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+import ArrowBackIcon from '@mui/icons-material/ArrowBack';
+import { useNavigate, useParams } from 'react-router-dom';
+import { useEffectAsync } from '../reactHelper';
+
+const useStyles = makeStyles((theme) => ({
+ root: {
+ height: '100%',
+ display: 'flex',
+ flexDirection: 'column',
+ },
+ content: {
+ overflow: 'auto',
+ paddingTop: theme.spacing(2),
+ paddingBottom: theme.spacing(2),
+ display: 'flex',
+ flexDirection: 'column',
+ gap: theme.spacing(2),
+ },
+}));
+
+const NetworkPage = () => {
+ const classes = useStyles();
+ const navigate = useNavigate();
+
+ const { positionId } = useParams();
+
+ const [item, setItem] = useState({});
+
+ useEffectAsync(async () => {
+ if (positionId) {
+ const response = await fetch(`/api/positions?id=${positionId}`);
+ if (response.ok) {
+ const positions = await response.json();
+ if (positions.length > 0) {
+ setItem(positions[0]);
+ }
+ } else {
+ throw Error(await response.text());
+ }
+ }
+ }, [positionId]);
+
+ const deviceName = useSelector((state) => {
+ if (item) {
+ const device = state.devices.items[item.deviceId];
+ if (device) {
+ return device.name;
+ }
+ }
+ return null;
+ });
+
+ return (
+ <div className={classes.root}>
+ <AppBar position="sticky" color="inherit">
+ <Toolbar>
+ <IconButton color="inherit" edge="start" sx={{ mr: 2 }} onClick={() => navigate(-1)}>
+ <ArrowBackIcon />
+ </IconButton>
+ <Typography variant="h6">
+ {deviceName}
+ </Typography>
+ </Toolbar>
+ </AppBar>
+ <div className={classes.content}>
+ <Container maxWidth="sm">
+ <Paper>
+ <Table>
+ <TableHead>
+ <TableRow>
+ <TableCell>MCC</TableCell>
+ <TableCell>MNC</TableCell>
+ <TableCell>LAC</TableCell>
+ <TableCell>CID</TableCell>
+ </TableRow>
+ </TableHead>
+ <TableBody>
+ {(item.network?.cellTowers || []).map((cell) => (
+ <TableRow key={cell.cellId}>
+ <TableCell>{cell.mobileCountryCode}</TableCell>
+ <TableCell>{cell.mobileNetworkCode}</TableCell>
+ <TableCell>{cell.locationAreaCode}</TableCell>
+ <TableCell>{cell.cellId}</TableCell>
+ </TableRow>
+ ))}
+ </TableBody>
+ </Table>
+ </Paper>
+ </Container>
+ <Container maxWidth="sm">
+ <Paper>
+ <Table>
+ <TableHead>
+ <TableRow>
+ <TableCell>MAC</TableCell>
+ <TableCell>RSSI</TableCell>
+ </TableRow>
+ </TableHead>
+ <TableBody>
+ {(item.network?.wifiAccessPoints || []).map((wifi) => (
+ <TableRow key={wifi.macAddress}>
+ <TableCell>{wifi.macAddress}</TableCell>
+ <TableCell>{wifi.signalStrength}</TableCell>
+ </TableRow>
+ ))}
+ </TableBody>
+ </Table>
+ </Paper>
+ </Container>
+ </div>
+ </div>
+ );
+};
+
+export default NetworkPage;
diff --git a/src/other/PositionPage.jsx b/src/other/PositionPage.jsx
new file mode 100644
index 00000000..f253cd2c
--- /dev/null
+++ b/src/other/PositionPage.jsx
@@ -0,0 +1,110 @@
+import React, { useState } from 'react';
+import { useSelector } from 'react-redux';
+
+import {
+ Typography, Container, Paper, AppBar, Toolbar, IconButton, Table, TableHead, TableRow, TableCell, TableBody,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+import ArrowBackIcon from '@mui/icons-material/ArrowBack';
+import { useNavigate, useParams } from 'react-router-dom';
+import { useEffectAsync } from '../reactHelper';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import PositionValue from '../common/components/PositionValue';
+import usePositionAttributes from '../common/attributes/usePositionAttributes';
+
+const useStyles = makeStyles((theme) => ({
+ root: {
+ height: '100%',
+ display: 'flex',
+ flexDirection: 'column',
+ },
+ content: {
+ overflow: 'auto',
+ paddingTop: theme.spacing(2),
+ paddingBottom: theme.spacing(2),
+ },
+}));
+
+const PositionPage = () => {
+ const classes = useStyles();
+ const navigate = useNavigate();
+ const t = useTranslation();
+
+ const positionAttributes = usePositionAttributes(t);
+
+ const { id } = useParams();
+
+ const [item, setItem] = useState();
+
+ useEffectAsync(async () => {
+ if (id) {
+ const response = await fetch(`/api/positions?id=${id}`);
+ if (response.ok) {
+ const positions = await response.json();
+ if (positions.length > 0) {
+ setItem(positions[0]);
+ }
+ } else {
+ throw Error(await response.text());
+ }
+ }
+ }, [id]);
+
+ const deviceName = useSelector((state) => {
+ if (item) {
+ const device = state.devices.items[item.deviceId];
+ if (device) {
+ return device.name;
+ }
+ }
+ return null;
+ });
+
+ return (
+ <div className={classes.root}>
+ <AppBar position="sticky" color="inherit">
+ <Toolbar>
+ <IconButton color="inherit" edge="start" sx={{ mr: 2 }} onClick={() => navigate(-1)}>
+ <ArrowBackIcon />
+ </IconButton>
+ <Typography variant="h6">
+ {deviceName}
+ </Typography>
+ </Toolbar>
+ </AppBar>
+ <div className={classes.content}>
+ <Container maxWidth="sm">
+ <Paper>
+ <Table>
+ <TableHead>
+ <TableRow>
+ <TableCell>{t('stateName')}</TableCell>
+ <TableCell>{t('sharedName')}</TableCell>
+ <TableCell>{t('stateValue')}</TableCell>
+ </TableRow>
+ </TableHead>
+ <TableBody>
+ {item && Object.getOwnPropertyNames(item).filter((it) => it !== 'attributes').map((property) => (
+ <TableRow key={property}>
+ <TableCell>{property}</TableCell>
+ <TableCell><strong>{positionAttributes[property]?.name || property}</strong></TableCell>
+ <TableCell><PositionValue position={item} property={property} /></TableCell>
+ </TableRow>
+ ))}
+ {item && Object.getOwnPropertyNames(item.attributes).map((attribute) => (
+ <TableRow key={attribute}>
+ <TableCell>{attribute}</TableCell>
+ <TableCell><strong>{positionAttributes[attribute]?.name || attribute}</strong></TableCell>
+ <TableCell><PositionValue position={item} attribute={attribute} /></TableCell>
+ </TableRow>
+ ))}
+ </TableBody>
+ </Table>
+ </Paper>
+ </Container>
+ </div>
+ </div>
+ );
+};
+
+export default PositionPage;
diff --git a/src/other/ReplayPage.jsx b/src/other/ReplayPage.jsx
new file mode 100644
index 00000000..1050b976
--- /dev/null
+++ b/src/other/ReplayPage.jsx
@@ -0,0 +1,233 @@
+import React, {
+ useState, useEffect, useRef, useCallback,
+} from 'react';
+import {
+ IconButton, Paper, Slider, Toolbar, Typography,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+import ArrowBackIcon from '@mui/icons-material/ArrowBack';
+import TuneIcon from '@mui/icons-material/Tune';
+import DownloadIcon from '@mui/icons-material/Download';
+import PlayArrowIcon from '@mui/icons-material/PlayArrow';
+import PauseIcon from '@mui/icons-material/Pause';
+import FastForwardIcon from '@mui/icons-material/FastForward';
+import FastRewindIcon from '@mui/icons-material/FastRewind';
+import { useNavigate } from 'react-router-dom';
+import { useSelector } from 'react-redux';
+import MapView from '../map/core/MapView';
+import MapRoutePath from '../map/MapRoutePath';
+import MapRoutePoints from '../map/MapRoutePoints';
+import MapPositions from '../map/MapPositions';
+import { formatTime } from '../common/util/formatter';
+import ReportFilter from '../reports/components/ReportFilter';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import { useCatch } from '../reactHelper';
+import MapCamera from '../map/MapCamera';
+import MapGeofence from '../map/MapGeofence';
+import StatusCard from '../common/components/StatusCard';
+import { usePreference } from '../common/util/preferences';
+
+const useStyles = makeStyles((theme) => ({
+ root: {
+ height: '100%',
+ },
+ sidebar: {
+ display: 'flex',
+ flexDirection: 'column',
+ position: 'fixed',
+ zIndex: 3,
+ left: 0,
+ top: 0,
+ margin: theme.spacing(1.5),
+ width: theme.dimensions.drawerWidthDesktop,
+ [theme.breakpoints.down('md')]: {
+ width: '100%',
+ margin: 0,
+ },
+ },
+ title: {
+ flexGrow: 1,
+ },
+ slider: {
+ width: '100%',
+ },
+ controls: {
+ display: 'flex',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ },
+ formControlLabel: {
+ height: '100%',
+ width: '100%',
+ paddingRight: theme.spacing(1),
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ },
+ content: {
+ display: 'flex',
+ flexDirection: 'column',
+ padding: theme.spacing(2),
+ [theme.breakpoints.down('md')]: {
+ margin: theme.spacing(1),
+ },
+ [theme.breakpoints.up('md')]: {
+ marginTop: theme.spacing(1),
+ },
+ },
+}));
+
+const ReplayPage = () => {
+ const t = useTranslation();
+ const classes = useStyles();
+ const navigate = useNavigate();
+ const timerRef = useRef();
+
+ const hours12 = usePreference('twelveHourFormat');
+
+ const defaultDeviceId = useSelector((state) => state.devices.selectedId);
+
+ const [positions, setPositions] = useState([]);
+ const [index, setIndex] = useState(0);
+ const [selectedDeviceId, setSelectedDeviceId] = useState(defaultDeviceId);
+ const [showCard, setShowCard] = useState(false);
+ const [from, setFrom] = useState();
+ const [to, setTo] = useState();
+ const [expanded, setExpanded] = useState(true);
+ const [playing, setPlaying] = useState(false);
+
+ const deviceName = useSelector((state) => {
+ if (selectedDeviceId) {
+ const device = state.devices.items[selectedDeviceId];
+ if (device) {
+ return device.name;
+ }
+ }
+ return null;
+ });
+
+ useEffect(() => {
+ if (playing && positions.length > 0) {
+ timerRef.current = setInterval(() => {
+ setIndex((index) => index + 1);
+ }, 500);
+ } else {
+ clearInterval(timerRef.current);
+ }
+
+ return () => clearInterval(timerRef.current);
+ }, [playing, positions]);
+
+ useEffect(() => {
+ if (index >= positions.length - 1) {
+ clearInterval(timerRef.current);
+ setPlaying(false);
+ }
+ }, [index, positions]);
+
+ const onPointClick = useCallback((_, index) => {
+ setIndex(index);
+ }, [setIndex]);
+
+ const onMarkerClick = useCallback((positionId) => {
+ setShowCard(!!positionId);
+ }, [setShowCard]);
+
+ const handleSubmit = useCatch(async ({ deviceId, from, to }) => {
+ setSelectedDeviceId(deviceId);
+ setFrom(from);
+ setTo(to);
+ const query = new URLSearchParams({ deviceId, from, to });
+ const response = await fetch(`/api/positions?${query.toString()}`);
+ if (response.ok) {
+ setIndex(0);
+ const positions = await response.json();
+ setPositions(positions);
+ if (positions.length) {
+ setExpanded(false);
+ } else {
+ throw Error(t('sharedNoData'));
+ }
+ } else {
+ throw Error(await response.text());
+ }
+ });
+
+ const handleDownload = () => {
+ const query = new URLSearchParams({ deviceId: selectedDeviceId, from, to });
+ window.location.assign(`/api/positions/kml?${query.toString()}`);
+ };
+
+ return (
+ <div className={classes.root}>
+ <MapView>
+ <MapGeofence />
+ <MapRoutePath positions={positions} />
+ <MapRoutePoints positions={positions} onClick={onPointClick} />
+ {index < positions.length && (
+ <MapPositions positions={[positions[index]]} onClick={onMarkerClick} titleField="fixTime" />
+ )}
+ </MapView>
+ <MapCamera positions={positions} />
+ <div className={classes.sidebar}>
+ <Paper elevation={3} square>
+ <Toolbar>
+ <IconButton edge="start" sx={{ mr: 2 }} onClick={() => navigate(-1)}>
+ <ArrowBackIcon />
+ </IconButton>
+ <Typography variant="h6" className={classes.title}>{t('reportReplay')}</Typography>
+ {!expanded && (
+ <>
+ <IconButton onClick={handleDownload}>
+ <DownloadIcon />
+ </IconButton>
+ <IconButton edge="end" onClick={() => setExpanded(true)}>
+ <TuneIcon />
+ </IconButton>
+ </>
+ )}
+ </Toolbar>
+ </Paper>
+ <Paper className={classes.content} square>
+ {!expanded ? (
+ <>
+ <Typography variant="subtitle1" align="center">{deviceName}</Typography>
+ <Slider
+ className={classes.slider}
+ max={positions.length - 1}
+ step={null}
+ marks={positions.map((_, index) => ({ value: index }))}
+ value={index}
+ onChange={(_, index) => setIndex(index)}
+ />
+ <div className={classes.controls}>
+ {`${index + 1}/${positions.length}`}
+ <IconButton onClick={() => setIndex((index) => index - 1)} disabled={playing || index <= 0}>
+ <FastRewindIcon />
+ </IconButton>
+ <IconButton onClick={() => setPlaying(!playing)} disabled={index >= positions.length - 1}>
+ {playing ? <PauseIcon /> : <PlayArrowIcon /> }
+ </IconButton>
+ <IconButton onClick={() => setIndex((index) => index + 1)} disabled={playing || index >= positions.length - 1}>
+ <FastForwardIcon />
+ </IconButton>
+ {formatTime(positions[index].fixTime, 'seconds', hours12)}
+ </div>
+ </>
+ ) : (
+ <ReportFilter handleSubmit={handleSubmit} fullScreen showOnly />
+ )}
+ </Paper>
+ </div>
+ {showCard && index < positions.length && (
+ <StatusCard
+ deviceId={selectedDeviceId}
+ position={positions[index]}
+ onClose={() => setShowCard(false)}
+ disableActions
+ />
+ )}
+ </div>
+ );
+};
+
+export default ReplayPage;
diff --git a/src/reactHelper.js b/src/reactHelper.js
new file mode 100644
index 00000000..c67f252b
--- /dev/null
+++ b/src/reactHelper.js
@@ -0,0 +1,40 @@
+import { useRef, useEffect, useCallback } from 'react';
+import { useDispatch } from 'react-redux';
+import { errorsActions } from './store';
+
+export const usePrevious = (value) => {
+ const ref = useRef();
+ useEffect(() => {
+ ref.current = value;
+ });
+ return ref.current;
+};
+
+/* eslint-disable */
+export const useEffectAsync = (effect, deps) => {
+ const dispatch = useDispatch();
+ const ref = useRef();
+ useEffect(() => {
+ effect()
+ .then((result) => ref.current = result)
+ .catch((error) => dispatch(errorsActions.push(error.message)));
+
+ return () => {
+ const result = ref.current;
+ if (result) {
+ result();
+ }
+ };
+ }, [...deps, dispatch]);
+};
+
+export const useCatch = (method) => {
+ const dispatch = useDispatch();
+ return (...parameters) => {
+ method(...parameters).catch((error) => dispatch(errorsActions.push(error.message)));
+ };
+};
+
+export const useCatchCallback = (method, deps) => {
+ return useCallback(useCatch(method), deps);
+};
diff --git a/src/reports/ChartReportPage.jsx b/src/reports/ChartReportPage.jsx
new file mode 100644
index 00000000..6175e1d8
--- /dev/null
+++ b/src/reports/ChartReportPage.jsx
@@ -0,0 +1,152 @@
+import dayjs from 'dayjs';
+import React, { useState } from 'react';
+import {
+ FormControl, InputLabel, Select, MenuItem,
+} from '@mui/material';
+import {
+ CartesianGrid, Line, LineChart, ResponsiveContainer, Tooltip, XAxis, YAxis,
+} from 'recharts';
+import ReportFilter from './components/ReportFilter';
+import { formatTime } from '../common/util/formatter';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import PageLayout from '../common/components/PageLayout';
+import ReportsMenu from './components/ReportsMenu';
+import usePositionAttributes from '../common/attributes/usePositionAttributes';
+import { useCatch } from '../reactHelper';
+import { useAttributePreference, usePreference } from '../common/util/preferences';
+import {
+ altitudeFromMeters, distanceFromMeters, speedFromKnots, volumeFromLiters,
+} from '../common/util/converter';
+import useReportStyles from './common/useReportStyles';
+
+const ChartReportPage = () => {
+ const classes = useReportStyles();
+ const t = useTranslation();
+
+ const positionAttributes = usePositionAttributes(t);
+
+ const distanceUnit = useAttributePreference('distanceUnit');
+ const altitudeUnit = useAttributePreference('altitudeUnit');
+ const speedUnit = useAttributePreference('speedUnit');
+ const volumeUnit = useAttributePreference('volumeUnit');
+ const hours12 = usePreference('twelveHourFormat');
+
+ const [items, setItems] = useState([]);
+ const [types, setTypes] = useState(['speed']);
+ const [type, setType] = useState('speed');
+
+ const values = items.map((it) => it[type]);
+ const minValue = Math.min(...values);
+ const maxValue = Math.max(...values);
+ const valueRange = maxValue - minValue;
+
+ const handleSubmit = useCatch(async ({ deviceId, from, to }) => {
+ const query = new URLSearchParams({ deviceId, from, to });
+ const response = await fetch(`/api/reports/route?${query.toString()}`, {
+ headers: { Accept: 'application/json' },
+ });
+ if (response.ok) {
+ const positions = await response.json();
+ const keySet = new Set();
+ const keyList = [];
+ const formattedPositions = positions.map((position) => {
+ const data = { ...position, ...position.attributes };
+ const formatted = {};
+ formatted.fixTime = dayjs(position.fixTime).valueOf();
+ Object.keys(data).filter((key) => !['id', 'deviceId'].includes(key)).forEach((key) => {
+ const value = data[key];
+ if (typeof value === 'number') {
+ keySet.add(key);
+ const definition = positionAttributes[key] || {};
+ switch (definition.dataType) {
+ case 'speed':
+ formatted[key] = speedFromKnots(value, speedUnit).toFixed(2);
+ break;
+ case 'altitude':
+ formatted[key] = altitudeFromMeters(value, altitudeUnit).toFixed(2);
+ break;
+ case 'distance':
+ formatted[key] = distanceFromMeters(value, distanceUnit).toFixed(2);
+ break;
+ case 'volume':
+ formatted[key] = volumeFromLiters(value, volumeUnit).toFixed(2);
+ break;
+ case 'hours':
+ formatted[key] = (value / 1000).toFixed(2);
+ break;
+ default:
+ formatted[key] = value;
+ break;
+ }
+ }
+ });
+ return formatted;
+ });
+ Object.keys(positionAttributes).forEach((key) => {
+ if (keySet.has(key)) {
+ keyList.push(key);
+ keySet.delete(key);
+ }
+ });
+ setTypes([...keyList, ...keySet]);
+ setItems(formattedPositions);
+ } else {
+ throw Error(await response.text());
+ }
+ });
+
+ return (
+ <PageLayout menu={<ReportsMenu />} breadcrumbs={['reportTitle', 'reportChart']}>
+ <ReportFilter handleSubmit={handleSubmit} showOnly>
+ <div className={classes.filterItem}>
+ <FormControl fullWidth>
+ <InputLabel>{t('reportChartType')}</InputLabel>
+ <Select
+ label={t('reportChartType')}
+ value={type}
+ onChange={(e) => setType(e.target.value)}
+ disabled={!items.length}
+ >
+ {types.map((key) => (
+ <MenuItem key={key} value={key}>{positionAttributes[key]?.name || key}</MenuItem>
+ ))}
+ </Select>
+ </FormControl>
+ </div>
+ </ReportFilter>
+ {items.length > 0 && (
+ <div className={classes.chart}>
+ <ResponsiveContainer>
+ <LineChart
+ data={items}
+ margin={{
+ top: 10, right: 40, left: 0, bottom: 10,
+ }}
+ >
+ <XAxis
+ dataKey="fixTime"
+ type="number"
+ tickFormatter={(value) => formatTime(value, 'time', hours12)}
+ domain={['dataMin', 'dataMax']}
+ scale="time"
+ />
+ <YAxis
+ type="number"
+ tickFormatter={(value) => value.toFixed(2)}
+ domain={[minValue - valueRange / 5, maxValue + valueRange / 5]}
+ />
+ <CartesianGrid strokeDasharray="3 3" />
+ <Tooltip
+ formatter={(value, key) => [value, positionAttributes[key]?.name || key]}
+ labelFormatter={(value) => formatTime(value, 'seconds', hours12)}
+ />
+ <Line type="monotone" dataKey={type} />
+ </LineChart>
+ </ResponsiveContainer>
+ </div>
+ )}
+ </PageLayout>
+ );
+};
+
+export default ChartReportPage;
diff --git a/src/reports/CombinedReportPage.jsx b/src/reports/CombinedReportPage.jsx
new file mode 100644
index 00000000..a5000839
--- /dev/null
+++ b/src/reports/CombinedReportPage.jsx
@@ -0,0 +1,105 @@
+import React, { useState } from 'react';
+import { useSelector } from 'react-redux';
+import {
+ Table, TableBody, TableCell, TableHead, TableRow,
+} from '@mui/material';
+import ReportFilter from './components/ReportFilter';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import PageLayout from '../common/components/PageLayout';
+import ReportsMenu from './components/ReportsMenu';
+import { useCatch } from '../reactHelper';
+import MapView from '../map/core/MapView';
+import MapRoutePath from '../map/MapRoutePath';
+import useReportStyles from './common/useReportStyles';
+import TableShimmer from '../common/components/TableShimmer';
+import MapCamera from '../map/MapCamera';
+import MapGeofence from '../map/MapGeofence';
+import { formatTime } from '../common/util/formatter';
+import { usePreference } from '../common/util/preferences';
+import { prefixString } from '../common/util/stringUtils';
+import MapMarkers from '../map/MapMarkers';
+
+const CombinedReportPage = () => {
+ const classes = useReportStyles();
+ const t = useTranslation();
+
+ const devices = useSelector((state) => state.devices.items);
+
+ const hours12 = usePreference('twelveHourFormat');
+
+ const [items, setItems] = useState([]);
+ const [loading, setLoading] = useState(false);
+
+ const createMarkers = () => items.flatMap((item) => item.events
+ .map((event) => item.positions.find((p) => event.positionId === p.id))
+ .filter((position) => position != null)
+ .map((position) => ({
+ latitude: position.latitude,
+ longitude: position.longitude,
+ })));
+
+ const handleSubmit = useCatch(async ({ deviceIds, groupIds, from, to }) => {
+ const query = new URLSearchParams({ from, to });
+ deviceIds.forEach((deviceId) => query.append('deviceId', deviceId));
+ groupIds.forEach((groupId) => query.append('groupId', groupId));
+ setLoading(true);
+ try {
+ const response = await fetch(`/api/reports/combined?${query.toString()}`);
+ if (response.ok) {
+ setItems(await response.json());
+ } else {
+ throw Error(await response.text());
+ }
+ } finally {
+ setLoading(false);
+ }
+ });
+
+ return (
+ <PageLayout menu={<ReportsMenu />} breadcrumbs={['reportTitle', 'reportCombined']}>
+ <div className={classes.container}>
+ {Boolean(items.length) && (
+ <div className={classes.containerMap}>
+ <MapView>
+ <MapGeofence />
+ {items.map((item) => (
+ <MapRoutePath
+ key={item.deviceId}
+ name={devices[item.deviceId].name}
+ coordinates={item.route}
+ />
+ ))}
+ <MapMarkers markers={createMarkers()} />
+ </MapView>
+ <MapCamera coordinates={items.flatMap((item) => item.route)} />
+ </div>
+ )}
+ <div className={classes.containerMain}>
+ <div className={classes.header}>
+ <ReportFilter handleSubmit={handleSubmit} showOnly multiDevice includeGroups />
+ </div>
+ <Table>
+ <TableHead>
+ <TableRow>
+ <TableCell>{t('sharedDevice')}</TableCell>
+ <TableCell>{t('positionFixTime')}</TableCell>
+ <TableCell>{t('sharedType')}</TableCell>
+ </TableRow>
+ </TableHead>
+ <TableBody>
+ {!loading ? items.flatMap((item) => item.events.map((event, index) => (
+ <TableRow key={event.id}>
+ <TableCell>{index ? '' : devices[item.deviceId].name}</TableCell>
+ <TableCell>{formatTime(event.eventTime, 'seconds', hours12)}</TableCell>
+ <TableCell>{t(prefixString('event', event.type))}</TableCell>
+ </TableRow>
+ ))) : (<TableShimmer columns={3} />)}
+ </TableBody>
+ </Table>
+ </div>
+ </div>
+ </PageLayout>
+ );
+};
+
+export default CombinedReportPage;
diff --git a/src/reports/EventReportPage.jsx b/src/reports/EventReportPage.jsx
new file mode 100644
index 00000000..5ffc8ac3
--- /dev/null
+++ b/src/reports/EventReportPage.jsx
@@ -0,0 +1,232 @@
+import React, { useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import {
+ FormControl, InputLabel, Select, MenuItem, Table, TableHead, TableRow, TableCell, TableBody, Link, IconButton,
+} from '@mui/material';
+import GpsFixedIcon from '@mui/icons-material/GpsFixed';
+import LocationSearchingIcon from '@mui/icons-material/LocationSearching';
+import { useSelector } from 'react-redux';
+import { formatSpeed, formatTime } from '../common/util/formatter';
+import ReportFilter from './components/ReportFilter';
+import { prefixString } from '../common/util/stringUtils';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import PageLayout from '../common/components/PageLayout';
+import ReportsMenu from './components/ReportsMenu';
+import usePersistedState from '../common/util/usePersistedState';
+import ColumnSelect from './components/ColumnSelect';
+import { useCatch, useEffectAsync } from '../reactHelper';
+import useReportStyles from './common/useReportStyles';
+import TableShimmer from '../common/components/TableShimmer';
+import { useAttributePreference, usePreference } from '../common/util/preferences';
+import MapView from '../map/core/MapView';
+import MapGeofence from '../map/MapGeofence';
+import MapPositions from '../map/MapPositions';
+import MapCamera from '../map/MapCamera';
+import scheduleReport from './common/scheduleReport';
+
+const columnsArray = [
+ ['eventTime', 'positionFixTime'],
+ ['type', 'sharedType'],
+ ['geofenceId', 'sharedGeofence'],
+ ['maintenanceId', 'sharedMaintenance'],
+ ['attributes', 'commandData'],
+];
+const columnsMap = new Map(columnsArray);
+
+const EventReportPage = () => {
+ const navigate = useNavigate();
+ const classes = useReportStyles();
+ const t = useTranslation();
+
+ const devices = useSelector((state) => state.devices.items);
+ const geofences = useSelector((state) => state.geofences.items);
+
+ const speedUnit = useAttributePreference('speedUnit');
+ const hours12 = usePreference('twelveHourFormat');
+
+ const [allEventTypes, setAllEventTypes] = useState([['allEvents', 'eventAll']]);
+
+ const [columns, setColumns] = usePersistedState('eventColumns', ['eventTime', 'type', 'attributes']);
+ const [eventTypes, setEventTypes] = useState(['allEvents']);
+ const [items, setItems] = useState([]);
+ const [loading, setLoading] = useState(false);
+ const [selectedItem, setSelectedItem] = useState(null);
+ const [position, setPosition] = useState(null);
+
+ useEffectAsync(async () => {
+ if (selectedItem) {
+ const response = await fetch(`/api/positions?id=${selectedItem.positionId}`);
+ if (response.ok) {
+ const positions = await response.json();
+ if (positions.length > 0) {
+ setPosition(positions[0]);
+ }
+ } else {
+ throw Error(await response.text());
+ }
+ } else {
+ setPosition(null);
+ }
+ }, [selectedItem]);
+
+ useEffectAsync(async () => {
+ const response = await fetch('/api/notifications/types');
+ if (response.ok) {
+ const types = await response.json();
+ setAllEventTypes([...allEventTypes, ...types.map((it) => [it.type, prefixString('event', it.type)])]);
+ } else {
+ throw Error(await response.text());
+ }
+ }, []);
+
+ const handleSubmit = useCatch(async ({ deviceId, from, to, type }) => {
+ const query = new URLSearchParams({ deviceId, from, to });
+ eventTypes.forEach((it) => query.append('type', it));
+ if (type === 'export') {
+ window.location.assign(`/api/reports/events/xlsx?${query.toString()}`);
+ } else if (type === 'mail') {
+ const response = await fetch(`/api/reports/events/mail?${query.toString()}`);
+ if (!response.ok) {
+ throw Error(await response.text());
+ }
+ } else {
+ setLoading(true);
+ try {
+ const response = await fetch(`/api/reports/events?${query.toString()}`, {
+ headers: { Accept: 'application/json' },
+ });
+ if (response.ok) {
+ setItems(await response.json());
+ } else {
+ throw Error(await response.text());
+ }
+ } finally {
+ setLoading(false);
+ }
+ }
+ });
+
+ const handleSchedule = useCatch(async (deviceIds, groupIds, report) => {
+ report.type = 'events';
+ if (eventTypes[0] !== 'allEvents') {
+ report.attributes.types = eventTypes.join(',');
+ }
+ const error = await scheduleReport(deviceIds, groupIds, report);
+ if (error) {
+ throw Error(error);
+ } else {
+ navigate('/reports/scheduled');
+ }
+ });
+
+ const formatValue = (item, key) => {
+ switch (key) {
+ case 'eventTime':
+ return formatTime(item[key], 'seconds', hours12);
+ case 'type':
+ return t(prefixString('event', item[key]));
+ case 'geofenceId':
+ if (item[key] > 0) {
+ const geofence = geofences[item[key]];
+ return geofence && geofence.name;
+ }
+ return null;
+ case 'maintenanceId':
+ return item[key] > 0 ? item[key] > 0 : null;
+ case 'attributes':
+ switch (item.type) {
+ case 'alarm':
+ return t(prefixString('alarm', item.attributes.alarm));
+ case 'deviceOverspeed':
+ return formatSpeed(item.attributes.speed, speedUnit, t);
+ case 'driverChanged':
+ return item.attributes.driverUniqueId;
+ case 'media':
+ return (<Link href={`/api/media/${devices[item.deviceId]?.uniqueId}/${item.attributes.file}`} target="_blank">{item.attributes.file}</Link>);
+ case 'commandResult':
+ return item.attributes.result;
+ default:
+ return '';
+ }
+ default:
+ return item[key];
+ }
+ };
+
+ return (
+ <PageLayout menu={<ReportsMenu />} breadcrumbs={['reportTitle', 'reportEvents']}>
+ <div className={classes.container}>
+ {selectedItem && (
+ <div className={classes.containerMap}>
+ <MapView>
+ <MapGeofence />
+ {position && <MapPositions positions={[position]} titleField="fixTime" />}
+ </MapView>
+ {position && <MapCamera latitude={position.latitude} longitude={position.longitude} />}
+ </div>
+ )}
+ <div className={classes.containerMain}>
+ <div className={classes.header}>
+ <ReportFilter handleSubmit={handleSubmit} handleSchedule={handleSchedule}>
+ <div className={classes.filterItem}>
+ <FormControl fullWidth>
+ <InputLabel>{t('reportEventTypes')}</InputLabel>
+ <Select
+ label={t('reportEventTypes')}
+ value={eventTypes}
+ onChange={(event, child) => {
+ let values = event.target.value;
+ const clicked = child.props.value;
+ if (values.includes('allEvents') && values.length > 1) {
+ values = [clicked];
+ }
+ setEventTypes(values);
+ }}
+ multiple
+ >
+ {allEventTypes.map(([key, string]) => (
+ <MenuItem key={key} value={key}>{t(string)}</MenuItem>
+ ))}
+ </Select>
+ </FormControl>
+ </div>
+ <ColumnSelect columns={columns} setColumns={setColumns} columnsArray={columnsArray} />
+ </ReportFilter>
+ </div>
+ <Table>
+ <TableHead>
+ <TableRow>
+ <TableCell className={classes.columnAction} />
+ {columns.map((key) => (<TableCell key={key}>{t(columnsMap.get(key))}</TableCell>))}
+ </TableRow>
+ </TableHead>
+ <TableBody>
+ {!loading ? items.map((item) => (
+ <TableRow key={item.id}>
+ <TableCell className={classes.columnAction} padding="none">
+ {(item.positionId && (selectedItem === item ? (
+ <IconButton size="small" onClick={() => setSelectedItem(null)}>
+ <GpsFixedIcon fontSize="small" />
+ </IconButton>
+ ) : (
+ <IconButton size="small" onClick={() => setSelectedItem(item)}>
+ <LocationSearchingIcon fontSize="small" />
+ </IconButton>
+ ))) || ''}
+ </TableCell>
+ {columns.map((key) => (
+ <TableCell key={key}>
+ {formatValue(item, key)}
+ </TableCell>
+ ))}
+ </TableRow>
+ )) : (<TableShimmer columns={columns.length + 1} />)}
+ </TableBody>
+ </Table>
+ </div>
+ </div>
+ </PageLayout>
+ );
+};
+
+export default EventReportPage;
diff --git a/src/reports/LogsPage.jsx b/src/reports/LogsPage.jsx
new file mode 100644
index 00000000..7bdbd309
--- /dev/null
+++ b/src/reports/LogsPage.jsx
@@ -0,0 +1,84 @@
+import React, { useEffect } from 'react';
+import { useNavigate } from 'react-router-dom';
+import { useDispatch, useSelector } from 'react-redux';
+import {
+ Table, TableRow, TableCell, TableHead, TableBody, IconButton, Tooltip,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline';
+import HelpOutlineIcon from '@mui/icons-material/HelpOutline';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import PageLayout from '../common/components/PageLayout';
+import ReportsMenu from './components/ReportsMenu';
+import { sessionActions } from '../store';
+
+const useStyles = makeStyles((theme) => ({
+ columnAction: {
+ width: '1%',
+ paddingLeft: theme.spacing(1),
+ },
+ success: {
+ color: theme.palette.success.main,
+ },
+ error: {
+ color: theme.palette.error.main,
+ },
+}));
+
+const LogsPage = () => {
+ const classes = useStyles();
+ const navigate = useNavigate();
+ const dispatch = useDispatch();
+ const t = useTranslation();
+
+ useEffect(() => {
+ dispatch(sessionActions.enableLogs(true));
+ return () => dispatch(sessionActions.enableLogs(false));
+ }, []);
+
+ const items = useSelector((state) => state.session.logs);
+
+ const registerDevice = (uniqueId) => {
+ const query = new URLSearchParams({ uniqueId });
+ navigate(`/settings/device?${query.toString()}`);
+ };
+
+ return (
+ <PageLayout menu={<ReportsMenu />} breadcrumbs={['reportTitle', 'statisticsTitle']}>
+ <Table>
+ <TableHead>
+ <TableRow>
+ <TableCell className={classes.columnAction} />
+ <TableCell>{t('deviceIdentifier')}</TableCell>
+ <TableCell>{t('positionProtocol')}</TableCell>
+ <TableCell>{t('commandData')}</TableCell>
+ </TableRow>
+ </TableHead>
+ <TableBody>
+ {items.map((item, index) => /* eslint-disable react/no-array-index-key */ (
+ <TableRow key={index}>
+ <TableCell className={classes.columnAction} padding="none">
+ {item.deviceId ? (
+ <IconButton size="small" disabled>
+ <CheckCircleOutlineIcon fontSize="small" className={classes.success} />
+ </IconButton>
+ ) : (
+ <Tooltip title={t('loginRegister')}>
+ <IconButton size="small" onClick={() => registerDevice(item.uniqueId)}>
+ <HelpOutlineIcon fontSize="small" className={classes.error} />
+ </IconButton>
+ </Tooltip>
+ )}
+ </TableCell>
+ <TableCell>{item.uniqueId}</TableCell>
+ <TableCell>{item.protocol}</TableCell>
+ <TableCell>{item.data}</TableCell>
+ </TableRow>
+ ))}
+ </TableBody>
+ </Table>
+ </PageLayout>
+ );
+};
+
+export default LogsPage;
diff --git a/src/reports/RouteReportPage.jsx b/src/reports/RouteReportPage.jsx
new file mode 100644
index 00000000..5003ff31
--- /dev/null
+++ b/src/reports/RouteReportPage.jsx
@@ -0,0 +1,173 @@
+import React, { Fragment, useCallback, useState } from 'react';
+import { useSelector } from 'react-redux';
+import { useNavigate } from 'react-router-dom';
+import {
+ IconButton, Table, TableBody, TableCell, TableHead, TableRow,
+} from '@mui/material';
+import GpsFixedIcon from '@mui/icons-material/GpsFixed';
+import LocationSearchingIcon from '@mui/icons-material/LocationSearching';
+import ReportFilter from './components/ReportFilter';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import PageLayout from '../common/components/PageLayout';
+import ReportsMenu from './components/ReportsMenu';
+import PositionValue from '../common/components/PositionValue';
+import ColumnSelect from './components/ColumnSelect';
+import usePositionAttributes from '../common/attributes/usePositionAttributes';
+import { useCatch } from '../reactHelper';
+import MapView from '../map/core/MapView';
+import MapRoutePath from '../map/MapRoutePath';
+import MapRoutePoints from '../map/MapRoutePoints';
+import MapPositions from '../map/MapPositions';
+import useReportStyles from './common/useReportStyles';
+import TableShimmer from '../common/components/TableShimmer';
+import MapCamera from '../map/MapCamera';
+import MapGeofence from '../map/MapGeofence';
+import scheduleReport from './common/scheduleReport';
+
+const RouteReportPage = () => {
+ const navigate = useNavigate();
+ const classes = useReportStyles();
+ const t = useTranslation();
+
+ const positionAttributes = usePositionAttributes(t);
+
+ const devices = useSelector((state) => state.devices.items);
+
+ const [available, setAvailable] = useState([]);
+ const [columns, setColumns] = useState(['fixTime', 'latitude', 'longitude', 'speed', 'address']);
+ const [items, setItems] = useState([]);
+ const [loading, setLoading] = useState(false);
+ const [selectedItem, setSelectedItem] = useState(null);
+
+ const onMapPointClick = useCallback((positionId) => {
+ setSelectedItem(items.find((it) => it.id === positionId));
+ }, [items, setSelectedItem]);
+
+ const handleSubmit = useCatch(async ({ deviceIds, from, to, type }) => {
+ const query = new URLSearchParams({ from, to });
+ deviceIds.forEach((deviceId) => query.append('deviceId', deviceId));
+ if (type === 'export') {
+ window.location.assign(`/api/reports/route/xlsx?${query.toString()}`);
+ } else if (type === 'mail') {
+ const response = await fetch(`/api/reports/route/mail?${query.toString()}`);
+ if (!response.ok) {
+ throw Error(await response.text());
+ }
+ } else {
+ setLoading(true);
+ try {
+ const response = await fetch(`/api/reports/route?${query.toString()}`, {
+ headers: { Accept: 'application/json' },
+ });
+ if (response.ok) {
+ const data = await response.json();
+ const keySet = new Set();
+ const keyList = [];
+ data.forEach((position) => {
+ Object.keys(position).forEach((it) => keySet.add(it));
+ Object.keys(position.attributes).forEach((it) => keySet.add(it));
+ });
+ ['id', 'deviceId', 'outdated', 'network', 'attributes'].forEach((key) => keySet.delete(key));
+ Object.keys(positionAttributes).forEach((key) => {
+ if (keySet.has(key)) {
+ keyList.push(key);
+ keySet.delete(key);
+ }
+ });
+ setAvailable([...keyList, ...keySet].map((key) => [key, positionAttributes[key]?.name || key]));
+ setItems(data);
+ } else {
+ throw Error(await response.text());
+ }
+ } finally {
+ setLoading(false);
+ }
+ }
+ });
+
+ const handleSchedule = useCatch(async (deviceIds, groupIds, report) => {
+ report.type = 'route';
+ const error = await scheduleReport(deviceIds, groupIds, report);
+ if (error) {
+ throw Error(error);
+ } else {
+ navigate('/reports/scheduled');
+ }
+ });
+
+ return (
+ <PageLayout menu={<ReportsMenu />} breadcrumbs={['reportTitle', 'reportRoute']}>
+ <div className={classes.container}>
+ {selectedItem && (
+ <div className={classes.containerMap}>
+ <MapView>
+ <MapGeofence />
+ {[...new Set(items.map((it) => it.deviceId))].map((deviceId) => {
+ const positions = items.filter((position) => position.deviceId === deviceId);
+ return (
+ <Fragment key={deviceId}>
+ <MapRoutePath positions={positions} />
+ <MapRoutePoints positions={positions} onClick={onMapPointClick} />
+ </Fragment>
+ );
+ })}
+ <MapPositions positions={[selectedItem]} titleField="fixTime" />
+ </MapView>
+ <MapCamera positions={items} />
+ </div>
+ )}
+ <div className={classes.containerMain}>
+ <div className={classes.header}>
+ <ReportFilter handleSubmit={handleSubmit} handleSchedule={handleSchedule} multiDevice>
+ <ColumnSelect
+ columns={columns}
+ setColumns={setColumns}
+ columnsArray={available}
+ rawValues
+ disabled={!items.length}
+ />
+ </ReportFilter>
+ </div>
+ <Table>
+ <TableHead>
+ <TableRow>
+ <TableCell className={classes.columnAction} />
+ <TableCell>{t('sharedDevice')}</TableCell>
+ {columns.map((key) => (<TableCell key={key}>{positionAttributes[key]?.name || key}</TableCell>))}
+ </TableRow>
+ </TableHead>
+ <TableBody>
+ {!loading ? items.slice(0, 4000).map((item) => (
+ <TableRow key={item.id}>
+ <TableCell className={classes.columnAction} padding="none">
+ {selectedItem === item ? (
+ <IconButton size="small" onClick={() => setSelectedItem(null)}>
+ <GpsFixedIcon fontSize="small" />
+ </IconButton>
+ ) : (
+ <IconButton size="small" onClick={() => setSelectedItem(item)}>
+ <LocationSearchingIcon fontSize="small" />
+ </IconButton>
+ )}
+ </TableCell>
+ <TableCell>{devices[item.deviceId].name}</TableCell>
+ {columns.map((key) => (
+ <TableCell key={key}>
+ <PositionValue
+ position={item}
+ property={item.hasOwnProperty(key) ? key : null}
+ attribute={item.hasOwnProperty(key) ? null : key}
+ />
+ </TableCell>
+ ))}
+ </TableRow>
+ )) : (<TableShimmer columns={columns.length + 2} startAction />)}
+ </TableBody>
+ </Table>
+ </div>
+ </div>
+ </PageLayout>
+ );
+};
+
+export default RouteReportPage;
diff --git a/src/reports/ScheduledPage.jsx b/src/reports/ScheduledPage.jsx
new file mode 100644
index 00000000..50e335d5
--- /dev/null
+++ b/src/reports/ScheduledPage.jsx
@@ -0,0 +1,106 @@
+import React, { useState } from 'react';
+import { useSelector } from 'react-redux';
+import {
+ Table, TableRow, TableCell, TableHead, TableBody, IconButton,
+} from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+import DeleteIcon from '@mui/icons-material/Delete';
+import { useEffectAsync } from '../reactHelper';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import PageLayout from '../common/components/PageLayout';
+import ReportsMenu from './components/ReportsMenu';
+import TableShimmer from '../common/components/TableShimmer';
+import RemoveDialog from '../common/components/RemoveDialog';
+
+const useStyles = makeStyles((theme) => ({
+ columnAction: {
+ width: '1%',
+ paddingRight: theme.spacing(1),
+ },
+}));
+
+const ScheduledPage = () => {
+ const classes = useStyles();
+ const t = useTranslation();
+
+ const calendars = useSelector((state) => state.calendars.items);
+
+ const [timestamp, setTimestamp] = useState(Date.now());
+ const [items, setItems] = useState([]);
+ const [loading, setLoading] = useState(false);
+ const [removingId, setRemovingId] = useState();
+
+ useEffectAsync(async () => {
+ setLoading(true);
+ try {
+ const response = await fetch('/api/reports');
+ if (response.ok) {
+ setItems(await response.json());
+ } else {
+ throw Error(await response.text());
+ }
+ } finally {
+ setLoading(false);
+ }
+ }, [timestamp]);
+
+ const formatType = (type) => {
+ switch (type) {
+ case 'events':
+ return t('reportEvents');
+ case 'route':
+ return t('reportRoute');
+ case 'summary':
+ return t('reportSummary');
+ case 'trips':
+ return t('reportTrips');
+ case 'stops':
+ return t('reportStops');
+ default:
+ return type;
+ }
+ };
+
+ return (
+ <PageLayout menu={<ReportsMenu />} breadcrumbs={['settingsTitle', 'reportScheduled']}>
+ <Table>
+ <TableHead>
+ <TableRow>
+ <TableCell>{t('sharedType')}</TableCell>
+ <TableCell>{t('sharedDescription')}</TableCell>
+ <TableCell>{t('sharedCalendar')}</TableCell>
+ <TableCell className={classes.columnAction} />
+ </TableRow>
+ </TableHead>
+ <TableBody>
+ {!loading ? items.map((item) => (
+ <TableRow key={item.id}>
+ <TableCell>{formatType(item.type)}</TableCell>
+ <TableCell>{item.description}</TableCell>
+ <TableCell>{calendars[item.calendarId].name}</TableCell>
+ <TableCell className={classes.columnAction} padding="none">
+ <IconButton size="small" onClick={() => setRemovingId(item.id)}>
+ <DeleteIcon fontSize="small" />
+ </IconButton>
+ </TableCell>
+ </TableRow>
+ )) : (<TableShimmer columns={4} endAction />)}
+ </TableBody>
+ </Table>
+ <RemoveDialog
+ style={{ transform: 'none' }}
+ open={!!removingId}
+ endpoint="reports"
+ itemId={removingId}
+ onResult={(removed) => {
+ setRemovingId(null);
+ if (removed) {
+ setTimestamp(Date.now());
+ }
+ }}
+ />
+ </PageLayout>
+ );
+};
+
+export default ScheduledPage;
diff --git a/src/reports/StatisticsPage.jsx b/src/reports/StatisticsPage.jsx
new file mode 100644
index 00000000..7b3f2879
--- /dev/null
+++ b/src/reports/StatisticsPage.jsx
@@ -0,0 +1,85 @@
+import React, { useState } from 'react';
+import {
+ Table, TableRow, TableCell, TableHead, TableBody,
+} from '@mui/material';
+import { formatTime } from '../common/util/formatter';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import PageLayout from '../common/components/PageLayout';
+import ReportsMenu from './components/ReportsMenu';
+import ReportFilter from './components/ReportFilter';
+import usePersistedState from '../common/util/usePersistedState';
+import ColumnSelect from './components/ColumnSelect';
+import { useCatch } from '../reactHelper';
+import useReportStyles from './common/useReportStyles';
+import TableShimmer from '../common/components/TableShimmer';
+import { usePreference } from '../common/util/preferences';
+
+const columnsArray = [
+ ['captureTime', 'statisticsCaptureTime'],
+ ['activeUsers', 'statisticsActiveUsers'],
+ ['activeDevices', 'statisticsActiveDevices'],
+ ['requests', 'statisticsRequests'],
+ ['messagesReceived', 'statisticsMessagesReceived'],
+ ['messagesStored', 'statisticsMessagesStored'],
+ ['mailSent', 'notificatorMail'],
+ ['smsSent', 'notificatorSms'],
+ ['geocoderRequests', 'statisticsGeocoder'],
+ ['geolocationRequests', 'statisticsGeolocation'],
+];
+const columnsMap = new Map(columnsArray);
+
+const StatisticsPage = () => {
+ const classes = useReportStyles();
+ const t = useTranslation();
+
+ const hours12 = usePreference('twelveHourFormat');
+
+ const [columns, setColumns] = usePersistedState('statisticsColumns', ['captureTime', 'activeUsers', 'activeDevices', 'messagesStored']);
+ const [items, setItems] = useState([]);
+ const [loading, setLoading] = useState(false);
+
+ const handleSubmit = useCatch(async ({ from, to }) => {
+ setLoading(true);
+ try {
+ const query = new URLSearchParams({ from, to });
+ const response = await fetch(`/api/statistics?${query.toString()}`);
+ if (response.ok) {
+ setItems(await response.json());
+ } else {
+ throw Error(await response.text());
+ }
+ } finally {
+ setLoading(false);
+ }
+ });
+
+ return (
+ <PageLayout menu={<ReportsMenu />} breadcrumbs={['reportTitle', 'statisticsTitle']}>
+ <div className={classes.header}>
+ <ReportFilter handleSubmit={handleSubmit} showOnly ignoreDevice>
+ <ColumnSelect columns={columns} setColumns={setColumns} columnsArray={columnsArray} />
+ </ReportFilter>
+ </div>
+ <Table>
+ <TableHead>
+ <TableRow>
+ {columns.map((key) => (<TableCell key={key}>{t(columnsMap.get(key))}</TableCell>))}
+ </TableRow>
+ </TableHead>
+ <TableBody>
+ {!loading ? items.map((item) => (
+ <TableRow key={item.id}>
+ {columns.map((key) => (
+ <TableCell key={key}>
+ {key === 'captureTime' ? formatTime(item[key], 'date', hours12) : item[key]}
+ </TableCell>
+ ))}
+ </TableRow>
+ )) : (<TableShimmer columns={columns.length} />)}
+ </TableBody>
+ </Table>
+ </PageLayout>
+ );
+};
+
+export default StatisticsPage;
diff --git a/src/reports/StopReportPage.jsx b/src/reports/StopReportPage.jsx
new file mode 100644
index 00000000..066b29a4
--- /dev/null
+++ b/src/reports/StopReportPage.jsx
@@ -0,0 +1,172 @@
+import React, { useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import {
+ IconButton,
+ Table, TableBody, TableCell, TableHead, TableRow,
+} from '@mui/material';
+import GpsFixedIcon from '@mui/icons-material/GpsFixed';
+import LocationSearchingIcon from '@mui/icons-material/LocationSearching';
+import {
+ formatDistance, formatVolume, formatTime, formatNumericHours,
+} from '../common/util/formatter';
+import ReportFilter from './components/ReportFilter';
+import { useAttributePreference, usePreference } from '../common/util/preferences';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import PageLayout from '../common/components/PageLayout';
+import ReportsMenu from './components/ReportsMenu';
+import ColumnSelect from './components/ColumnSelect';
+import usePersistedState from '../common/util/usePersistedState';
+import { useCatch } from '../reactHelper';
+import useReportStyles from './common/useReportStyles';
+import MapPositions from '../map/MapPositions';
+import MapView from '../map/core/MapView';
+import MapCamera from '../map/MapCamera';
+import AddressValue from '../common/components/AddressValue';
+import TableShimmer from '../common/components/TableShimmer';
+import MapGeofence from '../map/MapGeofence';
+import scheduleReport from './common/scheduleReport';
+
+const columnsArray = [
+ ['startTime', 'reportStartTime'],
+ ['startOdometer', 'positionOdometer'],
+ ['address', 'positionAddress'],
+ ['endTime', 'reportEndTime'],
+ ['duration', 'reportDuration'],
+ ['engineHours', 'reportEngineHours'],
+ ['spentFuel', 'reportSpentFuel'],
+];
+const columnsMap = new Map(columnsArray);
+
+const StopReportPage = () => {
+ const navigate = useNavigate();
+ const classes = useReportStyles();
+ const t = useTranslation();
+
+ const distanceUnit = useAttributePreference('distanceUnit');
+ const volumeUnit = useAttributePreference('volumeUnit');
+ const hours12 = usePreference('twelveHourFormat');
+
+ const [columns, setColumns] = usePersistedState('stopColumns', ['startTime', 'endTime', 'startOdometer', 'address']);
+ const [items, setItems] = useState([]);
+ const [loading, setLoading] = useState(false);
+ const [selectedItem, setSelectedItem] = useState(null);
+
+ const handleSubmit = useCatch(async ({ deviceId, from, to, type }) => {
+ const query = new URLSearchParams({ deviceId, from, to });
+ if (type === 'export') {
+ window.location.assign(`/api/reports/stops/xlsx?${query.toString()}`);
+ } else if (type === 'mail') {
+ const response = await fetch(`/api/reports/stops/mail?${query.toString()}`);
+ if (!response.ok) {
+ throw Error(await response.text());
+ }
+ } else {
+ setLoading(true);
+ try {
+ const response = await fetch(`/api/reports/stops?${query.toString()}`, {
+ headers: { Accept: 'application/json' },
+ });
+ if (response.ok) {
+ setItems(await response.json());
+ } else {
+ throw Error(await response.text());
+ }
+ } finally {
+ setLoading(false);
+ }
+ }
+ });
+
+ const handleSchedule = useCatch(async (deviceIds, groupIds, report) => {
+ report.type = 'stops';
+ const error = await scheduleReport(deviceIds, groupIds, report);
+ if (error) {
+ throw Error(error);
+ } else {
+ navigate('/reports/scheduled');
+ }
+ });
+
+ const formatValue = (item, key) => {
+ switch (key) {
+ case 'startTime':
+ case 'endTime':
+ return formatTime(item[key], 'minutes', hours12);
+ case 'startOdometer':
+ return formatDistance(item[key], distanceUnit, t);
+ case 'duration':
+ return formatNumericHours(item[key], t);
+ case 'engineHours':
+ return formatNumericHours(item[key], t);
+ case 'spentFuel':
+ return formatVolume(item[key], volumeUnit, t);
+ case 'address':
+ return (<AddressValue latitude={item.latitude} longitude={item.longitude} originalAddress={item[key]} />);
+ default:
+ return item[key];
+ }
+ };
+
+ return (
+ <PageLayout menu={<ReportsMenu />} breadcrumbs={['reportTitle', 'reportStops']}>
+ <div className={classes.container}>
+ {selectedItem && (
+ <div className={classes.containerMap}>
+ <MapView>
+ <MapGeofence />
+ <MapPositions
+ positions={[{
+ deviceId: selectedItem.deviceId,
+ fixTime: selectedItem.startTime,
+ latitude: selectedItem.latitude,
+ longitude: selectedItem.longitude,
+ }]}
+ titleField="fixTime"
+ />
+ </MapView>
+ <MapCamera latitude={selectedItem.latitude} longitude={selectedItem.longitude} />
+ </div>
+ )}
+ <div className={classes.containerMain}>
+ <div className={classes.header}>
+ <ReportFilter handleSubmit={handleSubmit} handleSchedule={handleSchedule}>
+ <ColumnSelect columns={columns} setColumns={setColumns} columnsArray={columnsArray} />
+ </ReportFilter>
+ </div>
+ <Table>
+ <TableHead>
+ <TableRow>
+ <TableCell className={classes.columnAction} />
+ {columns.map((key) => (<TableCell key={key}>{t(columnsMap.get(key))}</TableCell>))}
+ </TableRow>
+ </TableHead>
+ <TableBody>
+ {!loading ? items.map((item) => (
+ <TableRow key={item.positionId}>
+ <TableCell className={classes.columnAction} padding="none">
+ {selectedItem === item ? (
+ <IconButton size="small" onClick={() => setSelectedItem(null)}>
+ <GpsFixedIcon fontSize="small" />
+ </IconButton>
+ ) : (
+ <IconButton size="small" onClick={() => setSelectedItem(item)}>
+ <LocationSearchingIcon fontSize="small" />
+ </IconButton>
+ )}
+ </TableCell>
+ {columns.map((key) => (
+ <TableCell key={key}>
+ {formatValue(item, key)}
+ </TableCell>
+ ))}
+ </TableRow>
+ )) : (<TableShimmer columns={columns.length + 1} startAction />)}
+ </TableBody>
+ </Table>
+ </div>
+ </div>
+ </PageLayout>
+ );
+};
+
+export default StopReportPage;
diff --git a/src/reports/SummaryReportPage.jsx b/src/reports/SummaryReportPage.jsx
new file mode 100644
index 00000000..ae7e043e
--- /dev/null
+++ b/src/reports/SummaryReportPage.jsx
@@ -0,0 +1,152 @@
+import React, { useState } from 'react';
+import { useSelector } from 'react-redux';
+import { useNavigate } from 'react-router-dom';
+import {
+ FormControl, InputLabel, Select, MenuItem, Table, TableHead, TableRow, TableBody, TableCell,
+} from '@mui/material';
+import {
+ formatDistance, formatSpeed, formatVolume, formatTime, formatNumericHours,
+} from '../common/util/formatter';
+import ReportFilter from './components/ReportFilter';
+import { useAttributePreference, usePreference } from '../common/util/preferences';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import PageLayout from '../common/components/PageLayout';
+import ReportsMenu from './components/ReportsMenu';
+import usePersistedState from '../common/util/usePersistedState';
+import ColumnSelect from './components/ColumnSelect';
+import { useCatch } from '../reactHelper';
+import useReportStyles from './common/useReportStyles';
+import TableShimmer from '../common/components/TableShimmer';
+import scheduleReport from './common/scheduleReport';
+
+const columnsArray = [
+ ['startTime', 'reportStartDate'],
+ ['distance', 'sharedDistance'],
+ ['startOdometer', 'reportStartOdometer'],
+ ['endOdometer', 'reportEndOdometer'],
+ ['averageSpeed', 'reportAverageSpeed'],
+ ['maxSpeed', 'reportMaximumSpeed'],
+ ['engineHours', 'reportEngineHours'],
+ ['spentFuel', 'reportSpentFuel'],
+];
+const columnsMap = new Map(columnsArray);
+
+const SummaryReportPage = () => {
+ const navigate = useNavigate();
+ const classes = useReportStyles();
+ const t = useTranslation();
+
+ const devices = useSelector((state) => state.devices.items);
+
+ const distanceUnit = useAttributePreference('distanceUnit');
+ const speedUnit = useAttributePreference('speedUnit');
+ const volumeUnit = useAttributePreference('volumeUnit');
+ const hours12 = usePreference('twelveHourFormat');
+
+ const [columns, setColumns] = usePersistedState('summaryColumns', ['startTime', 'distance', 'averageSpeed']);
+ const [daily, setDaily] = useState(false);
+ const [items, setItems] = useState([]);
+ const [loading, setLoading] = useState(false);
+
+ const handleSubmit = useCatch(async ({ deviceIds, groupIds, from, to, type }) => {
+ const query = new URLSearchParams({ from, to, daily });
+ deviceIds.forEach((deviceId) => query.append('deviceId', deviceId));
+ groupIds.forEach((groupId) => query.append('groupId', groupId));
+ if (type === 'export') {
+ window.location.assign(`/api/reports/summary/xlsx?${query.toString()}`);
+ } else if (type === 'mail') {
+ const response = await fetch(`/api/reports/summary/mail?${query.toString()}`);
+ if (!response.ok) {
+ throw Error(await response.text());
+ }
+ } else {
+ setLoading(true);
+ try {
+ const response = await fetch(`/api/reports/summary?${query.toString()}`, {
+ headers: { Accept: 'application/json' },
+ });
+ if (response.ok) {
+ setItems(await response.json());
+ } else {
+ throw Error(await response.text());
+ }
+ } finally {
+ setLoading(false);
+ }
+ }
+ });
+
+ const handleSchedule = useCatch(async (deviceIds, groupIds, report) => {
+ report.type = 'summary';
+ report.attributes.daily = daily;
+ const error = await scheduleReport(deviceIds, groupIds, report);
+ if (error) {
+ throw Error(error);
+ } else {
+ navigate('/reports/scheduled');
+ }
+ });
+
+ const formatValue = (item, key) => {
+ switch (key) {
+ case 'deviceId':
+ return devices[item[key]].name;
+ case 'startTime':
+ return formatTime(item[key], 'date', hours12);
+ case 'startOdometer':
+ case 'endOdometer':
+ case 'distance':
+ return formatDistance(item[key], distanceUnit, t);
+ case 'averageSpeed':
+ case 'maxSpeed':
+ return formatSpeed(item[key], speedUnit, t);
+ case 'engineHours':
+ return formatNumericHours(item[key], t);
+ case 'spentFuel':
+ return formatVolume(item[key], volumeUnit, t);
+ default:
+ return item[key];
+ }
+ };
+
+ return (
+ <PageLayout menu={<ReportsMenu />} breadcrumbs={['reportTitle', 'reportSummary']}>
+ <div className={classes.header}>
+ <ReportFilter handleSubmit={handleSubmit} handleSchedule={handleSchedule} multiDevice includeGroups>
+ <div className={classes.filterItem}>
+ <FormControl fullWidth>
+ <InputLabel>{t('sharedType')}</InputLabel>
+ <Select label={t('sharedType')} value={daily} onChange={(e) => setDaily(e.target.value)}>
+ <MenuItem value={false}>{t('reportSummary')}</MenuItem>
+ <MenuItem value>{t('reportDaily')}</MenuItem>
+ </Select>
+ </FormControl>
+ </div>
+ <ColumnSelect columns={columns} setColumns={setColumns} columnsArray={columnsArray} />
+ </ReportFilter>
+ </div>
+ <Table>
+ <TableHead>
+ <TableRow>
+ <TableCell>{t('sharedDevice')}</TableCell>
+ {columns.map((key) => (<TableCell key={key}>{t(columnsMap.get(key))}</TableCell>))}
+ </TableRow>
+ </TableHead>
+ <TableBody>
+ {!loading ? items.map((item) => (
+ <TableRow key={(`${item.deviceId}_${Date.parse(item.startTime)}`)}>
+ <TableCell>{devices[item.deviceId].name}</TableCell>
+ {columns.map((key) => (
+ <TableCell key={key}>
+ {formatValue(item, key)}
+ </TableCell>
+ ))}
+ </TableRow>
+ )) : (<TableShimmer columns={columns.length + 1} />)}
+ </TableBody>
+ </Table>
+ </PageLayout>
+ );
+};
+
+export default SummaryReportPage;
diff --git a/src/reports/TripReportPage.jsx b/src/reports/TripReportPage.jsx
new file mode 100644
index 00000000..897ee506
--- /dev/null
+++ b/src/reports/TripReportPage.jsx
@@ -0,0 +1,216 @@
+import React, { useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import {
+ IconButton, Table, TableBody, TableCell, TableHead, TableRow,
+} from '@mui/material';
+import GpsFixedIcon from '@mui/icons-material/GpsFixed';
+import LocationSearchingIcon from '@mui/icons-material/LocationSearching';
+import {
+ formatDistance, formatSpeed, formatVolume, formatTime, formatNumericHours,
+} from '../common/util/formatter';
+import ReportFilter from './components/ReportFilter';
+import { useAttributePreference, usePreference } from '../common/util/preferences';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import PageLayout from '../common/components/PageLayout';
+import ReportsMenu from './components/ReportsMenu';
+import ColumnSelect from './components/ColumnSelect';
+import usePersistedState from '../common/util/usePersistedState';
+import { useCatch, useEffectAsync } from '../reactHelper';
+import useReportStyles from './common/useReportStyles';
+import MapView from '../map/core/MapView';
+import MapRoutePath from '../map/MapRoutePath';
+import AddressValue from '../common/components/AddressValue';
+import TableShimmer from '../common/components/TableShimmer';
+import MapMarkers from '../map/MapMarkers';
+import MapCamera from '../map/MapCamera';
+import MapGeofence from '../map/MapGeofence';
+import scheduleReport from './common/scheduleReport';
+
+const columnsArray = [
+ ['startTime', 'reportStartTime'],
+ ['startOdometer', 'reportStartOdometer'],
+ ['startAddress', 'reportStartAddress'],
+ ['endTime', 'reportEndTime'],
+ ['endOdometer', 'reportEndOdometer'],
+ ['endAddress', 'reportEndAddress'],
+ ['distance', 'sharedDistance'],
+ ['averageSpeed', 'reportAverageSpeed'],
+ ['maxSpeed', 'reportMaximumSpeed'],
+ ['duration', 'reportDuration'],
+ ['spentFuel', 'reportSpentFuel'],
+ ['driverName', 'sharedDriver'],
+];
+const columnsMap = new Map(columnsArray);
+
+const TripReportPage = () => {
+ const navigate = useNavigate();
+ const classes = useReportStyles();
+ const t = useTranslation();
+
+ const distanceUnit = useAttributePreference('distanceUnit');
+ const speedUnit = useAttributePreference('speedUnit');
+ const volumeUnit = useAttributePreference('volumeUnit');
+ const hours12 = usePreference('twelveHourFormat');
+
+ const [columns, setColumns] = usePersistedState('tripColumns', ['startTime', 'endTime', 'distance', 'averageSpeed']);
+ const [items, setItems] = useState([]);
+ const [loading, setLoading] = useState(false);
+ const [selectedItem, setSelectedItem] = useState(null);
+ const [route, setRoute] = useState(null);
+
+ const createMarkers = () => ([
+ {
+ latitude: selectedItem.startLat,
+ longitude: selectedItem.startLon,
+ image: 'default-error',
+ },
+ {
+ latitude: selectedItem.endLat,
+ longitude: selectedItem.endLon,
+ image: 'default-success',
+ },
+ ]);
+
+ useEffectAsync(async () => {
+ if (selectedItem) {
+ const query = new URLSearchParams({
+ deviceId: selectedItem.deviceId,
+ from: selectedItem.startTime,
+ to: selectedItem.endTime,
+ });
+ const response = await fetch(`/api/reports/route?${query.toString()}`, {
+ headers: {
+ Accept: 'application/json',
+ },
+ });
+ if (response.ok) {
+ setRoute(await response.json());
+ } else {
+ throw Error(await response.text());
+ }
+ } else {
+ setRoute(null);
+ }
+ }, [selectedItem]);
+
+ const handleSubmit = useCatch(async ({ deviceId, from, to, type }) => {
+ const query = new URLSearchParams({ deviceId, from, to });
+ if (type === 'export') {
+ window.location.assign(`/api/reports/trips/xlsx?${query.toString()}`);
+ } else if (type === 'mail') {
+ const response = await fetch(`/api/reports/trips/mail?${query.toString()}`);
+ if (!response.ok) {
+ throw Error(await response.text());
+ }
+ } else {
+ setLoading(true);
+ try {
+ const response = await fetch(`/api/reports/trips?${query.toString()}`, {
+ headers: { Accept: 'application/json' },
+ });
+ if (response.ok) {
+ setItems(await response.json());
+ } else {
+ throw Error(await response.text());
+ }
+ } finally {
+ setLoading(false);
+ }
+ }
+ });
+
+ const handleSchedule = useCatch(async (deviceIds, groupIds, report) => {
+ report.type = 'trips';
+ const error = await scheduleReport(deviceIds, groupIds, report);
+ if (error) {
+ throw Error(error);
+ } else {
+ navigate('/reports/scheduled');
+ }
+ });
+
+ const formatValue = (item, key) => {
+ switch (key) {
+ case 'startTime':
+ case 'endTime':
+ return formatTime(item[key], 'minutes', hours12);
+ case 'startOdometer':
+ case 'endOdometer':
+ case 'distance':
+ return formatDistance(item[key], distanceUnit, t);
+ case 'averageSpeed':
+ case 'maxSpeed':
+ return formatSpeed(item[key], speedUnit, t);
+ case 'duration':
+ return formatNumericHours(item[key], t);
+ case 'spentFuel':
+ return formatVolume(item[key], volumeUnit, t);
+ case 'startAddress':
+ return (<AddressValue latitude={item.startLat} longitude={item.startLon} originalAddress={item[key]} />);
+ case 'endAddress':
+ return (<AddressValue latitude={item.endLat} longitude={item.endLon} originalAddress={item[key]} />);
+ default:
+ return item[key];
+ }
+ };
+
+ return (
+ <PageLayout menu={<ReportsMenu />} breadcrumbs={['reportTitle', 'reportTrips']}>
+ <div className={classes.container}>
+ {selectedItem && (
+ <div className={classes.containerMap}>
+ <MapView>
+ <MapGeofence />
+ {route && (
+ <>
+ <MapRoutePath positions={route} />
+ <MapMarkers markers={createMarkers()} />
+ <MapCamera positions={route} />
+ </>
+ )}
+ </MapView>
+ </div>
+ )}
+ <div className={classes.containerMain}>
+ <div className={classes.header}>
+ <ReportFilter handleSubmit={handleSubmit} handleSchedule={handleSchedule}>
+ <ColumnSelect columns={columns} setColumns={setColumns} columnsArray={columnsArray} />
+ </ReportFilter>
+ </div>
+ <Table>
+ <TableHead>
+ <TableRow>
+ <TableCell className={classes.columnAction} />
+ {columns.map((key) => (<TableCell key={key}>{t(columnsMap.get(key))}</TableCell>))}
+ </TableRow>
+ </TableHead>
+ <TableBody>
+ {!loading ? items.map((item) => (
+ <TableRow key={item.startPositionId}>
+ <TableCell className={classes.columnAction} padding="none">
+ {selectedItem === item ? (
+ <IconButton size="small" onClick={() => setSelectedItem(null)}>
+ <GpsFixedIcon fontSize="small" />
+ </IconButton>
+ ) : (
+ <IconButton size="small" onClick={() => setSelectedItem(item)}>
+ <LocationSearchingIcon fontSize="small" />
+ </IconButton>
+ )}
+ </TableCell>
+ {columns.map((key) => (
+ <TableCell key={key}>
+ {formatValue(item, key)}
+ </TableCell>
+ ))}
+ </TableRow>
+ )) : (<TableShimmer columns={columns.length + 1} startAction />)}
+ </TableBody>
+ </Table>
+ </div>
+ </div>
+ </PageLayout>
+ );
+};
+
+export default TripReportPage;
diff --git a/src/reports/common/scheduleReport.js b/src/reports/common/scheduleReport.js
new file mode 100644
index 00000000..5d8f9e28
--- /dev/null
+++ b/src/reports/common/scheduleReport.js
@@ -0,0 +1,26 @@
+export default async (deviceIds, groupIds, report) => {
+ const response = await fetch('/api/reports', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(report),
+ });
+ if (response.ok) {
+ report = await response.json();
+ if (deviceIds.length) {
+ await fetch('/api/permissions/bulk', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(deviceIds.map((id) => ({ deviceId: id, reportId: report.id }))),
+ });
+ }
+ if (groupIds.length) {
+ await fetch('/api/permissions/bulk', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(groupIds.map((id) => ({ groupId: id, reportId: report.id }))),
+ });
+ }
+ return null;
+ }
+ return response.text();
+};
diff --git a/src/reports/common/useReportStyles.js b/src/reports/common/useReportStyles.js
new file mode 100644
index 00000000..e09c8695
--- /dev/null
+++ b/src/reports/common/useReportStyles.js
@@ -0,0 +1,49 @@
+import { makeStyles } from '@mui/styles';
+
+export default makeStyles((theme) => ({
+ container: {
+ height: '100%',
+ display: 'flex',
+ flexDirection: 'column',
+ },
+ containerMap: {
+ flexBasis: '40%',
+ flexShrink: 0,
+ },
+ containerMain: {
+ overflow: 'auto',
+ },
+ header: {
+ position: 'sticky',
+ left: 0,
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'stretch',
+ },
+ columnAction: {
+ width: '1%',
+ paddingLeft: theme.spacing(1),
+ },
+ filter: {
+ display: 'inline-flex',
+ flexWrap: 'wrap',
+ gap: theme.spacing(2),
+ padding: theme.spacing(3, 2, 2),
+ },
+ filterItem: {
+ minWidth: 0,
+ flex: `1 1 ${theme.dimensions.filterFormWidth}`,
+ },
+ filterButtons: {
+ display: 'flex',
+ gap: theme.spacing(1),
+ flex: `1 1 ${theme.dimensions.filterFormWidth}`,
+ },
+ filterButton: {
+ flexGrow: 1,
+ },
+ chart: {
+ flexGrow: 1,
+ overflow: 'hidden',
+ },
+}));
diff --git a/src/reports/components/ColumnSelect.jsx b/src/reports/components/ColumnSelect.jsx
new file mode 100644
index 00000000..d08394ea
--- /dev/null
+++ b/src/reports/components/ColumnSelect.jsx
@@ -0,0 +1,34 @@
+import React from 'react';
+import {
+ FormControl, InputLabel, MenuItem, Select,
+} from '@mui/material';
+import { useTranslation } from '../../common/components/LocalizationProvider';
+import useReportStyles from '../common/useReportStyles';
+
+const ColumnSelect = ({
+ columns, setColumns, columnsArray, rawValues, disabled,
+}) => {
+ const classes = useReportStyles();
+ const t = useTranslation();
+
+ return (
+ <div className={classes.filterItem}>
+ <FormControl fullWidth>
+ <InputLabel>{t('sharedColumns')}</InputLabel>
+ <Select
+ label={t('sharedColumns')}
+ value={columns}
+ onChange={(e) => setColumns(e.target.value)}
+ multiple
+ disabled={disabled}
+ >
+ {columnsArray.map(([key, string]) => (
+ <MenuItem key={key} value={key}>{rawValues ? string : t(string)}</MenuItem>
+ ))}
+ </Select>
+ </FormControl>
+ </div>
+ );
+};
+
+export default ColumnSelect;
diff --git a/src/reports/components/ReportFilter.jsx b/src/reports/components/ReportFilter.jsx
new file mode 100644
index 00000000..e6e08f16
--- /dev/null
+++ b/src/reports/components/ReportFilter.jsx
@@ -0,0 +1,215 @@
+import React, { useState } from 'react';
+import {
+ FormControl, InputLabel, Select, MenuItem, Button, TextField, Typography,
+} from '@mui/material';
+import { useDispatch, useSelector } from 'react-redux';
+import dayjs from 'dayjs';
+import { useTranslation } from '../../common/components/LocalizationProvider';
+import useReportStyles from '../common/useReportStyles';
+import { devicesActions, reportsActions } from '../../store';
+import SplitButton from '../../common/components/SplitButton';
+import SelectField from '../../common/components/SelectField';
+import { useRestriction } from '../../common/util/permissions';
+
+const ReportFilter = ({ children, handleSubmit, handleSchedule, showOnly, ignoreDevice, multiDevice, includeGroups }) => {
+ const classes = useReportStyles();
+ const dispatch = useDispatch();
+ const t = useTranslation();
+
+ const readonly = useRestriction('readonly');
+
+ const devices = useSelector((state) => state.devices.items);
+ const groups = useSelector((state) => state.groups.items);
+
+ const deviceId = useSelector((state) => state.devices.selectedId);
+ const deviceIds = useSelector((state) => state.devices.selectedIds);
+ const groupIds = useSelector((state) => state.reports.groupIds);
+ const period = useSelector((state) => state.reports.period);
+ const from = useSelector((state) => state.reports.from);
+ const to = useSelector((state) => state.reports.to);
+ const [button, setButton] = useState('json');
+
+ const [description, setDescription] = useState();
+ const [calendarId, setCalendarId] = useState();
+
+ const scheduleDisabled = button === 'schedule' && (!description || !calendarId);
+ const disabled = (!ignoreDevice && !deviceId && !deviceIds.length && !groupIds.length) || scheduleDisabled;
+
+ const handleClick = (type) => {
+ if (type === 'schedule') {
+ handleSchedule(deviceIds, groupIds, {
+ description,
+ calendarId,
+ attributes: {},
+ });
+ } else {
+ let selectedFrom;
+ let selectedTo;
+ switch (period) {
+ case 'today':
+ selectedFrom = dayjs().startOf('day');
+ selectedTo = dayjs().endOf('day');
+ break;
+ case 'yesterday':
+ selectedFrom = dayjs().subtract(1, 'day').startOf('day');
+ selectedTo = dayjs().subtract(1, 'day').endOf('day');
+ break;
+ case 'thisWeek':
+ selectedFrom = dayjs().startOf('week');
+ selectedTo = dayjs().endOf('week');
+ break;
+ case 'previousWeek':
+ selectedFrom = dayjs().subtract(1, 'week').startOf('week');
+ selectedTo = dayjs().subtract(1, 'week').endOf('week');
+ break;
+ case 'thisMonth':
+ selectedFrom = dayjs().startOf('month');
+ selectedTo = dayjs().endOf('month');
+ break;
+ case 'previousMonth':
+ selectedFrom = dayjs().subtract(1, 'month').startOf('month');
+ selectedTo = dayjs().subtract(1, 'month').endOf('month');
+ break;
+ default:
+ selectedFrom = dayjs(from, 'YYYY-MM-DDTHH:mm');
+ selectedTo = dayjs(to, 'YYYY-MM-DDTHH:mm');
+ break;
+ }
+
+ handleSubmit({
+ deviceId,
+ deviceIds,
+ groupIds,
+ from: selectedFrom.toISOString(),
+ to: selectedTo.toISOString(),
+ calendarId,
+ type,
+ });
+ }
+ };
+
+ return (
+ <div className={classes.filter}>
+ {!ignoreDevice && (
+ <div className={classes.filterItem}>
+ <SelectField
+ label={t(multiDevice ? 'deviceTitle' : 'reportDevice')}
+ data={Object.values(devices).sort((a, b) => a.name.localeCompare(b.name))}
+ value={multiDevice ? deviceIds : deviceId}
+ onChange={(e) => dispatch(multiDevice ? devicesActions.selectIds(e.target.value) : devicesActions.selectId(e.target.value))}
+ multiple={multiDevice}
+ fullWidth
+ />
+ </div>
+ )}
+ {includeGroups && (
+ <div className={classes.filterItem}>
+ <SelectField
+ label={t('settingsGroups')}
+ data={Object.values(groups).sort((a, b) => a.name.localeCompare(b.name))}
+ value={groupIds}
+ onChange={(e) => dispatch(reportsActions.updateGroupIds(e.target.value))}
+ multiple
+ fullWidth
+ />
+ </div>
+ )}
+ {button !== 'schedule' ? (
+ <>
+ <div className={classes.filterItem}>
+ <FormControl fullWidth>
+ <InputLabel>{t('reportPeriod')}</InputLabel>
+ <Select label={t('reportPeriod')} value={period} onChange={(e) => dispatch(reportsActions.updatePeriod(e.target.value))}>
+ <MenuItem value="today">{t('reportToday')}</MenuItem>
+ <MenuItem value="yesterday">{t('reportYesterday')}</MenuItem>
+ <MenuItem value="thisWeek">{t('reportThisWeek')}</MenuItem>
+ <MenuItem value="previousWeek">{t('reportPreviousWeek')}</MenuItem>
+ <MenuItem value="thisMonth">{t('reportThisMonth')}</MenuItem>
+ <MenuItem value="previousMonth">{t('reportPreviousMonth')}</MenuItem>
+ <MenuItem value="custom">{t('reportCustom')}</MenuItem>
+ </Select>
+ </FormControl>
+ </div>
+ {period === 'custom' && (
+ <div className={classes.filterItem}>
+ <TextField
+ label={t('reportFrom')}
+ type="datetime-local"
+ value={from}
+ onChange={(e) => dispatch(reportsActions.updateFrom(e.target.value))}
+ fullWidth
+ />
+ </div>
+ )}
+ {period === 'custom' && (
+ <div className={classes.filterItem}>
+ <TextField
+ label={t('reportTo')}
+ type="datetime-local"
+ value={to}
+ onChange={(e) => dispatch(reportsActions.updateTo(e.target.value))}
+ fullWidth
+ />
+ </div>
+ )}
+ </>
+ ) : (
+ <>
+ <div className={classes.filterItem}>
+ <TextField
+ value={description || ''}
+ onChange={(event) => setDescription(event.target.value)}
+ label={t('sharedDescription')}
+ fullWidth
+ />
+ </div>
+ <div className={classes.filterItem}>
+ <SelectField
+ value={calendarId}
+ onChange={(event) => setCalendarId(Number(event.target.value))}
+ endpoint="/api/calendars"
+ label={t('sharedCalendar')}
+ fullWidth
+ />
+ </div>
+ </>
+ )}
+ {children}
+ <div className={classes.filterItem}>
+ {showOnly ? (
+ <Button
+ fullWidth
+ variant="outlined"
+ color="secondary"
+ disabled={disabled}
+ onClick={() => handleClick('json')}
+ >
+ <Typography variant="button" noWrap>{t('reportShow')}</Typography>
+ </Button>
+ ) : (
+ <SplitButton
+ fullWidth
+ variant="outlined"
+ color="secondary"
+ disabled={disabled}
+ onClick={handleClick}
+ selected={button}
+ setSelected={(value) => setButton(value)}
+ options={readonly ? {
+ json: t('reportShow'),
+ export: t('reportExport'),
+ mail: t('reportEmail'),
+ } : {
+ json: t('reportShow'),
+ export: t('reportExport'),
+ mail: t('reportEmail'),
+ schedule: t('reportSchedule'),
+ }}
+ />
+ )}
+ </div>
+ </div>
+ );
+};
+
+export default ReportFilter;
diff --git a/src/reports/components/ReportsMenu.jsx b/src/reports/components/ReportsMenu.jsx
new file mode 100644
index 00000000..a45a4500
--- /dev/null
+++ b/src/reports/components/ReportsMenu.jsx
@@ -0,0 +1,116 @@
+import React from 'react';
+import {
+ Divider, List, ListItemButton, ListItemIcon, ListItemText,
+} from '@mui/material';
+import StarIcon from '@mui/icons-material/Star';
+import TimelineIcon from '@mui/icons-material/Timeline';
+import PauseCircleFilledIcon from '@mui/icons-material/PauseCircleFilled';
+import PlayCircleFilledIcon from '@mui/icons-material/PlayCircleFilled';
+import NotificationsActiveIcon from '@mui/icons-material/NotificationsActive';
+import FormatListBulletedIcon from '@mui/icons-material/FormatListBulleted';
+import TrendingUpIcon from '@mui/icons-material/TrendingUp';
+import BarChartIcon from '@mui/icons-material/BarChart';
+import RouteIcon from '@mui/icons-material/Route';
+import EventRepeatIcon from '@mui/icons-material/EventRepeat';
+import NotesIcon from '@mui/icons-material/Notes';
+import { Link, useLocation } from 'react-router-dom';
+import { useTranslation } from '../../common/components/LocalizationProvider';
+import { useAdministrator, useRestriction } from '../../common/util/permissions';
+
+const MenuItem = ({
+ title, link, icon, selected,
+}) => (
+ <ListItemButton key={link} component={Link} to={link} selected={selected}>
+ <ListItemIcon>{icon}</ListItemIcon>
+ <ListItemText primary={title} />
+ </ListItemButton>
+);
+
+const ReportsMenu = () => {
+ const t = useTranslation();
+ const location = useLocation();
+
+ const admin = useAdministrator();
+ const readonly = useRestriction('readonly');
+
+ return (
+ <>
+ <List>
+ <MenuItem
+ title={t('reportCombined')}
+ link="/reports/combined"
+ icon={<StarIcon />}
+ selected={location.pathname === '/reports/combined'}
+ />
+ <MenuItem
+ title={t('reportRoute')}
+ link="/reports/route"
+ icon={<TimelineIcon />}
+ selected={location.pathname === '/reports/route'}
+ />
+ <MenuItem
+ title={t('reportEvents')}
+ link="/reports/event"
+ icon={<NotificationsActiveIcon />}
+ selected={location.pathname === '/reports/event'}
+ />
+ <MenuItem
+ title={t('reportTrips')}
+ link="/reports/trip"
+ icon={<PlayCircleFilledIcon />}
+ selected={location.pathname === '/reports/trip'}
+ />
+ <MenuItem
+ title={t('reportStops')}
+ link="/reports/stop"
+ icon={<PauseCircleFilledIcon />}
+ selected={location.pathname === '/reports/stop'}
+ />
+ <MenuItem
+ title={t('reportSummary')}
+ link="/reports/summary"
+ icon={<FormatListBulletedIcon />}
+ selected={location.pathname === '/reports/summary'}
+ />
+ <MenuItem
+ title={t('reportChart')}
+ link="/reports/chart"
+ icon={<TrendingUpIcon />}
+ selected={location.pathname === '/reports/chart'}
+ />
+ <MenuItem
+ title={t('reportReplay')}
+ link="/replay"
+ icon={<RouteIcon />}
+ />
+ </List>
+ <Divider />
+ <List>
+ <MenuItem
+ title={t('sharedLogs')}
+ link="/reports/logs"
+ icon={<NotesIcon />}
+ selected={location.pathname === '/reports/logs'}
+ />
+ {!readonly && (
+ <MenuItem
+ title={t('reportScheduled')}
+ link="/reports/scheduled"
+ icon={<EventRepeatIcon />}
+ selected={location.pathname === '/reports/scheduled'}
+ />
+ )}
+ {admin && (
+ <MenuItem
+ title={t('statisticsTitle')}
+ link="/reports/statistics"
+ icon={<BarChartIcon />}
+ selected={location.pathname === '/reports/statistics'}
+ />
+ )}
+ </List>
+ </>
+ );
+};
+
+export default ReportsMenu;
diff --git a/src/resources/alarm.mp3 b/src/resources/alarm.mp3
new file mode 100644
index 00000000..43d3687e
--- /dev/null
+++ b/src/resources/alarm.mp3
Binary files differ
diff --git a/src/resources/images/arrow.svg b/src/resources/images/arrow.svg
new file mode 100644
index 00000000..d0f30a2e
--- /dev/null
+++ b/src/resources/images/arrow.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="20px" height="20px" fill="#000000" version="1.1" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
+ <path d="m12 2-7.5 18.29 0.71 0.71 6.79-3 6.79 3 0.71-0.71z" fill="#3bb2d0"/>
+</svg>
diff --git a/src/resources/images/background.svg b/src/resources/images/background.svg
new file mode 100644
index 00000000..3dcb6870
--- /dev/null
+++ b/src/resources/images/background.svg
@@ -0,0 +1,10 @@
+<svg width="48px" height="48px" viewBox="0 0 10 10" xmlns="http://www.w3.org/2000/svg">
+ <defs>
+ <filter id="shadow">
+ <feDropShadow dx="0" dy="0.1" stdDeviation="0.3" flood-color="grey"/>
+ </filter>
+ </defs>
+
+ <circle cx="5" cy="50%" r="4"
+ style="fill:white; filter:url(#shadow);"/>
+</svg>
diff --git a/src/resources/images/data/engine.svg b/src/resources/images/data/engine.svg
new file mode 100644
index 00000000..1e91751a
--- /dev/null
+++ b/src/resources/images/data/engine.svg
@@ -0,0 +1,4 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
+<title>Engine</title>
+<path fill="currentColor" d="M13.778 6.667v2.222h3.333v2.222h2.222v-2.222h3.333v-2.222h-8.889zM13.778 13.333c-0.3 0-0.579 0.121-0.79 0.321l-1.888 1.901h-2.878c-0.611 0-1.111 0.5-1.111 1.111v6.667c0 0.611 0.5 1.111 1.111 1.111h1.888l3.056 2.033c0.178 0.122 0.39 0.189 0.612 0.189h5.556c0.244 0 0.477-0.077 0.666-0.221l4.444-3.333c0.278-0.211 0.445-0.545 0.445-0.89v-7.778c0-0.611-0.5-1.111-1.111-1.111h-10zM27.111 14.444v6.667h2.222v-6.667h-2.222zM17.111 15.556v3.333h2.222l-3.333 5.556v-3.333h-2.222l3.333-5.556zM2.667 16.667v5.556h2.222v-5.556h-2.222z"></path>
+</svg>
diff --git a/src/resources/images/direction.svg b/src/resources/images/direction.svg
new file mode 100644
index 00000000..9f2874fc
--- /dev/null
+++ b/src/resources/images/direction.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="56" height="56" version="1.1" viewBox="0 0 11.667 11.667" xmlns="http://www.w3.org/2000/svg">
+ <path transform="rotate(45)" d="m4.6303-3.6193v1.2838a4.3079 4.3079 0 0 1 0.57314-0.71067 4.3079 4.3079 0 0 1 0.7101-0.57314h-1.2832z" fill="#283593"/>
+</svg>
diff --git a/src/resources/images/icon/animal.svg b/src/resources/images/icon/animal.svg
new file mode 100644
index 00000000..5a239349
--- /dev/null
+++ b/src/resources/images/icon/animal.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="#000000"><path d="M 8.9003906 3.0019531 C 8.7956094 2.9959687 8.6911875 3.0029844 8.5859375 3.0214844 C 7.4759375 3.2044844 6.813875 4.6223594 7.046875 6.3183594 C 7.182875 7.3053594 7.6113594 8.1708594 8.1933594 8.6308594 C 8.5023594 8.8738594 8.8396406 9 9.1816406 9 C 9.2586406 9 9.3360625 8.9934687 9.4140625 8.9804688 C 10.524062 8.7974688 11.186125 7.3795937 10.953125 5.6835938 C 10.817125 4.6965937 10.390594 3.8310937 9.8085938 3.3710938 C 9.5243437 3.1468438 9.2147344 3.0199063 8.9003906 3.0019531 z M 15.097656 3.0039062 C 14.782656 3.0227031 14.474906 3.1448906 14.191406 3.3691406 C 13.609406 3.8291406 13.181875 4.6936406 13.046875 5.6816406 C 12.814875 7.3786406 13.474984 8.7974688 14.583984 8.9804688 C 14.661984 8.9924688 14.740359 9 14.818359 9 C 15.161359 9 15.499594 8.8738594 15.808594 8.6308594 C 16.390594 8.1708594 16.818125 7.3063594 16.953125 6.3183594 C 17.185125 4.6213594 16.524062 3.2025312 15.414062 3.0195312 C 15.308063 3.0025313 15.202656 2.9976406 15.097656 3.0039062 z M 4.6484375 8 C 4.5197266 8.0056641 4.39225 8.0274063 4.265625 8.0664062 C 3.824625 8.2044063 3.4583281 8.5369062 3.2363281 9.0039062 C 2.9493281 9.6089062 2.9221094 10.382 3.1621094 11.125 C 3.5271094 12.257 4.4012969 13 5.2792969 13 C 5.4322969 13 5.584375 12.977641 5.734375 12.931641 C 6.175375 12.793641 6.5416719 12.461141 6.7636719 11.994141 C 7.0506719 11.389141 7.0778906 10.616047 6.8378906 9.8730469 C 6.4625156 8.7110469 5.5494141 7.9603516 4.6484375 8 z M 19.349609 8 C 18.447197 7.9608438 17.537484 8.7101719 17.162109 9.8730469 C 16.922109 10.615047 16.948328 11.389141 17.236328 11.994141 C 17.458328 12.461141 17.824625 12.793641 18.265625 12.931641 C 18.415625 12.977641 18.568703 13 18.720703 13 C 19.598703 13 20.471891 12.256047 20.837891 11.123047 C 21.077891 10.381047 21.051672 9.6089063 20.763672 9.0039062 C 20.541672 8.5369062 20.175375 8.2044062 19.734375 8.0664062 C 19.607375 8.0275313 19.478525 8.0055937 19.349609 8 z M 12 10 C 10.835 10 9.7605 11.077125 9.0625 12.828125 C 8.5395 14.138125 7.503625 15.152125 6.265625 15.828125 C 5.154625 16.434125 5 17.11725 5 17.65625 C 5 19.82025 8.533 20.998047 12 20.998047 C 15.467 20.998047 19 19.82025 19 17.65625 C 19 17.11725 18.845375 16.433172 17.734375 15.826172 C 16.495375 15.151172 15.4605 14.138125 14.9375 12.828125 C 14.2395 11.078125 13.165 10 12 10 z" fill="#000000"/></svg>
diff --git a/src/resources/images/icon/bicycle.svg b/src/resources/images/icon/bicycle.svg
new file mode 100644
index 00000000..a473f4bc
--- /dev/null
+++ b/src/resources/images/icon/bicycle.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="#000000"><path d="M 15 3 A 1.0001 1.0001 0 0 0 14 4 L 14 4.1855469 A 1.0001 1.0001 0 0 0 14.052734 4.5058594 L 14.978516 7.2382812 L 9.9550781 15 L 9.8925781 15 C 9.5602248 13.416061 8.4541484 12.127761 6.9960938 11.492188 L 8.5527344 9 L 11 9 L 11 7 L 5 7 L 5 9 L 6.1972656 9 L 4.9453125 11.003906 A 1.0001 1.0001 0 0 0 4.9003906 11.005859 A 1.0001 1.0001 0 0 0 4.8007812 11.023438 A 1.0001 1.0001 0 0 0 4.640625 11.072266 C 2.0692551 11.267733 5.9211895e-16 13.381669 0 16 C 0 18.745455 2.2545455 21 5 21 C 7.4035927 21 9.4308229 19.272621 9.8984375 17 L 10.5 17 A 1.0001 1.0001 0 0 0 11.339844 16.542969 L 15.796875 9.65625 L 16.490234 11.705078 C 15.010817 12.576973 14 14.171245 14 16 C 14 18.745455 16.254545 21 19 21 C 21.745455 21 24 18.745455 24 16 C 24 13.381005 21.929707 11.266824 19.357422 11.072266 A 1.0001 1.0001 0 0 0 19 11 C 18.788557 11 18.581767 11.017029 18.376953 11.042969 L 16.332031 5 L 19 5 C 19.56503 5 20 5.4349698 20 6 C 20 6.5650302 19.56503 7 19 7 L 19 9 C 20.64497 9 22 7.6449698 22 6 C 22 4.3550302 20.64497 3 19 3 L 15 3 z M 5 13 C 6.3020387 13 7.4022636 13.838627 7.8164062 15 L 5 15 L 5 17 L 7.8164062 17 C 7.4022636 18.161373 6.3020387 19 5 19 C 3.3454545 19 2 17.654545 2 16 C 2 14.345455 3.3454545 13 5 13 z M 19.041016 13.003906 C 20.676234 13.026486 22 14.359504 22 16 C 22 17.654545 20.654545 19 19 19 C 17.345455 19 16 17.654545 16 16 C 16 15.046643 16.45524 14.2061 17.150391 13.65625 L 18.052734 16.320312 L 19.947266 15.679688 L 19.041016 13.003906 z" fill="#000000"/></svg>
diff --git a/src/resources/images/icon/boat.svg b/src/resources/images/icon/boat.svg
new file mode 100644
index 00000000..33bfe27d
--- /dev/null
+++ b/src/resources/images/icon/boat.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="#000000"><path d="M 8.984375 2 A 1.0001 1.0001 0 0 0 8 3 L 8 3.171875 A 1.0001 1.0001 0 0 0 8.1796875 3.7441406 C 8.1180983 3.655854 8.2336897 3.8448895 8.3242188 4.0742188 C 8.4147476 4.303548 8.5237599 4.6182502 8.625 4.9804688 C 8.8274803 5.7049057 9 6.6278333 9 7.5 C 9 8.3721667 8.826034 9.3020135 8.6230469 10.035156 C 8.5215533 10.401728 8.4134185 10.720739 8.3222656 10.955078 C 8.2311127 11.189417 8.1185954 11.380905 8.1699219 11.304688 A 1.0001 1.0001 0 0 0 8 11.863281 L 8 12 A 1.0001 1.0001 0 0 0 9.125 12.992188 L 17.125 11.992188 A 1.0001 1.0001 0 0 0 18 11 C 18 7.3353738 15.68235 4.987833 13.509766 3.6992188 C 11.337181 2.4106043 9.1835938 2.0175781 9.1835938 2.0175781 A 1.0001 1.0001 0 0 0 8.984375 2 z M 10.568359 4.5625 C 11.166859 4.7839713 11.793876 5.0049413 12.490234 5.4179688 C 14.111319 6.3794746 15.54945 7.8407091 15.869141 10.132812 L 10.472656 10.808594 C 10.498986 10.720814 10.524991 10.661471 10.550781 10.568359 C 10.789539 9.7060027 11 8.6278333 11 7.5 C 11 6.4168081 10.795681 5.4010041 10.568359 4.5625 z M 3 14.990234 C 2.735 14.990234 2.4799687 15.095203 2.2929688 15.283203 C 2.1059688 15.471203 2 15.725234 2 15.990234 C 2 18.038234 2.5637344 19.616687 3.6777344 20.679688 C 5.1287344 22.064688 6.9660469 21.991234 6.9980469 21.990234 L 17.998047 22 L 18 22 C 21.007 22 23 17.73 23 16 C 23 15.448 22.553 15 22 15 L 3 14.990234 z" fill="#000000"/></svg>
diff --git a/src/resources/images/icon/bus.svg b/src/resources/images/icon/bus.svg
new file mode 100644
index 00000000..5e881481
--- /dev/null
+++ b/src/resources/images/icon/bus.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="#000000"><path d="M 11 3 C 7.375 3 3 3.237 3 7 L 3 18 C 3 18.884916 3.3906671 19.671627 4 20.220703 L 4 22 C 4 22.552 4.448 23 5 23 L 6 23 C 6.552 23 7 22.552 7 22 L 7 21 L 17 21 L 17 22 C 17 22.552 17.448 23 18 23 L 19 23 C 19.552 23 20 22.552 20 22 L 20 20.220703 C 20.609333 19.671627 21 18.884916 21 18 L 21 7 C 21 3.237 16.625 3 13 3 L 11 3 z M 5 7 L 19 7 L 19 13 L 5 13 L 5 7 z M 6.5 16 C 7.328 16 8 16.672 8 17.5 C 8 18.328 7.328 19 6.5 19 C 5.672 19 5 18.328 5 17.5 C 5 16.672 5.672 16 6.5 16 z M 17.5 16 C 18.328 16 19 16.672 19 17.5 C 19 18.328 18.328 19 17.5 19 C 16.672 19 16 18.328 16 17.5 C 16 16.672 16.672 16 17.5 16 z" fill="#000000"/></svg>
diff --git a/src/resources/images/icon/camper.svg b/src/resources/images/icon/camper.svg
new file mode 100644
index 00000000..19e95cc5
--- /dev/null
+++ b/src/resources/images/icon/camper.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="#000000"><path d="M3.75,2.5C2.231,2.5 1,3.731 1,5.25L1,17.25C1,18.493 2.007,19.5 3.25,19.5L4.55,19.5C4.631,19.933 4.816,20.352 5.127,20.701C5.556,21.184 6.229,21.5 7,21.5C7.771,21.5 8.444,21.184 8.873,20.701C9.184,20.352 9.369,19.933 9.45,19.5L14,19.5L14,9.5C14,8.672 14.672,8 15.5,8L19.75,8C21.269,8 22.5,6.769 22.5,5.25C22.5,3.731 21.269,2.5 19.75,2.5L3.75,2.5ZM17.75,4.5L19.75,4.5C20.165,4.5 20.5,4.836 20.5,5.25C20.5,5.665 20.165,6 19.75,6L17.75,6C17.336,6 17,5.665 17,5.25C17,4.836 17.336,4.5 17.75,4.5ZM5,8L11,8L11,13L5,13L5,8ZM16,9C15.448,9 15,9.448 15,10L15,19.5L16.05,19.5C16.131,19.933 16.316,20.352 16.627,20.701C17.056,21.184 17.729,21.5 18.5,21.5C19.271,21.5 19.944,21.184 20.373,20.701C20.689,20.346 20.876,19.92 20.955,19.479C22.1,19.374 23,18.422 23,17.25L23,13.75C23,11.127 20.874,9 18.25,9L16,9ZM7,18C7.396,18 7.598,18.121 7.752,18.295C7.906,18.468 8,18.729 8,19C8,19.271 7.906,19.532 7.752,19.705C7.598,19.879 7.396,20 7,20C6.604,20 6.402,19.879 6.248,19.705C6.094,19.532 6,19.271 6,19C6,18.729 6.094,18.468 6.248,18.295C6.402,18.121 6.604,18 7,18ZM18.5,18C18.896,18 19.098,18.121 19.252,18.295C19.406,18.468 19.5,18.729 19.5,19C19.5,19.271 19.406,19.532 19.252,19.705C19.098,19.879 18.896,20 18.5,20C18.104,20 17.902,19.879 17.748,19.705C17.594,19.532 17.5,19.271 17.5,19C17.5,18.729 17.594,18.468 17.748,18.295C17.902,18.121 18.104,18 18.5,18Z"/></svg>
diff --git a/src/resources/images/icon/car.svg b/src/resources/images/icon/car.svg
new file mode 100644
index 00000000..f919e0be
--- /dev/null
+++ b/src/resources/images/icon/car.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="#000000"><path d="M18.947,5.013C18.74,4.407,18.169,4,17.529,4H6.471C5.831,4,5.26,4.407,5.053,5.013L3,11v8c0,0.552,0.448,1,1,1h1 c0.552,0,1-0.448,1-1v-1h12v1c0,0.552,0.448,1,1,1h1c0.552,0,1-0.448,1-1v-8L18.947,5.013z M6.829,6h10.343l1.371,4H5.457L6.829,6z M6.5,15C5.672,15,5,14.328,5,13.5S5.672,12,6.5,12S8,12.672,8,13.5S7.328,15,6.5,15z M17.5,15c-0.828,0-1.5-0.672-1.5-1.5 s0.672-1.5,1.5-1.5s1.5,0.672,1.5,1.5S18.328,15,17.5,15z" fill="#000000"/></svg>
diff --git a/src/resources/images/icon/crane.svg b/src/resources/images/icon/crane.svg
new file mode 100644
index 00000000..23db0916
--- /dev/null
+++ b/src/resources/images/icon/crane.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="#000000"><path d="M 20.970703 1 A 1.0001 1.0001 0 0 0 20.59375 1.0859375 L 11.714844 5.03125 C 11.624515 5.018834 11.53526 5 11.443359 5 L 7 5 C 5.897 5 5 5.897 5 7 L 5 13 C 2.8026661 13 1 14.802666 1 17 L 1 18 L 1.203125 18 C 1.6594792 19.706363 3.1564058 21 5 21 L 17 21 C 18.843594 21 20.340521 19.706363 20.796875 18 L 21 18 L 21 17 C 21 14.802666 19.197334 13 17 13 L 17 11.220703 C 17 10.653703 16.808922 10.092703 16.419922 9.5957031 L 13.585938 6.390625 L 20 3.5390625 L 20 7.5 C 20 7.7869372 19.786937 8 19.5 8 C 19.213063 8 19 7.7869372 19 7.5 L 17 7.5 C 17 8.8690628 18.130937 10 19.5 10 C 20.869063 10 22 8.8690628 22 7.5 L 22 2 A 1.0001 1.0001 0 0 0 20.970703 1 z M 9 7 L 11.517578 7.0664062 L 14.111328 10 L 9 10 L 9 7 z M 5 15 L 17 15 C 18.116666 15 19 15.883334 19 17 C 19 18.116666 18.116666 19 17 19 L 5 19 C 3.8833339 19 3 18.116666 3 17 C 3 15.883334 3.8833339 15 5 15 z M 5 16 A 1 1 0 0 0 4 17 A 1 1 0 0 0 5 18 A 1 1 0 0 0 6 17 A 1 1 0 0 0 5 16 z M 9 16 A 1 1 0 0 0 8 17 A 1 1 0 0 0 9 18 A 1 1 0 0 0 10 17 A 1 1 0 0 0 9 16 z M 13 16 A 1 1 0 0 0 12 17 A 1 1 0 0 0 13 18 A 1 1 0 0 0 14 17 A 1 1 0 0 0 13 16 z M 17 16 A 1 1 0 0 0 16 17 A 1 1 0 0 0 17 18 A 1 1 0 0 0 18 17 A 1 1 0 0 0 17 16 z" fill="#000000"/></svg>
diff --git a/src/resources/images/icon/default.svg b/src/resources/images/icon/default.svg
new file mode 100644
index 00000000..a4ba881f
--- /dev/null
+++ b/src/resources/images/icon/default.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="#000000"><path d="M12,2C8.134,2,5,5.134,5,9c0,5,7,13,7,13s7-8,7-13C19,5.134,15.866,2,12,2z M12,11.5c-1.381,0-2.5-1.119-2.5-2.5 c0-1.381,1.119-2.5,2.5-2.5s2.5,1.119,2.5,2.5C14.5,10.381,13.381,11.5,12,11.5z" fill="#000000"/></svg>
diff --git a/src/resources/images/icon/helicopter.svg b/src/resources/images/icon/helicopter.svg
new file mode 100644
index 00000000..2849ee8d
--- /dev/null
+++ b/src/resources/images/icon/helicopter.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="#000000"><path d="M 2 3 L 2 5 L 9 5 L 9 3 L 2 3 z M 11 3 L 11 6.2050781 C 8.584 6.6840781 7.012375 7.956 6.359375 9 L 4 9 L 4 7 L 2 7 L 2 13 L 4 13 L 4 11 L 6 11 L 6 13.300781 C 6 15.370781 7.8795313 17 10.269531 17 L 13.810547 17 C 16.170547 17 21.570703 17 21.970703 13 L 17 13 C 14.79 13 13 11.21 13 9 L 13 3 L 11 3 z M 15 3 L 15 5 L 22 5 L 22 3 L 15 3 z M 15 6.1699219 L 15 9 C 15 10.1 15.9 11 17 11 L 21.740234 11 C 20.940234 8.64 18.28 6.7299219 15 6.1699219 z M 21 18 C 21 18.552 20.552 19 20 19 L 6 19 L 6 21 L 20 21 C 21.654 21 23 19.654 23 18 L 21 18 z" fill="#000000"/></svg>
diff --git a/src/resources/images/icon/motorcycle.svg b/src/resources/images/icon/motorcycle.svg
new file mode 100644
index 00000000..0289a346
--- /dev/null
+++ b/src/resources/images/icon/motorcycle.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="#000000"><path d="M 12 2 L 12 4 L 14.300781 4 L 15.228516 6.5371094 L 13.837891 7 L 11 7 A 1.0001 1.0001 0 0 0 10.292969 7.2929688 L 8.5859375 9 L 6 9 L 4 9 L 2 9 L 2 11 L 4 11 L 4 12.050781 C 1.7620407 12.304527 0 14.197514 0 16.5 C 0 18.973437 2.0265633 21 4.5 21 C 6.9734367 21 9 18.973437 9 16.5 C 9 16.272263 8.9647678 16.052912 8.9316406 15.833984 L 13.195312 14.980469 A 1.0001 1.0001 0 0 0 13.986328 14.164062 L 14.886719 8.7597656 L 15.916016 8.4160156 L 17.421875 12.535156 C 15.991607 13.291295 15 14.778882 15 16.5 C 15 18.973437 17.026563 21 19.5 21 C 21.973437 21 24 18.973437 24 16.5 C 24 14.026563 21.973437 12 19.5 12 C 19.452602 12 19.408387 12.012195 19.361328 12.013672 L 17.894531 8 L 21 8 L 21 6 L 17.162109 6 L 15.939453 2.65625 A 1.0001 1.0001 0 0 0 15 2 L 12 2 z M 11.414062 9 L 12.820312 9 L 12.126953 13.154297 L 8.1914062 13.941406 C 7.6586419 13.176759 6.8905699 12.598894 6 12.279297 L 6 11 L 9 11 A 1.0001 1.0001 0 0 0 9.7070312 10.707031 L 11.414062 9 z M 4.5 14 C 5.8925565 14 7 15.107443 7 16.5 C 7 17.892557 5.8925565 19 4.5 19 C 3.1074435 19 2 17.892557 2 16.5 C 2 15.107443 3.1074435 14 4.5 14 z M 19.5 14 C 20.892557 14 22 15.107443 22 16.5 C 22 17.892557 20.892557 19 19.5 19 C 18.107443 19 17 17.892557 17 16.5 C 17 15.107443 18.107443 14 19.5 14 z" fill="#000000"/></svg>
diff --git a/src/resources/images/icon/offroad.svg b/src/resources/images/icon/offroad.svg
new file mode 100644
index 00000000..b0072b69
--- /dev/null
+++ b/src/resources/images/icon/offroad.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="#000000"><path d="M22,6h-2v2.65l-1.02-4.84C18.89,3.34,18.48,3,18,3H6C5.52,3,5.11,3.34,5.02,3.81L4,8.64V6H2v3h1.62L2.3,10.28 C2.11,10.47,2,10.73,2,11v9c0,0.552,0.448,1,1,1h2l-0.006-3h14.011L19,21h2c0.552,0,1-0.448,1-1v-9c0-0.27-0.11-0.53-0.31-0.72 L20.37,9H22V6z M6.82,5h10.36l0.79,4H6.03L6.82,5z M5.5,15C4.67,15,4,14.33,4,13.5C4,12.67,4.67,12,5.5,12S7,12.67,7,13.5 C7,14.33,6.33,15,5.5,15z M15,16H9v-4h6V16z M18.5,15c-0.83,0-1.5-0.67-1.5-1.5c0-0.83,0.67-1.5,1.5-1.5s1.5,0.67,1.5,1.5 C20,14.33,19.33,15,18.5,15z" fill="#000000"/></svg>
diff --git a/src/resources/images/icon/person.svg b/src/resources/images/icon/person.svg
new file mode 100644
index 00000000..805ba442
--- /dev/null
+++ b/src/resources/images/icon/person.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="#000000"><path d="M 12 4 C 9.789063 4 8 5.789063 8 8 C 8 10.210938 9.789063 12 12 12 C 14.210938 12 16 10.210938 16 8 C 16 5.789063 14.210938 4 12 4 Z M 9.03125 13.40625 C 5.253906 14.550781 4 17.65625 4 17.65625 L 4 20 L 20 20 L 20 17.65625 C 20 17.65625 18.746094 14.550781 14.96875 13.40625 C 14.761719 14.863281 13.511719 16 12 16 C 10.488281 16 9.238281 14.863281 9.03125 13.40625 Z" fill="#000000"/></svg>
diff --git a/src/resources/images/icon/pickup.svg b/src/resources/images/icon/pickup.svg
new file mode 100644
index 00000000..db4494b7
--- /dev/null
+++ b/src/resources/images/icon/pickup.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="#000000"><path d="M 8 3 A 1.0001 1.0001 0 0 0 7 4 L 7 10 L 1 10 C 0.45 10 0 10.45 0 11 L 0 18 C 0 18.55 0.45 19 1 19 L 2.5605469 19 C 3.2568565 20.187528 4.5333802 21 6 21 C 7.4666198 21 8.7431435 20.187528 9.4394531 19 L 14.560547 19 C 15.256857 20.187528 16.53338 21 18 21 C 19.46662 21 20.743143 20.187528 21.439453 19 L 23 19 C 23.55 19 24 18.55 24 18 L 24 12.820312 C 24 11.870312 23.320625 11.039375 22.390625 10.859375 L 18.642578 10.109375 L 14.869141 3.5039062 A 1.0001 1.0001 0 0 0 14 3 L 8 3 z M 9 5 L 13.419922 5 L 16.275391 10 L 9 10 L 9 5 z M 6 15 C 7.1164141 15 8 15.883586 8 17 C 8 18.116414 7.1164141 19 6 19 C 4.8835859 19 4 18.116414 4 17 C 4 15.883586 4.8835859 15 6 15 z M 18 15 C 19.116414 15 20 15.883586 20 17 C 20 18.116414 19.116414 19 18 19 C 16.883586 19 16 18.116414 16 17 C 16 15.883586 16.883586 15 18 15 z" fill="#000000"/></svg>
diff --git a/src/resources/images/icon/plane.svg b/src/resources/images/icon/plane.svg
new file mode 100644
index 00000000..5210e4c9
--- /dev/null
+++ b/src/resources/images/icon/plane.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="#000000"><path d="M8,22h2l4.997-8H20c1.105,0,2-0.895,2-2s-0.895-2-2-2h-5.003L10,2H8l2.493,8H4.996L3.5,8H2l1,4l-1,4h1.5l1.496-2h5.497 L8,22z" fill="#000000"/></svg>
diff --git a/src/resources/images/icon/scooter.svg b/src/resources/images/icon/scooter.svg
new file mode 100644
index 00000000..ea23f9fa
--- /dev/null
+++ b/src/resources/images/icon/scooter.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="#000000"><path d="M 17.5 3 C 16.672 3 16 3.672 16 4.5 C 16 4.6761216 16.035954 4.8429605 16.091797 5 L 14 5 L 14 7 L 16.310547 7 L 16.519531 7.5605469 L 12.619141 11 L 9 11 L 9 9 L 12 9 L 12 7 L 2 7 L 2 9 L 4.2402344 9 L 3.7304688 9.4492188 C 2.5804687 10.439219 1 11.8 1 14 C 1 14.27 1.1090625 14.520938 1.2890625 14.710938 L 1.4472656 14.869141 C 1.1853855 15.359439 1.0234375 15.909585 1.0234375 16.5 C 1.0234375 18.418558 2.5945875 20 4.5117188 20 C 6.2579417 20 7.7029312 18.683767 7.9492188 17 L 16.074219 17 C 16.320506 18.683767 17.765496 20 19.511719 20 C 21.42885 20 23 18.418558 23 16.5 C 23 15.033786 22.079411 13.770945 20.791016 13.253906 L 20.695312 13 L 23 13 L 23 11 L 19.945312 11 L 18.070312 6 L 20 6 L 20 3 L 17.5 3 z M 4.5117188 15 C 5.3427564 15 6 15.657134 6 16.5 C 6 17.342866 5.3427564 18 4.5117188 18 C 3.6806811 18 3.0234375 17.342866 3.0234375 16.5 C 3.0234375 15.657134 3.6806811 15 4.5117188 15 z M 19.511719 15 C 20.342756 15 21 15.657134 21 16.5 C 21 17.342866 20.342756 18 19.511719 18 C 18.680681 18 18.023438 17.342866 18.023438 16.5 C 18.023438 15.657134 18.680681 15 19.511719 15 z" fill="#000000"/></svg>
diff --git a/src/resources/images/icon/ship.svg b/src/resources/images/icon/ship.svg
new file mode 100644
index 00000000..b8c563c8
--- /dev/null
+++ b/src/resources/images/icon/ship.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="#000000"><path d="M 11 2 L 11 3 L 9 3 A 1.0001 1.0001 0 0 0 8 4 L 8 5 L 6 5 A 1.0001 1.0001 0 0 0 5 6 L 5 10.880859 L 4.1601562 11.109375 C 3.3001563 11.289375 2.5804688 11.889219 2.2304688 12.699219 C 1.8804687 13.509219 1.940625 14.439453 2.390625 15.189453 L 4 17.880859 L 4 18.980469 C 3.9171433 18.988279 3.8366386 19 3.75 19 C 3.1869011 19 2.694455 18.824995 2.3925781 18.605469 C 2.0907013 18.385943 2 18.181158 2 18 L 0 18 C 0 18.923842 0.52342369 19.718432 1.2167969 20.222656 C 1.91017 20.72688 2.7940989 21 3.75 21 C 4.7059011 21 5.58983 20.72688 6.2832031 20.222656 C 6.3611489 20.165974 6.4268517 20.093089 6.5 20.029297 C 6.5731483 20.093089 6.6388511 20.165974 6.7167969 20.222656 C 7.41017 20.72688 8.2940989 21 9.25 21 C 10.205901 21 11.08983 20.72688 11.783203 20.222656 C 11.861149 20.165974 11.926852 20.093089 12 20.029297 C 12.073148 20.093089 12.138851 20.165974 12.216797 20.222656 C 12.91017 20.72688 13.794099 21 14.75 21 C 15.705901 21 16.58983 20.72688 17.283203 20.222656 C 17.361149 20.165974 17.426852 20.093089 17.5 20.029297 C 17.573148 20.093089 17.638851 20.165974 17.716797 20.222656 C 18.41017 20.72688 19.294099 21 20.25 21 C 21.205901 21 22.08983 20.72688 22.783203 20.222656 C 23.476576 19.718432 24 18.923842 24 18 L 22 18 C 22 18.181158 21.9093 18.385943 21.607422 18.605469 C 21.305545 18.824995 20.813099 19 20.25 19 C 20.163361 19 20.082857 18.988279 20 18.980469 L 20 17.880859 L 21.609375 15.199219 C 22.059375 14.439219 22.119531 13.509219 21.769531 12.699219 C 21.419531 11.889219 20.700625 11.299141 19.890625 11.119141 L 19 10.876953 L 19 6 A 1.0001 1.0001 0 0 0 18 5 L 16 5 L 16 4 A 1.0001 1.0001 0 0 0 15 3 L 13 3 L 13 2 L 11 2 z M 10 5 L 14 5 L 14 6 A 1.0001 1.0001 0 0 0 15 7 L 17 7 L 17 10.335938 L 12 8.9804688 L 7 10.337891 L 7 7 L 9 7 A 1.0001 1.0001 0 0 0 10 6 L 10 5 z M 8 14 C 8.6 14 9 14.47 9 15 C 9 15.53 8.6 16 8 16 C 7.4 16 7 15.53 7 15 C 7 14.47 7.4 14 8 14 z M 16 14 C 16.6 14 17 14.47 17 15 C 17 15.53 16.6 16 16 16 C 15.4 16 15 15.53 15 15 C 15 14.47 15.4 14 16 14 z" fill="#000000"/></svg>
diff --git a/src/resources/images/icon/tractor.svg b/src/resources/images/icon/tractor.svg
new file mode 100644
index 00000000..3a9e9f25
--- /dev/null
+++ b/src/resources/images/icon/tractor.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="#000000"><path d="M 5 4 C 4.448 4 4 4.448 4 5 L 4 11.609375 C 2.2234889 12.523708 1 14.3737 1 16.5 C 1 19.525721 3.4742785 22 6.5 22 C 9.0054253 22 11.12762 20.301307 11.785156 18 L 16.050781 18 C 16.026575 18.164905 16 18.328781 16 18.5 C 16 20.421152 17.578848 22 19.5 22 C 21.421152 22 23 20.421152 23 18.5 C 23 17.116848 22.175306 15.9223 21 15.355469 L 21 10 C 21 9.448 20.552 9 20 9 L 17 9 L 17 6 L 18 6 L 19 6 L 19 4 L 18 4 L 17 4 C 15.906937 4 15 4.9069372 15 6 L 15 9 L 12.677734 9 L 10.927734 4.6289062 C 10.775734 4.2499062 10.409 4 10 4 L 5 4 z M 6.5 13 C 8.4448413 13 10 14.555159 10 16.5 C 10 18.444841 8.4448413 20 6.5 20 C 4.5551587 20 3 18.444841 3 16.5 C 3 14.555159 4.5551587 13 6.5 13 z M 6.5 15 A 1.5 1.5 0 0 0 5 16.5 A 1.5 1.5 0 0 0 6.5 18 A 1.5 1.5 0 0 0 8 16.5 A 1.5 1.5 0 0 0 6.5 15 z M 19.5 17 C 20.340272 17 21 17.659728 21 18.5 C 21 19.340272 20.340272 20 19.5 20 C 18.659728 20 18 19.340272 18 18.5 C 18 17.659728 18.659728 17 19.5 17 z" fill="#000000"/></svg>
diff --git a/src/resources/images/icon/train.svg b/src/resources/images/icon/train.svg
new file mode 100644
index 00000000..125b468e
--- /dev/null
+++ b/src/resources/images/icon/train.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="#000000"><path d="M 7 2 C 5.34375 2 4 3.34375 4 5 L 4 16.84375 C 4 18.53125 5.332031 19.921875 7 20 L 6 21 L 6 22 L 18 22 L 18 21 L 17 20 C 18.667969 19.921875 20 18.53125 20 16.84375 L 20 5 C 20 3.34375 18.65625 2 17 2 Z M 9 4 L 15 4 L 15 5 L 9 5 Z M 6 7 L 11 7 L 11 12 L 6 12 Z M 13 7 L 18 7 L 18 12 L 13 12 Z M 7.5 15 C 8.328125 15 9 15.671875 9 16.5 C 9 17.328125 8.328125 18 7.5 18 C 6.671875 18 6 17.328125 6 16.5 C 6 15.671875 6.671875 15 7.5 15 Z M 16.5 15 C 17.328125 15 18 15.671875 18 16.5 C 18 17.328125 17.328125 18 16.5 18 C 15.671875 18 15 17.328125 15 16.5 C 15 15.671875 15.671875 15 16.5 15 Z" fill="#000000"/></svg>
diff --git a/src/resources/images/icon/tram.svg b/src/resources/images/icon/tram.svg
new file mode 100644
index 00000000..f54084f7
--- /dev/null
+++ b/src/resources/images/icon/tram.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="#000000"><path d="M 12 0 C 8.9426113 0 6.309955 0.29967448 4.7363281 0.73242188 L 5.2675781 2.6601562 C 6.4939513 2.3229037 9.0813887 2 12 2 C 14.920658 2 17.508002 2.3229037 18.734375 2.6601562 L 19.265625 0.73242188 C 17.691998 0.29967448 15.059342 0 12 0 z M 8 4 C 5.79 4 4 5.79 4 8 L 4 19 L 4 22.263672 C 4 22.670672 4.336 23 4.75 23 L 7 23 L 7 22 L 17 22 L 17 23 L 19.25 23 C 19.664 23 20 22.670672 20 22.263672 L 20 19 L 20 8 C 20 5.79 18.21 4 16 4 L 8 4 z M 8 6 L 16 6 C 17.1 6 18 6.9 18 8 L 18 13 L 6 13 L 6 8 C 6 6.9 6.9 6 8 6 z M 12 16 C 12.55 16 13 16.45 13 17 C 13 17.55 12.55 18 12 18 C 11.45 18 11 17.55 11 17 C 11 16.45 11.45 16 12 16 z" fill="#000000"/></svg>
diff --git a/src/resources/images/icon/trolleybus.svg b/src/resources/images/icon/trolleybus.svg
new file mode 100644
index 00000000..46ecc712
--- /dev/null
+++ b/src/resources/images/icon/trolleybus.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="#000000"><path d="M17,4h-0.52l1.2-3h-2.16l-1.2,3H9.68l-1.2-3H6.32l1.2,3H7C4.79,4,3,5.79,3,8v13.26C3,21.67,3.34,22,3.75,22H6v-2h12v2h2.25 c0.41,0,0.75-0.33,0.75-0.74V8C21,5.79,19.21,4,17,4z M8,16c-0.55,0-1-0.45-1-1s0.45-1,1-1s1,0.45,1,1S8.55,16,8,16z M16,16 c-0.55,0-1-0.45-1-1s0.45-1,1-1s1,0.45,1,1S16.55,16,16,16z M19,11H5V8c0-1.1,0.9-2,2-2h10c1.1,0,2,0.9,2,2V11z" fill="#000000"/></svg>
diff --git a/src/resources/images/icon/truck.svg b/src/resources/images/icon/truck.svg
new file mode 100644
index 00000000..df5c164c
--- /dev/null
+++ b/src/resources/images/icon/truck.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="#000000"><path d="M 3 3 C 2.45 3 2 3.45 2 4 L 2 17 C 2 17.55 2.45 18 3 18 L 3.0507812 18 C 3.2958204 19.692046 4.7411114 21 6.5 21 C 8.2588886 21 9.7041796 19.692046 9.9492188 18 L 14.050781 18 C 14.29582 19.692046 15.741111 21 17.5 21 C 19.258889 21 20.70418 19.692046 20.949219 18 L 22 18 C 22.55 18 23 17.55 23 17 L 23 12.310547 C 23 11.750547 22.850781 11.210469 22.550781 10.730469 L 20.519531 7.4296875 C 19.969531 6.5496875 19.000937 6 17.960938 6 L 14 6 L 14 4 C 14 3.45 13.55 3 13 3 L 3 3 z M 14 8 L 17.960938 8 C 18.310938 8 18.630547 8.1804687 18.810547 8.4804688 L 20.849609 11.779297 C 20.949609 11.939297 21 12.120547 21 12.310547 L 21 16 L 20.648438 16 C 20.084435 14.821937 18.891001 14 17.5 14 C 16.108999 14 14.915565 14.821937 14.351562 16 L 14 16 L 14 8 z M 6.5 16 C 7.327 16 8 16.673 8 17.5 C 8 18.327 7.327 19 6.5 19 C 5.673 19 5 18.327 5 17.5 C 5 16.673 5.673 16 6.5 16 z M 17.5 16 C 18.327 16 19 16.673 19 17.5 C 19 18.327 18.327 19 17.5 19 C 16.673 19 16 18.327 16 17.5 C 16 16.673 16.673 16 17.5 16 z" fill="#000000"/></svg>
diff --git a/src/resources/images/icon/van.svg b/src/resources/images/icon/van.svg
new file mode 100644
index 00000000..a3ee13c7
--- /dev/null
+++ b/src/resources/images/icon/van.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="#000000"><path d="M 5 3 C 3.35 3 2 4.35 2 6 L 2 17 C 2 17.55 2.45 18 3 18 L 4.0507812 18 C 4.2958204 19.692046 5.7411114 21 7.5 21 C 9.2588886 21 10.70418 19.692046 10.949219 18 L 13.050781 18 C 13.29582 19.692046 14.741111 21 16.5 21 C 18.258889 21 19.70418 19.692046 19.949219 18 L 21 18 C 21.55 18 22 17.55 22 17 L 22 11.740234 C 22 11.020234 21.739297 10.329063 21.279297 9.7890625 L 16.359375 4.0507812 C 15.789375 3.3807813 14.960078 3 14.080078 3 L 5 3 z M 12 5 L 14.080078 5 C 14.370078 5 14.649844 5.1296094 14.839844 5.3496094 L 19.679688 11 L 12 11 L 12 5 z M 7.5 16 C 8.327 16 9 16.673 9 17.5 C 9 18.327 8.327 19 7.5 19 C 6.673 19 6 18.327 6 17.5 C 6 16.673 6.673 16 7.5 16 z M 16.5 16 C 17.327 16 18 16.673 18 17.5 C 18 18.327 17.327 19 16.5 19 C 15.673 19 15 18.327 15 17.5 C 15 16.673 15.673 16 16.5 16 z" fill="#000000"/></svg>
diff --git a/src/resources/images/logo.svg b/src/resources/images/logo.svg
new file mode 100644
index 00000000..94097d1f
--- /dev/null
+++ b/src/resources/images/logo.svg
@@ -0,0 +1,23 @@
+<svg width="240" height="64" viewBox="0 0 240 64" xmlns="http://www.w3.org/2000/svg">
+ <g id="img">
+ <rect id="rect3778" height="64" width="240" y="0" x="0" fill="none"/>
+ <g id="g4194">
+ <g id="g4145" fill="currentColor">
+ <circle id="path2993" stroke-width="1.3262" transform="rotate(-30)" cy="43.713" cx="9.4364" r="2.2765"/>
+ <path id="path3004" stroke-width="1.0095" d="m37.012 24.177-2.8428 3.6128c0.66345 0.52205 1.3255 1.1576 1.7734 1.9333 0.4479 0.77578 0.66726 1.6669 0.78764 2.5025l4.5502-0.65558c-0.193-1.42-0.633-2.804-1.394-4.123s-1.74-2.391-2.874-3.27z"/>
+ <path id="path3014" stroke-width="1.0095" d="m42.504 16.9-2.8428 3.6128c1.607 1.2355 3.0914 2.7935 4.1679 4.6581s1.6835 3.9291 1.95 5.9386l4.5502-0.65558c-0.33967-2.5954-1.1669-5.1513-2.5573-7.5594-1.3903-2.4081-3.1901-4.4025-5.268-5.9944z"/>
+ <path id="path3036" stroke-width="3.6204" d="m28.667 44.439a9.1058 9.1058 0 0 1 -9.1058 0.000023 9.1058 9.1058 0 0 1 -4.5529 -7.8859 9.1058 9.1058 0 0 1 4.5529 -7.8859l4.5529 7.8859z"/>
+ <path id="path3038-8" stroke-width="1.0095" d="m17.502 6.8895c-13.868 8.0065-18.619 25.74-10.612 39.608 8.006 13.868 25.739 18.619 39.608 10.613 13.868-8.007 18.619-25.74 10.613-39.609-8.007-13.868-25.74-18.62-39.609-10.612zm1.706 2.9541c12.237-7.0648 27.884-2.8722 34.948 9.3644 7.065 12.237 2.873 27.884-9.364 34.948-12.237 7.065-27.884 2.873-34.948-9.364-7.0652-12.237-2.8726-27.884 9.364-34.948z"/>
+ </g>
+ </g>
+ <g id="logotext" aria-label="Traccar" fill="currentColor">
+ <path id="path4172" d="m89.719 48.671h-3.915v-30.192h-10.663v-3.4775h25.241v3.4775h-10.663v30.192z"/>
+ <path id="path4174" d="m116.36 22.969q1.6812 0 3.0169 0.27636l-0.52968 3.5466q-1.566-0.34544-2.7636-0.34544-3.063 0-5.2508 2.4872-2.1648 2.4872-2.1648 6.195v13.541h-3.8229v-25.241h3.1551l0.43756 4.675h0.18424q1.4048-2.4642 3.3854-3.7999t4.3526-1.3357z"/>
+ <path id="path4176" d="m139.62 48.671-0.75998-3.5926h-0.18424q-1.8884 2.3721-3.7769 3.2242-1.8654 0.82907-4.675 0.82907-3.7538 0-5.8956-1.9345-2.1187-1.9345-2.1187-5.5041 0-7.6459 12.229-8.0143l4.2835-0.13818v-1.566q0-2.9708-1.2897-4.3756-1.2666-1.4278-4.0763-1.4278-3.1551 0-7.1392 1.9345l-1.1745-2.9248q1.8654-1.0133 4.0762-1.589 2.2339-0.57574 4.4678-0.57574 4.5138 0 6.6786 2.0036 2.1878 2.0036 2.1878 6.4253v17.226h-2.8326zm-8.6361-2.6945q3.5696 0 5.5962-1.9575 2.0496-1.9575 2.0496-5.4811v-2.2799l-3.8229 0.16121q-4.5599 0.16121-6.5865 1.4278-2.0036 1.2436-2.0036 3.892 0 2.0727 1.2436 3.1551 1.2666 1.0824 3.5236 1.0824z"/>
+ <path id="path4178" d="m160.44 49.131q-5.4811 0-8.498-3.3623-2.9939-3.3854-2.9939-9.5573 0-6.3332 3.0399-9.7876 3.063-3.4545 8.7052-3.4545 1.8194 0 3.6387 0.3915t2.8557 0.92119l-1.1745 3.2472q-1.2666-0.50665-2.7636-0.82907-1.4969-0.34544-2.6484-0.34544-7.6919 0-7.6919 9.8106 0 4.652 1.8654 7.1392 1.8884 2.4872 5.5732 2.4872 3.1551 0 6.4713-1.3588v3.3854q-2.5333 1.3127-6.3792 1.3127z"/>
+ <path id="path4180" d="m182.92 49.131q-5.4811 0-8.498-3.3623-2.9939-3.3854-2.9939-9.5573 0-6.3332 3.0399-9.7876 3.063-3.4545 8.7052-3.4545 1.8193 0 3.6387 0.3915t2.8557 0.92119l-1.1745 3.2472q-1.2666-0.50665-2.7636-0.82907-1.4969-0.34544-2.6484-0.34544-7.6919 0-7.6919 9.8106 0 4.652 1.8654 7.1392 1.8884 2.4872 5.5732 2.4872 3.1551 0 6.4714-1.3588v3.3854q-2.5333 1.3127-6.3792 1.3127z"/>
+ <path id="path4182" d="m210.83 48.671-0.75998-3.5926h-0.18424q-1.8884 2.3721-3.7769 3.2242-1.8654 0.82907-4.675 0.82907-3.7538 0-5.8956-1.9345-2.1187-1.9345-2.1187-5.5041 0-7.6459 12.229-8.0143l4.2835-0.13818v-1.566q0-2.9708-1.2897-4.3756-1.2666-1.4278-4.0762-1.4278-3.1551 0-7.1392 1.9345l-1.1745-2.9248q1.8654-1.0133 4.0762-1.589 2.2339-0.57574 4.4678-0.57574 4.5138 0 6.6786 2.0036 2.1878 2.0036 2.1878 6.4253v17.226h-2.8326zm-8.6361-2.6945q3.5696 0 5.5962-1.9575 2.0496-1.9575 2.0496-5.4811v-2.2799l-3.8229 0.16121q-4.5599 0.16121-6.5865 1.4278-2.0036 1.2436-2.0036 3.892 0 2.0727 1.2436 3.1551 1.2666 1.0824 3.5235 1.0824z"/>
+ <path id="path4184" d="m233.08 22.969q1.6812 0 3.0169 0.27636l-0.52968 3.5466q-1.566-0.34544-2.7636-0.34544-3.0629 0-5.2508 2.4872-2.1648 2.4872-2.1648 6.195v13.541h-3.8229v-25.241h3.1551l0.43757 4.675h0.18423q1.4048-2.4642 3.3854-3.7999t4.3526-1.3357z"/>
+ </g>
+ </g>
+</svg>
diff --git a/src/resources/l10n/af.json b/src/resources/l10n/af.json
new file mode 100644
index 00000000..5ecd845e
--- /dev/null
+++ b/src/resources/l10n/af.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "Laai tans...",
+ "sharedHide": "Verberg",
+ "sharedSave": "Stoor",
+ "sharedUpload": "Upload",
+ "sharedSet": "Stel",
+ "sharedCancel": "Kanselleer",
+ "sharedCopy": "Copy",
+ "sharedAdd": "Sit by",
+ "sharedEdit": "Redigeer",
+ "sharedRemove": "Verwyder",
+ "sharedRemoveConfirm": "Verwyder item",
+ "sharedNoData": "Geen data",
+ "sharedSubject": "Subject",
+ "sharedYes": "Ja",
+ "sharedNo": "Nee",
+ "sharedKm": "km",
+ "sharedMi": "mi",
+ "sharedNmi": "nmi",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "kn",
+ "sharedKmh": "km/h",
+ "sharedMph": "mph",
+ "sharedHour": "Uur",
+ "sharedMinute": "Minuut",
+ "sharedSecond": "Sekonde",
+ "sharedDays": "dae",
+ "sharedHours": "ure",
+ "sharedMinutes": "minute",
+ "sharedDecimalDegrees": "Desimale grade",
+ "sharedDegreesDecimalMinutes": "Grade Desimale minute",
+ "sharedDegreesMinutesSeconds": "Grade minute sekondes",
+ "sharedName": "Naam",
+ "sharedDescription": "Beskrywing",
+ "sharedSearch": "Soek",
+ "sharedIconScale": "Icon Scale",
+ "sharedGeofence": "Geografiese omheining",
+ "sharedGeofences": "Geografiese omheinings",
+ "sharedCreateGeofence": "Create Geofence",
+ "sharedNotifications": "Kennisgewings",
+ "sharedNotification": "Kennisgewing",
+ "sharedAttributes": "Attribute",
+ "sharedAttribute": "Attribuut",
+ "sharedDrivers": "Bestuurders",
+ "sharedDriver": "Bestuurder",
+ "sharedArea": "Gebied",
+ "sharedSound": "Kennisgewing klank",
+ "sharedType": "Tiepe",
+ "sharedDistance": "Afstand",
+ "sharedHourAbbreviation": "u",
+ "sharedMinuteAbbreviation": "m",
+ "sharedSecondAbbreviation": "s",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "I",
+ "sharedGallonAbbreviation": "gal",
+ "sharedLiter": "Liter",
+ "sharedImpGallon": "Imp. Gallon",
+ "sharedUsGallon": "V.S. Gallon",
+ "sharedLiterPerHourAbbreviation": "l/h",
+ "sharedGetMapState": "Kry Kaartstaat",
+ "sharedComputedAttribute": "Berekende attribuut",
+ "sharedComputedAttributes": "Berekende attribute",
+ "sharedCheckComputedAttribute": "Kontroleer Berekende attribuut",
+ "sharedExpression": "Uitdrukking",
+ "sharedDevice": "Toestel",
+ "sharedTest": "Test",
+ "sharedTestNotification": "Stuur Toets Kennisgewing",
+ "sharedTestNotificators": "Test Channels",
+ "sharedTestExpression": "Test Expression",
+ "sharedCalendar": "Kalender",
+ "sharedCalendars": "Kalenders",
+ "sharedFile": "Lêer",
+ "sharedSearchDevices": "Soek Toestelle",
+ "sharedSortBy": "Sorteer Volgens",
+ "sharedFilterMap": "Filter on Map",
+ "sharedSelectFile": "Selekteer Lêer",
+ "sharedPhone": "Foon",
+ "sharedRequired": "Word Vereis",
+ "sharedPreferences": "Voorkeure",
+ "sharedPermissions": "Toelaatbaarhede",
+ "sharedConnections": "Verbindings",
+ "sharedExtra": "Ekstra",
+ "sharedPrimary": "Primary",
+ "sharedSecondary": "Secondary",
+ "sharedTypeString": "String",
+ "sharedTypeNumber": "Nommer",
+ "sharedTypeBoolean": "Boole",
+ "sharedTimezone": "Tydsone",
+ "sharedInfoTitle": "Info",
+ "sharedSavedCommand": "Gestoorde Bevel",
+ "sharedSavedCommands": "Gestoorde Bevele",
+ "sharedNew": "Nuut...",
+ "sharedShowAddress": "Wys Adres",
+ "sharedShowDetails": "Addisionele besonderhede",
+ "sharedDisabled": "Buite aksie gestel",
+ "sharedMaintenance": "Onderhoud",
+ "sharedDeviceAccumulators": "Akkumulators ",
+ "sharedAlarms": "Alarms",
+ "sharedLocation": "Location",
+ "sharedImport": "Import",
+ "sharedColumns": "Kolomme",
+ "sharedDropzoneText": "Drag and drop a file here or click",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "Simple",
+ "calendarRecurrence": "Recurrence",
+ "calendarOnce": "Once",
+ "calendarDaily": "Daily",
+ "calendarWeekly": "Weekly",
+ "calendarMonthly": "Monthly",
+ "calendarDays": "Days",
+ "calendarSunday": "Sunday",
+ "calendarMonday": "Monday",
+ "calendarTuesday": "Tuesday",
+ "calendarWednesday": "Wednesday",
+ "calendarThursday": "Thursday",
+ "calendarFriday": "Friday",
+ "calendarSaturday": "Saturday",
+ "attributeShowGeofences": "Show Geofences",
+ "attributeSpeedLimit": "Spoedgrens",
+ "attributeFuelDropThreshold": "Fuel Drop Threshold",
+ "attributeFuelIncreaseThreshold": "Fuel Increase Threshold",
+ "attributePolylineDistance": "Polielynafstand",
+ "attributeReportIgnoreOdometer": "Verslag: Ignoreer Odometer",
+ "attributeWebReportColor": "Web: Rapporteer kleur",
+ "attributeDevicePassword": "Toestelwagwoord",
+ "attributeDeviceImage": "Device Image",
+ "attributeDeviceInactivityStart": "Device Inactivity Start",
+ "attributeDeviceInactivityPeriod": "Device Inactivity Period",
+ "attributeProcessingCopyAttributes": "Verwerking: Kopieër attribute",
+ "attributeColor": "Kleur",
+ "attributeWebLiveRouteLength": "Web: Regstreekse Roete Lengte",
+ "attributeWebSelectZoom": "Web: Zoom op Kies",
+ "attributeWebMaxZoom": "Web: Maksimum Zoom",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Pushover User Key",
+ "attributePushoverDeviceNames": "Pushover Device Names",
+ "attributeMailSmtpHost": "Pos: SMTP-gasheer",
+ "attributeMailSmtpPort": "Pos: SMTP-poort",
+ "attributeMailSmtpStarttlsEnable": "Pos: SMTP STARTTLS Aktiveer",
+ "attributeMailSmtpStarttlsRequired": "Pos: SMTP STARTTLS word vereis",
+ "attributeMailSmtpSslEnable": "Pos: SMTP SSL Aktiveer",
+ "attributeMailSmtpSslTrust": "Pos: SMTP SSL Vertrou",
+ "attributeMailSmtpSslProtocols": "Pos: SMTP SSL-protokolle",
+ "attributeMailSmtpFrom": "Pos: SMTP vanaf",
+ "attributeMailSmtpAuth": "Pos: SMTP Auth Aktiveer",
+ "attributeMailSmtpUsername": "Pos: SMTP-gebruikersnaam",
+ "attributeMailSmtpPassword": "Pos: SMTP wagwoord",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: Disable Attributes",
+ "attributeUiDisableGroups": "UI: Disable Groups",
+ "attributeUiDisableEvents": "UI: deaktiveer gebeure",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "UI: deaktiveer bestuurders",
+ "attributeUiDisableComputedAttributes": "UI: deaktiveer berekende attribute",
+ "attributeUiDisableCalendars": "UI: deaktiveer kalenders",
+ "attributeUiDisableMaintenance": "UI: deaktiveer onderhoud",
+ "attributeUiHidePositionAttributes": "UI: Verberg posisie attribute",
+ "attributeUiDisableLoginLanguage": "UI: Disable Login Language",
+ "attributeNotificationTokens": "Notification Tokens",
+ "attributePopupInfo": "Popup Info",
+ "errorTitle": "Fout",
+ "errorGeneral": "Ongeldige parameters of beperking oortreding",
+ "errorConnection": "Verbindings fout",
+ "errorSocket": "Web aansluiting verbinding fout",
+ "errorZero": "Kan nie nul wees nie",
+ "userEmail": "E-pos",
+ "userPassword": "Wagwoord",
+ "userAdmin": "Admin",
+ "userRemember": "Onthou",
+ "userExpirationTime": "Verval",
+ "userDeviceLimit": "Toestel Limiet",
+ "userUserLimit": "Gebruiker Limiet",
+ "userDeviceReadonly": "Toestel Leesalleen",
+ "userLimitCommands": "Beperk Bevele",
+ "userDisableReports": "Deaktiveer Verslae",
+ "userFixedEmail": "No Email Change",
+ "userToken": "Token",
+ "userDeleteAccount": "Delete Account",
+ "userTemporary": "Temporary",
+ "loginTitle": "Teken aan",
+ "loginLanguage": "Taal",
+ "loginReset": "Herstel Wagwoord",
+ "loginRegister": "Registreer",
+ "loginLogin": "Teken aan",
+ "loginOpenId": "Login with OpenID",
+ "loginFailed": "Verkeerde e-posadres of wagwoord",
+ "loginCreated": "Nuwe gebruiker is geregistreer",
+ "loginResetSuccess": "Check your email",
+ "loginUpdateSuccess": "Nuwe wagwoord is ingestel",
+ "loginLogout": "Teken uit",
+ "loginLogo": "Logo",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "Toestelle en staat",
+ "deviceSelected": "Selected Device",
+ "deviceTitle": "Toestelle",
+ "devicePrimaryInfo": "Device Title",
+ "deviceSecondaryInfo": "Device Detail",
+ "deviceIdentifier": "Identifiseerder",
+ "deviceModel": "Model",
+ "deviceContact": "Kontak",
+ "deviceCategory": "Kategorie",
+ "deviceLastUpdate": "Laaste Opdatering",
+ "deviceCommand": "Bevel",
+ "deviceFollow": "Volg",
+ "deviceTotalDistance": "Totale Afstand",
+ "deviceStatus": "Toestand",
+ "deviceStatusOnline": "Aanlyn",
+ "deviceStatusOffline": "Van lyn af",
+ "deviceStatusUnknown": "Onbekend",
+ "deviceRegisterFirst": "Register your first device",
+ "deviceIdentifierHelp": "IMEI, serial number or other id. It has to match the identifier device reports to the server.",
+ "deviceShare": "Share Device",
+ "groupDialog": "Groep",
+ "groupParent": "Groep",
+ "groupNoGroup": "Geen Groep",
+ "settingsTitle": "Instellings",
+ "settingsUser": "Rekening",
+ "settingsGroups": "Groepe",
+ "settingsServer": "Bediener",
+ "settingsUsers": "Gebruikers",
+ "settingsDistanceUnit": "Afstand Eenheid",
+ "settingsAltitudeUnit": "Altitude Unit",
+ "settingsSpeedUnit": "Spoed Eenheid",
+ "settingsVolumeUnit": "Volume Eenheid",
+ "settingsTwelveHourFormat": "12-uur Formaat",
+ "settingsCoordinateFormat": "Koördinate Formaat",
+ "settingsServerVersion": "Server Version",
+ "settingsAppVersion": "App Version",
+ "settingsConnection": "Connection",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "Verslae",
+ "reportScheduled": "Scheduled Reports",
+ "reportDevice": "Toestel",
+ "reportGroup": "Groep",
+ "reportFrom": "Van",
+ "reportTo": "Na",
+ "reportShow": "Wys",
+ "reportClear": "Maak skoon",
+ "linkGoogleMaps": "Google Maps",
+ "linkAppleMaps": "Apple Maps",
+ "linkStreetView": "Street View",
+ "positionFixTime": "Fix Time",
+ "positionDeviceTime": "Toestel Tyd",
+ "positionServerTime": "Server Time",
+ "positionValid": "Geldig",
+ "positionAccuracy": "Akkuraatheid",
+ "positionLatitude": "Breedtegraad",
+ "positionLongitude": "Lengtegraad",
+ "positionAltitude": "Hoogte",
+ "positionSpeed": "Spoed",
+ "positionCourse": "Koers",
+ "positionAddress": "Adres",
+ "positionProtocol": "Protokol",
+ "positionDistance": "Afstand",
+ "positionRpm": "RPM",
+ "positionFuel": "Brandstof",
+ "positionPower": "Krag",
+ "positionBattery": "Battery",
+ "positionRaw": "Rou",
+ "positionIndex": "Indeks",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "Satelliete",
+ "positionSatVisible": "Sigbare Satelliete",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "Wandel",
+ "positionEvent": "Gebeurtenis",
+ "positionAlarm": "Alarm",
+ "positionStatus": "Status",
+ "positionOdometer": "Odometer",
+ "positionServiceOdometer": "Diens Odometer",
+ "positionTripOdometer": "Reis Odometer",
+ "positionHours": "Ure",
+ "positionSteps": "Stappe",
+ "positionInput": "Inset",
+ "positionHeartRate": "Heart Rate",
+ "positionOutput": "Uitset",
+ "positionBatteryLevel": "Batteryvlak",
+ "positionFuelConsumption": "Brandstofverbruik",
+ "positionRfid": "RFID",
+ "positionVersionFw": "Firmware-weergawe",
+ "positionVersionHw": "Hardeware weergawe",
+ "positionIgnition": "Ontsteking",
+ "positionFlags": "Vlae",
+ "positionCharge": "Herlaai",
+ "positionIp": "IP",
+ "positionArchive": "Argiveer",
+ "positionVin": "VIN",
+ "positionApproximate": "Benader",
+ "positionThrottle": "Versneller",
+ "positionMotion": "Beweging ",
+ "positionArmed": "Gereed",
+ "positionAcceleration": "Versnelling",
+ "positionTemp": "Temperatuur",
+ "positionDeviceTemp": "Toestel Temperatuur",
+ "positionCoolantTemp": "Coolant Temperature",
+ "positionOperator": "Operateur",
+ "positionCommand": "Bevel",
+ "positionBlocked": "Geblokkeer",
+ "positionDtcs": "DTCs",
+ "positionObdSpeed": "OBD Spoed",
+ "positionObdOdometer": "OBD Odometer",
+ "positionDrivingTime": "Driving Time",
+ "positionDriverUniqueId": "Bestuurder unieke identiteit",
+ "positionCard": "Card",
+ "positionImage": "Beeld",
+ "positionVideo": "Video",
+ "positionAudio": "Audio",
+ "serverTitle": "Bedienerinstellings",
+ "serverZoom": "Zoom",
+ "serverRegistration": "Registrasie",
+ "serverReadonly": "Leesalleen",
+ "serverForceSettings": "Dwing instellings",
+ "serverAnnouncement": "Announcement",
+ "serverName": "Server Name",
+ "serverDescription": "Server Description",
+ "serverColorPrimary": "Color Primary",
+ "serverColorSecondary": "Color Secondary",
+ "serverLogo": "Logo Image",
+ "serverLogoInverted": "Inverted Logo Image",
+ "serverChangeDisable": "Disable Server Change",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "Kaart",
+ "mapActive": "Active Maps",
+ "mapOverlay": "Map Overlay",
+ "mapOverlayCustom": "Custom Overlay",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API Sleutel",
+ "mapOpenWeatherClouds": "OpenWeather Clouds",
+ "mapOpenWeatherPrecipitation": "OpenWeather Precipitation",
+ "mapOpenWeatherPressure": "OpenWeather Pressure",
+ "mapOpenWeatherWind": "OpenWeather Wind",
+ "mapOpenWeatherTemperature": "OpenWeather Temperature",
+ "mapLayer": "Kaartlaag",
+ "mapCustom": "Pasgemaak (XYZ)",
+ "mapCustomArcgis": "Pasgemaak (ArcGIS)",
+ "mapCustomLabel": "Pasgemaakte kaart",
+ "mapCarto": "Carto Basemaps",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Bing Kaart Sleutel",
+ "mapBingRoad": "Bing Kaart Pad",
+ "mapBingAerial": "Bing Kaart Lug",
+ "mapBingHybrid": "Bing Kaart Hybrid",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Yandex Kaart",
+ "mapYandexSat": "Yandex Satelliet",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox Streets",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox Outdoors",
+ "mapMapboxSatellite": "Mapbox Satellite",
+ "mapMapboxKey": "Mapbox Toegang Sleutel",
+ "mapMapTilerBasic": "MapTiler Basic",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "MapTiler API Sleutel",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ Toegang Sleutel",
+ "mapTomTomBasic": "TomTom Basic",
+ "mapTomTomFlow": "TomTom Traffic Flow",
+ "mapTomTomIncidents": "TomTom Traffic Incidents",
+ "mapTomTomKey": "TomTom API Sleutel",
+ "mapHereBasic": "Here Basic",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satellite",
+ "mapHereFlow": "Here Traffic Flow",
+ "mapHereKey": "Here API Sleutel",
+ "mapShapePolygon": "Veelhoek",
+ "mapShapeCircle": "Sirkel",
+ "mapShapePolyline": "Veelhoeklyn",
+ "mapLiveRoutes": "Lewendige Routes",
+ "mapDirection": "Show Direction",
+ "mapCurrentLocation": "Current Location",
+ "mapPoiLayer": "POI Vlak",
+ "mapClustering": "Markers Clustering",
+ "mapOnSelect": "Show Map on Selection",
+ "mapDefault": "Default Map",
+ "stateTitle": "Toestand",
+ "stateName": "Attribuut",
+ "stateValue": "Waarde",
+ "commandTitle": "Bevel",
+ "commandSend": "Stuur",
+ "commandSent": "Bevel gestuur",
+ "commandQueued": "Bevel in ry",
+ "commandUnit": "Eenheid",
+ "commandCustom": "Pasgemaakte bevel",
+ "commandDeviceIdentification": "Toestel Identifisering",
+ "commandPositionSingle": "Enkel Verslag",
+ "commandPositionPeriodic": "Periodiese Verslag",
+ "commandPositionStop": "Halt Verslag",
+ "commandEngineStop": "Engin Stop",
+ "commandEngineResume": "Engin Herbegin",
+ "commandAlarmArm": "Aktivering Alarm",
+ "commandAlarmDisarm": "Onaktivering Alarm",
+ "commandAlarmDismiss": "Dismiss Alarm",
+ "commandSetTimezone": "Stel Tydsone",
+ "commandRequestPhoto": "Versoek Foto",
+ "commandPowerOff": "Skakel Toestel Af",
+ "commandRebootDevice": "Herbegin Toestel",
+ "commandFactoryReset": "Factory Reset",
+ "commandSendSms": "Stuur SMS",
+ "commandSendUssd": "Stuur USSD",
+ "commandSosNumber": "Stel SOS Nommer",
+ "commandSilenceTime": "Stel Stil Tyd",
+ "commandSetPhonebook": "Stel Telefoon boek",
+ "commandVoiceMessage": "Stemboodskap",
+ "commandOutputControl": "Uitsetbeheer",
+ "commandVoiceMonitoring": "Stemmonitering",
+ "commandSetAgps": "Stel AGPS",
+ "commandSetIndicator": "Stel aanwyser",
+ "commandConfiguration": "Konfigureer",
+ "commandGetVersion": "Kry weergawe",
+ "commandFirmwareUpdate": "Opdateer firmware",
+ "commandSetConnection": "Stel Verbinding",
+ "commandSetOdometer": "Stel Odometer",
+ "commandGetModemStatus": "Kry Modemstatus",
+ "commandGetDeviceStatus": "Kry Toestel Gestel",
+ "commandSetSpeedLimit": "Stel spoedbeperking",
+ "commandModePowerSaving": "Power Saving Mode",
+ "commandModeDeepSleep": "Deep Sleep Mode",
+ "commandAlarmGeofence": "Set Geofence Alarm",
+ "commandAlarmBattery": "Stel Battery Alarm",
+ "commandAlarmSos": "Set SOS Alarm",
+ "commandAlarmRemove": "Set Remove Alarm",
+ "commandAlarmClock": "Set Clock Alarm",
+ "commandAlarmSpeed": "Stel Spoed Alarm",
+ "commandAlarmFall": "Set Fall Alarm",
+ "commandAlarmVibration": "Set Vibration Alarm",
+ "commandFrequency": "Frekwensie",
+ "commandTimezone": "Tydsone Verreken",
+ "commandMessage": "Boodskap",
+ "commandRadius": "Radius",
+ "commandEnable": "In Staat Stel",
+ "commandData": "Data",
+ "commandIndex": "Indeks",
+ "commandPhone": "Telefoon nommer",
+ "commandServer": "Bediener",
+ "commandPort": "Poort",
+ "eventAll": "Alle gebeure",
+ "eventDeviceOnline": "Status aanlyn",
+ "eventDeviceUnknown": "Status onbekend",
+ "eventDeviceOffline": "Status vanlyn",
+ "eventDeviceInactive": "Device inactive",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "Toestel in beweging",
+ "eventDeviceStopped": "Toestel tot stilstand gekom",
+ "eventDeviceOverspeed": "Spoedgrens oortree",
+ "eventDeviceFuelDrop": "Brandstofval",
+ "eventDeviceFuelIncrease": "Fuel increase",
+ "eventCommandResult": "Bevel resultaat",
+ "eventGeofenceEnter": "Geografiese omheining binnegegaan",
+ "eventGeofenceExit": "Geografiese omheining verlaat",
+ "eventAlarm": "Alarm",
+ "eventIgnitionOn": "Ontsteking aan",
+ "eventIgnitionOff": "Ontsteking af",
+ "eventMaintenance": "Onderhoud vereis",
+ "eventTextMessage": "Teks boodskap ontvang",
+ "eventDriverChanged": "Bestuurder geruil",
+ "eventMedia": "Media",
+ "eventsScrollToLast": "Blaai na laaste",
+ "eventsSoundEvents": "Sound Events",
+ "eventsSoundAlarms": "Sound Alarms",
+ "alarmGeneral": "Algemeen",
+ "alarmSos": "SOS",
+ "alarmVibration": "Vibrasie",
+ "alarmMovement": "Beweging",
+ "alarmLowspeed": "Lae Spoed",
+ "alarmOverspeed": "Spoed oortreding",
+ "alarmFallDown": "Val af",
+ "alarmLowPower": "Lae krag",
+ "alarmLowBattery": "Batteryvlak laag",
+ "alarmFault": "Fout",
+ "alarmPowerOff": "Krag af",
+ "alarmPowerOn": "Krag aan",
+ "alarmDoor": "Deur",
+ "alarmLock": "Sluit",
+ "alarmUnlock": "Oopsluit",
+ "alarmGeofence": "Geografiese omheining",
+ "alarmGeofenceEnter": "Geografiese omheining binnegegaan",
+ "alarmGeofenceExit": "Geografiese omheining verlaat",
+ "alarmGpsAntennaCut": "GPS Antenna gesny",
+ "alarmAccident": "Ongeluk",
+ "alarmTow": "Sleep",
+ "alarmIdle": "Luier",
+ "alarmHighRpm": "Hoë RPM",
+ "alarmHardAcceleration": "Harde Versnelling",
+ "alarmHardBraking": "Harde Remming",
+ "alarmHardCornering": "Harde Draai",
+ "alarmLaneChange": "Baanverandering",
+ "alarmFatigueDriving": "Moeg Bestuur",
+ "alarmPowerCut": "Kragonderbreking",
+ "alarmPowerRestored": "Krag herstel",
+ "alarmJamming": "Seinverstoring",
+ "alarmTemperature": "Temperatuur",
+ "alarmParking": "Parkeer",
+ "alarmBonnet": "Enjinkap",
+ "alarmFootBrake": "Voetrem",
+ "alarmFuelLeak": "Brandstoflek",
+ "alarmTampering": "Peuter",
+ "alarmRemoving": "Verwydering",
+ "notificationType": "Kennisgewing Soort",
+ "notificationAlways": "Alle Toestelle",
+ "notificationNotificators": "Kanale",
+ "notificatorCommand": "Command",
+ "notificatorWeb": "Web",
+ "notificatorMail": "Pos",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Replay",
+ "reportCombined": "Combined",
+ "reportRoute": "Roete",
+ "reportEvents": "Gebeure",
+ "reportTrips": "Ritte",
+ "reportStops": "Stops",
+ "reportSummary": "Opsomming",
+ "reportDaily": "Daaglikse Opsomming",
+ "reportChart": "Grafiek",
+ "reportConfigure": "Konfigureer",
+ "reportEventTypes": "Gebeurtenistipes",
+ "reportChartType": "Grafiek Soort",
+ "reportShowMarkers": "Wys Merkers",
+ "reportExport": "Uitvoer",
+ "reportEmail": "E-pos Verslag",
+ "reportSchedule": "Schedule",
+ "reportPeriod": "Periode",
+ "reportCustom": "Pasgemaak",
+ "reportToday": "Vandag",
+ "reportYesterday": "Gister",
+ "reportThisWeek": "Hierdie Week",
+ "reportPreviousWeek": "Vorige Week",
+ "reportThisMonth": "Hierdie Maand",
+ "reportPreviousMonth": "Vorige Maand",
+ "reportDeviceName": "Toestel Naam",
+ "reportAverageSpeed": "Gemiddelde Spoed",
+ "reportMaximumSpeed": "Maksimum Spoed",
+ "reportEngineHours": "Engin Ure",
+ "reportDuration": "Duur",
+ "reportStartDate": "Begindatum",
+ "reportStartTime": "Begin tyd",
+ "reportStartAddress": "Begin Adres",
+ "reportEndTime": "Einde tyd",
+ "reportEndAddress": "Einde Adres",
+ "reportSpentFuel": "Brandstof Gebruik",
+ "reportStartOdometer": "Odometer Begin",
+ "reportEndOdometer": "Odometer Einde",
+ "statisticsTitle": "Statistieke",
+ "statisticsCaptureTime": "Vang tyd vas",
+ "statisticsActiveUsers": "Aktiewe Gebruikers",
+ "statisticsActiveDevices": "Aktiewe Toestelle",
+ "statisticsRequests": "Versoeke",
+ "statisticsMessagesReceived": "Boodskappe ontvang",
+ "statisticsMessagesStored": "Boodskappe gestoor",
+ "statisticsGeocoder": "Geokodeerder Versoeke",
+ "statisticsGeolocation": "Geoligging Versoeke",
+ "categoryArrow": "Pyl",
+ "categoryDefault": "Verstrek",
+ "categoryAnimal": "Dier",
+ "categoryBicycle": "Fiets",
+ "categoryBoat": "Boot",
+ "categoryBus": "Bus",
+ "categoryCar": "Kar",
+ "categoryCamper": "Camper",
+ "categoryCrane": "Hyskraan",
+ "categoryHelicopter": "Helikopter",
+ "categoryMotorcycle": "Motorfiets",
+ "categoryOffroad": "Veeldoelig",
+ "categoryPerson": "Persoon",
+ "categoryPickup": "Bakkie",
+ "categoryPlane": "Vliegtuig",
+ "categoryShip": "Skip",
+ "categoryTractor": "Trekker",
+ "categoryTrain": "Trein",
+ "categoryTram": "Trem",
+ "categoryTrolleybus": "Trolleybus",
+ "categoryTruck": "Trok",
+ "categoryVan": "Paneelwa",
+ "categoryScooter": "Moffiebike",
+ "maintenanceStart": "Aan",
+ "maintenancePeriod": "Periode"
+} \ No newline at end of file
diff --git a/src/resources/l10n/ar.json b/src/resources/l10n/ar.json
new file mode 100644
index 00000000..19f4c9d8
--- /dev/null
+++ b/src/resources/l10n/ar.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "جاري التحميل...",
+ "sharedHide": "إخفاء",
+ "sharedSave": "حفظ",
+ "sharedUpload": "Upload",
+ "sharedSet": "ضبط",
+ "sharedCancel": "إلغاء",
+ "sharedCopy": "Copy",
+ "sharedAdd": "إضافة",
+ "sharedEdit": "تعديل",
+ "sharedRemove": "حذف",
+ "sharedRemoveConfirm": "حذف العنصر؟",
+ "sharedNoData": "No data",
+ "sharedSubject": "Subject",
+ "sharedYes": "نعم",
+ "sharedNo": "لا",
+ "sharedKm": "كم",
+ "sharedMi": "ميل",
+ "sharedNmi": "معرف العداد المحلي",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "عقدة",
+ "sharedKmh": "كم/ساعه",
+ "sharedMph": "ميل/ساعة",
+ "sharedHour": "ساعه",
+ "sharedMinute": "دقيقة",
+ "sharedSecond": "ثانية",
+ "sharedDays": "أيام",
+ "sharedHours": "ساعات",
+ "sharedMinutes": "دقائق",
+ "sharedDecimalDegrees": "درجات عشرية",
+ "sharedDegreesDecimalMinutes": "درجات عشرية للدقائق",
+ "sharedDegreesMinutesSeconds": "درجات دقائق للثواني",
+ "sharedName": "الاسم",
+ "sharedDescription": "الوصف",
+ "sharedSearch": "بحث",
+ "sharedIconScale": "Icon Scale",
+ "sharedGeofence": "السياج الجغرافي",
+ "sharedGeofences": "السياجات الجغرافية",
+ "sharedCreateGeofence": "Create Geofence",
+ "sharedNotifications": "التنبيهات",
+ "sharedNotification": "إشعار",
+ "sharedAttributes": "الخصائص",
+ "sharedAttribute": "خاصية",
+ "sharedDrivers": "السائقون",
+ "sharedDriver": "السائق",
+ "sharedArea": "منطقة",
+ "sharedSound": "صوت التنبيهات",
+ "sharedType": "النوع",
+ "sharedDistance": "المسافة",
+ "sharedHourAbbreviation": "س",
+ "sharedMinuteAbbreviation": "د",
+ "sharedSecondAbbreviation": "ث",
+ "sharedVoltAbbreviation": "جهد كهربائي",
+ "sharedLiterAbbreviation": "تيار",
+ "sharedGallonAbbreviation": "جالون",
+ "sharedLiter": "ليتر",
+ "sharedImpGallon": "جالون الإمبراطورية",
+ "sharedUsGallon": "جالون أمريكي",
+ "sharedLiterPerHourAbbreviation": "لتر/ساعة",
+ "sharedGetMapState": "الحصول على حالة الخريطة",
+ "sharedComputedAttribute": "السمة المحصاة",
+ "sharedComputedAttributes": "السمات المحصاة",
+ "sharedCheckComputedAttribute": "التحقق من السمة المحصاة",
+ "sharedExpression": "العبارة الجبرية",
+ "sharedDevice": "جهاز",
+ "sharedTest": "Test",
+ "sharedTestNotification": "ارسل تنبيه تجريبي",
+ "sharedTestNotificators": "Test Channels",
+ "sharedTestExpression": "Test Expression",
+ "sharedCalendar": "التقويم",
+ "sharedCalendars": "التقويمات",
+ "sharedFile": "ملف",
+ "sharedSearchDevices": "Search Devices",
+ "sharedSortBy": "Sort By",
+ "sharedFilterMap": "Filter on Map",
+ "sharedSelectFile": "حدد ملف",
+ "sharedPhone": "هاتف",
+ "sharedRequired": "اجباري",
+ "sharedPreferences": "التفضيلات",
+ "sharedPermissions": "الصلاحيات",
+ "sharedConnections": "الاتصالات",
+ "sharedExtra": "إضافي",
+ "sharedPrimary": "Primary",
+ "sharedSecondary": "Secondary",
+ "sharedTypeString": "نص",
+ "sharedTypeNumber": "رقم",
+ "sharedTypeBoolean": "منطقية",
+ "sharedTimezone": "منطقة زمنية",
+ "sharedInfoTitle": "معلومات",
+ "sharedSavedCommand": "أمر مخزن",
+ "sharedSavedCommands": "أوامر مخزنة",
+ "sharedNew": "جديد...",
+ "sharedShowAddress": "إظهار العنوان",
+ "sharedShowDetails": "تفاصيل إضافية",
+ "sharedDisabled": "معطل",
+ "sharedMaintenance": "صيانة",
+ "sharedDeviceAccumulators": "المراكم",
+ "sharedAlarms": "الإنذارات",
+ "sharedLocation": "الموقع",
+ "sharedImport": "استيراد",
+ "sharedColumns": "Columns",
+ "sharedDropzoneText": "Drag and drop a file here or click",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "Simple",
+ "calendarRecurrence": "Recurrence",
+ "calendarOnce": "Once",
+ "calendarDaily": "Daily",
+ "calendarWeekly": "Weekly",
+ "calendarMonthly": "Monthly",
+ "calendarDays": "Days",
+ "calendarSunday": "Sunday",
+ "calendarMonday": "Monday",
+ "calendarTuesday": "Tuesday",
+ "calendarWednesday": "Wednesday",
+ "calendarThursday": "Thursday",
+ "calendarFriday": "Friday",
+ "calendarSaturday": "Saturday",
+ "attributeShowGeofences": "Show Geofences",
+ "attributeSpeedLimit": "الحد الأقصى للسرعة",
+ "attributeFuelDropThreshold": "Fuel Drop Threshold",
+ "attributeFuelIncreaseThreshold": "Fuel Increase Threshold",
+ "attributePolylineDistance": "مسافة متعددة الخطوط",
+ "attributeReportIgnoreOdometer": "تقرير: إهمال عداد المسافة",
+ "attributeWebReportColor": "الويب: لون التقرير",
+ "attributeDevicePassword": "الرقم السري للجهاز",
+ "attributeDeviceImage": "Device Image",
+ "attributeDeviceInactivityStart": "بداية عدم فعالية الجهاز",
+ "attributeDeviceInactivityPeriod": "فترة عدم فعالية الجهاز",
+ "attributeProcessingCopyAttributes": "المعالجة: نسخ السمات",
+ "attributeColor": "اللون",
+ "attributeWebLiveRouteLength": "الويب: طول العرض المباشر للمسار",
+ "attributeWebSelectZoom": "الويب: التقريب عند التحديد",
+ "attributeWebMaxZoom": "ويب: التكبير الأقصى",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Pushover User Key",
+ "attributePushoverDeviceNames": "Pushover Device Names",
+ "attributeMailSmtpHost": "البريد: مستضيف الـ SMTP",
+ "attributeMailSmtpPort": "البريد: منفذ الـ SMTP",
+ "attributeMailSmtpStarttlsEnable": "البريد: تمكين SMTP SSL",
+ "attributeMailSmtpStarttlsRequired": "البريد: مطلوب SMTP SSL",
+ "attributeMailSmtpSslEnable": "البريد: تمكين SMTP SSL",
+ "attributeMailSmtpSslTrust": "البريد: وثوق بـ SMTP SSL",
+ "attributeMailSmtpSslProtocols": "البريد: بروتوكولات SMTP SSL",
+ "attributeMailSmtpFrom": "البريد: SMTP من",
+ "attributeMailSmtpAuth": "البريد: تفعيل مصادقة SMTP",
+ "attributeMailSmtpUsername": "البريد: أسم مستخدم الـ SMTP",
+ "attributeMailSmtpPassword": "البريد: كلمة مرور الـ SMTP",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: Disable Attributes",
+ "attributeUiDisableGroups": "UI: Disable Groups",
+ "attributeUiDisableEvents": "واجهة المستخدم: تعطيل الأحداث",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "واجهة المستخدم: تعطيل السائقين",
+ "attributeUiDisableComputedAttributes": "واجهة المستخدم: تعطيل السمات المحصاة",
+ "attributeUiDisableCalendars": "واجهة المستخدم: تعطيل التقويمات",
+ "attributeUiDisableMaintenance": "واجهة المستخدم: تعطيل الصيانة",
+ "attributeUiHidePositionAttributes": "واجهة المستخدم: إخفاء سمات الموقع",
+ "attributeUiDisableLoginLanguage": "UI: Disable Login Language",
+ "attributeNotificationTokens": "رموز الإشعارات",
+ "attributePopupInfo": "Popup Info",
+ "errorTitle": "خطأ",
+ "errorGeneral": "معطيات غير صحيحة او مخالفة للصلاحيات",
+ "errorConnection": "خطأ في الاتصال",
+ "errorSocket": "خطأ في اتصال حزم البيانات",
+ "errorZero": "لا يمكن ان تكون صفر",
+ "userEmail": "البريد الالكتروني",
+ "userPassword": "كلمة المرور",
+ "userAdmin": "مدير النظام",
+ "userRemember": "تذكر",
+ "userExpirationTime": "انتهاء الصلاحية",
+ "userDeviceLimit": "الحد الأقصى للأجهزة",
+ "userUserLimit": "الحد الأقصى للمستخدمين",
+ "userDeviceReadonly": "جهاز للقراءة فقط",
+ "userLimitCommands": "تقييد الاوامر",
+ "userDisableReports": "Disable Reports",
+ "userFixedEmail": "No Email Change",
+ "userToken": "رمز",
+ "userDeleteAccount": "Delete Account",
+ "userTemporary": "Temporary",
+ "loginTitle": "تسجيل الدخول",
+ "loginLanguage": "اللغة",
+ "loginReset": "Reset Password",
+ "loginRegister": "تسجيل جديد",
+ "loginLogin": "تسجيل الدخول",
+ "loginOpenId": "Login with OpenID",
+ "loginFailed": "البريد الالكتروني أو كلمة المرور غير صحيحة",
+ "loginCreated": "تم تسجيل مستخدم جديد",
+ "loginResetSuccess": "Check your email",
+ "loginUpdateSuccess": "New password is set",
+ "loginLogout": "تسجيل الخروج",
+ "loginLogo": "الشعار",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "الأجهزة والحالة",
+ "deviceSelected": "Selected Device",
+ "deviceTitle": "الأجهزة",
+ "devicePrimaryInfo": "Device Title",
+ "deviceSecondaryInfo": "Device Detail",
+ "deviceIdentifier": "المعرف",
+ "deviceModel": "الطراز",
+ "deviceContact": "بيانات التواصل",
+ "deviceCategory": "الصنف",
+ "deviceLastUpdate": "آخر تحديث",
+ "deviceCommand": "أمر",
+ "deviceFollow": "متابعة",
+ "deviceTotalDistance": "المسافة الكلية",
+ "deviceStatus": "الحالة",
+ "deviceStatusOnline": "متصل",
+ "deviceStatusOffline": "غير متصل",
+ "deviceStatusUnknown": "غير معروف",
+ "deviceRegisterFirst": "Register your first device",
+ "deviceIdentifierHelp": "IMEI, serial number or other id. It has to match the identifier device reports to the server.",
+ "deviceShare": "Share Device",
+ "groupDialog": "مجموعة",
+ "groupParent": "مجموعة",
+ "groupNoGroup": "لا توجد مجموعة",
+ "settingsTitle": "الإعدادات",
+ "settingsUser": "الحساب",
+ "settingsGroups": "المجموعات",
+ "settingsServer": "الخادم",
+ "settingsUsers": "المستخدمون",
+ "settingsDistanceUnit": "وحدة المسافة",
+ "settingsAltitudeUnit": "Altitude Unit",
+ "settingsSpeedUnit": "وحدة السرعة",
+ "settingsVolumeUnit": "وحدة الحجم",
+ "settingsTwelveHourFormat": "صيغة 12-ساعة",
+ "settingsCoordinateFormat": "صيغة الإحداثيات",
+ "settingsServerVersion": "Server Version",
+ "settingsAppVersion": "App Version",
+ "settingsConnection": "Connection",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "التقارير",
+ "reportScheduled": "Scheduled Reports",
+ "reportDevice": "الجهاز",
+ "reportGroup": "المجموعة",
+ "reportFrom": "من",
+ "reportTo": "الى",
+ "reportShow": "إظهار",
+ "reportClear": "تفريغ الحقول",
+ "linkGoogleMaps": "Google Maps",
+ "linkAppleMaps": "Apple Maps",
+ "linkStreetView": "Street View",
+ "positionFixTime": "Fix Time",
+ "positionDeviceTime": "Device Time",
+ "positionServerTime": "Server Time",
+ "positionValid": "صالح",
+ "positionAccuracy": "الدقة",
+ "positionLatitude": "خط العرض",
+ "positionLongitude": "خط الطول",
+ "positionAltitude": "ارتفاع عن سطح البحر",
+ "positionSpeed": "السرعة",
+ "positionCourse": "الإتجاه",
+ "positionAddress": "العنوان",
+ "positionProtocol": "بروتوكول",
+ "positionDistance": "المسافة",
+ "positionRpm": "دورة في الدقيقة",
+ "positionFuel": "الوقود",
+ "positionPower": "الطاقة",
+ "positionBattery": "بطارية",
+ "positionRaw": "خام",
+ "positionIndex": "قائمة",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "أقمار صناعية",
+ "positionSatVisible": "الأقمار الصناعية المرئية",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "التجوال",
+ "positionEvent": "حدث",
+ "positionAlarm": "إنذار",
+ "positionStatus": "الحالة",
+ "positionOdometer": "عداد المسافة",
+ "positionServiceOdometer": "عداد مسافة الصيانة",
+ "positionTripOdometer": "عداد مسافة الرحلة",
+ "positionHours": "الساعات",
+ "positionSteps": "الخطوات",
+ "positionInput": "مدخلات",
+ "positionHeartRate": "Heart Rate",
+ "positionOutput": "نواتج",
+ "positionBatteryLevel": "مستوى البطارية",
+ "positionFuelConsumption": "الوقود المستهلك",
+ "positionRfid": "RFID",
+ "positionVersionFw": "نسخة البرنامج",
+ "positionVersionHw": "نسخة الجهاز",
+ "positionIgnition": "إشعال",
+ "positionFlags": "أعلام",
+ "positionCharge": "شحن",
+ "positionIp": "عنوان البروتوكول",
+ "positionArchive": "أرشفة",
+ "positionVin": "رقم الهيكل",
+ "positionApproximate": "تقريبي",
+ "positionThrottle": "الخانق",
+ "positionMotion": "الحركة",
+ "positionArmed": "الانذار مفعل",
+ "positionAcceleration": "التسارع",
+ "positionTemp": "Temperature",
+ "positionDeviceTemp": "حرارة الجهاز",
+ "positionCoolantTemp": "Coolant Temperature",
+ "positionOperator": "المشغل",
+ "positionCommand": "أمر",
+ "positionBlocked": "محظور",
+ "positionDtcs": "DTCs",
+ "positionObdSpeed": "سرعة OBD",
+ "positionObdOdometer": "عداد مسافة OBD",
+ "positionDrivingTime": "Driving Time",
+ "positionDriverUniqueId": "معرف فريد للسائق",
+ "positionCard": "Card",
+ "positionImage": "صورة",
+ "positionVideo": "Video",
+ "positionAudio": "صوت",
+ "serverTitle": "اعدادت الخادم",
+ "serverZoom": "تقريب",
+ "serverRegistration": "تسجيل",
+ "serverReadonly": "متابعة فقط",
+ "serverForceSettings": "إجبار الإعدادات",
+ "serverAnnouncement": "الإعلانات",
+ "serverName": "Server Name",
+ "serverDescription": "Server Description",
+ "serverColorPrimary": "Color Primary",
+ "serverColorSecondary": "Color Secondary",
+ "serverLogo": "Logo Image",
+ "serverLogoInverted": "Inverted Logo Image",
+ "serverChangeDisable": "Disable Server Change",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "خريطة",
+ "mapActive": "Active Maps",
+ "mapOverlay": "Map Overlay",
+ "mapOverlayCustom": "Custom Overlay",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API Key",
+ "mapOpenWeatherClouds": "OpenWeather Clouds",
+ "mapOpenWeatherPrecipitation": "OpenWeather Precipitation",
+ "mapOpenWeatherPressure": "OpenWeather Pressure",
+ "mapOpenWeatherWind": "OpenWeather Wind",
+ "mapOpenWeatherTemperature": "OpenWeather Temperature",
+ "mapLayer": "طبقة الخريطة",
+ "mapCustom": "مخصص (XYZ)",
+ "mapCustomArcgis": "مخصص (ArcGIS)",
+ "mapCustomLabel": "خريطة مخصصة",
+ "mapCarto": "خرائط Carto Base",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "مفتاح خرائط Bing",
+ "mapBingRoad": "خرائط الطرق Bing",
+ "mapBingAerial": "خرائط جوية Bing",
+ "mapBingHybrid": "خرائط هجينة Bing",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "خرائط Yandex",
+ "mapYandexSat": "خرائط صورية Yandex",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox للشوارع",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox بالخارج",
+ "mapMapboxSatellite": "Mapbox بالقمر الصناعي",
+ "mapMapboxKey": "Mapbox Access Token",
+ "mapMapTilerBasic": "MapTiler Basic",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "MapTiler API Key",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ Access Token",
+ "mapTomTomBasic": "TomTom Basic",
+ "mapTomTomFlow": "TomTom Traffic Flow",
+ "mapTomTomIncidents": "TomTom Traffic Incidents",
+ "mapTomTomKey": "TomTom API Key",
+ "mapHereBasic": "Here Basic",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satellite",
+ "mapHereFlow": "Here Traffic Flow",
+ "mapHereKey": "Here API Key",
+ "mapShapePolygon": "مضلع",
+ "mapShapeCircle": "دائرة",
+ "mapShapePolyline": "متعدد الضلوع",
+ "mapLiveRoutes": "مسار حي",
+ "mapDirection": "Show Direction",
+ "mapCurrentLocation": "Current Location",
+ "mapPoiLayer": "طبقة POI نقاط الإهتمام",
+ "mapClustering": "Markers Clustering",
+ "mapOnSelect": "Show Map on Selection",
+ "mapDefault": "Default Map",
+ "stateTitle": "حالة",
+ "stateName": "خاصية",
+ "stateValue": "قيمة",
+ "commandTitle": "أمر",
+ "commandSend": "ارسال",
+ "commandSent": "تم ارسال الامر",
+ "commandQueued": "الامر في قائمة الانتظار",
+ "commandUnit": "وحدة",
+ "commandCustom": "أمر مخصص",
+ "commandDeviceIdentification": "تعريف الجهاز",
+ "commandPositionSingle": "تقرير مفرد",
+ "commandPositionPeriodic": "تقارير دورية",
+ "commandPositionStop": "ايقاف التقرير",
+ "commandEngineStop": "ايقاف المحرك",
+ "commandEngineResume": "استئناف المحرك",
+ "commandAlarmArm": "بدء تشغيل المنبه",
+ "commandAlarmDisarm": "تعطيل المنبه",
+ "commandAlarmDismiss": "إيقاف المنبه",
+ "commandSetTimezone": "حدد المنطقة الزمنية",
+ "commandRequestPhoto": "اطلب صورة",
+ "commandPowerOff": "اطفاء الجهاز",
+ "commandRebootDevice": "أعد تشغيل الجهاز",
+ "commandFactoryReset": "إعادة ضبط المصنع",
+ "commandSendSms": "إرسال رسالة قصيرة",
+ "commandSendUssd": "إرسال الـ USSD",
+ "commandSosNumber": "ظبط رقم الطوارئ",
+ "commandSilenceTime": "حدد التوقيت الصامت",
+ "commandSetPhonebook": "ضبط سجل الهاتف",
+ "commandVoiceMessage": "رسالة صوتية",
+ "commandOutputControl": "التحكم بالإخراج",
+ "commandVoiceMonitoring": "مراقبة صوتية",
+ "commandSetAgps": "تعيين GPS المساعد",
+ "commandSetIndicator": "تعيين المؤشر",
+ "commandConfiguration": "ضبط",
+ "commandGetVersion": "الحصول على الإصدار",
+ "commandFirmwareUpdate": "تحديث البرنامج المشغل",
+ "commandSetConnection": "تعيين اتصال",
+ "commandSetOdometer": "تعيين عداد المسافة",
+ "commandGetModemStatus": "الحصول على حالة المودم",
+ "commandGetDeviceStatus": "الحصول على حالة الجهاز",
+ "commandSetSpeedLimit": "ضبط حد السرعة",
+ "commandModePowerSaving": "Power Saving Mode",
+ "commandModeDeepSleep": "Deep Sleep Mode",
+ "commandAlarmGeofence": "Set Geofence Alarm",
+ "commandAlarmBattery": "Set Battery Alarm",
+ "commandAlarmSos": "Set SOS Alarm",
+ "commandAlarmRemove": "Set Remove Alarm",
+ "commandAlarmClock": "Set Clock Alarm",
+ "commandAlarmSpeed": "Set Speed Alarm",
+ "commandAlarmFall": "Set Fall Alarm",
+ "commandAlarmVibration": "Set Vibration Alarm",
+ "commandFrequency": "التكرر",
+ "commandTimezone": "إزاحة المنطقة الزمنية",
+ "commandMessage": "رسالة",
+ "commandRadius": "القطر",
+ "commandEnable": "تفعيل",
+ "commandData": "بيانات",
+ "commandIndex": "قائمة",
+ "commandPhone": "رقم الهاتف",
+ "commandServer": "خادم",
+ "commandPort": "منفذ",
+ "eventAll": "جميع الأحداث",
+ "eventDeviceOnline": "الحالة متصل",
+ "eventDeviceUnknown": "الحالة غير معروف",
+ "eventDeviceOffline": "الحالة غير متصل",
+ "eventDeviceInactive": "الجهاز غير مفعل",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "الجهاز يتحرك",
+ "eventDeviceStopped": "الجهاز متوقف",
+ "eventDeviceOverspeed": "تجاوز للسرعة",
+ "eventDeviceFuelDrop": "انخفاض الوقود",
+ "eventDeviceFuelIncrease": "Fuel increase",
+ "eventCommandResult": "نتيجة الأمر",
+ "eventGeofenceEnter": "دخول المنطقة الجغرافية",
+ "eventGeofenceExit": "خروج من المنطقة الجغرافية",
+ "eventAlarm": "إنذار",
+ "eventIgnitionOn": "اشعال المحرك",
+ "eventIgnitionOff": "ايقاف المحرك",
+ "eventMaintenance": "حان وقت الصيانة",
+ "eventTextMessage": "تم استلام رسالة نصية",
+ "eventDriverChanged": "تم تغيير السائق",
+ "eventMedia": "Media",
+ "eventsScrollToLast": "الانتقال للأخير",
+ "eventsSoundEvents": "Sound Events",
+ "eventsSoundAlarms": "Sound Alarms",
+ "alarmGeneral": "عام",
+ "alarmSos": "استغاثة",
+ "alarmVibration": "اهتزاز",
+ "alarmMovement": "حركة",
+ "alarmLowspeed": "سرعة منخفضة",
+ "alarmOverspeed": "تجاوز السرعة",
+ "alarmFallDown": "سقوط",
+ "alarmLowPower": "طاقة منخفضة",
+ "alarmLowBattery": "انخفاض البطارية",
+ "alarmFault": "خلل",
+ "alarmPowerOff": "اطفاء",
+ "alarmPowerOn": "تشغيل",
+ "alarmDoor": "باب",
+ "alarmLock": "اقفال",
+ "alarmUnlock": "فتح",
+ "alarmGeofence": "سياج جغرافي",
+ "alarmGeofenceEnter": "دخول السياج الجغرافي",
+ "alarmGeofenceExit": "الخروج من السياج الجغرافي",
+ "alarmGpsAntennaCut": "انقطاع هوائي القمر الصناعي",
+ "alarmAccident": "حادث",
+ "alarmTow": "جر",
+ "alarmIdle": "خامل",
+ "alarmHighRpm": "دوران في الدقيقة عالٍ",
+ "alarmHardAcceleration": "تسارع شديد",
+ "alarmHardBraking": "كبح فرامل شديد",
+ "alarmHardCornering": "انعطاف شديد",
+ "alarmLaneChange": "تغيير الممر",
+ "alarmFatigueDriving": "قيادة في حالة تعب",
+ "alarmPowerCut": "انقطاء الطاقة",
+ "alarmPowerRestored": "استعادة الطاقة",
+ "alarmJamming": "التشويش",
+ "alarmTemperature": "الحرارة",
+ "alarmParking": "اصطفاف",
+ "alarmBonnet": "غطاء محرك السيارة",
+ "alarmFootBrake": "فرامل القدم",
+ "alarmFuelLeak": "تسرب وقود",
+ "alarmTampering": "عبث وتلاعب",
+ "alarmRemoving": "الازالة",
+ "notificationType": "نوع التنبيه",
+ "notificationAlways": "جميع الاجهزة",
+ "notificationNotificators": "قنواة",
+ "notificatorCommand": "Command",
+ "notificatorWeb": "ويب",
+ "notificatorMail": "البريد",
+ "notificatorSms": "رسائل قصيرة",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "إعادة تشغيل",
+ "reportCombined": "Combined",
+ "reportRoute": "مسار",
+ "reportEvents": "الأحداث",
+ "reportTrips": "رحلات",
+ "reportStops": "وقفات",
+ "reportSummary": "ملخص",
+ "reportDaily": "الملخص اليومي",
+ "reportChart": "رسم بياني",
+ "reportConfigure": "تهيئة",
+ "reportEventTypes": "أنواع الأحداث",
+ "reportChartType": "نوع الرسم البياني",
+ "reportShowMarkers": "إظهار العلامات",
+ "reportExport": "تصدير",
+ "reportEmail": "تقرير بالبريد",
+ "reportSchedule": "Schedule",
+ "reportPeriod": "الفترة",
+ "reportCustom": "مخصص",
+ "reportToday": "اليوم",
+ "reportYesterday": "أمس",
+ "reportThisWeek": "هذا الاسبوع",
+ "reportPreviousWeek": "الاسبوع السابق",
+ "reportThisMonth": "هذا الشهر",
+ "reportPreviousMonth": "الشهر السابق",
+ "reportDeviceName": "إسم الجهاز",
+ "reportAverageSpeed": "متوسط السرعة",
+ "reportMaximumSpeed": "السرعة القصوى",
+ "reportEngineHours": "ساعات عمل المحرك",
+ "reportDuration": "المدة الزمنية",
+ "reportStartDate": "تاريخ البدء",
+ "reportStartTime": "وقت البدء",
+ "reportStartAddress": "عنوان البدء",
+ "reportEndTime": "وقت النهاية",
+ "reportEndAddress": "عنوان النهاية",
+ "reportSpentFuel": "الوقود المستنفد",
+ "reportStartOdometer": "بدء عداد المسافة",
+ "reportEndOdometer": "إنهاء Odometer",
+ "statisticsTitle": "إحصائيات",
+ "statisticsCaptureTime": "وقت الالتقاط",
+ "statisticsActiveUsers": "المستخدمون النشطون",
+ "statisticsActiveDevices": "الاجهزة النشطة",
+ "statisticsRequests": "طلبات",
+ "statisticsMessagesReceived": "الرسائل المستلمة",
+ "statisticsMessagesStored": "الرسائل المخزنة",
+ "statisticsGeocoder": "طلبات Geocoder",
+ "statisticsGeolocation": "طلبات تحديد الموقع الجغرافي",
+ "categoryArrow": "سهم",
+ "categoryDefault": "افتراضي",
+ "categoryAnimal": "حيوان",
+ "categoryBicycle": "دراجة هوائية",
+ "categoryBoat": "قارب",
+ "categoryBus": "حافلة",
+ "categoryCar": "سيارة",
+ "categoryCamper": "Camper",
+ "categoryCrane": "رافعة ونش",
+ "categoryHelicopter": "طائرة مروحية",
+ "categoryMotorcycle": "دراجة نارية",
+ "categoryOffroad": "رباعية",
+ "categoryPerson": "شخص",
+ "categoryPickup": "شاحنة صغيرة",
+ "categoryPlane": "طائرة",
+ "categoryShip": "سفينة",
+ "categoryTractor": "جرار زراعى",
+ "categoryTrain": "قطار",
+ "categoryTram": "Tram",
+ "categoryTrolleybus": "Trolleybus",
+ "categoryTruck": "شاحنة",
+ "categoryVan": "نقل",
+ "categoryScooter": "سكوتر",
+ "maintenanceStart": "بداية",
+ "maintenancePeriod": "فترة"
+} \ No newline at end of file
diff --git a/src/resources/l10n/az.json b/src/resources/l10n/az.json
new file mode 100644
index 00000000..02430a47
--- /dev/null
+++ b/src/resources/l10n/az.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "Yükləmə...",
+ "sharedHide": "Gizlətmək",
+ "sharedSave": "Yadda saxlamaq",
+ "sharedUpload": "Upload",
+ "sharedSet": "Qurmaq",
+ "sharedCancel": "Ləğv etmək",
+ "sharedCopy": "Copy",
+ "sharedAdd": "Əlavə etmək",
+ "sharedEdit": "Redaktə etmək",
+ "sharedRemove": "Silmək",
+ "sharedRemoveConfirm": "Elementi silmək?",
+ "sharedNoData": "No data",
+ "sharedSubject": "Subject",
+ "sharedYes": "Yes",
+ "sharedNo": "No",
+ "sharedKm": "km",
+ "sharedMi": "mil",
+ "sharedNmi": "m.mil",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "Düyün",
+ "sharedKmh": "km/saat",
+ "sharedMph": "mil/saat",
+ "sharedHour": "Saat",
+ "sharedMinute": "Dəqiqə",
+ "sharedSecond": "Saniyə",
+ "sharedDays": "Günlər",
+ "sharedHours": "Saatlar",
+ "sharedMinutes": "Dəqiqələr",
+ "sharedDecimalDegrees": "Onluq dərəcələr",
+ "sharedDegreesDecimalMinutes": "Dərəcələr Onluq Dəqiqələr",
+ "sharedDegreesMinutesSeconds": "Dərəcələr Dəqiqələr Saniyələr",
+ "sharedName": "Ad",
+ "sharedDescription": "Təsvir",
+ "sharedSearch": "Axtarış",
+ "sharedIconScale": "Icon Scale",
+ "sharedGeofence": "Geozona",
+ "sharedGeofences": "Geozonalar",
+ "sharedCreateGeofence": "Create Geofence",
+ "sharedNotifications": "Bildirişlər",
+ "sharedNotification": "Bildirişlər",
+ "sharedAttributes": "Atributlar",
+ "sharedAttribute": "Atribut",
+ "sharedDrivers": "Sürücülər",
+ "sharedDriver": "Sürücü",
+ "sharedArea": "Ərazi",
+ "sharedSound": "Səsli xəbərdarlıq",
+ "sharedType": "Tip",
+ "sharedDistance": "Məsafə",
+ "sharedHourAbbreviation": "h",
+ "sharedMinuteAbbreviation": "dəq",
+ "sharedSecondAbbreviation": "s",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "l",
+ "sharedGallonAbbreviation": "gal",
+ "sharedLiter": "Liter",
+ "sharedImpGallon": "Imp. Gallon",
+ "sharedUsGallon": "U.S. Gallon",
+ "sharedLiterPerHourAbbreviation": "litr/saat",
+ "sharedGetMapState": "Xəritənin vəziyyətini almaq",
+ "sharedComputedAttribute": "Hesablanan atribut",
+ "sharedComputedAttributes": "Hesablanan atributlar",
+ "sharedCheckComputedAttribute": "Hesablanan atributu yoxlamaq",
+ "sharedExpression": "İfadə",
+ "sharedDevice": "Cihaz",
+ "sharedTest": "Test",
+ "sharedTestNotification": "Test bildirişi göndərmək",
+ "sharedTestNotificators": "Test Channels",
+ "sharedTestExpression": "Test Expression",
+ "sharedCalendar": "Təqvim",
+ "sharedCalendars": "Təqvimlər",
+ "sharedFile": "Fayl",
+ "sharedSearchDevices": "Search Devices",
+ "sharedSortBy": "Sort By",
+ "sharedFilterMap": "Filter on Map",
+ "sharedSelectFile": "Fayl seçin",
+ "sharedPhone": "Telefon",
+ "sharedRequired": "Məcburi",
+ "sharedPreferences": "Tənzimləmələr",
+ "sharedPermissions": "İzinlər",
+ "sharedConnections": "Connections",
+ "sharedExtra": "Əlavə",
+ "sharedPrimary": "Primary",
+ "sharedSecondary": "Secondary",
+ "sharedTypeString": "Sətir",
+ "sharedTypeNumber": "Rəqəm",
+ "sharedTypeBoolean": "Məntiqi dəyəri",
+ "sharedTimezone": "Saat qurşağı",
+ "sharedInfoTitle": "Məlumat",
+ "sharedSavedCommand": "Tarif Saxlamish",
+ "sharedSavedCommands": "Tarifler Saxlamish",
+ "sharedNew": "yeni",
+ "sharedShowAddress": "şou ünvan",
+ "sharedShowDetails": "More Details",
+ "sharedDisabled": "şikəst",
+ "sharedMaintenance": "təmir",
+ "sharedDeviceAccumulators": "akkumulyatorlar",
+ "sharedAlarms": "Alarms",
+ "sharedLocation": "Location",
+ "sharedImport": "Import",
+ "sharedColumns": "Columns",
+ "sharedDropzoneText": "Drag and drop a file here or click",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "Simple",
+ "calendarRecurrence": "Recurrence",
+ "calendarOnce": "Once",
+ "calendarDaily": "Daily",
+ "calendarWeekly": "Weekly",
+ "calendarMonthly": "Monthly",
+ "calendarDays": "Days",
+ "calendarSunday": "Sunday",
+ "calendarMonday": "Monday",
+ "calendarTuesday": "Tuesday",
+ "calendarWednesday": "Wednesday",
+ "calendarThursday": "Thursday",
+ "calendarFriday": "Friday",
+ "calendarSaturday": "Saturday",
+ "attributeShowGeofences": "Show Geofences",
+ "attributeSpeedLimit": "Sürət həddi",
+ "attributeFuelDropThreshold": "Fuel Drop Threshold",
+ "attributeFuelIncreaseThreshold": "Fuel Increase Threshold",
+ "attributePolylineDistance": "Polyline məsafə",
+ "attributeReportIgnoreOdometer": "Hesabat: Odometri rədd etməki",
+ "attributeWebReportColor": "Veb: Hesabat rəngi",
+ "attributeDevicePassword": "Cihazın şifrəsi",
+ "attributeDeviceImage": "Device Image",
+ "attributeDeviceInactivityStart": "Device Inactivity Start",
+ "attributeDeviceInactivityPeriod": "Device Inactivity Period",
+ "attributeProcessingCopyAttributes": "Emal: Atributların üzünün köçürülməsi",
+ "attributeColor": "Rəng",
+ "attributeWebLiveRouteLength": "Veb: Onlayn marşrutun uzunluğu",
+ "attributeWebSelectZoom": "Veb: Seçim zamanı böyütmə",
+ "attributeWebMaxZoom": "Veb: Maksimum böyütmə",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Pushover User Key",
+ "attributePushoverDeviceNames": "Pushover Device Names",
+ "attributeMailSmtpHost": "Poçt: SMTP Hostu",
+ "attributeMailSmtpPort": "Poçt: SMTP Portu",
+ "attributeMailSmtpStarttlsEnable": "Poçt: SMTP STARTTLS-i aktiv etmək",
+ "attributeMailSmtpStarttlsRequired": "Poçt: SMTP STARTTLS tələb olunur",
+ "attributeMailSmtpSslEnable": "Poçt: SMTP SSL-i aktiv etmək",
+ "attributeMailSmtpSslTrust": "Poçt: SMTP SSL etibar",
+ "attributeMailSmtpSslProtocols": "Poçt: SMTP SSL protokolları",
+ "attributeMailSmtpFrom": "Poçt: SMTP göndərən",
+ "attributeMailSmtpAuth": "Poçt: SMTP autentifikasiyasını aktiv etmək",
+ "attributeMailSmtpUsername": "Poçt: SMTP istifadəçi adı",
+ "attributeMailSmtpPassword": "Poçt: SMTP şifrə",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: Disable Attributes",
+ "attributeUiDisableGroups": "UI: Disable Groups",
+ "attributeUiDisableEvents": "UI: şikəst vaqiəler",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "UI: Sürücüləri deaktiv etmək",
+ "attributeUiDisableComputedAttributes": "UI: Hesablanılan atributları deaktiv etmək",
+ "attributeUiDisableCalendars": "UI: Təqvimləri deaktiv etmək",
+ "attributeUiDisableMaintenance": "UI: Disable Maintenance",
+ "attributeUiHidePositionAttributes": "UI : Vəzifə xüsusiyyətlərini gizləyin",
+ "attributeUiDisableLoginLanguage": "UI: Disable Login Language",
+ "attributeNotificationTokens": "Notification Tokens",
+ "attributePopupInfo": "Popup Info",
+ "errorTitle": "Xəta",
+ "errorGeneral": "Yanlış parametrlər və ya məhdudiyyətlərin pozulması",
+ "errorConnection": "Bağlantı xətası",
+ "errorSocket": "Veb socket bağlantı xətası",
+ "errorZero": "Sıfır ola bilməz",
+ "userEmail": "E-poçt",
+ "userPassword": "Şifrə",
+ "userAdmin": "Administrator",
+ "userRemember": "Yadda saxla",
+ "userExpirationTime": "Bitmə vaxtı",
+ "userDeviceLimit": "Cihaz məhdudiyyəti",
+ "userUserLimit": "İstifadəçilərin məhdudiyyəti",
+ "userDeviceReadonly": "Sadəcə cihaza baxış",
+ "userLimitCommands": "Komandaları məhdudlaşdırın",
+ "userDisableReports": "Disable Reports",
+ "userFixedEmail": "No Email Change",
+ "userToken": "Açar",
+ "userDeleteAccount": "Delete Account",
+ "userTemporary": "Temporary",
+ "loginTitle": "Giriş",
+ "loginLanguage": "Dil",
+ "loginReset": "Reset Password",
+ "loginRegister": "Qeydiyyat",
+ "loginLogin": "Giriş",
+ "loginOpenId": "Login with OpenID",
+ "loginFailed": "Yanlış e-poçt ünvanı və ya şifrə",
+ "loginCreated": "Yeni istifadəçi qeydiyyatdan keçmişdir",
+ "loginResetSuccess": "Check your email",
+ "loginUpdateSuccess": "New password is set",
+ "loginLogout": "Çıxış",
+ "loginLogo": "Giriş",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "Cihaz və vəziyyəti",
+ "deviceSelected": "Selected Device",
+ "deviceTitle": "Cihaz",
+ "devicePrimaryInfo": "Device Title",
+ "deviceSecondaryInfo": "Device Detail",
+ "deviceIdentifier": "İdentifikator",
+ "deviceModel": "Model",
+ "deviceContact": "Kontakt",
+ "deviceCategory": "Kateqoriya",
+ "deviceLastUpdate": "Son yenilənmə",
+ "deviceCommand": "Komanda",
+ "deviceFollow": "Əməl etmək",
+ "deviceTotalDistance": "Ümumi hərəkət məsafəsi",
+ "deviceStatus": "Status",
+ "deviceStatusOnline": "Onlayn",
+ "deviceStatusOffline": "Offlayn",
+ "deviceStatusUnknown": "Naməlum",
+ "deviceRegisterFirst": "Register your first device",
+ "deviceIdentifierHelp": "IMEI, serial number or other id. It has to match the identifier device reports to the server.",
+ "deviceShare": "Share Device",
+ "groupDialog": "Qrup",
+ "groupParent": "Qrup",
+ "groupNoGroup": "Qrupsuz",
+ "settingsTitle": "Tənzimləmələr",
+ "settingsUser": "İstifadəçi Hesabı",
+ "settingsGroups": "Qruplar",
+ "settingsServer": "Server",
+ "settingsUsers": "İstifadəçilər",
+ "settingsDistanceUnit": "Məsafə vahidi",
+ "settingsAltitudeUnit": "Altitude Unit",
+ "settingsSpeedUnit": "Sürət",
+ "settingsVolumeUnit": "Həcm vahidi",
+ "settingsTwelveHourFormat": "12 saatlıq format",
+ "settingsCoordinateFormat": "Koordinatlar formatı",
+ "settingsServerVersion": "Server Version",
+ "settingsAppVersion": "App Version",
+ "settingsConnection": "Connection",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "Hesabatlar",
+ "reportScheduled": "Scheduled Reports",
+ "reportDevice": "Cihaz",
+ "reportGroup": "Qrup",
+ "reportFrom": "dan",
+ "reportTo": "dək",
+ "reportShow": "Göstərmək",
+ "reportClear": "Təmizləmək",
+ "linkGoogleMaps": "Google Maps",
+ "linkAppleMaps": "Apple Maps",
+ "linkStreetView": "Street View",
+ "positionFixTime": "Fix Time",
+ "positionDeviceTime": "Device Time",
+ "positionServerTime": "Server Time",
+ "positionValid": "Düzgünlük",
+ "positionAccuracy": "Dəqiqlik",
+ "positionLatitude": "En dairəsi",
+ "positionLongitude": "Uzunluq dairəsi",
+ "positionAltitude": "Hündürlük",
+ "positionSpeed": "Sürət",
+ "positionCourse": "İstiqamət",
+ "positionAddress": "Ünvan",
+ "positionProtocol": "Protokol",
+ "positionDistance": "Məsafə",
+ "positionRpm": "Dönmələr",
+ "positionFuel": "Yanacaq",
+ "positionPower": "Qidalanma",
+ "positionBattery": "Batareya",
+ "positionRaw": "Natamamlar",
+ "positionIndex": "İndeks",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "Peyklər",
+ "positionSatVisible": "Görünən peyklər",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "Roaming",
+ "positionEvent": "Hadisə",
+ "positionAlarm": "Həyəcan",
+ "positionStatus": "Status",
+ "positionOdometer": "Odometr",
+ "positionServiceOdometer": "Xidmət odometri",
+ "positionTripOdometer": "Gediş odometri",
+ "positionHours": "Saatlar",
+ "positionSteps": "Addımlar",
+ "positionInput": "Daxilolmalar",
+ "positionHeartRate": "Heart Rate",
+ "positionOutput": "Çıxışlar",
+ "positionBatteryLevel": "Batareya səviyyəsi",
+ "positionFuelConsumption": "Yanacaq sərfiyyatı",
+ "positionRfid": "RFID",
+ "positionVersionFw": "Tikilmə versiyası",
+ "positionVersionHw": "Metal versiyası",
+ "positionIgnition": "İşə düşmə",
+ "positionFlags": "Bayraqlar",
+ "positionCharge": "Yükləmə",
+ "positionIp": "IP",
+ "positionArchive": "Arxiv",
+ "positionVin": "VIN",
+ "positionApproximate": "Təxmini",
+ "positionThrottle": "Drossel",
+ "positionMotion": "Hərəkət",
+ "positionArmed": "Mühafizə",
+ "positionAcceleration": "Sürətlənmə",
+ "positionTemp": "Temperature",
+ "positionDeviceTemp": "Cihazın temperaturu",
+ "positionCoolantTemp": "Coolant Temperature",
+ "positionOperator": "Operator",
+ "positionCommand": "Komanda",
+ "positionBlocked": "Bloklama",
+ "positionDtcs": "Səhvlər",
+ "positionObdSpeed": "OBD sürət",
+ "positionObdOdometer": "OBD odometr",
+ "positionDrivingTime": "Driving Time",
+ "positionDriverUniqueId": "Sürücü İD-si",
+ "positionCard": "Card",
+ "positionImage": "Şəkil",
+ "positionVideo": "Video",
+ "positionAudio": "səs",
+ "serverTitle": "Server tənzimləmələri",
+ "serverZoom": "Yaxınlaşma",
+ "serverRegistration": "Qeydiyyat",
+ "serverReadonly": "Sadəcə baxış",
+ "serverForceSettings": "Tənzimləmələri sürətləndirmək",
+ "serverAnnouncement": "Announcement",
+ "serverName": "Server Name",
+ "serverDescription": "Server Description",
+ "serverColorPrimary": "Color Primary",
+ "serverColorSecondary": "Color Secondary",
+ "serverLogo": "Logo Image",
+ "serverLogoInverted": "Inverted Logo Image",
+ "serverChangeDisable": "Disable Server Change",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "Xəritə",
+ "mapActive": "Active Maps",
+ "mapOverlay": "Map Overlay",
+ "mapOverlayCustom": "Custom Overlay",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API Key",
+ "mapOpenWeatherClouds": "OpenWeather Clouds",
+ "mapOpenWeatherPrecipitation": "OpenWeather Precipitation",
+ "mapOpenWeatherPressure": "OpenWeather Pressure",
+ "mapOpenWeatherWind": "OpenWeather Wind",
+ "mapOpenWeatherTemperature": "OpenWeather Temperature",
+ "mapLayer": "Xəritə qatı",
+ "mapCustom": "Custom (XYZ)",
+ "mapCustomArcgis": "Custom (ArcGIS)",
+ "mapCustomLabel": "Custom map",
+ "mapCarto": "Carto Basemaps",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Bing Maps Key",
+ "mapBingRoad": "Bing Maps Road",
+ "mapBingAerial": "Bing Maps Aerial",
+ "mapBingHybrid": "Bing Maps Hybrid",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Yandex Map",
+ "mapYandexSat": "Yandex Satellite",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox Streets",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox Outdoors",
+ "mapMapboxSatellite": "Mapbox Satellite",
+ "mapMapboxKey": "Mapbox Access Token",
+ "mapMapTilerBasic": "MapTiler Basic",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "MapTiler API Key",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ Access Token",
+ "mapTomTomBasic": "TomTom Basic",
+ "mapTomTomFlow": "TomTom Traffic Flow",
+ "mapTomTomIncidents": "TomTom Traffic Incidents",
+ "mapTomTomKey": "TomTom API Key",
+ "mapHereBasic": "Here Basic",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satellite",
+ "mapHereFlow": "Here Traffic Flow",
+ "mapHereKey": "Here API Key",
+ "mapShapePolygon": "Çoxbucaq",
+ "mapShapeCircle": "Dairə",
+ "mapShapePolyline": "Xətt",
+ "mapLiveRoutes": "Canlı marşrutlar",
+ "mapDirection": "Show Direction",
+ "mapCurrentLocation": "Current Location",
+ "mapPoiLayer": "POI təbəqə",
+ "mapClustering": "Markers Clustering",
+ "mapOnSelect": "Show Map on Selection",
+ "mapDefault": "Default Map",
+ "stateTitle": "Vəziyyət",
+ "stateName": "Parametr",
+ "stateValue": "Dəyər",
+ "commandTitle": "Komanda",
+ "commandSend": "Göndərmək",
+ "commandSent": "Komandir göndərildi",
+ "commandQueued": "Command queued",
+ "commandUnit": "Vahidlər",
+ "commandCustom": "İstifadəçi komandası",
+ "commandDeviceIdentification": "Cihazın identifikasiyası",
+ "commandPositionSingle": "Birdəfəlik təqib",
+ "commandPositionPeriodic": "Təqibi başlamaq",
+ "commandPositionStop": "Təqibi dayandırmaq",
+ "commandEngineStop": "Mühərriki bloklamaq",
+ "commandEngineResume": "Mühərriki blokdan çıxarmaq",
+ "commandAlarmArm": "Siqnalizasiyanı aktivləşdirmək",
+ "commandAlarmDisarm": "Siqnalizasiyanı deaktivləşdirmək",
+ "commandAlarmDismiss": "Dismiss Alarm",
+ "commandSetTimezone": "Saat qurşağını tənzimləmək",
+ "commandRequestPhoto": "Şəkil tələb etmək",
+ "commandPowerOff": "Power Off Device",
+ "commandRebootDevice": "Cihazı yenidən yükləmək",
+ "commandFactoryReset": "Factory Reset",
+ "commandSendSms": "SMS göndərmək",
+ "commandSendUssd": "USSD göndərmək",
+ "commandSosNumber": "SOS nömrəni tənzimləmək",
+ "commandSilenceTime": "Sükut vaxtını tənzimləmək",
+ "commandSetPhonebook": "Telefon kitabçasını tənzimləmək",
+ "commandVoiceMessage": "Səsli ismarıc",
+ "commandOutputControl": "Çıxışın yoxlanması",
+ "commandVoiceMonitoring": "Səs yoxlaması",
+ "commandSetAgps": "AGPS-i tənzimləmək",
+ "commandSetIndicator": "İndikatoru tənzimləmək",
+ "commandConfiguration": "Konfiqurasiya",
+ "commandGetVersion": "Versiyanı müəyyənləşdirmək",
+ "commandFirmwareUpdate": "Tikilməni yeniləmək",
+ "commandSetConnection": "Bağlantını tənzimləmək",
+ "commandSetOdometer": "Odometri tənzimləmək",
+ "commandGetModemStatus": "Modemin vəziyyətini müəyyənləşdirmək",
+ "commandGetDeviceStatus": "Cihazın vəziyyətini müəyyənləşdirmək",
+ "commandSetSpeedLimit": "Set Speed Limit",
+ "commandModePowerSaving": "Power Saving Mode",
+ "commandModeDeepSleep": "Deep Sleep Mode",
+ "commandAlarmGeofence": "Set Geofence Alarm",
+ "commandAlarmBattery": "Set Battery Alarm",
+ "commandAlarmSos": "Set SOS Alarm",
+ "commandAlarmRemove": "Set Remove Alarm",
+ "commandAlarmClock": "Set Clock Alarm",
+ "commandAlarmSpeed": "Set Speed Alarm",
+ "commandAlarmFall": "Set Fall Alarm",
+ "commandAlarmVibration": "Set Vibration Alarm",
+ "commandFrequency": "Tezlik",
+ "commandTimezone": "Vaxt zonasının yerdəyişməsi",
+ "commandMessage": "İsmarıc",
+ "commandRadius": "Radius",
+ "commandEnable": "Enable",
+ "commandData": "Verilənlər",
+ "commandIndex": "Indeks",
+ "commandPhone": "Telefon nömrəs",
+ "commandServer": "Server",
+ "commandPort": "Port",
+ "eventAll": "Bütün hadisələr",
+ "eventDeviceOnline": "vəziyyət online",
+ "eventDeviceUnknown": "vəziyyət unknown",
+ "eventDeviceOffline": "vəziyyət offline",
+ "eventDeviceInactive": "Device inactive",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "Cihaz hərəkət edir",
+ "eventDeviceStopped": "Aygıt durdu",
+ "eventDeviceOverspeed": "Sürət həddi aşıldı",
+ "eventDeviceFuelDrop": "Yanacaq düşməsi",
+ "eventDeviceFuelIncrease": "Fuel increase",
+ "eventCommandResult": "Komanda nəticəsi",
+ "eventGeofenceEnter": "Geofence entered",
+ "eventGeofenceExit": "Geofence exited",
+ "eventAlarm": "qudok",
+ "eventIgnitionOn": "Ignition on",
+ "eventIgnitionOff": "Ignition off",
+ "eventMaintenance": "Texniki xidmətə ehtiyac var",
+ "eventTextMessage": "Mətn ismarıcı alındı",
+ "eventDriverChanged": "Driver changed",
+ "eventMedia": "Media",
+ "eventsScrollToLast": "Sonadək fırlanma",
+ "eventsSoundEvents": "Sound Events",
+ "eventsSoundAlarms": "Sound Alarms",
+ "alarmGeneral": "General",
+ "alarmSos": "fəlakət siqnalı",
+ "alarmVibration": "Vibration",
+ "alarmMovement": "Movement",
+ "alarmLowspeed": "Low Speed",
+ "alarmOverspeed": "Overspeed",
+ "alarmFallDown": "Fall Down",
+ "alarmLowPower": "Low Power",
+ "alarmLowBattery": "Low Battery",
+ "alarmFault": "Fault",
+ "alarmPowerOff": "Power Off",
+ "alarmPowerOn": "Power On",
+ "alarmDoor": "Door",
+ "alarmLock": "Lock",
+ "alarmUnlock": "Unlock",
+ "alarmGeofence": "Geofence",
+ "alarmGeofenceEnter": "Geofence Enter",
+ "alarmGeofenceExit": "Geofence Exit",
+ "alarmGpsAntennaCut": "GPS Antenna Cut",
+ "alarmAccident": "Accident",
+ "alarmTow": "Tow",
+ "alarmIdle": "Idle",
+ "alarmHighRpm": "High RPM",
+ "alarmHardAcceleration": "Hard Acceleration",
+ "alarmHardBraking": "Hard Braking",
+ "alarmHardCornering": "Hard Cornering",
+ "alarmLaneChange": "Lane Change",
+ "alarmFatigueDriving": "Fatigue Driving",
+ "alarmPowerCut": "Power Cut",
+ "alarmPowerRestored": "Power Restored",
+ "alarmJamming": "Jamming",
+ "alarmTemperature": "Temperature",
+ "alarmParking": "düşərgə",
+ "alarmBonnet": "Bonnet",
+ "alarmFootBrake": "Ayaq əyləci",
+ "alarmFuelLeak": "Yanacaq sızması",
+ "alarmTampering": "Tampering",
+ "alarmRemoving": "Çıxarma",
+ "notificationType": "Xəbərdarlıq növü",
+ "notificationAlways": "Bütün Cihazlar",
+ "notificationNotificators": "Kanallar",
+ "notificatorCommand": "Command",
+ "notificatorWeb": "Web",
+ "notificatorMail": "Mail",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Replay",
+ "reportCombined": "Combined",
+ "reportRoute": "Marşrut",
+ "reportEvents": "Hadisələr",
+ "reportTrips": "Gedişlər",
+ "reportStops": "Dayanacaqlar",
+ "reportSummary": "Xülasə",
+ "reportDaily": "Daily Summary",
+ "reportChart": "Diaqram",
+ "reportConfigure": "Konfiqurasiya etmək",
+ "reportEventTypes": "Hadisənin tipi",
+ "reportChartType": "Diaqram tipi",
+ "reportShowMarkers": "Markerləri göstərmək",
+ "reportExport": "Eksport",
+ "reportEmail": "Email Report",
+ "reportSchedule": "Schedule",
+ "reportPeriod": "Dövr",
+ "reportCustom": "Xüsusi",
+ "reportToday": "Bu gün",
+ "reportYesterday": "Dünən",
+ "reportThisWeek": "Bu həftə",
+ "reportPreviousWeek": "Əvvəlki həftə",
+ "reportThisMonth": "Bu ay",
+ "reportPreviousMonth": "Əvvəlki Ay",
+ "reportDeviceName": "Cihazın adı",
+ "reportAverageSpeed": "Orta sürət",
+ "reportMaximumSpeed": "Maksimal sürə",
+ "reportEngineHours": "Mühərrik saatları",
+ "reportDuration": "Davamiyyəti",
+ "reportStartDate": "Start Date",
+ "reportStartTime": "Başlanğıc vaxtı",
+ "reportStartAddress": "Başlanğıc ünvanı",
+ "reportEndTime": "Qurtarma vaxtı",
+ "reportEndAddress": "Qurtarma ünvanı",
+ "reportSpentFuel": "Yanacaq işləndi",
+ "reportStartOdometer": "Odometer Start",
+ "reportEndOdometer": "Odometer End",
+ "statisticsTitle": "Statistika",
+ "statisticsCaptureTime": "Yığım vaxtı",
+ "statisticsActiveUsers": "Aktiv istifadəçilər",
+ "statisticsActiveDevices": "Aktiv cihazlar",
+ "statisticsRequests": "Sorğular",
+ "statisticsMessagesReceived": "İsmarıclar alındı",
+ "statisticsMessagesStored": "İsmarıclar yadda saxlanıldı",
+ "statisticsGeocoder": "Geokoder sorğuları",
+ "statisticsGeolocation": "Geolokasiya sorğuları",
+ "categoryArrow": "Ox",
+ "categoryDefault": "Default rejimi",
+ "categoryAnimal": "Heyvan",
+ "categoryBicycle": "Velosiped",
+ "categoryBoat": "Qayıq",
+ "categoryBus": "Avtobus",
+ "categoryCar": "Avtomobil",
+ "categoryCamper": "Camper",
+ "categoryCrane": "Kran",
+ "categoryHelicopter": "Vertolyot",
+ "categoryMotorcycle": "Motosikl",
+ "categoryOffroad": "Offroad",
+ "categoryPerson": "İnsan",
+ "categoryPickup": "Pikap",
+ "categoryPlane": "Təyyarə",
+ "categoryShip": "Gəmi",
+ "categoryTractor": "Traktor",
+ "categoryTrain": "qatar",
+ "categoryTram": "Tram",
+ "categoryTrolleybus": "Trolleybus",
+ "categoryTruck": "Yük maşını",
+ "categoryVan": "Furqon",
+ "categoryScooter": "Scooter",
+ "maintenanceStart": "Başlamaq",
+ "maintenancePeriod": "Dövr"
+} \ No newline at end of file
diff --git a/src/resources/l10n/bg.json b/src/resources/l10n/bg.json
new file mode 100644
index 00000000..299b7a15
--- /dev/null
+++ b/src/resources/l10n/bg.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "Зареждане...",
+ "sharedHide": "Скрий",
+ "sharedSave": "Запази",
+ "sharedUpload": "Upload",
+ "sharedSet": "Настрой",
+ "sharedCancel": "Отказ",
+ "sharedCopy": "Copy",
+ "sharedAdd": "Добави",
+ "sharedEdit": "Редактирай",
+ "sharedRemove": "Премахни",
+ "sharedRemoveConfirm": "Потвърди премахването!",
+ "sharedNoData": "No data",
+ "sharedSubject": "Subject",
+ "sharedYes": "Yes",
+ "sharedNo": "No",
+ "sharedKm": "км",
+ "sharedMi": "мил",
+ "sharedNmi": "Морска миля",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "възел",
+ "sharedKmh": "км/ч",
+ "sharedMph": "мил/ч",
+ "sharedHour": "Час",
+ "sharedMinute": "Минута",
+ "sharedSecond": "Секунда",
+ "sharedDays": "дни",
+ "sharedHours": "часа",
+ "sharedMinutes": "мин.",
+ "sharedDecimalDegrees": "Градуси",
+ "sharedDegreesDecimalMinutes": "Градуси, минути",
+ "sharedDegreesMinutesSeconds": "Градуси, минути, секунди",
+ "sharedName": "Име",
+ "sharedDescription": "Описание",
+ "sharedSearch": "Търси",
+ "sharedIconScale": "Icon Scale",
+ "sharedGeofence": "Зона",
+ "sharedGeofences": "Зони",
+ "sharedCreateGeofence": "Create Geofence",
+ "sharedNotifications": "Известия",
+ "sharedNotification": "Известиe",
+ "sharedAttributes": "Параметри",
+ "sharedAttribute": "Параметър",
+ "sharedDrivers": "Шофьори",
+ "sharedDriver": "Шофьор",
+ "sharedArea": "Район",
+ "sharedSound": "Тон на известията",
+ "sharedType": "Тип",
+ "sharedDistance": "Разстояние",
+ "sharedHourAbbreviation": "ч.",
+ "sharedMinuteAbbreviation": "мин.",
+ "sharedSecondAbbreviation": "сек.",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "лит.",
+ "sharedGallonAbbreviation": "гал.",
+ "sharedLiter": "Литър",
+ "sharedImpGallon": "Британски галон",
+ "sharedUsGallon": "Американски галон",
+ "sharedLiterPerHourAbbreviation": "л/ч",
+ "sharedGetMapState": "Състояние на картата",
+ "sharedComputedAttribute": "Изчислен параметър",
+ "sharedComputedAttributes": "Изчислени параметри",
+ "sharedCheckComputedAttribute": "Провери изчисления параметър",
+ "sharedExpression": "Израз",
+ "sharedDevice": "Устройство",
+ "sharedTest": "Test",
+ "sharedTestNotification": "Изпрати пробно известие",
+ "sharedTestNotificators": "Test Channels",
+ "sharedTestExpression": "Test Expression",
+ "sharedCalendar": "Календар",
+ "sharedCalendars": "Календари",
+ "sharedFile": "Архив",
+ "sharedSearchDevices": "Search Devices",
+ "sharedSortBy": "Sort By",
+ "sharedFilterMap": "Filter on Map",
+ "sharedSelectFile": "Избери архив",
+ "sharedPhone": "Телефон",
+ "sharedRequired": "Задължително",
+ "sharedPreferences": "Предпочитания",
+ "sharedPermissions": "Разрешения",
+ "sharedConnections": "Connections",
+ "sharedExtra": "Допълнително",
+ "sharedPrimary": "Primary",
+ "sharedSecondary": "Secondary",
+ "sharedTypeString": "Условие",
+ "sharedTypeNumber": "Номер",
+ "sharedTypeBoolean": "Boolean",
+ "sharedTimezone": "Часова зона",
+ "sharedInfoTitle": "Инфо",
+ "sharedSavedCommand": "Запазена команда",
+ "sharedSavedCommands": "Запазени команди",
+ "sharedNew": "Нова команда",
+ "sharedShowAddress": "Покажи адреса",
+ "sharedShowDetails": "More Details",
+ "sharedDisabled": "Блокиран",
+ "sharedMaintenance": "Обслужване",
+ "sharedDeviceAccumulators": "Акумулатор",
+ "sharedAlarms": "Аларми",
+ "sharedLocation": "Location",
+ "sharedImport": "Import",
+ "sharedColumns": "Columns",
+ "sharedDropzoneText": "Drag and drop a file here or click",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "Simple",
+ "calendarRecurrence": "Recurrence",
+ "calendarOnce": "Once",
+ "calendarDaily": "Daily",
+ "calendarWeekly": "Weekly",
+ "calendarMonthly": "Monthly",
+ "calendarDays": "Days",
+ "calendarSunday": "Sunday",
+ "calendarMonday": "Monday",
+ "calendarTuesday": "Tuesday",
+ "calendarWednesday": "Wednesday",
+ "calendarThursday": "Thursday",
+ "calendarFriday": "Friday",
+ "calendarSaturday": "Saturday",
+ "attributeShowGeofences": "Show Geofences",
+ "attributeSpeedLimit": "Ограничение на скоростта",
+ "attributeFuelDropThreshold": "Fuel Drop Threshold",
+ "attributeFuelIncreaseThreshold": "Fuel Increase Threshold",
+ "attributePolylineDistance": "Дължина на линията",
+ "attributeReportIgnoreOdometer": "Отчет: Игнорирай километража",
+ "attributeWebReportColor": "Цвят на Отчета",
+ "attributeDevicePassword": "Парола на устройството",
+ "attributeDeviceImage": "Device Image",
+ "attributeDeviceInactivityStart": "Device Inactivity Start",
+ "attributeDeviceInactivityPeriod": "Device Inactivity Period",
+ "attributeProcessingCopyAttributes": "Обработка: копиране на сензори",
+ "attributeColor": "Цвят",
+ "attributeWebLiveRouteLength": "Дължина на дирята",
+ "attributeWebSelectZoom": "Приближи избраното",
+ "attributeWebMaxZoom": "Максимално приближение",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Pushover User Key",
+ "attributePushoverDeviceNames": "Pushover Device Names",
+ "attributeMailSmtpHost": "Поща: SMTP хост",
+ "attributeMailSmtpPort": "Поща: SMTP порт",
+ "attributeMailSmtpStarttlsEnable": "Поща: SMTP STARTTLS активирай",
+ "attributeMailSmtpStarttlsRequired": "Поща: SMTP STARTTLS задължително",
+ "attributeMailSmtpSslEnable": "Поща: SMTP SSL активирай",
+ "attributeMailSmtpSslTrust": "Поща: SMTP SSL Trust",
+ "attributeMailSmtpSslProtocols": "Поща: SMTP SSL протокол",
+ "attributeMailSmtpFrom": "Поща: SMTP от",
+ "attributeMailSmtpAuth": "Поща: SMTP Auth активирай",
+ "attributeMailSmtpUsername": "Поща: SMTP потребител",
+ "attributeMailSmtpPassword": "Поща: SMTP парола",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: Disable Attributes",
+ "attributeUiDisableGroups": "UI: Disable Groups",
+ "attributeUiDisableEvents": "Деактивирай Събития",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "Деактивирай Шофьори",
+ "attributeUiDisableComputedAttributes": "Деактивирай изчислените параметри",
+ "attributeUiDisableCalendars": "Деактивирай Календара",
+ "attributeUiDisableMaintenance": "Деактивирай Обслужване",
+ "attributeUiHidePositionAttributes": "Скрий атрибутите на позицията",
+ "attributeUiDisableLoginLanguage": "UI: Disable Login Language",
+ "attributeNotificationTokens": "Notification Tokens",
+ "attributePopupInfo": "Popup Info",
+ "errorTitle": "Грешка",
+ "errorGeneral": "Невалидни параметри",
+ "errorConnection": "Грешка във връзката",
+ "errorSocket": "Грешка в WebSocket връзката",
+ "errorZero": "Не може да бъде нула",
+ "userEmail": "Пощенска кутия",
+ "userPassword": "Парола",
+ "userAdmin": "Администратор",
+ "userRemember": "Запомни",
+ "userExpirationTime": "Изтичане",
+ "userDeviceLimit": "Макс. брой у-ва",
+ "userUserLimit": "Макс. брой потребители",
+ "userDeviceReadonly": "Заключи устройството",
+ "userLimitCommands": "Ограничи командите",
+ "userDisableReports": "Disable Reports",
+ "userFixedEmail": "No Email Change",
+ "userToken": "Жетон",
+ "userDeleteAccount": "Delete Account",
+ "userTemporary": "Temporary",
+ "loginTitle": "Вход",
+ "loginLanguage": "Език",
+ "loginReset": "Reset Password",
+ "loginRegister": "Регистрация",
+ "loginLogin": "Вход",
+ "loginOpenId": "Login with OpenID",
+ "loginFailed": "Грешен потребител или парола",
+ "loginCreated": "Регистриран нов потребител",
+ "loginResetSuccess": "Check your email",
+ "loginUpdateSuccess": "New password is set",
+ "loginLogout": "Изход",
+ "loginLogo": "Лого",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "Устройства и състояние",
+ "deviceSelected": "Selected Device",
+ "deviceTitle": "Обекти",
+ "devicePrimaryInfo": "Device Title",
+ "deviceSecondaryInfo": "Device Detail",
+ "deviceIdentifier": "Идентификатор",
+ "deviceModel": "Модел",
+ "deviceContact": "Контакт",
+ "deviceCategory": "Категория",
+ "deviceLastUpdate": "Последно обновяване",
+ "deviceCommand": "Команда",
+ "deviceFollow": "Следвай",
+ "deviceTotalDistance": "Общ пробег",
+ "deviceStatus": "Статус",
+ "deviceStatusOnline": "На линия",
+ "deviceStatusOffline": "Няма връзка",
+ "deviceStatusUnknown": "Неизвестен",
+ "deviceRegisterFirst": "Register your first device",
+ "deviceIdentifierHelp": "IMEI, serial number or other id. It has to match the identifier device reports to the server.",
+ "deviceShare": "Share Device",
+ "groupDialog": "Група",
+ "groupParent": "Група",
+ "groupNoGroup": "Без група",
+ "settingsTitle": "Настройки",
+ "settingsUser": "Профил",
+ "settingsGroups": "Групи",
+ "settingsServer": "Сървър",
+ "settingsUsers": "Потребители",
+ "settingsDistanceUnit": "Мерна единица за разстояние",
+ "settingsAltitudeUnit": "Altitude Unit",
+ "settingsSpeedUnit": "Мерна единица за скорост",
+ "settingsVolumeUnit": "Мерна единица за обем",
+ "settingsTwelveHourFormat": "12 часов формат",
+ "settingsCoordinateFormat": "Формат на координатите",
+ "settingsServerVersion": "Server Version",
+ "settingsAppVersion": "App Version",
+ "settingsConnection": "Connection",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "Отчети",
+ "reportScheduled": "Scheduled Reports",
+ "reportDevice": "Устройство",
+ "reportGroup": "Група",
+ "reportFrom": "От",
+ "reportTo": "До",
+ "reportShow": "Покажи",
+ "reportClear": "Изчисти",
+ "linkGoogleMaps": "Google Maps",
+ "linkAppleMaps": "Apple Maps",
+ "linkStreetView": "Street View",
+ "positionFixTime": "Fix Time",
+ "positionDeviceTime": "Device Time",
+ "positionServerTime": "Server Time",
+ "positionValid": "Валидност",
+ "positionAccuracy": "Точност",
+ "positionLatitude": "Географска Ширина",
+ "positionLongitude": "Географска Дължина",
+ "positionAltitude": "Надморска височина",
+ "positionSpeed": "Скорост",
+ "positionCourse": "Посока",
+ "positionAddress": "Адрес",
+ "positionProtocol": "Протокол",
+ "positionDistance": "Разстояние",
+ "positionRpm": "Обороти",
+ "positionFuel": "Гориво",
+ "positionPower": "Захранване",
+ "positionBattery": "Батерия",
+ "positionRaw": "Необработен",
+ "positionIndex": "Индекс",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "Сателити",
+ "positionSatVisible": "Видими сателити",
+ "positionRssi": "GSM сигнал",
+ "positionGps": "ДжиПиЕс",
+ "positionRoaming": "Роуминг",
+ "positionEvent": "Събитие",
+ "positionAlarm": "Аларма",
+ "positionStatus": "Статус",
+ "positionOdometer": "Километраж",
+ "positionServiceOdometer": "Служебен километраж",
+ "positionTripOdometer": "Километраж пътуване",
+ "positionHours": "Часове",
+ "positionSteps": "Стъпки",
+ "positionInput": "Входни данни",
+ "positionHeartRate": "Heart Rate",
+ "positionOutput": "Изходящи данни",
+ "positionBatteryLevel": "Батерия %",
+ "positionFuelConsumption": "Разход на гориво",
+ "positionRfid": "RFID",
+ "positionVersionFw": "Фърмуеър версия",
+ "positionVersionHw": "Версия на апаратурата",
+ "positionIgnition": "На контакт",
+ "positionFlags": "Флагове",
+ "positionCharge": "Зареждане",
+ "positionIp": "АйПи",
+ "positionArchive": "Архив",
+ "positionVin": "VIN",
+ "positionApproximate": "Приблизително",
+ "positionThrottle": "Газ",
+ "positionMotion": "В движение",
+ "positionArmed": "Активиран",
+ "positionAcceleration": "Ускорение",
+ "positionTemp": "Temperature",
+ "positionDeviceTemp": "Температура на устройството",
+ "positionCoolantTemp": "Coolant Temperature",
+ "positionOperator": "Оператор",
+ "positionCommand": "Команда",
+ "positionBlocked": "Блокиран",
+ "positionDtcs": "DTCs",
+ "positionObdSpeed": "OBD скорост",
+ "positionObdOdometer": "OBD километраж",
+ "positionDrivingTime": "Driving Time",
+ "positionDriverUniqueId": "Идентификатор на шофьора",
+ "positionCard": "Card",
+ "positionImage": "Изображение",
+ "positionVideo": "Video",
+ "positionAudio": "Звук",
+ "serverTitle": "Настройки на сървъра",
+ "serverZoom": "Приближение",
+ "serverRegistration": "Регистрация",
+ "serverReadonly": "Ограничени права",
+ "serverForceSettings": "Наложи настройките",
+ "serverAnnouncement": "Announcement",
+ "serverName": "Server Name",
+ "serverDescription": "Server Description",
+ "serverColorPrimary": "Color Primary",
+ "serverColorSecondary": "Color Secondary",
+ "serverLogo": "Logo Image",
+ "serverLogoInverted": "Inverted Logo Image",
+ "serverChangeDisable": "Disable Server Change",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "Карта",
+ "mapActive": "Active Maps",
+ "mapOverlay": "Map Overlay",
+ "mapOverlayCustom": "Custom Overlay",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API Key",
+ "mapOpenWeatherClouds": "OpenWeather Clouds",
+ "mapOpenWeatherPrecipitation": "OpenWeather Precipitation",
+ "mapOpenWeatherPressure": "OpenWeather Pressure",
+ "mapOpenWeatherWind": "OpenWeather Wind",
+ "mapOpenWeatherTemperature": "OpenWeather Temperature",
+ "mapLayer": "Карта",
+ "mapCustom": "По избор (XYZ)",
+ "mapCustomArcgis": "По избор (ArcGIS)",
+ "mapCustomLabel": "Потребителска карта",
+ "mapCarto": "Carto Basemaps",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Ключ за Bing Maps",
+ "mapBingRoad": "Bing Maps Road",
+ "mapBingAerial": "Bing Maps Aerial",
+ "mapBingHybrid": "Bing карта хибрид",
+ "mapBaidu": "Байду",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Yandex карта",
+ "mapYandexSat": "Yandex сателитна карта ",
+ "mapWikimedia": "Уикимедия",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox Streets",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox Outdoors",
+ "mapMapboxSatellite": "Mapbox Satellite",
+ "mapMapboxKey": "Mapbox Access Token",
+ "mapMapTilerBasic": "MapTiler Basic",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "MapTiler API Key",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ Access Token",
+ "mapTomTomBasic": "TomTom Basic",
+ "mapTomTomFlow": "TomTom Traffic Flow",
+ "mapTomTomIncidents": "TomTom Traffic Incidents",
+ "mapTomTomKey": "TomTom API Key",
+ "mapHereBasic": "Here Basic",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satellite",
+ "mapHereFlow": "Here Traffic Flow",
+ "mapHereKey": "Here API Key",
+ "mapShapePolygon": "Многоъгълник",
+ "mapShapeCircle": "Кръг",
+ "mapShapePolyline": "Линия",
+ "mapLiveRoutes": "Маршрут",
+ "mapDirection": "Show Direction",
+ "mapCurrentLocation": "Current Location",
+ "mapPoiLayer": "POI слой",
+ "mapClustering": "Markers Clustering",
+ "mapOnSelect": "Show Map on Selection",
+ "mapDefault": "Default Map",
+ "stateTitle": "Състояние",
+ "stateName": "Параметър",
+ "stateValue": "Стойност",
+ "commandTitle": "Команда",
+ "commandSend": "Изпрати",
+ "commandSent": "Командата изпратена",
+ "commandQueued": "Командата на изчакване",
+ "commandUnit": "Мерна ед.",
+ "commandCustom": "Персонализирана команда",
+ "commandDeviceIdentification": "Идентификатор на устройството",
+ "commandPositionSingle": "Единичен доклад",
+ "commandPositionPeriodic": "Периодичен доклад",
+ "commandPositionStop": "Спри доклада",
+ "commandEngineStop": "Спри двигателя",
+ "commandEngineResume": "Стартирай двигателя",
+ "commandAlarmArm": "Активирай Аларма",
+ "commandAlarmDisarm": "Деактивирай Аларма",
+ "commandAlarmDismiss": "Dismiss Alarm",
+ "commandSetTimezone": "Задай часова зона",
+ "commandRequestPhoto": "Изискай снимка",
+ "commandPowerOff": "Изключи устройството",
+ "commandRebootDevice": "Рестартирай устройството",
+ "commandFactoryReset": "Factory Reset",
+ "commandSendSms": "Изпрати SMS",
+ "commandSendUssd": "Изпрати USSD",
+ "commandSosNumber": "Задай SOS номер",
+ "commandSilenceTime": "Задай \"Тих час\"",
+ "commandSetPhonebook": "Задай тел. указател",
+ "commandVoiceMessage": "Гласово съобщение",
+ "commandOutputControl": "Изходен контрол",
+ "commandVoiceMonitoring": "Гласово наблюдение",
+ "commandSetAgps": "Настрой AGPS",
+ "commandSetIndicator": "Настрой индикатор",
+ "commandConfiguration": "Конфигурация",
+ "commandGetVersion": "Получи версията",
+ "commandFirmwareUpdate": "Обнови фърмуеър",
+ "commandSetConnection": "Настрой връзката",
+ "commandSetOdometer": "Настрой километража",
+ "commandGetModemStatus": "Виж състоянието на модема",
+ "commandGetDeviceStatus": "Виж състоянието на устройството",
+ "commandSetSpeedLimit": "Set Speed Limit",
+ "commandModePowerSaving": "Power Saving Mode",
+ "commandModeDeepSleep": "Deep Sleep Mode",
+ "commandAlarmGeofence": "Set Geofence Alarm",
+ "commandAlarmBattery": "Set Battery Alarm",
+ "commandAlarmSos": "Set SOS Alarm",
+ "commandAlarmRemove": "Set Remove Alarm",
+ "commandAlarmClock": "Set Clock Alarm",
+ "commandAlarmSpeed": "Set Speed Alarm",
+ "commandAlarmFall": "Set Fall Alarm",
+ "commandAlarmVibration": "Set Vibration Alarm",
+ "commandFrequency": "Честота",
+ "commandTimezone": "Компенсация на часовата зона",
+ "commandMessage": "Съобщение",
+ "commandRadius": "Радиус",
+ "commandEnable": "Активирай",
+ "commandData": "Данни",
+ "commandIndex": "Индекс",
+ "commandPhone": "Телефонен номер",
+ "commandServer": "Сървър",
+ "commandPort": "Порт",
+ "eventAll": "Всички събития",
+ "eventDeviceOnline": "Статус на линия",
+ "eventDeviceUnknown": "Статус неизвестен",
+ "eventDeviceOffline": "Статус без връзка",
+ "eventDeviceInactive": "Device inactive",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "Обектът в движение",
+ "eventDeviceStopped": "Обектът спря",
+ "eventDeviceOverspeed": "Ограничението на скоростта превишено",
+ "eventDeviceFuelDrop": "Спад на горивото",
+ "eventDeviceFuelIncrease": "Fuel increase",
+ "eventCommandResult": "Резултат от командата",
+ "eventGeofenceEnter": "Влизане в зона",
+ "eventGeofenceExit": "Излизане от зона",
+ "eventAlarm": "Аларма",
+ "eventIgnitionOn": "Запалването включено",
+ "eventIgnitionOff": "Запалването изключено",
+ "eventMaintenance": "Необходимо е обслужване",
+ "eventTextMessage": "Получен SMS",
+ "eventDriverChanged": "Шофьорът се смени",
+ "eventMedia": "Media",
+ "eventsScrollToLast": "Отиди на последния",
+ "eventsSoundEvents": "Sound Events",
+ "eventsSoundAlarms": "Sound Alarms",
+ "alarmGeneral": "Общи",
+ "alarmSos": "SOS",
+ "alarmVibration": "Вибрация",
+ "alarmMovement": "Движение",
+ "alarmLowspeed": "Ниска скорост",
+ "alarmOverspeed": "Превишена скорост",
+ "alarmFallDown": "Падане",
+ "alarmLowPower": "Ниска мощност",
+ "alarmLowBattery": "Слаба батерия",
+ "alarmFault": "Повреда",
+ "alarmPowerOff": "Захранването изключено",
+ "alarmPowerOn": "Захранването включено",
+ "alarmDoor": "Врата",
+ "alarmLock": "Заключи",
+ "alarmUnlock": "Отключи",
+ "alarmGeofence": "Зона",
+ "alarmGeofenceEnter": "Влизане в зона",
+ "alarmGeofenceExit": "Излизане от зона",
+ "alarmGpsAntennaCut": "GPS антената откачена",
+ "alarmAccident": "Инцидент",
+ "alarmTow": "Дърпане",
+ "alarmIdle": "В покой",
+ "alarmHighRpm": "Високи обороти",
+ "alarmHardAcceleration": "Рязко ускорение",
+ "alarmHardBraking": "Рязко спиране",
+ "alarmHardCornering": "Рязко завиване",
+ "alarmLaneChange": "Смяна на лентите",
+ "alarmFatigueDriving": "Уморен водач",
+ "alarmPowerCut": "Захранването прекъснато",
+ "alarmPowerRestored": "Захранването възстановено",
+ "alarmJamming": "Заглушаване",
+ "alarmTemperature": "Температура",
+ "alarmParking": "Паркиране",
+ "alarmBonnet": "Капак",
+ "alarmFootBrake": "Спирачен педал",
+ "alarmFuelLeak": "Теч на гориво",
+ "alarmTampering": "Намеса ",
+ "alarmRemoving": "Премахване",
+ "notificationType": "Тип на известието",
+ "notificationAlways": "Всички устройства",
+ "notificationNotificators": "Канали",
+ "notificatorCommand": "Command",
+ "notificatorWeb": "Интернет",
+ "notificatorMail": "Поща",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Replay",
+ "reportCombined": "Combined",
+ "reportRoute": "Маршрут",
+ "reportEvents": "Събития",
+ "reportTrips": "Пътувания",
+ "reportStops": "Престой",
+ "reportSummary": "Общо",
+ "reportDaily": "Daily Summary",
+ "reportChart": "Графика",
+ "reportConfigure": "Конфигуриране",
+ "reportEventTypes": "Тип събития",
+ "reportChartType": "Тип на графиката",
+ "reportShowMarkers": "Покажи маркери",
+ "reportExport": "Експорт",
+ "reportEmail": "Имейл отчет",
+ "reportSchedule": "Schedule",
+ "reportPeriod": "Период",
+ "reportCustom": "По избор",
+ "reportToday": "Днес",
+ "reportYesterday": "Вчера",
+ "reportThisWeek": "Тази седмица",
+ "reportPreviousWeek": "Предната седмица",
+ "reportThisMonth": "Този месец",
+ "reportPreviousMonth": "Предният месец",
+ "reportDeviceName": "Име на обект",
+ "reportAverageSpeed": "Средна скорост",
+ "reportMaximumSpeed": "Максимална скорост",
+ "reportEngineHours": "Машиночас",
+ "reportDuration": "Продължителност",
+ "reportStartDate": "Start Date",
+ "reportStartTime": "Начален час",
+ "reportStartAddress": "Начален адрес",
+ "reportEndTime": "Краен час",
+ "reportEndAddress": "Краен адрес",
+ "reportSpentFuel": "Изразходвано гориво",
+ "reportStartOdometer": "Одометър Старт",
+ "reportEndOdometer": "Одометър Стоп",
+ "statisticsTitle": "Статистики",
+ "statisticsCaptureTime": "Дата",
+ "statisticsActiveUsers": "Активни потребители",
+ "statisticsActiveDevices": "Активни устройства",
+ "statisticsRequests": "Повиквания",
+ "statisticsMessagesReceived": "Приети съобщения",
+ "statisticsMessagesStored": "Запазени съобщения",
+ "statisticsGeocoder": "Запитвания за адрес",
+ "statisticsGeolocation": "Запитвания за позиция",
+ "categoryArrow": "Стрелка",
+ "categoryDefault": "По подразбиране",
+ "categoryAnimal": "Животно",
+ "categoryBicycle": "Колело",
+ "categoryBoat": "Лодка",
+ "categoryBus": "Автобус",
+ "categoryCar": "Кола",
+ "categoryCamper": "Camper",
+ "categoryCrane": "Кран",
+ "categoryHelicopter": "Въртолет",
+ "categoryMotorcycle": "Мотор",
+ "categoryOffroad": "Джип",
+ "categoryPerson": "Човек",
+ "categoryPickup": "Пикап",
+ "categoryPlane": "Самолет",
+ "categoryShip": "Кораб",
+ "categoryTractor": "Трактор",
+ "categoryTrain": "Влак",
+ "categoryTram": "Трамвай",
+ "categoryTrolleybus": "Тролейбус",
+ "categoryTruck": "Камион",
+ "categoryVan": "Ван",
+ "categoryScooter": "Скутер",
+ "maintenanceStart": "Старт",
+ "maintenancePeriod": "Период"
+} \ No newline at end of file
diff --git a/src/resources/l10n/bn.json b/src/resources/l10n/bn.json
new file mode 100644
index 00000000..79d6c3d0
--- /dev/null
+++ b/src/resources/l10n/bn.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "লোড হচ্ছে ...",
+ "sharedHide": "লুকিয়ে রাখুন",
+ "sharedSave": " সংরক্ষণ করুন",
+ "sharedUpload": "Upload",
+ "sharedSet": "স্থাপন করুন",
+ "sharedCancel": " বাতিল করুন",
+ "sharedCopy": "Copy",
+ "sharedAdd": "যুক্ত করুন",
+ "sharedEdit": "সম্পাদন করুন",
+ "sharedRemove": "অপসারণ করুন",
+ "sharedRemoveConfirm": "আইটেম অপসারণ?",
+ "sharedNoData": "No data",
+ "sharedSubject": "Subject",
+ "sharedYes": "Yes",
+ "sharedNo": "No",
+ "sharedKm": "কিমি",
+ "sharedMi": "মাইল",
+ "sharedNmi": "নটিক্যাল মাইল",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "কিলোনটিক্যাল",
+ "sharedKmh": "কিমি / ঘঃ",
+ "sharedMph": "mph",
+ "sharedHour": "ঘন্টা",
+ "sharedMinute": "মিনিট",
+ "sharedSecond": "সেকেন্ড",
+ "sharedDays": "দিন",
+ "sharedHours": "ঘন্টা",
+ "sharedMinutes": "মিনিট",
+ "sharedDecimalDegrees": "দশমিক ডিগ্রী",
+ "sharedDegreesDecimalMinutes": "ডিগ্রি দশমিক মিনিট",
+ "sharedDegreesMinutesSeconds": "ডিগ্রি মিনিট সেকেন্ড",
+ "sharedName": "নাম",
+ "sharedDescription": "বিবরণ / বর্ণনা",
+ "sharedSearch": "অনুসন্ধান / খোঁজা",
+ "sharedIconScale": "Icon Scale",
+ "sharedGeofence": "জিওফেন্স / ভৌগোলিক বেষ্টনী",
+ "sharedGeofences": "জিওফেন্স / ভৌগোলিক বেষ্টনী",
+ "sharedCreateGeofence": "Create Geofence",
+ "sharedNotifications": "অধিসূচনা সামুহ",
+ "sharedNotification": "অধিসূচনা",
+ "sharedAttributes": "বৈশিষ্ট্যাবলী / গুণাবলী",
+ "sharedAttribute": "বৈশিষ্ট্যাবলী / গুণাবলী",
+ "sharedDrivers": "চালক সমুহ",
+ "sharedDriver": "চালক",
+ "sharedArea": "এলাকা",
+ "sharedSound": "অধিসূচনা শব্দ",
+ "sharedType": "ধরন",
+ "sharedDistance": "দূরত্ব",
+ "sharedHourAbbreviation": "ঘ",
+ "sharedMinuteAbbreviation": "মি",
+ "sharedSecondAbbreviation": "সে",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "l",
+ "sharedGallonAbbreviation": "গ্যালন",
+ "sharedLiter": "লিটার",
+ "sharedImpGallon": "ইম্পেরিয়াল গ্যালন",
+ "sharedUsGallon": "আমেরিকি গ্যালন",
+ "sharedLiterPerHourAbbreviation": "l/h",
+ "sharedGetMapState": "মানচিত্রের অবস্থা",
+ "sharedComputedAttribute": "Computed Attribute",
+ "sharedComputedAttributes": "Computed Attributes",
+ "sharedCheckComputedAttribute": "Check Computed Attribute",
+ "sharedExpression": "অভিব্যক্তি",
+ "sharedDevice": "ডিভাইস",
+ "sharedTest": "Test",
+ "sharedTestNotification": "Send Test Notification",
+ "sharedTestNotificators": "Test Channels",
+ "sharedTestExpression": "Test Expression",
+ "sharedCalendar": "পঞ্জিকা",
+ "sharedCalendars": "পঞ্জিকাগুলি",
+ "sharedFile": "File",
+ "sharedSearchDevices": "Search Devices",
+ "sharedSortBy": "Sort By",
+ "sharedFilterMap": "Filter on Map",
+ "sharedSelectFile": "Select File",
+ "sharedPhone": "ফোন",
+ "sharedRequired": "অবশ্যক",
+ "sharedPreferences": "পছন্দসমূহ",
+ "sharedPermissions": "অনুমতিসমূহ",
+ "sharedConnections": "Connections",
+ "sharedExtra": "অতিরিক্ত",
+ "sharedPrimary": "Primary",
+ "sharedSecondary": "Secondary",
+ "sharedTypeString": "স্ট্রিং",
+ "sharedTypeNumber": "নম্বর",
+ "sharedTypeBoolean": "Boolean",
+ "sharedTimezone": "টাইমজোন",
+ "sharedInfoTitle": "Info",
+ "sharedSavedCommand": "Saved Command",
+ "sharedSavedCommands": "Saved Commands",
+ "sharedNew": "New…",
+ "sharedShowAddress": "ঠিকানা দেখান",
+ "sharedShowDetails": "More Details",
+ "sharedDisabled": "Disabled",
+ "sharedMaintenance": "Maintenance",
+ "sharedDeviceAccumulators": "Accumulators",
+ "sharedAlarms": "Alarms",
+ "sharedLocation": "Location",
+ "sharedImport": "Import",
+ "sharedColumns": "Columns",
+ "sharedDropzoneText": "Drag and drop a file here or click",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "Simple",
+ "calendarRecurrence": "Recurrence",
+ "calendarOnce": "Once",
+ "calendarDaily": "Daily",
+ "calendarWeekly": "Weekly",
+ "calendarMonthly": "Monthly",
+ "calendarDays": "Days",
+ "calendarSunday": "Sunday",
+ "calendarMonday": "Monday",
+ "calendarTuesday": "Tuesday",
+ "calendarWednesday": "Wednesday",
+ "calendarThursday": "Thursday",
+ "calendarFriday": "Friday",
+ "calendarSaturday": "Saturday",
+ "attributeShowGeofences": "Show Geofences",
+ "attributeSpeedLimit": "ডিভাইসের গতি সীমা",
+ "attributeFuelDropThreshold": "Fuel Drop Threshold",
+ "attributeFuelIncreaseThreshold": "Fuel Increase Threshold",
+ "attributePolylineDistance": "Polyline Distance",
+ "attributeReportIgnoreOdometer": "Report: Ignore Odometer",
+ "attributeWebReportColor": "ওয়েব: রিপোর্টের রঙ",
+ "attributeDevicePassword": "ডিভাইস পাসওয়ার্ড",
+ "attributeDeviceImage": "Device Image",
+ "attributeDeviceInactivityStart": "Device Inactivity Start",
+ "attributeDeviceInactivityPeriod": "Device Inactivity Period",
+ "attributeProcessingCopyAttributes": "Processing: Copy Attributes",
+ "attributeColor": "রং",
+ "attributeWebLiveRouteLength": "Web: Live Route Length",
+ "attributeWebSelectZoom": "Web: Zoom On Select",
+ "attributeWebMaxZoom": "Web: Maximum Zoom",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Pushover User Key",
+ "attributePushoverDeviceNames": "Pushover Device Names",
+ "attributeMailSmtpHost": "Mail: SMTP Host",
+ "attributeMailSmtpPort": "Mail: SMTP Port",
+ "attributeMailSmtpStarttlsEnable": "Mail: SMTP STARTTLS Enable",
+ "attributeMailSmtpStarttlsRequired": "Mail: SMTP STARTTLS Required",
+ "attributeMailSmtpSslEnable": "Mail: SMTP SSL Enable",
+ "attributeMailSmtpSslTrust": "Mail: SMTP SSL Trust",
+ "attributeMailSmtpSslProtocols": "Mail: SMTP SSL Protocols",
+ "attributeMailSmtpFrom": "Mail: SMTP From",
+ "attributeMailSmtpAuth": "Mail: SMTP Auth Enable",
+ "attributeMailSmtpUsername": "Mail: SMTP Username",
+ "attributeMailSmtpPassword": "Mail: SMTP Password",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: Disable Attributes",
+ "attributeUiDisableGroups": "UI: Disable Groups",
+ "attributeUiDisableEvents": "UI: Disable Events",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "UI: Disable Drivers",
+ "attributeUiDisableComputedAttributes": "UI: Disable Computed Attributes",
+ "attributeUiDisableCalendars": "UI: Disable Calendars",
+ "attributeUiDisableMaintenance": "UI: Disable Maintenance",
+ "attributeUiHidePositionAttributes": "UI: Hide Position Attributes",
+ "attributeUiDisableLoginLanguage": "UI: Disable Login Language",
+ "attributeNotificationTokens": "Notification Tokens",
+ "attributePopupInfo": "Popup Info",
+ "errorTitle": "ত্রুটি",
+ "errorGeneral": "Invalid parameters or constraints violation",
+ "errorConnection": "সংযোগ ত্রুটি",
+ "errorSocket": "ওয়েব সকেট সংযোগ ত্রুটি",
+ "errorZero": "Can't be zero",
+ "userEmail": "ইমেইল",
+ "userPassword": "পাসওয়ার্ড",
+ "userAdmin": "অ্যাডমিন",
+ "userRemember": "মনে রাখো",
+ "userExpirationTime": "Expiration",
+ "userDeviceLimit": "ডিভাইসের গতি সীমা",
+ "userUserLimit": "ব্যবহারকারীর সীমা",
+ "userDeviceReadonly": "ডিভাইস রিড অনলি",
+ "userLimitCommands": "Limit Commands",
+ "userDisableReports": "Disable Reports",
+ "userFixedEmail": "No Email Change",
+ "userToken": "টোকেন",
+ "userDeleteAccount": "Delete Account",
+ "userTemporary": "Temporary",
+ "loginTitle": "লগইন",
+ "loginLanguage": "ভাষা",
+ "loginReset": "Reset Password",
+ "loginRegister": "নিবন্ধন",
+ "loginLogin": "লগইন",
+ "loginOpenId": "Login with OpenID",
+ "loginFailed": "ভুল ইমেল এড্রেস বা পাসওয়ার্ড",
+ "loginCreated": "নতুন ব্যবহারকারী নিবন্ধিত হয়েছে",
+ "loginResetSuccess": "Check your email",
+ "loginUpdateSuccess": "New password is set",
+ "loginLogout": "লগ আউট",
+ "loginLogo": "লোগো",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "ডিভাইস ও অবস্থা",
+ "deviceSelected": "Selected Device",
+ "deviceTitle": "ডিভাইস",
+ "devicePrimaryInfo": "Device Title",
+ "deviceSecondaryInfo": "Device Detail",
+ "deviceIdentifier": "Identifier",
+ "deviceModel": "মডেল",
+ "deviceContact": "যোগাযোগ",
+ "deviceCategory": "বিভাগ",
+ "deviceLastUpdate": "সর্বশেষ আপডেট",
+ "deviceCommand": "কমান্ড",
+ "deviceFollow": "Follow",
+ "deviceTotalDistance": "সম্পুর্ণ দুরত্ব",
+ "deviceStatus": "অবস্থা",
+ "deviceStatusOnline": "অনলাইন",
+ "deviceStatusOffline": "অফলাইন",
+ "deviceStatusUnknown": "অজানা",
+ "deviceRegisterFirst": "Register your first device",
+ "deviceIdentifierHelp": "IMEI, serial number or other id. It has to match the identifier device reports to the server.",
+ "deviceShare": "Share Device",
+ "groupDialog": "গ্রুপ",
+ "groupParent": "গ্রুপ",
+ "groupNoGroup": "No Group",
+ "settingsTitle": "সেটিংস",
+ "settingsUser": "অ্যাকাউন্ট",
+ "settingsGroups": "Groups",
+ "settingsServer": "সার্ভার",
+ "settingsUsers": "Users",
+ "settingsDistanceUnit": "Distance Unit",
+ "settingsAltitudeUnit": "Altitude Unit",
+ "settingsSpeedUnit": "Speed Unit",
+ "settingsVolumeUnit": "Volume Unit",
+ "settingsTwelveHourFormat": "12-hour Format",
+ "settingsCoordinateFormat": "Coordinates Format",
+ "settingsServerVersion": "Server Version",
+ "settingsAppVersion": "App Version",
+ "settingsConnection": "Connection",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "Reports",
+ "reportScheduled": "Scheduled Reports",
+ "reportDevice": "ডিভাইস",
+ "reportGroup": "গ্রুপ",
+ "reportFrom": "From",
+ "reportTo": "To",
+ "reportShow": "দেখাও",
+ "reportClear": "পরিষ্কার",
+ "linkGoogleMaps": "Google Maps",
+ "linkAppleMaps": "Apple Maps",
+ "linkStreetView": "Street View",
+ "positionFixTime": "Fix Time",
+ "positionDeviceTime": "Device Time",
+ "positionServerTime": "Server Time",
+ "positionValid": "Valid",
+ "positionAccuracy": "সঠিকতা",
+ "positionLatitude": "অক্ষাংশ",
+ "positionLongitude": "দ্রাঘিমাংশ",
+ "positionAltitude": "উচ্চতা",
+ "positionSpeed": "গতি",
+ "positionCourse": "Course",
+ "positionAddress": "ঠিকানা",
+ "positionProtocol": "প্রোটোকল",
+ "positionDistance": "দূরত্ব",
+ "positionRpm": "RPM",
+ "positionFuel": "জ্বালানি",
+ "positionPower": "Power",
+ "positionBattery": "ব্যাটারি",
+ "positionRaw": "Raw",
+ "positionIndex": "Index",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "Satellites",
+ "positionSatVisible": "Visible Satellites",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS/জিপিএস",
+ "positionRoaming": "Roaming",
+ "positionEvent": "ঘটনা",
+ "positionAlarm": "অ্যালার্ম / সংকেত",
+ "positionStatus": "অবস্থা",
+ "positionOdometer": "ওডোমিটার",
+ "positionServiceOdometer": "সার্ভিস ওডোমিটার",
+ "positionTripOdometer": "Trip Odometer",
+ "positionHours": "ঘন্টা",
+ "positionSteps": "Steps",
+ "positionInput": "ইনপুট",
+ "positionHeartRate": "Heart Rate",
+ "positionOutput": "আউটপুট",
+ "positionBatteryLevel": "ব্যাটারি স্তর",
+ "positionFuelConsumption": "জ্বালানি খরচ",
+ "positionRfid": "RFID",
+ "positionVersionFw": "ফার্মওয়্যার সংস্করণ",
+ "positionVersionHw": "হার্ডওয়্যার সংস্করণ",
+ "positionIgnition": "ইগনিশন",
+ "positionFlags": "Flags",
+ "positionCharge": "চার্জ",
+ "positionIp": "IP",
+ "positionArchive": "আর্কাইভ / সংরক্ষাণাগার",
+ "positionVin": "VIN",
+ "positionApproximate": "আনুমানিক",
+ "positionThrottle": "থ্রটল / নির্গমন নিয়ন্ত্রণ",
+ "positionMotion": "গতি",
+ "positionArmed": "রক্ষিত করা",
+ "positionAcceleration": "ত্বরণ",
+ "positionTemp": "Temperature",
+ "positionDeviceTemp": "ডিভাইসের তাপমাত্রা",
+ "positionCoolantTemp": "Coolant Temperature",
+ "positionOperator": "Operator",
+ "positionCommand": "কমান্ড",
+ "positionBlocked": "Blocked",
+ "positionDtcs": "DTCs",
+ "positionObdSpeed": "OBD Speed",
+ "positionObdOdometer": "OBD Odometer",
+ "positionDrivingTime": "Driving Time",
+ "positionDriverUniqueId": "Driver Unique Id",
+ "positionCard": "Card",
+ "positionImage": "Image",
+ "positionVideo": "Video",
+ "positionAudio": "Audio",
+ "serverTitle": "সার্ভার সেটিংস ",
+ "serverZoom": "জুম্",
+ "serverRegistration": "নিবন্ধন",
+ "serverReadonly": "রিড অনলি",
+ "serverForceSettings": "ফোর্স সেটিংস",
+ "serverAnnouncement": "Announcement",
+ "serverName": "Server Name",
+ "serverDescription": "Server Description",
+ "serverColorPrimary": "Color Primary",
+ "serverColorSecondary": "Color Secondary",
+ "serverLogo": "Logo Image",
+ "serverLogoInverted": "Inverted Logo Image",
+ "serverChangeDisable": "Disable Server Change",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "মানচিত্র",
+ "mapActive": "Active Maps",
+ "mapOverlay": "Map Overlay",
+ "mapOverlayCustom": "Custom Overlay",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API Key",
+ "mapOpenWeatherClouds": "OpenWeather Clouds",
+ "mapOpenWeatherPrecipitation": "OpenWeather Precipitation",
+ "mapOpenWeatherPressure": "OpenWeather Pressure",
+ "mapOpenWeatherWind": "OpenWeather Wind",
+ "mapOpenWeatherTemperature": "OpenWeather Temperature",
+ "mapLayer": "মানচিত্র স্তর",
+ "mapCustom": "Custom (XYZ)",
+ "mapCustomArcgis": "Custom (ArcGIS)",
+ "mapCustomLabel": "Custom map",
+ "mapCarto": "Carto Basemaps",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Bing Maps Key",
+ "mapBingRoad": "Bing Maps Road",
+ "mapBingAerial": "Bing Maps Aerial",
+ "mapBingHybrid": "Bing Maps Hybrid",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Yandex Map",
+ "mapYandexSat": "Yandex Satellite",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox Streets",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox Outdoors",
+ "mapMapboxSatellite": "Mapbox Satellite",
+ "mapMapboxKey": "Mapbox Access Token",
+ "mapMapTilerBasic": "MapTiler Basic",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "MapTiler API Key",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ Access Token",
+ "mapTomTomBasic": "TomTom Basic",
+ "mapTomTomFlow": "TomTom Traffic Flow",
+ "mapTomTomIncidents": "TomTom Traffic Incidents",
+ "mapTomTomKey": "TomTom API Key",
+ "mapHereBasic": "Here Basic",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satellite",
+ "mapHereFlow": "Here Traffic Flow",
+ "mapHereKey": "Here API Key",
+ "mapShapePolygon": "বহুভুজ",
+ "mapShapeCircle": "বৃত্ত / বৃত্তাকার",
+ "mapShapePolyline": "পলিলাইন",
+ "mapLiveRoutes": "Live Routes",
+ "mapDirection": "Show Direction",
+ "mapCurrentLocation": "Current Location",
+ "mapPoiLayer": "POI Layer",
+ "mapClustering": "Markers Clustering",
+ "mapOnSelect": "Show Map on Selection",
+ "mapDefault": "Default Map",
+ "stateTitle": "State",
+ "stateName": "বৈশিষ্ট্যাবলী / গুণাবলী",
+ "stateValue": "মান",
+ "commandTitle": "কমান্ড",
+ "commandSend": "পাঠান",
+ "commandSent": "Command sent",
+ "commandQueued": "Command queued",
+ "commandUnit": "ইউনিট",
+ "commandCustom": "Custom command",
+ "commandDeviceIdentification": "ডিভাইস সনাক্তকরণ",
+ "commandPositionSingle": "একক রিপোর্টিং",
+ "commandPositionPeriodic": "পর্যায়ক্রমিক রিপোর্ট",
+ "commandPositionStop": "রিপোর্টিং বন্ধ করা",
+ "commandEngineStop": "ইঞ্জিন বন্ধ",
+ "commandEngineResume": "ইঞ্জিন পুনরায় চালু করুন",
+ "commandAlarmArm": "রক্ষিত করার অ্যালার্ম / সংকেত",
+ "commandAlarmDisarm": "অরক্ষিত করার অ্যালার্ম / সংকেত",
+ "commandAlarmDismiss": "Dismiss Alarm",
+ "commandSetTimezone": "টাইমজোন সেট করুন",
+ "commandRequestPhoto": "Request Photo",
+ "commandPowerOff": "Power Off Device",
+ "commandRebootDevice": "রিবুট ডিভাইস",
+ "commandFactoryReset": "Factory Reset",
+ "commandSendSms": "Send SMS",
+ "commandSendUssd": "Send USSD",
+ "commandSosNumber": "Set SOS Number",
+ "commandSilenceTime": "Set Silence Time",
+ "commandSetPhonebook": "Set Phonebook",
+ "commandVoiceMessage": "ভয়েস বার্তা",
+ "commandOutputControl": "Output Control",
+ "commandVoiceMonitoring": "Voice Monitoring",
+ "commandSetAgps": "Set AGPS",
+ "commandSetIndicator": "Set Indicator",
+ "commandConfiguration": "Configuration",
+ "commandGetVersion": "Get Version",
+ "commandFirmwareUpdate": "Update Firmware",
+ "commandSetConnection": "Set Connection",
+ "commandSetOdometer": "Set Odometer",
+ "commandGetModemStatus": "Get Modem Status",
+ "commandGetDeviceStatus": "Get Device Status",
+ "commandSetSpeedLimit": "Set Speed Limit",
+ "commandModePowerSaving": "Power Saving Mode",
+ "commandModeDeepSleep": "Deep Sleep Mode",
+ "commandAlarmGeofence": "Set Geofence Alarm",
+ "commandAlarmBattery": "Set Battery Alarm",
+ "commandAlarmSos": "Set SOS Alarm",
+ "commandAlarmRemove": "Set Remove Alarm",
+ "commandAlarmClock": "Set Clock Alarm",
+ "commandAlarmSpeed": "Set Speed Alarm",
+ "commandAlarmFall": "Set Fall Alarm",
+ "commandAlarmVibration": "Set Vibration Alarm",
+ "commandFrequency": "ফ্রিকোয়েন্সি",
+ "commandTimezone": "Timezone Offset",
+ "commandMessage": "বার্তা",
+ "commandRadius": "Radius",
+ "commandEnable": "Enable",
+ "commandData": "উপাত্ত / ডেটা",
+ "commandIndex": "Index",
+ "commandPhone": "ফোন নম্বর",
+ "commandServer": "Server",
+ "commandPort": "Port",
+ "eventAll": "সকল ঘটনা",
+ "eventDeviceOnline": "Status online",
+ "eventDeviceUnknown": "Status unknown",
+ "eventDeviceOffline": "Status offline",
+ "eventDeviceInactive": "Device inactive",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "Device moving",
+ "eventDeviceStopped": "Device stopped",
+ "eventDeviceOverspeed": "Speed limit exceeded",
+ "eventDeviceFuelDrop": "Fuel drop",
+ "eventDeviceFuelIncrease": "Fuel increase",
+ "eventCommandResult": "Command result",
+ "eventGeofenceEnter": "Geofence entered",
+ "eventGeofenceExit": "Geofence exited",
+ "eventAlarm": "Alarm",
+ "eventIgnitionOn": "Ignition on",
+ "eventIgnitionOff": "Ignition off",
+ "eventMaintenance": "রক্ষণাবেক্ষণ প্রয়োজন",
+ "eventTextMessage": "Text message received",
+ "eventDriverChanged": "Driver changed",
+ "eventMedia": "Media",
+ "eventsScrollToLast": "Scroll To Last",
+ "eventsSoundEvents": "Sound Events",
+ "eventsSoundAlarms": "Sound Alarms",
+ "alarmGeneral": "General",
+ "alarmSos": "SOS",
+ "alarmVibration": "Vibration",
+ "alarmMovement": "Movement",
+ "alarmLowspeed": "Low Speed",
+ "alarmOverspeed": "Overspeed",
+ "alarmFallDown": "Fall Down",
+ "alarmLowPower": "Low Power",
+ "alarmLowBattery": "Low Battery",
+ "alarmFault": "Fault",
+ "alarmPowerOff": "Power Off",
+ "alarmPowerOn": "Power On",
+ "alarmDoor": "Door",
+ "alarmLock": "Lock",
+ "alarmUnlock": "Unlock",
+ "alarmGeofence": "Geofence",
+ "alarmGeofenceEnter": "Geofence Enter",
+ "alarmGeofenceExit": "Geofence Exit",
+ "alarmGpsAntennaCut": "GPS Antenna Cut",
+ "alarmAccident": "Accident",
+ "alarmTow": "Tow",
+ "alarmIdle": "Idle",
+ "alarmHighRpm": "High RPM",
+ "alarmHardAcceleration": "Hard Acceleration",
+ "alarmHardBraking": "Hard Braking",
+ "alarmHardCornering": "Hard Cornering",
+ "alarmLaneChange": "Lane Change",
+ "alarmFatigueDriving": "Fatigue Driving",
+ "alarmPowerCut": "Power Cut",
+ "alarmPowerRestored": "Power Restored",
+ "alarmJamming": "Jamming",
+ "alarmTemperature": "Temperature",
+ "alarmParking": "Parking",
+ "alarmBonnet": "Bonnet",
+ "alarmFootBrake": "Foot Brake",
+ "alarmFuelLeak": "Fuel Leak",
+ "alarmTampering": "Tampering",
+ "alarmRemoving": "Removing",
+ "notificationType": "Type of Notification",
+ "notificationAlways": "All Devices",
+ "notificationNotificators": "Channels",
+ "notificatorCommand": "Command",
+ "notificatorWeb": "Web",
+ "notificatorMail": "Mail",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Replay",
+ "reportCombined": "Combined",
+ "reportRoute": "Route",
+ "reportEvents": "Events",
+ "reportTrips": "Trips",
+ "reportStops": "Stops",
+ "reportSummary": "Summary",
+ "reportDaily": "Daily Summary",
+ "reportChart": "Chart",
+ "reportConfigure": "কনফিগার",
+ "reportEventTypes": "Event Types",
+ "reportChartType": "Chart Type",
+ "reportShowMarkers": "মার্কার দেখাও",
+ "reportExport": "Export",
+ "reportEmail": "Email Report",
+ "reportSchedule": "Schedule",
+ "reportPeriod": "Period",
+ "reportCustom": "Custom",
+ "reportToday": "Today",
+ "reportYesterday": "Yesterday",
+ "reportThisWeek": "This Week",
+ "reportPreviousWeek": "Previous Week",
+ "reportThisMonth": "This Month",
+ "reportPreviousMonth": "Previous Month",
+ "reportDeviceName": "ডিভাইসের নাম",
+ "reportAverageSpeed": "Average Speed",
+ "reportMaximumSpeed": "Maximum Speed",
+ "reportEngineHours": "Engine Hours",
+ "reportDuration": "Duration",
+ "reportStartDate": "Start Date",
+ "reportStartTime": "শুরুর সময় ",
+ "reportStartAddress": "শুরুর ঠিকানা",
+ "reportEndTime": "শেষ সময়",
+ "reportEndAddress": "শেষ ঠিকানা",
+ "reportSpentFuel": "ব্যয়িত জ্বালানি",
+ "reportStartOdometer": "Odometer Start",
+ "reportEndOdometer": "Odometer End",
+ "statisticsTitle": "পরিসংখ্যান",
+ "statisticsCaptureTime": "সময় ক্যাপচার",
+ "statisticsActiveUsers": "সক্রিয় ব্যবহারকারীগণ",
+ "statisticsActiveDevices": "সক্রিয় ডিভাইস",
+ "statisticsRequests": "রিকোয়েস্টসমূহ",
+ "statisticsMessagesReceived": "Messages Received",
+ "statisticsMessagesStored": "Messages Stored",
+ "statisticsGeocoder": "Geocoder Requests",
+ "statisticsGeolocation": "Geolocation Requests",
+ "categoryArrow": "তীর",
+ "categoryDefault": "ডিফল্ট",
+ "categoryAnimal": "পশু",
+ "categoryBicycle": "সাইকেল",
+ "categoryBoat": "নৌকা",
+ "categoryBus": "বাস",
+ "categoryCar": "কার",
+ "categoryCamper": "Camper",
+ "categoryCrane": "ক্রেন",
+ "categoryHelicopter": "হেলিকপ্টার",
+ "categoryMotorcycle": "মোটরসাইকেল",
+ "categoryOffroad": "Offroad",
+ "categoryPerson": "ব্যক্তি",
+ "categoryPickup": "পিকআপ",
+ "categoryPlane": "প্লেন",
+ "categoryShip": "জাহাজ",
+ "categoryTractor": "ট্র্যাক্টর",
+ "categoryTrain": "Train",
+ "categoryTram": "Tram",
+ "categoryTrolleybus": "Trolleybus",
+ "categoryTruck": "ট্রাক",
+ "categoryVan": "ভ্যান",
+ "categoryScooter": "Scooter",
+ "maintenanceStart": "Start",
+ "maintenancePeriod": "Period"
+} \ No newline at end of file
diff --git a/src/resources/l10n/ca.json b/src/resources/l10n/ca.json
new file mode 100644
index 00000000..6ac0b872
--- /dev/null
+++ b/src/resources/l10n/ca.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "Carregant…",
+ "sharedHide": "Ocultar",
+ "sharedSave": "Guardar",
+ "sharedUpload": "Carrega",
+ "sharedSet": "Establir",
+ "sharedCancel": "Cancel·lar",
+ "sharedCopy": "Copy",
+ "sharedAdd": "Afegir",
+ "sharedEdit": "Editar",
+ "sharedRemove": "Eliminar",
+ "sharedRemoveConfirm": "Eliminar element?",
+ "sharedNoData": "Sense dades",
+ "sharedSubject": "Subject",
+ "sharedYes": "Sí",
+ "sharedNo": "No",
+ "sharedKm": "Km",
+ "sharedMi": "MI",
+ "sharedNmi": "nmi",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "Nusos",
+ "sharedKmh": "km/h",
+ "sharedMph": "mph",
+ "sharedHour": "Hora",
+ "sharedMinute": "Minut",
+ "sharedSecond": "Segon",
+ "sharedDays": "Dies",
+ "sharedHours": "Hores",
+ "sharedMinutes": "minuts",
+ "sharedDecimalDegrees": "Graus amb decimals",
+ "sharedDegreesDecimalMinutes": "Graus i Minuts amb decimals",
+ "sharedDegreesMinutesSeconds": "Graus Minuts i Segons",
+ "sharedName": "Nom",
+ "sharedDescription": "Descripció",
+ "sharedSearch": "Cercar",
+ "sharedIconScale": "Icon Scale",
+ "sharedGeofence": "Geo-Zona",
+ "sharedGeofences": "Geo-Zones",
+ "sharedCreateGeofence": "Create Geofence",
+ "sharedNotifications": "Notificacions",
+ "sharedNotification": "Notificació",
+ "sharedAttributes": "Atributs",
+ "sharedAttribute": "Atribut",
+ "sharedDrivers": "Conductors",
+ "sharedDriver": "Conductor",
+ "sharedArea": "Àrea",
+ "sharedSound": "So de notificació",
+ "sharedType": "Tipus",
+ "sharedDistance": "Distància",
+ "sharedHourAbbreviation": "h",
+ "sharedMinuteAbbreviation": "m",
+ "sharedSecondAbbreviation": "s",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "l",
+ "sharedGallonAbbreviation": "g",
+ "sharedLiter": "Litre",
+ "sharedImpGallon": "Galó Imp.",
+ "sharedUsGallon": "Galó U.S.",
+ "sharedLiterPerHourAbbreviation": "L/h",
+ "sharedGetMapState": "Obtenir Estat del Mapa",
+ "sharedComputedAttribute": "Atribut calculat",
+ "sharedComputedAttributes": "Atributs calculats",
+ "sharedCheckComputedAttribute": "Revisar atribut calculat",
+ "sharedExpression": "Expressió",
+ "sharedDevice": "Dispositiu",
+ "sharedTest": "Prova",
+ "sharedTestNotification": "Enviar notificació de prova",
+ "sharedTestNotificators": "Canals de prova",
+ "sharedTestExpression": "Prova d'\\expressió",
+ "sharedCalendar": "Calendari",
+ "sharedCalendars": "Calendaris",
+ "sharedFile": "Arxiu",
+ "sharedSearchDevices": "Buscar Dispositius",
+ "sharedSortBy": "Ordenar per",
+ "sharedFilterMap": "Filtrar en el Mapa",
+ "sharedSelectFile": "Seleccioni arxiu",
+ "sharedPhone": "Telèfon",
+ "sharedRequired": "Obligatori",
+ "sharedPreferences": "Preferències",
+ "sharedPermissions": "Permisos",
+ "sharedConnections": "Connexions",
+ "sharedExtra": "Extra",
+ "sharedPrimary": "Primari",
+ "sharedSecondary": "Secondari",
+ "sharedTypeString": "Cadena",
+ "sharedTypeNumber": "Número",
+ "sharedTypeBoolean": "Booleà",
+ "sharedTimezone": "Zona Horària",
+ "sharedInfoTitle": "Informació",
+ "sharedSavedCommand": "Comanda guardada",
+ "sharedSavedCommands": "Comandes guardades",
+ "sharedNew": "Nou…",
+ "sharedShowAddress": "Mostrar carrer",
+ "sharedShowDetails": "Més detalls",
+ "sharedDisabled": "Deshabilitat",
+ "sharedMaintenance": "Manteniments",
+ "sharedDeviceAccumulators": "Acumulador",
+ "sharedAlarms": "Alarmes",
+ "sharedLocation": "Ubicació",
+ "sharedImport": "Importar",
+ "sharedColumns": "Columnes",
+ "sharedDropzoneText": "Arrossegueu i deixeu anar un fitxer aquí o feu clic",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "Simple",
+ "calendarRecurrence": "Recurrència",
+ "calendarOnce": "Una vegada",
+ "calendarDaily": "Diari",
+ "calendarWeekly": "Setmanal",
+ "calendarMonthly": "Mensual",
+ "calendarDays": "Dies",
+ "calendarSunday": "Diumenge",
+ "calendarMonday": "Dilluns",
+ "calendarTuesday": "Dimarts",
+ "calendarWednesday": "Dimecres",
+ "calendarThursday": "Dijous",
+ "calendarFriday": "Divendres",
+ "calendarSaturday": "Dissabte",
+ "attributeShowGeofences": "Mostra tanques geogràfiques",
+ "attributeSpeedLimit": "Límit de velocitat",
+ "attributeFuelDropThreshold": "Llindar de caiguda de combustible",
+ "attributeFuelIncreaseThreshold": "Llindar d\\\\'augment del combustible",
+ "attributePolylineDistance": "Distància de polilínea",
+ "attributeReportIgnoreOdometer": "Informe: Ignorar el odòmetre",
+ "attributeWebReportColor": "Web: Color de l\\'informe",
+ "attributeDevicePassword": "Contrasenya de dispositiu",
+ "attributeDeviceImage": "Imatge del dispositiu",
+ "attributeDeviceInactivityStart": "Inici d\\'inactivitat del dispositiu",
+ "attributeDeviceInactivityPeriod": "Període de inactivitat del dispositiu",
+ "attributeProcessingCopyAttributes": "Processant: Còpia dels atributs",
+ "attributeColor": "Color",
+ "attributeWebLiveRouteLength": "Web: Longitud de la ruta en directe",
+ "attributeWebSelectZoom": "Web: fer zoom al seleccionar",
+ "attributeWebMaxZoom": "Web: Zoom màxim",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Pushover User Key",
+ "attributePushoverDeviceNames": "Pushover Device Names",
+ "attributeMailSmtpHost": "Correu: Servidor SMTP",
+ "attributeMailSmtpPort": "Correu: Port SMTP",
+ "attributeMailSmtpStarttlsEnable": "Correu: Habilitar SMTP STARTTLS",
+ "attributeMailSmtpStarttlsRequired": "Correu: SMTP STARTTLS requerit",
+ "attributeMailSmtpSslEnable": "Correu: Habilitar SMTP SSL",
+ "attributeMailSmtpSslTrust": "Correu: SMTP SSL de confiança",
+ "attributeMailSmtpSslProtocols": "Correu: SMTP SSL protocols",
+ "attributeMailSmtpFrom": "Correu: SMTP desde",
+ "attributeMailSmtpAuth": "Correu: Habilitar autenticació SMTP",
+ "attributeMailSmtpUsername": "Correu: Nom d\\'usuari SMTP",
+ "attributeMailSmtpPassword": "Correu: Contrasenya SMTP",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: Disable Attributes",
+ "attributeUiDisableGroups": "UI: Deshabilitar Grups",
+ "attributeUiDisableEvents": "UI: Deshabilitar esdeveniments",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "UI: Deshabilitar Conductors",
+ "attributeUiDisableComputedAttributes": "UI: Deshabilitar Atributs calculats",
+ "attributeUiDisableCalendars": "UI: Deshabilitar Calendari",
+ "attributeUiDisableMaintenance": "UI: Deshabilitar Manteniment",
+ "attributeUiHidePositionAttributes": "UI: Ocultar Atributs de Posició",
+ "attributeUiDisableLoginLanguage": "UI: Deshabilitar Idioma de inici de sessió",
+ "attributeNotificationTokens": "Notificació Tokens",
+ "attributePopupInfo": "Informació emergent",
+ "errorTitle": "Error",
+ "errorGeneral": "Paràmetres no vàlids o fora dels límits",
+ "errorConnection": "Error en la connexió",
+ "errorSocket": "Error del Web-Socket",
+ "errorZero": "No pot ser zero",
+ "userEmail": "Email",
+ "userPassword": "Contrasenya",
+ "userAdmin": "Administrador",
+ "userRemember": "Recordar",
+ "userExpirationTime": "Caducitat",
+ "userDeviceLimit": "Límit de dispositius",
+ "userUserLimit": "Límit d\\'usuaris",
+ "userDeviceReadonly": "Dispositiu de sòls lectura",
+ "userLimitCommands": "Limitar Comandes",
+ "userDisableReports": "Deshabilitar Informes",
+ "userFixedEmail": "No Email Change",
+ "userToken": "Token Accés",
+ "userDeleteAccount": "Delete Account",
+ "userTemporary": "Temporary",
+ "loginTitle": "Iniciar sessió",
+ "loginLanguage": "Idioma",
+ "loginReset": "Reiniciar contrasenya",
+ "loginRegister": "Registrar-se",
+ "loginLogin": "Iniciar sessió",
+ "loginOpenId": "Inicieu sessió amb OpenID",
+ "loginFailed": "Email o contrasenya incorrecta",
+ "loginCreated": "S'ha registrat un usuari nou",
+ "loginResetSuccess": "Comprova el teu email",
+ "loginUpdateSuccess": "S'ha creat la nova contrasenya",
+ "loginLogout": "Tancar Sessió",
+ "loginLogo": "Logotipus",
+ "loginTotpCode": "Codi de contrasenya d'un sol ús",
+ "loginTotpKey": "Una clau de contrasenya d'un sol ús",
+ "devicesAndState": "Dispositius i Estat",
+ "deviceSelected": "Dispositiu seleccionat",
+ "deviceTitle": "Dispositius",
+ "devicePrimaryInfo": "Títol del dispositiu",
+ "deviceSecondaryInfo": "Detall del dispositiu",
+ "deviceIdentifier": "Identificador",
+ "deviceModel": "Model",
+ "deviceContact": "Contacte",
+ "deviceCategory": "Categoria",
+ "deviceLastUpdate": "Última Actualització",
+ "deviceCommand": "Comanda",
+ "deviceFollow": "Seguir",
+ "deviceTotalDistance": "Distància Total",
+ "deviceStatus": "Estat",
+ "deviceStatusOnline": "En línia",
+ "deviceStatusOffline": "Fora de línia",
+ "deviceStatusUnknown": "Desconegut",
+ "deviceRegisterFirst": "Registreu el vostre primer dispositiu",
+ "deviceIdentifierHelp": "IMEI, número de sèrie o un altre identificador. Ha de fer coincidir els informes del dispositiu identificador amb el servidor.",
+ "deviceShare": "Compartir dispositiu",
+ "groupDialog": "Grup",
+ "groupParent": "Grup",
+ "groupNoGroup": "Sense grup",
+ "settingsTitle": "Preferències",
+ "settingsUser": "Compte",
+ "settingsGroups": "Grups",
+ "settingsServer": "Servidor",
+ "settingsUsers": "Usuaris",
+ "settingsDistanceUnit": "Unitat de Distància",
+ "settingsAltitudeUnit": "Unitat d\\'Altitud",
+ "settingsSpeedUnit": "Unitat de Velocitat",
+ "settingsVolumeUnit": "Unitat de Volum",
+ "settingsTwelveHourFormat": "Format de 12h.",
+ "settingsCoordinateFormat": "Format de Coordenades",
+ "settingsServerVersion": "Versió del servidor",
+ "settingsAppVersion": "Versió de l'App",
+ "settingsConnection": "Connexió",
+ "settingsDarkMode": "Mode Fosc",
+ "settingsTotpEnable": "Habilita la contrasenya d'un sol ús",
+ "settingsTotpForce": "Força contrasenya d'un sol ús",
+ "settingsServiceWorkerUpdateInterval": "Interval d'actualització del ServiceWorker",
+ "settingsUpdateAvailable": "Hi ha una actualització disponible.",
+ "settingsSupport": "Support",
+ "reportTitle": "Informes",
+ "reportScheduled": "Informes programats",
+ "reportDevice": "Dispositius",
+ "reportGroup": "Grup",
+ "reportFrom": "Desde",
+ "reportTo": "Fins",
+ "reportShow": "Mostrar",
+ "reportClear": "Netejar",
+ "linkGoogleMaps": "Google Maps",
+ "linkAppleMaps": "Apple Maps",
+ "linkStreetView": "Street View",
+ "positionFixTime": "Hora registrada",
+ "positionDeviceTime": "Hora del Dispositiu",
+ "positionServerTime": "Hora del Servidor",
+ "positionValid": "Vàlida",
+ "positionAccuracy": "Precisió",
+ "positionLatitude": "Latitud",
+ "positionLongitude": "Longitud",
+ "positionAltitude": "Altitud",
+ "positionSpeed": "Velocitat",
+ "positionCourse": "Rumb",
+ "positionAddress": "Adreça",
+ "positionProtocol": "Protocol",
+ "positionDistance": "Distància",
+ "positionRpm": "RPM",
+ "positionFuel": "Combustible",
+ "positionPower": "Energia",
+ "positionBattery": "Bateria",
+ "positionRaw": "Cru",
+ "positionIndex": "Índex",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "Satèl·lits",
+ "positionSatVisible": "Satèl·lits Visibles",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "Roaming",
+ "positionEvent": "Esdeveniment",
+ "positionAlarm": "Alarma",
+ "positionStatus": "Estat",
+ "positionOdometer": "Odòmetre",
+ "positionServiceOdometer": "Odòmetre de manteniment",
+ "positionTripOdometer": "Odòmetre de viatge",
+ "positionHours": "Hores",
+ "positionSteps": "Passos",
+ "positionInput": "Entrada",
+ "positionHeartRate": "Freqüència cardíaca",
+ "positionOutput": "Sortida",
+ "positionBatteryLevel": "Nivell de bateria",
+ "positionFuelConsumption": "Consum de combustible",
+ "positionRfid": "RFDI",
+ "positionVersionFw": "Versió del firmware",
+ "positionVersionHw": "Versió del hardware",
+ "positionIgnition": "Encès",
+ "positionFlags": "Banderes",
+ "positionCharge": "Càrrega",
+ "positionIp": "IP",
+ "positionArchive": "Arxiu",
+ "positionVin": "VIN",
+ "positionApproximate": "Aproximat",
+ "positionThrottle": "Accelerador",
+ "positionMotion": "Moviment",
+ "positionArmed": "Armat",
+ "positionAcceleration": "Acceleració",
+ "positionTemp": "Temperatura",
+ "positionDeviceTemp": "Temperatura del dispositiu",
+ "positionCoolantTemp": "Temperatura del refrigerant",
+ "positionOperator": "Operador",
+ "positionCommand": "Comanda",
+ "positionBlocked": "Blocat",
+ "positionDtcs": "DTCs",
+ "positionObdSpeed": "Velocitat OBD",
+ "positionObdOdometer": "Odòmetre OBD",
+ "positionDrivingTime": "Temps de conducció",
+ "positionDriverUniqueId": "ID única del conductor",
+ "positionCard": "Targeta",
+ "positionImage": "Imatge",
+ "positionVideo": "Video",
+ "positionAudio": "Àudio",
+ "serverTitle": "Paràmetres del servidor",
+ "serverZoom": "Zoom",
+ "serverRegistration": "Registre",
+ "serverReadonly": "Sòls lectura",
+ "serverForceSettings": "Forçar aquests paràmetres",
+ "serverAnnouncement": "Mostrar anunci",
+ "serverName": "Nom del servidor",
+ "serverDescription": "Descripció del servidor",
+ "serverColorPrimary": "Color Primari",
+ "serverColorSecondary": "Color Secundari",
+ "serverLogo": "Imatge del logotip",
+ "serverLogoInverted": "Imatge de logotip invertida",
+ "serverChangeDisable": "Desactiva el canvi de servidor",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "Mapa",
+ "mapActive": "Mapes actius",
+ "mapOverlay": "Capa sobre el mapa",
+ "mapOverlayCustom": "Capa personalitzada",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API Key",
+ "mapOpenWeatherClouds": "OpenWeather Clouds",
+ "mapOpenWeatherPrecipitation": "OpenWeather Precipitation",
+ "mapOpenWeatherPressure": "OpenWeather Pressure",
+ "mapOpenWeatherWind": "OpenWeather Wind",
+ "mapOpenWeatherTemperature": "OpenWeather Temperature",
+ "mapLayer": "Capes del Mapa",
+ "mapCustom": "Personalitzat (XYZ)",
+ "mapCustomArcgis": "Personalitzat (ArcGIS)",
+ "mapCustomLabel": "Mapa Personalitzat",
+ "mapCarto": "Carto",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Clau Bing Maps",
+ "mapBingRoad": "Bing Maps - Carretera",
+ "mapBingAerial": "Bing Maps - Aèro",
+ "mapBingHybrid": "Bing Maps - Híbrid",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Yandex",
+ "mapYandexSat": "Yandex Satèl·lite",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox Streets",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox Outdoors",
+ "mapMapboxSatellite": "Mapbox Satellite",
+ "mapMapboxKey": "Mapbox Access Token",
+ "mapMapTilerBasic": "MapTiler Basic",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "MapTiler API Key",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Fosc",
+ "mapLocationIqKey": "LocationIQ Access Token",
+ "mapTomTomBasic": "TomTom Basic",
+ "mapTomTomFlow": "TomTom Traffic Flow",
+ "mapTomTomIncidents": "TomTom Traffic Incidents",
+ "mapTomTomKey": "TomTom API Key",
+ "mapHereBasic": "Here Basic",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satellite",
+ "mapHereFlow": "Here Traffic Flow",
+ "mapHereKey": "Here API Key",
+ "mapShapePolygon": "Polígon",
+ "mapShapeCircle": "Cercle",
+ "mapShapePolyline": "Polilínia",
+ "mapLiveRoutes": "Rutes en Directe",
+ "mapDirection": "Mostra la direcció",
+ "mapCurrentLocation": "Ubicació Actual",
+ "mapPoiLayer": "Capa POI",
+ "mapClustering": "Agrupar Marcadors",
+ "mapOnSelect": "Mostrar Mapes en la selecció",
+ "mapDefault": "Mapa per defecte",
+ "stateTitle": "Estat",
+ "stateName": "Paràmetre",
+ "stateValue": "Valor",
+ "commandTitle": "Comanda",
+ "commandSend": "Enviar",
+ "commandSent": "Comanda enviat",
+ "commandQueued": "Comanda en cua",
+ "commandUnit": "Unitat",
+ "commandCustom": "Comanda personalitzada",
+ "commandDeviceIdentification": "Identificació al Dispositiu",
+ "commandPositionSingle": "Informe Únic",
+ "commandPositionPeriodic": "Informe Periòdic",
+ "commandPositionStop": "Aturar informe",
+ "commandEngineStop": "Apagar motor",
+ "commandEngineResume": "Desblocar Encès de Motor",
+ "commandAlarmArm": "Armar Alarma",
+ "commandAlarmDisarm": "Desarmar Alarma",
+ "commandAlarmDismiss": "Descartar Alarma",
+ "commandSetTimezone": "Establir Zona Horària",
+ "commandRequestPhoto": "Sol·licitar Foto",
+ "commandPowerOff": "Apagar dispositiu",
+ "commandRebootDevice": "Reiniciar dispositiu",
+ "commandFactoryReset": "Valors de fàbrica",
+ "commandSendSms": "Enviar SMS",
+ "commandSendUssd": "Enviar USSD",
+ "commandSosNumber": "Establir el número SOS",
+ "commandSilenceTime": "Establir horari de silenci",
+ "commandSetPhonebook": "Establir contacte",
+ "commandVoiceMessage": "Missatge de veu",
+ "commandOutputControl": "Control de Sortides",
+ "commandVoiceMonitoring": "Monitoratge de Veu",
+ "commandSetAgps": "Establir AGPS",
+ "commandSetIndicator": "Establir indicador",
+ "commandConfiguration": "Configuració",
+ "commandGetVersion": "Obtenir Versió",
+ "commandFirmwareUpdate": "Actualitzar Firmware",
+ "commandSetConnection": "Establir Connexió",
+ "commandSetOdometer": "Establir Odòmetre",
+ "commandGetModemStatus": "Obtenir Estat del Mòdem",
+ "commandGetDeviceStatus": "Obtener Estat del Dispositiu",
+ "commandSetSpeedLimit": "Establir Límit de Velocitat",
+ "commandModePowerSaving": "Mode Estalvi de Energia",
+ "commandModeDeepSleep": "Mode Estalvi d'Energia Profund",
+ "commandAlarmGeofence": "Establir Alarma de Geo-Zona",
+ "commandAlarmBattery": "Establir Alarma de Bateria",
+ "commandAlarmSos": "Establir Alarma de SOS",
+ "commandAlarmRemove": "Establir eliminar Alarma",
+ "commandAlarmClock": "Establir Alarma d'Hora",
+ "commandAlarmSpeed": "Establir Alarma de Velocitat",
+ "commandAlarmFall": "Establir Alarma de Caiguda",
+ "commandAlarmVibration": "Establir Alarma de Vibració",
+ "commandFrequency": "Freqüència",
+ "commandTimezone": "Compensació de zona horaria",
+ "commandMessage": "Missatge",
+ "commandRadius": "Ràdio",
+ "commandEnable": "Activat",
+ "commandData": "Dades",
+ "commandIndex": "Índex",
+ "commandPhone": "Número de Telèfon",
+ "commandServer": "Servidor",
+ "commandPort": "Port",
+ "eventAll": "Tots els events",
+ "eventDeviceOnline": "Dispositiu en Línia",
+ "eventDeviceUnknown": "Dispositiu en estat Desconegut",
+ "eventDeviceOffline": "Dispositiu Fora de Línia",
+ "eventDeviceInactive": "Dispositiu Inactiu",
+ "eventQueuedCommandSent": "Comanda en cua enviada",
+ "eventDeviceMoving": "Dispositiu en Moviment",
+ "eventDeviceStopped": "Dispositiu Aturat",
+ "eventDeviceOverspeed": "Excés del límit de Velocitat",
+ "eventDeviceFuelDrop": "Pèrdua de Combustible",
+ "eventDeviceFuelIncrease": "Fuel increase",
+ "eventCommandResult": "Resultat de la comanda",
+ "eventGeofenceEnter": "Entrada a la Geo-Zona",
+ "eventGeofenceExit": "Sortida de la Geo-Zona",
+ "eventAlarm": "Alarma",
+ "eventIgnitionOn": "Ignició ON",
+ "eventIgnitionOff": "Ignició OFF",
+ "eventMaintenance": "Es requereix manteniment",
+ "eventTextMessage": "Missatge de text rebut",
+ "eventDriverChanged": "El conductor ha canviat",
+ "eventMedia": "Media",
+ "eventsScrollToLast": "Desplaçar-se fins l'últim",
+ "eventsSoundEvents": "So d'Esdeveniments",
+ "eventsSoundAlarms": "So d'Alarma",
+ "alarmGeneral": "General",
+ "alarmSos": "SOS",
+ "alarmVibration": "Vibració",
+ "alarmMovement": "Moviment",
+ "alarmLowspeed": "Baixa Velocitat",
+ "alarmOverspeed": "Excés de Velocitat",
+ "alarmFallDown": "Alarma de caiguda",
+ "alarmLowPower": "Energia baixa",
+ "alarmLowBattery": "Bateria Baixa",
+ "alarmFault": "Alarma de error",
+ "alarmPowerOff": "Apagat",
+ "alarmPowerOn": "Encès",
+ "alarmDoor": "Porta",
+ "alarmLock": "Blocat",
+ "alarmUnlock": "Desblocat",
+ "alarmGeofence": "Geo-Zona",
+ "alarmGeofenceEnter": "El Dispositiu ha entrat a la Geo-Zona",
+ "alarmGeofenceExit": "El Dispositiu ha sortit de la Geo-Zona",
+ "alarmGpsAntennaCut": "Antena del GPS tallada",
+ "alarmAccident": "Accident",
+ "alarmTow": "Grua d'arrossegament",
+ "alarmIdle": "Repòs",
+ "alarmHighRpm": "Revolucions altes",
+ "alarmHardAcceleration": "Acceleració brusca",
+ "alarmHardBraking": "Frenada extrema",
+ "alarmHardCornering": "Gir brusc",
+ "alarmLaneChange": "Canvi de carril",
+ "alarmFatigueDriving": "Conducció amb fatiga",
+ "alarmPowerCut": "Energia desconectada",
+ "alarmPowerRestored": "Energia restaurada",
+ "alarmJamming": "Interferència",
+ "alarmTemperature": "Temperatura",
+ "alarmParking": "Aparcament",
+ "alarmBonnet": "Capó",
+ "alarmFootBrake": "Fre de peu",
+ "alarmFuelLeak": "Fuga de combustible",
+ "alarmTampering": "Manipulació",
+ "alarmRemoving": "Eliminant",
+ "notificationType": "Tipus de Notificació",
+ "notificationAlways": "Tots els dispositius",
+ "notificationNotificators": "Canals",
+ "notificatorCommand": "Comanda",
+ "notificatorWeb": "Web",
+ "notificatorMail": "Correu",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Repetir",
+ "reportCombined": "Combinat",
+ "reportRoute": "Ruta",
+ "reportEvents": "Esdeveniments",
+ "reportTrips": "Viatges",
+ "reportStops": "Parades",
+ "reportSummary": "Resum",
+ "reportDaily": "Resum diari",
+ "reportChart": "Gràfica",
+ "reportConfigure": "Configurar",
+ "reportEventTypes": "Tipus d'esdeveniment",
+ "reportChartType": "Tipus de gràfica",
+ "reportShowMarkers": "Mostrar marcadors",
+ "reportExport": "Exportar",
+ "reportEmail": "Informe per correu",
+ "reportSchedule": "Horari",
+ "reportPeriod": "Període",
+ "reportCustom": "Personalitzat",
+ "reportToday": "Avui",
+ "reportYesterday": "Ahir",
+ "reportThisWeek": "Setmana Actual",
+ "reportPreviousWeek": "Setmana Anterior",
+ "reportThisMonth": "Mes Actual",
+ "reportPreviousMonth": "Mes Anterior",
+ "reportDeviceName": "Nom",
+ "reportAverageSpeed": "Velocitat Mitjana",
+ "reportMaximumSpeed": "Velocitat Màxima",
+ "reportEngineHours": "Hores de Motor",
+ "reportDuration": "Durada",
+ "reportStartDate": "Data Inical",
+ "reportStartTime": "Hora Inical",
+ "reportStartAddress": "Adreça Inicial",
+ "reportEndTime": "Hora Final",
+ "reportEndAddress": "Adreça Final",
+ "reportSpentFuel": "Combustible consumit",
+ "reportStartOdometer": "Odòmetre inical",
+ "reportEndOdometer": "Odòmetre final",
+ "statisticsTitle": "Estadístiques",
+ "statisticsCaptureTime": "Data de captura",
+ "statisticsActiveUsers": "Usuaris Actius",
+ "statisticsActiveDevices": "Dispositius Actius",
+ "statisticsRequests": "Peticions",
+ "statisticsMessagesReceived": "Missatges rebuts",
+ "statisticsMessagesStored": "Missatges guardats",
+ "statisticsGeocoder": "Sol·licituds del codificador geogràfic",
+ "statisticsGeolocation": "Sol·licituds de geolocalització",
+ "categoryArrow": "Fletxa",
+ "categoryDefault": "Predeterminat",
+ "categoryAnimal": "Animal",
+ "categoryBicycle": "Bicicleta",
+ "categoryBoat": "Vaixell",
+ "categoryBus": "Autobús",
+ "categoryCar": "Automòbil",
+ "categoryCamper": "Camper",
+ "categoryCrane": "Grua",
+ "categoryHelicopter": "Helicòpter",
+ "categoryMotorcycle": "Motocicleta",
+ "categoryOffroad": "Totterreny",
+ "categoryPerson": "Persona",
+ "categoryPickup": "Pickup",
+ "categoryPlane": "Avió",
+ "categoryShip": "Vaixell",
+ "categoryTractor": "Tractor",
+ "categoryTrain": "Tren",
+ "categoryTram": "Tramvia",
+ "categoryTrolleybus": "Troleibús",
+ "categoryTruck": "Camió",
+ "categoryVan": "Furgoneta",
+ "categoryScooter": "Moto",
+ "maintenanceStart": "Iniciar",
+ "maintenancePeriod": "Període"
+} \ No newline at end of file
diff --git a/src/resources/l10n/cs.json b/src/resources/l10n/cs.json
new file mode 100644
index 00000000..10a1f1fa
--- /dev/null
+++ b/src/resources/l10n/cs.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "Načítám...",
+ "sharedHide": "Skrýt",
+ "sharedSave": "Uložit",
+ "sharedUpload": "Upload",
+ "sharedSet": "Nastavit",
+ "sharedCancel": "Zrušit",
+ "sharedCopy": "Copy",
+ "sharedAdd": "Přidat",
+ "sharedEdit": "Změnit",
+ "sharedRemove": "Odstranit",
+ "sharedRemoveConfirm": "Odstranit položku?",
+ "sharedNoData": "No data",
+ "sharedSubject": "Subject",
+ "sharedYes": "Ano",
+ "sharedNo": "Ne",
+ "sharedKm": "km",
+ "sharedMi": "mi",
+ "sharedNmi": "nmi",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "kn",
+ "sharedKmh": "km/h",
+ "sharedMph": "mph",
+ "sharedHour": "Hodina",
+ "sharedMinute": "Minuta",
+ "sharedSecond": "Sekunda",
+ "sharedDays": "dny",
+ "sharedHours": "hodiny",
+ "sharedMinutes": "minuty",
+ "sharedDecimalDegrees": "Desetinné stupně",
+ "sharedDegreesDecimalMinutes": "Desetinné minuty",
+ "sharedDegreesMinutesSeconds": "Desetinné sekundy",
+ "sharedName": "Jméno",
+ "sharedDescription": "Popis",
+ "sharedSearch": "Hledat",
+ "sharedIconScale": "Icon Scale",
+ "sharedGeofence": "Zóna",
+ "sharedGeofences": "Zóny",
+ "sharedCreateGeofence": "Create Geofence",
+ "sharedNotifications": "Upozornění",
+ "sharedNotification": "Upozornění",
+ "sharedAttributes": "Atributy",
+ "sharedAttribute": "Atribut",
+ "sharedDrivers": "Řidiči",
+ "sharedDriver": "Řidič",
+ "sharedArea": "Oblast",
+ "sharedSound": "Zvuk upozornění",
+ "sharedType": "Typ",
+ "sharedDistance": "Vzdálenost",
+ "sharedHourAbbreviation": "h",
+ "sharedMinuteAbbreviation": "m",
+ "sharedSecondAbbreviation": "s",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "l",
+ "sharedGallonAbbreviation": "gal",
+ "sharedLiter": "Litr",
+ "sharedImpGallon": "Britský galon",
+ "sharedUsGallon": "Americký galon",
+ "sharedLiterPerHourAbbreviation": "l/h",
+ "sharedGetMapState": "Získat stav mapy",
+ "sharedComputedAttribute": "Vypočítaná vlastnost",
+ "sharedComputedAttributes": "Vypočítané vlastnosti",
+ "sharedCheckComputedAttribute": "Zkontrolovat vypočítanou vlastnost",
+ "sharedExpression": "Výraz",
+ "sharedDevice": "Zařízení",
+ "sharedTest": "Test",
+ "sharedTestNotification": "Odeslat testovací oznámení",
+ "sharedTestNotificators": "Test Channels",
+ "sharedTestExpression": "Test Expression",
+ "sharedCalendar": "Kalendář",
+ "sharedCalendars": "Kalendáře",
+ "sharedFile": "Soubor",
+ "sharedSearchDevices": "Search Devices",
+ "sharedSortBy": "Sort By",
+ "sharedFilterMap": "Filter on Map",
+ "sharedSelectFile": "Vybrat soubor",
+ "sharedPhone": "Telefon",
+ "sharedRequired": "Povinné",
+ "sharedPreferences": "Nastavení",
+ "sharedPermissions": "Oprávnění",
+ "sharedConnections": "Připojení",
+ "sharedExtra": "Volitelné",
+ "sharedPrimary": "Primary",
+ "sharedSecondary": "Secondary",
+ "sharedTypeString": "Řetězec",
+ "sharedTypeNumber": "Číslo",
+ "sharedTypeBoolean": "Logická hodnota",
+ "sharedTimezone": "Časová zóna",
+ "sharedInfoTitle": "Info",
+ "sharedSavedCommand": "Uložený příkaz",
+ "sharedSavedCommands": "Uložené příkazy",
+ "sharedNew": "Nový...",
+ "sharedShowAddress": "Zobrazit adresu",
+ "sharedShowDetails": "Více podrobností",
+ "sharedDisabled": "Zakázáno",
+ "sharedMaintenance": "Údržba",
+ "sharedDeviceAccumulators": "Stavy měřidel",
+ "sharedAlarms": "Alarmy",
+ "sharedLocation": "Lokace",
+ "sharedImport": "Import",
+ "sharedColumns": "Columns",
+ "sharedDropzoneText": "Drag and drop a file here or click",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "Simple",
+ "calendarRecurrence": "Recurrence",
+ "calendarOnce": "Once",
+ "calendarDaily": "Daily",
+ "calendarWeekly": "Weekly",
+ "calendarMonthly": "Monthly",
+ "calendarDays": "Days",
+ "calendarSunday": "Sunday",
+ "calendarMonday": "Monday",
+ "calendarTuesday": "Tuesday",
+ "calendarWednesday": "Wednesday",
+ "calendarThursday": "Thursday",
+ "calendarFriday": "Friday",
+ "calendarSaturday": "Saturday",
+ "attributeShowGeofences": "Show Geofences",
+ "attributeSpeedLimit": "Rychlostní limit",
+ "attributeFuelDropThreshold": "Fuel Drop Threshold",
+ "attributeFuelIncreaseThreshold": "Fuel Increase Threshold",
+ "attributePolylineDistance": "Vzdálenost křivky",
+ "attributeReportIgnoreOdometer": "Report: Počítadlo kilometrů",
+ "attributeWebReportColor": "Web: Barva reportu",
+ "attributeDevicePassword": "Heslo zařízení",
+ "attributeDeviceImage": "Device Image",
+ "attributeDeviceInactivityStart": "Počátek nečinnosti zařízení",
+ "attributeDeviceInactivityPeriod": "Období nečinnosti zařízení",
+ "attributeProcessingCopyAttributes": "Procesuji: Kopírování vlastností",
+ "attributeColor": "Barva",
+ "attributeWebLiveRouteLength": "Web: Aktuální délka trasy",
+ "attributeWebSelectZoom": "Web: Přiblížit při výběru",
+ "attributeWebMaxZoom": "Web: Maximální přiblížení",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Pushover User Key",
+ "attributePushoverDeviceNames": "Pushover Device Names",
+ "attributeMailSmtpHost": "Mail: SMTP server",
+ "attributeMailSmtpPort": "Mail: SMTP port",
+ "attributeMailSmtpStarttlsEnable": "Mail: SMTP STARTTLS povolit",
+ "attributeMailSmtpStarttlsRequired": "Mail: SMTP STARTTLS vyžadováno",
+ "attributeMailSmtpSslEnable": "Mail: SMTP SSL povolit",
+ "attributeMailSmtpSslTrust": "Mail: SMTP SSL důvěřovat",
+ "attributeMailSmtpSslProtocols": "Mail: SMTP SSL protokoly",
+ "attributeMailSmtpFrom": "Mail: SMTP od",
+ "attributeMailSmtpAuth": "Mail: SMTP povolení ověřování",
+ "attributeMailSmtpUsername": "Mail: SMTP uživatelské jméno",
+ "attributeMailSmtpPassword": "Mail: SMTP heslo",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: Disable Attributes",
+ "attributeUiDisableGroups": "UI: Disable Groups",
+ "attributeUiDisableEvents": "UI: Zakázat události",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "UI: Zakázat řidiče",
+ "attributeUiDisableComputedAttributes": "UI: Zakázat vypočítané vlastnosti",
+ "attributeUiDisableCalendars": "UI: Zakázat kalendáře",
+ "attributeUiDisableMaintenance": "UI: Zakázat údržbu",
+ "attributeUiHidePositionAttributes": "UI: Skrýt atributy pozice",
+ "attributeUiDisableLoginLanguage": "UI: Disable Login Language",
+ "attributeNotificationTokens": "Notifikační tokeny",
+ "attributePopupInfo": "Popup Info",
+ "errorTitle": "Chyba",
+ "errorGeneral": "Nesprávné parametry nebo překročení omezení",
+ "errorConnection": "Chyba spojení",
+ "errorSocket": "Chyba připojení webového socketu",
+ "errorZero": "Nemůže být nula",
+ "userEmail": "Email",
+ "userPassword": "Heslo",
+ "userAdmin": "Admin",
+ "userRemember": "Zapamatovat",
+ "userExpirationTime": "Konec platnosti",
+ "userDeviceLimit": "Limit zařízení",
+ "userUserLimit": "Limit uživatele",
+ "userDeviceReadonly": "Zařízení pouze pro čtení",
+ "userLimitCommands": "Limit příkazů",
+ "userDisableReports": "Disable Reports",
+ "userFixedEmail": "No Email Change",
+ "userToken": "Token",
+ "userDeleteAccount": "Delete Account",
+ "userTemporary": "Temporary",
+ "loginTitle": "Přihlášení",
+ "loginLanguage": "Jazyk",
+ "loginReset": "Obnovit Heslo",
+ "loginRegister": "Registrace",
+ "loginLogin": "Přihlášení",
+ "loginOpenId": "Login with OpenID",
+ "loginFailed": "Nesprávný email nebo heslo",
+ "loginCreated": "Nový uživatel byl zaregistrován",
+ "loginResetSuccess": "Zkontroluj si email ",
+ "loginUpdateSuccess": "Nové heslo je nastaveno",
+ "loginLogout": "Odhlášení",
+ "loginLogo": "Logo",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "Zařízení a stav",
+ "deviceSelected": "Selected Device",
+ "deviceTitle": "Zařízení",
+ "devicePrimaryInfo": "Device Title",
+ "deviceSecondaryInfo": "Device Detail",
+ "deviceIdentifier": "Identifikace",
+ "deviceModel": "Model",
+ "deviceContact": "Kontakt",
+ "deviceCategory": "Kategorie",
+ "deviceLastUpdate": "Poslední změna",
+ "deviceCommand": "Příkaz",
+ "deviceFollow": "Sledovat",
+ "deviceTotalDistance": "Celková vzdálenost",
+ "deviceStatus": "Stav",
+ "deviceStatusOnline": "Online",
+ "deviceStatusOffline": "Offline",
+ "deviceStatusUnknown": "Neznámý",
+ "deviceRegisterFirst": "Register your first device",
+ "deviceIdentifierHelp": "IMEI, serial number or other id. It has to match the identifier device reports to the server.",
+ "deviceShare": "Share Device",
+ "groupDialog": "Skupina",
+ "groupParent": "Skupina",
+ "groupNoGroup": "Žádná skupina",
+ "settingsTitle": "Nastavení",
+ "settingsUser": "Účet",
+ "settingsGroups": "Skupiny",
+ "settingsServer": "Server",
+ "settingsUsers": "Uživatelé",
+ "settingsDistanceUnit": "Jednotka vzdálenosti",
+ "settingsAltitudeUnit": "Altitude Unit",
+ "settingsSpeedUnit": "Jednotka rychlosti",
+ "settingsVolumeUnit": "Jednotka objemu",
+ "settingsTwelveHourFormat": "12-hodinový formát",
+ "settingsCoordinateFormat": "Formát souřadnic",
+ "settingsServerVersion": "Server Version",
+ "settingsAppVersion": "App Version",
+ "settingsConnection": "Connection",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "Reporty",
+ "reportScheduled": "Scheduled Reports",
+ "reportDevice": "Zařízení",
+ "reportGroup": "Skupina",
+ "reportFrom": "Od",
+ "reportTo": "Komu",
+ "reportShow": "Zobrazit",
+ "reportClear": "Vyprázdnit",
+ "linkGoogleMaps": "Google Maps",
+ "linkAppleMaps": "Apple Maps",
+ "linkStreetView": "Street View",
+ "positionFixTime": "Fix Time",
+ "positionDeviceTime": "Device Time",
+ "positionServerTime": "Server Time",
+ "positionValid": "Správný",
+ "positionAccuracy": "Přesnost",
+ "positionLatitude": "Šířka",
+ "positionLongitude": "Délka",
+ "positionAltitude": "Výška",
+ "positionSpeed": "Rychlost",
+ "positionCourse": "Směr",
+ "positionAddress": "Adresa",
+ "positionProtocol": "Protokol",
+ "positionDistance": "Vzdálenost",
+ "positionRpm": "otáček",
+ "positionFuel": "Palivo",
+ "positionPower": "Energie",
+ "positionBattery": "Baterie",
+ "positionRaw": "Čisté",
+ "positionIndex": "Index",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "Satelity",
+ "positionSatVisible": "Viditelné satelity",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "Roaming",
+ "positionEvent": "Událost",
+ "positionAlarm": "Alarm",
+ "positionStatus": "Status",
+ "positionOdometer": "Počítadlo kilometrů",
+ "positionServiceOdometer": "Servisní počítadlo kilometrů",
+ "positionTripOdometer": "Počítadlo kilometrů cesty",
+ "positionHours": "Hodiny",
+ "positionSteps": "Kroky",
+ "positionInput": "Vstup",
+ "positionHeartRate": "Heart Rate",
+ "positionOutput": "Výstup",
+ "positionBatteryLevel": "Úroveň baterie",
+ "positionFuelConsumption": "Spotřeba paliva",
+ "positionRfid": "RFID",
+ "positionVersionFw": "Verze firmwaru",
+ "positionVersionHw": "Verze hardwaru",
+ "positionIgnition": "Zapalování",
+ "positionFlags": "Příznaky",
+ "positionCharge": "Nabití",
+ "positionIp": "IP",
+ "positionArchive": "Archiv",
+ "positionVin": "VIN",
+ "positionApproximate": "Přibližný",
+ "positionThrottle": "Škrtící klapka",
+ "positionMotion": "Pohyb",
+ "positionArmed": "Odjištěno",
+ "positionAcceleration": "Akcelerace",
+ "positionTemp": "Temperature",
+ "positionDeviceTemp": "Teplota zařízení",
+ "positionCoolantTemp": "Coolant Temperature",
+ "positionOperator": "Operátor",
+ "positionCommand": "Příkaz",
+ "positionBlocked": "Blokované",
+ "positionDtcs": "DTC",
+ "positionObdSpeed": "OBD rychlost",
+ "positionObdOdometer": "OBD počítadlo kilometrů",
+ "positionDrivingTime": "Driving Time",
+ "positionDriverUniqueId": "Unikátní ID ridiče",
+ "positionCard": "Card",
+ "positionImage": "Obraz",
+ "positionVideo": "Video",
+ "positionAudio": "Zvuk",
+ "serverTitle": "Nastavení serveru",
+ "serverZoom": "Přiblížení",
+ "serverRegistration": "Registrace",
+ "serverReadonly": "Pouze pro čtení",
+ "serverForceSettings": "Vynutit nastavení",
+ "serverAnnouncement": "Oznámení",
+ "serverName": "Server Name",
+ "serverDescription": "Server Description",
+ "serverColorPrimary": "Color Primary",
+ "serverColorSecondary": "Color Secondary",
+ "serverLogo": "Logo Image",
+ "serverLogoInverted": "Inverted Logo Image",
+ "serverChangeDisable": "Disable Server Change",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "Mapa",
+ "mapActive": "Active Maps",
+ "mapOverlay": "Map Overlay",
+ "mapOverlayCustom": "Custom Overlay",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API Key",
+ "mapOpenWeatherClouds": "OpenWeather Clouds",
+ "mapOpenWeatherPrecipitation": "OpenWeather Precipitation",
+ "mapOpenWeatherPressure": "OpenWeather Pressure",
+ "mapOpenWeatherWind": "OpenWeather Wind",
+ "mapOpenWeatherTemperature": "OpenWeather Temperature",
+ "mapLayer": "Vrstva mapy",
+ "mapCustom": "Vlastní (XYZ)",
+ "mapCustomArcgis": "Vlastní (ArcGIS)",
+ "mapCustomLabel": "Vlastní mapa",
+ "mapCarto": "Základní mapy nákladu",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Bing Maps klíč",
+ "mapBingRoad": "Bing Maps silniční",
+ "mapBingAerial": "Bing Maps Letecký",
+ "mapBingHybrid": "Bing Maps Hybridní",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Yandex mapa",
+ "mapYandexSat": "Yandex satelitní",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox ulice",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox venkovní",
+ "mapMapboxSatellite": "Mapbox satelit",
+ "mapMapboxKey": "Mapbox Access Token",
+ "mapMapTilerBasic": "MapTiler Základní",
+ "mapMapTilerHybrid": "MapTiler Hybridní",
+ "mapMapTilerKey": "MapTiler API Key",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ Access Token",
+ "mapTomTomBasic": "TomTom Basic",
+ "mapTomTomFlow": "TomTom Traffic Flow",
+ "mapTomTomIncidents": "TomTom Traffic Incidents",
+ "mapTomTomKey": "TomTom API Key",
+ "mapHereBasic": "Here Basic",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satellite",
+ "mapHereFlow": "Here Traffic Flow",
+ "mapHereKey": "Here API Key",
+ "mapShapePolygon": "Mnohoúhelník",
+ "mapShapeCircle": "Kruh",
+ "mapShapePolyline": "Křivka",
+ "mapLiveRoutes": "Trasy živě",
+ "mapDirection": "Show Direction",
+ "mapCurrentLocation": "Současná pozice",
+ "mapPoiLayer": "Vrstva s POI",
+ "mapClustering": "Markers Clustering",
+ "mapOnSelect": "Show Map on Selection",
+ "mapDefault": "Default Map",
+ "stateTitle": "Stav",
+ "stateName": "Atribut",
+ "stateValue": "Hodnota",
+ "commandTitle": "Příkaz",
+ "commandSend": "Odeslat",
+ "commandSent": "Příkaz odeslán",
+ "commandQueued": "Příkaz zařazen do fronty",
+ "commandUnit": "Jednotka",
+ "commandCustom": "Volitelný příkaz",
+ "commandDeviceIdentification": "Identifikace zařízení",
+ "commandPositionSingle": "Jednotný report",
+ "commandPositionPeriodic": "Pravidelný report",
+ "commandPositionStop": "Zastavit report",
+ "commandEngineStop": "Zastavit motor",
+ "commandEngineResume": "Nastartovat motor",
+ "commandAlarmArm": "Aktivovat alarm",
+ "commandAlarmDisarm": "Deaktivovat alarm",
+ "commandAlarmDismiss": "Zrušit alarm",
+ "commandSetTimezone": "Nastavit časovou zónu",
+ "commandRequestPhoto": "Vyžádat fotku",
+ "commandPowerOff": "Vypnout zařízení",
+ "commandRebootDevice": "Restartovat zařízení",
+ "commandFactoryReset": "Obnovit nastavení",
+ "commandSendSms": "Odeslat SMS",
+ "commandSendUssd": "Odeslat USSD",
+ "commandSosNumber": "Nastavit SOS číslo",
+ "commandSilenceTime": "Nastavit čas tichého módu",
+ "commandSetPhonebook": "Nastavit telefonní seznam",
+ "commandVoiceMessage": "Hlasová zpráva",
+ "commandOutputControl": "Ovládání výstupu",
+ "commandVoiceMonitoring": "Monitorování hlasu",
+ "commandSetAgps": "Nastavit AGPS",
+ "commandSetIndicator": "Nastavit indikátor",
+ "commandConfiguration": "Nastavení",
+ "commandGetVersion": "Zjistit verzi",
+ "commandFirmwareUpdate": "Aktualizovat firmware",
+ "commandSetConnection": "Nastavit spojení",
+ "commandSetOdometer": "Nastavit počítadlo kilometrů",
+ "commandGetModemStatus": "Získat stav modemu",
+ "commandGetDeviceStatus": "Získat stav zařízení",
+ "commandSetSpeedLimit": "Nastavit rychlostní limit",
+ "commandModePowerSaving": "Power Saving Mode",
+ "commandModeDeepSleep": "Deep Sleep Mode",
+ "commandAlarmGeofence": "Set Geofence Alarm",
+ "commandAlarmBattery": "Set Battery Alarm",
+ "commandAlarmSos": "Set SOS Alarm",
+ "commandAlarmRemove": "Set Remove Alarm",
+ "commandAlarmClock": "Set Clock Alarm",
+ "commandAlarmSpeed": "Set Speed Alarm",
+ "commandAlarmFall": "Set Fall Alarm",
+ "commandAlarmVibration": "Set Vibration Alarm",
+ "commandFrequency": "Frekvence",
+ "commandTimezone": "Ofset časové zóny",
+ "commandMessage": "Zpráva",
+ "commandRadius": "Okruh",
+ "commandEnable": "Povolit",
+ "commandData": "Data",
+ "commandIndex": "Index",
+ "commandPhone": "Telefonní číslo",
+ "commandServer": "Server",
+ "commandPort": "Port",
+ "eventAll": "Všechny události",
+ "eventDeviceOnline": "Status online",
+ "eventDeviceUnknown": "Status neznámý",
+ "eventDeviceOffline": "Status offline",
+ "eventDeviceInactive": "Zařízení je neaktivní",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "Zařízení se přesouvá",
+ "eventDeviceStopped": "Zařízení bylo zastaveno",
+ "eventDeviceOverspeed": "Rychlostní limit překročen",
+ "eventDeviceFuelDrop": "Pokles paliva",
+ "eventDeviceFuelIncrease": "Fuel increase",
+ "eventCommandResult": "Výsledek příkazu",
+ "eventGeofenceEnter": "Vstoupeno do geozóny",
+ "eventGeofenceExit": "Geozóna opuštěna",
+ "eventAlarm": "Alarm",
+ "eventIgnitionOn": "Zážeh zapnutý",
+ "eventIgnitionOff": "Zážeh vypnutý",
+ "eventMaintenance": "Vyžadována údržba",
+ "eventTextMessage": "Obdržena textová zpráva",
+ "eventDriverChanged": "Řidič vyměněn",
+ "eventMedia": "Media",
+ "eventsScrollToLast": "Posunout na poslední",
+ "eventsSoundEvents": "Sound Events",
+ "eventsSoundAlarms": "Sound Alarms",
+ "alarmGeneral": "Hlavní",
+ "alarmSos": "SOS",
+ "alarmVibration": "Vibrace",
+ "alarmMovement": "Pohyb",
+ "alarmLowspeed": "Nízká rychlost",
+ "alarmOverspeed": "Překročení rychlosti",
+ "alarmFallDown": "Pád",
+ "alarmLowPower": "Nízký výkon",
+ "alarmLowBattery": "Nízké napětí baterie",
+ "alarmFault": "Chyba",
+ "alarmPowerOff": "Vypnout",
+ "alarmPowerOn": "Zapnout",
+ "alarmDoor": "Dveře",
+ "alarmLock": "Zamknout",
+ "alarmUnlock": "Odemknout",
+ "alarmGeofence": "Geozóna",
+ "alarmGeofenceEnter": "Vstup do geozóny",
+ "alarmGeofenceExit": "Konec geozóny",
+ "alarmGpsAntennaCut": "Odpojení GPS antény",
+ "alarmAccident": "Nehoda",
+ "alarmTow": "Vlek",
+ "alarmIdle": "Nečinnost",
+ "alarmHighRpm": "Vysoké ot/min",
+ "alarmHardAcceleration": "Silná akcelerace",
+ "alarmHardBraking": "Silné brždění",
+ "alarmHardCornering": "Jízda smykem",
+ "alarmLaneChange": "Změna pruhu",
+ "alarmFatigueDriving": "Únava při řízení",
+ "alarmPowerCut": "Výpadek napájení",
+ "alarmPowerRestored": "Napájení obnoveno",
+ "alarmJamming": "Zaseknutí",
+ "alarmTemperature": "Teplota",
+ "alarmParking": "Parkování",
+ "alarmBonnet": "Kapota",
+ "alarmFootBrake": "Nožní brzda",
+ "alarmFuelLeak": "Únik paliva",
+ "alarmTampering": "Manipulování",
+ "alarmRemoving": "Odstraňuji",
+ "notificationType": "Typ oznámení",
+ "notificationAlways": "Všechna zařízení",
+ "notificationNotificators": "Kanály",
+ "notificatorCommand": "Command",
+ "notificatorWeb": "Web",
+ "notificatorMail": "Mail",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Přehrát",
+ "reportCombined": "Combined",
+ "reportRoute": "Trasa",
+ "reportEvents": "Události",
+ "reportTrips": "Cesty",
+ "reportStops": "Zastávky",
+ "reportSummary": "Souhrn",
+ "reportDaily": "Denní přehled",
+ "reportChart": "Graf",
+ "reportConfigure": "Nastavit",
+ "reportEventTypes": "Typy událostí",
+ "reportChartType": "Typ grafu",
+ "reportShowMarkers": "Zobrazit značky",
+ "reportExport": "Exportovat",
+ "reportEmail": "Zaslat report mailem",
+ "reportSchedule": "Schedule",
+ "reportPeriod": "Období",
+ "reportCustom": "Vlastní",
+ "reportToday": "Dnes",
+ "reportYesterday": "Včera",
+ "reportThisWeek": "Tento týden",
+ "reportPreviousWeek": "Minulý týden",
+ "reportThisMonth": "Tento měsíc",
+ "reportPreviousMonth": "Minulý měsíc",
+ "reportDeviceName": "Jméno zařízení",
+ "reportAverageSpeed": "Průměrná rychlost",
+ "reportMaximumSpeed": "Maximální rychlost",
+ "reportEngineHours": "Motohodiny",
+ "reportDuration": "Trvání",
+ "reportStartDate": "Počáteční datum",
+ "reportStartTime": "Čas startu",
+ "reportStartAddress": "Adresa startu",
+ "reportEndTime": "Čas konce",
+ "reportEndAddress": "Adresa konce",
+ "reportSpentFuel": "Spotřebované palivo",
+ "reportStartOdometer": "Start počítadla kilometrů",
+ "reportEndOdometer": "Konec počítadla kilometrů",
+ "statisticsTitle": "Statistiky",
+ "statisticsCaptureTime": "Čas zachycení",
+ "statisticsActiveUsers": "Aktivní uživatelé",
+ "statisticsActiveDevices": "Aktivní zařízení",
+ "statisticsRequests": "Požadavky",
+ "statisticsMessagesReceived": "Obdržené zprávy",
+ "statisticsMessagesStored": "Uložené zprávy",
+ "statisticsGeocoder": "Požadavky Geocodera",
+ "statisticsGeolocation": "Požadavky Geolokace",
+ "categoryArrow": "Šipka",
+ "categoryDefault": "Výchozí",
+ "categoryAnimal": "Zvíře",
+ "categoryBicycle": "Kolo",
+ "categoryBoat": "Loď",
+ "categoryBus": "Autobus",
+ "categoryCar": "Auto",
+ "categoryCamper": "Camper",
+ "categoryCrane": "Jeřáb",
+ "categoryHelicopter": "Helikoptéra",
+ "categoryMotorcycle": "Motocykl",
+ "categoryOffroad": "Offroad",
+ "categoryPerson": "Osoba",
+ "categoryPickup": "Pickup",
+ "categoryPlane": "Letadlo",
+ "categoryShip": "Loď",
+ "categoryTractor": "Traktor",
+ "categoryTrain": "Vlak",
+ "categoryTram": "Tramvaj",
+ "categoryTrolleybus": "Trolejbus",
+ "categoryTruck": "Nákladní auto",
+ "categoryVan": "Dodávka",
+ "categoryScooter": "Koloběžka",
+ "maintenanceStart": "Start",
+ "maintenancePeriod": "Perioda"
+} \ No newline at end of file
diff --git a/src/resources/l10n/da.json b/src/resources/l10n/da.json
new file mode 100644
index 00000000..4c24a3c6
--- /dev/null
+++ b/src/resources/l10n/da.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "Indlæser... ",
+ "sharedHide": "Skjul",
+ "sharedSave": "Gem",
+ "sharedUpload": "Upload",
+ "sharedSet": "Indstil",
+ "sharedCancel": "Fortryd",
+ "sharedCopy": "Copy",
+ "sharedAdd": "Tilføj",
+ "sharedEdit": "Rediger",
+ "sharedRemove": "Fjern",
+ "sharedRemoveConfirm": "Fjern enhed?",
+ "sharedNoData": "No data",
+ "sharedSubject": "Subject",
+ "sharedYes": "Ja",
+ "sharedNo": "Nej",
+ "sharedKm": "km",
+ "sharedMi": "mi",
+ "sharedNmi": "Nml",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "knob",
+ "sharedKmh": "km/t",
+ "sharedMph": "mph",
+ "sharedHour": "Time",
+ "sharedMinute": "Minut",
+ "sharedSecond": "Sekund",
+ "sharedDays": "dage",
+ "sharedHours": "timer",
+ "sharedMinutes": "minutter",
+ "sharedDecimalDegrees": "Decimal grader",
+ "sharedDegreesDecimalMinutes": "Grader decimal minutter",
+ "sharedDegreesMinutesSeconds": "Grader decimal sekunder",
+ "sharedName": "Navn",
+ "sharedDescription": "Beskrivelse",
+ "sharedSearch": "Søg",
+ "sharedIconScale": "Icon Scale",
+ "sharedGeofence": "Geofence",
+ "sharedGeofences": "Geofences",
+ "sharedCreateGeofence": "Create Geofence",
+ "sharedNotifications": "Notifikationer",
+ "sharedNotification": "Notifikation",
+ "sharedAttributes": "Egenskaber",
+ "sharedAttribute": "Egenskab",
+ "sharedDrivers": "Chauffører",
+ "sharedDriver": "Chauffør",
+ "sharedArea": "Område",
+ "sharedSound": "Advarsels lyd",
+ "sharedType": "Type",
+ "sharedDistance": "Afstand",
+ "sharedHourAbbreviation": "t",
+ "sharedMinuteAbbreviation": "m",
+ "sharedSecondAbbreviation": "s",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "I",
+ "sharedGallonAbbreviation": "gal",
+ "sharedLiter": "Liter",
+ "sharedImpGallon": "Motorhjelm",
+ "sharedUsGallon": "Amerikansk gallon",
+ "sharedLiterPerHourAbbreviation": "l/time",
+ "sharedGetMapState": "Kort status",
+ "sharedComputedAttribute": "Udregnet attribut",
+ "sharedComputedAttributes": "Udregnede attributter",
+ "sharedCheckComputedAttribute": "Kontroller udregnet attribut",
+ "sharedExpression": "Udtryk",
+ "sharedDevice": "Enhed",
+ "sharedTest": "Test",
+ "sharedTestNotification": "Send Test notifikation",
+ "sharedTestNotificators": "Test Channels",
+ "sharedTestExpression": "Test Expression",
+ "sharedCalendar": "Kalender",
+ "sharedCalendars": "Kalendere",
+ "sharedFile": "Fil",
+ "sharedSearchDevices": "Search Devices",
+ "sharedSortBy": "Sort By",
+ "sharedFilterMap": "Filter on Map",
+ "sharedSelectFile": "Vælg fil",
+ "sharedPhone": "Telefon",
+ "sharedRequired": "Krævet",
+ "sharedPreferences": "Foretrukne",
+ "sharedPermissions": "Tilladelser",
+ "sharedConnections": "Forbindelser",
+ "sharedExtra": "Ekstra",
+ "sharedPrimary": "Primary",
+ "sharedSecondary": "Secondary",
+ "sharedTypeString": "tekststreng",
+ "sharedTypeNumber": "ciffer",
+ "sharedTypeBoolean": "Boolsk",
+ "sharedTimezone": "Tidszone",
+ "sharedInfoTitle": "Info",
+ "sharedSavedCommand": "Gemt kommando",
+ "sharedSavedCommands": "Gemte Kommandoer",
+ "sharedNew": "Ny...",
+ "sharedShowAddress": "Vis adresse",
+ "sharedShowDetails": "Flere detailer",
+ "sharedDisabled": "Deaktiveret",
+ "sharedMaintenance": "Vedligeholdelse",
+ "sharedDeviceAccumulators": "Akkumulatorer",
+ "sharedAlarms": "Alarmer",
+ "sharedLocation": "Beliggenhed",
+ "sharedImport": "Importer",
+ "sharedColumns": "Columns",
+ "sharedDropzoneText": "Drag and drop a file here or click",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "Simple",
+ "calendarRecurrence": "Recurrence",
+ "calendarOnce": "Once",
+ "calendarDaily": "Daily",
+ "calendarWeekly": "Weekly",
+ "calendarMonthly": "Monthly",
+ "calendarDays": "Days",
+ "calendarSunday": "Sunday",
+ "calendarMonday": "Monday",
+ "calendarTuesday": "Tuesday",
+ "calendarWednesday": "Wednesday",
+ "calendarThursday": "Thursday",
+ "calendarFriday": "Friday",
+ "calendarSaturday": "Saturday",
+ "attributeShowGeofences": "Show Geofences",
+ "attributeSpeedLimit": "Hastigheds grænse",
+ "attributeFuelDropThreshold": "Fuel Drop Threshold",
+ "attributeFuelIncreaseThreshold": "Fuel Increase Threshold",
+ "attributePolylineDistance": "Polyline afstand",
+ "attributeReportIgnoreOdometer": "Rapport: Ignorer odometer",
+ "attributeWebReportColor": "Web : Rapport farve",
+ "attributeDevicePassword": "Enheds kodeord",
+ "attributeDeviceImage": "Device Image",
+ "attributeDeviceInactivityStart": "Start af enhedens inaktivitet",
+ "attributeDeviceInactivityPeriod": "Enheds inaktivitetsperiode",
+ "attributeProcessingCopyAttributes": "Bearbejder: Kopier attribut",
+ "attributeColor": "Farve",
+ "attributeWebLiveRouteLength": "Web: live rute længde",
+ "attributeWebSelectZoom": "Web: Zoom ved vælg",
+ "attributeWebMaxZoom": "Web: Maksimal zoom",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Pushover User Key",
+ "attributePushoverDeviceNames": "Pushover Device Names",
+ "attributeMailSmtpHost": "Mail: SMTP host",
+ "attributeMailSmtpPort": "Mail : SMTP port",
+ "attributeMailSmtpStarttlsEnable": "Mail: SMTP STARTTLS Enable",
+ "attributeMailSmtpStarttlsRequired": "Mail: SMTP STARTTLS Required",
+ "attributeMailSmtpSslEnable": "Mail: SMTP slå SSL til",
+ "attributeMailSmtpSslTrust": "Mail: SMTP SSL Trust ",
+ "attributeMailSmtpSslProtocols": "Mail: SMTP SSL Protocols",
+ "attributeMailSmtpFrom": "Mail: SMTP From ",
+ "attributeMailSmtpAuth": "Mail: SMTP Auth Enable ",
+ "attributeMailSmtpUsername": "Mail: SMTP Brugernavn",
+ "attributeMailSmtpPassword": "Mail: SMTP Kodeord",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: Disable Attributes",
+ "attributeUiDisableGroups": "UI: Disable Groups",
+ "attributeUiDisableEvents": "Deaktiver begivenheder",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "Deaktiver chauffører",
+ "attributeUiDisableComputedAttributes": "Deaktiver beregnede attributter",
+ "attributeUiDisableCalendars": "Deaktiver kalendere",
+ "attributeUiDisableMaintenance": "Deaktiver vedligeholdelse",
+ "attributeUiHidePositionAttributes": "Skjul positionsattributter",
+ "attributeUiDisableLoginLanguage": "UI: Disable Login Language",
+ "attributeNotificationTokens": "Notifikations Tokens",
+ "attributePopupInfo": "Popup Info",
+ "errorTitle": "Fejl",
+ "errorGeneral": "ulovlig parameter ",
+ "errorConnection": "Tilslutning fejl",
+ "errorSocket": "Web tilslutnings fejl",
+ "errorZero": "Kan ikke være nul",
+ "userEmail": "Email",
+ "userPassword": "Kodeord",
+ "userAdmin": "Admin",
+ "userRemember": "Husk",
+ "userExpirationTime": "Udløb",
+ "userDeviceLimit": "Enheds grænse",
+ "userUserLimit": "Bruger grænse",
+ "userDeviceReadonly": "Enhed kun læsbar",
+ "userLimitCommands": "Begrænset kommandoer",
+ "userDisableReports": "Disable Reports",
+ "userFixedEmail": "No Email Change",
+ "userToken": "Token",
+ "userDeleteAccount": "Delete Account",
+ "userTemporary": "Temporary",
+ "loginTitle": "Log på",
+ "loginLanguage": "Sprog",
+ "loginReset": "Nulstille kodeord",
+ "loginRegister": "Registrer",
+ "loginLogin": "Log på",
+ "loginOpenId": "Login with OpenID",
+ "loginFailed": "Fejl i email adresse eller kodeord",
+ "loginCreated": "Ny bruger er registreret",
+ "loginResetSuccess": "Tjek din e-mail",
+ "loginUpdateSuccess": "Ny adgangskode er indstillet",
+ "loginLogout": "Log af",
+ "loginLogo": "Logo",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "Enheder og status",
+ "deviceSelected": "Selected Device",
+ "deviceTitle": "Enheder",
+ "devicePrimaryInfo": "Device Title",
+ "deviceSecondaryInfo": "Device Detail",
+ "deviceIdentifier": "Imei nr",
+ "deviceModel": "Model",
+ "deviceContact": "Kontakt",
+ "deviceCategory": "Kategori",
+ "deviceLastUpdate": "Seneste opdatering",
+ "deviceCommand": "Kommando",
+ "deviceFollow": "Følg",
+ "deviceTotalDistance": "Total distance",
+ "deviceStatus": "Status",
+ "deviceStatusOnline": "On-line",
+ "deviceStatusOffline": "Off-line",
+ "deviceStatusUnknown": "Ukendt",
+ "deviceRegisterFirst": "Register your first device",
+ "deviceIdentifierHelp": "IMEI, serial number or other id. It has to match the identifier device reports to the server.",
+ "deviceShare": "Share Device",
+ "groupDialog": "Gruppe",
+ "groupParent": "Gruppe",
+ "groupNoGroup": "Ingen gruppe",
+ "settingsTitle": "Indstillinger",
+ "settingsUser": "Konto",
+ "settingsGroups": "Grupper",
+ "settingsServer": "Server",
+ "settingsUsers": "Brugere",
+ "settingsDistanceUnit": "Afstands enhed",
+ "settingsAltitudeUnit": "Altitude Unit",
+ "settingsSpeedUnit": "Hastigheds enhed",
+ "settingsVolumeUnit": "Volume enhed",
+ "settingsTwelveHourFormat": "12 timers format",
+ "settingsCoordinateFormat": "Koordinats format",
+ "settingsServerVersion": "Server Version",
+ "settingsAppVersion": "App Version",
+ "settingsConnection": "Connection",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "Rapporter",
+ "reportScheduled": "Scheduled Reports",
+ "reportDevice": "Enhed",
+ "reportGroup": "Gruppe",
+ "reportFrom": "Fra",
+ "reportTo": "Til",
+ "reportShow": "Vis",
+ "reportClear": "Ryd",
+ "linkGoogleMaps": "Google Maps",
+ "linkAppleMaps": "Apple Maps",
+ "linkStreetView": "Street View",
+ "positionFixTime": "Fix Time",
+ "positionDeviceTime": "Device Time",
+ "positionServerTime": "Server Time",
+ "positionValid": "Valid",
+ "positionAccuracy": "Nøjagtighed",
+ "positionLatitude": "Breddegrad",
+ "positionLongitude": "Længdegrad",
+ "positionAltitude": "Højde",
+ "positionSpeed": "Hastighed",
+ "positionCourse": "Kurs",
+ "positionAddress": "Adresse",
+ "positionProtocol": "Protokol",
+ "positionDistance": "Distance",
+ "positionRpm": "Omdr.",
+ "positionFuel": "Brændstof",
+ "positionPower": "Strøm",
+ "positionBattery": "Batteri",
+ "positionRaw": "Rå",
+ "positionIndex": "Indeks",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "Satelitter",
+ "positionSatVisible": "Synlige satelitter",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "Roaming",
+ "positionEvent": "Hændelse",
+ "positionAlarm": "Alarm",
+ "positionStatus": "Status",
+ "positionOdometer": "Km tæller",
+ "positionServiceOdometer": "Service Km tæller",
+ "positionTripOdometer": "Trip tæller",
+ "positionHours": "timer",
+ "positionSteps": "Skridt",
+ "positionInput": "Input",
+ "positionHeartRate": "Heart Rate",
+ "positionOutput": "Output",
+ "positionBatteryLevel": "Batteri niveau",
+ "positionFuelConsumption": "Brændstof forbrug",
+ "positionRfid": "RFID",
+ "positionVersionFw": "Firmware version",
+ "positionVersionHw": "Hardware version",
+ "positionIgnition": "Tænding på ",
+ "positionFlags": "Flag",
+ "positionCharge": "Oplad",
+ "positionIp": "IP",
+ "positionArchive": "Arkiv",
+ "positionVin": "VIN",
+ "positionApproximate": "Cirka",
+ "positionThrottle": "Gas",
+ "positionMotion": "Bevægelse",
+ "positionArmed": "Armeret",
+ "positionAcceleration": "Acceleration",
+ "positionTemp": "Temperature",
+ "positionDeviceTemp": "Enheds temperatur",
+ "positionCoolantTemp": "Coolant Temperature",
+ "positionOperator": "Operatør",
+ "positionCommand": "Kommando",
+ "positionBlocked": "Blokeret",
+ "positionDtcs": "DTC's",
+ "positionObdSpeed": "OBD Hastighed",
+ "positionObdOdometer": "OBD km tæller",
+ "positionDrivingTime": "Driving Time",
+ "positionDriverUniqueId": "Chaufførs unikke ID",
+ "positionCard": "Card",
+ "positionImage": "Billede",
+ "positionVideo": "Video",
+ "positionAudio": "Lyd",
+ "serverTitle": "Server indstillinger",
+ "serverZoom": "Zoom",
+ "serverRegistration": "Registrering",
+ "serverReadonly": "Læs",
+ "serverForceSettings": "Gennemtving opdatering",
+ "serverAnnouncement": "Meddelelse",
+ "serverName": "Server Name",
+ "serverDescription": "Server Description",
+ "serverColorPrimary": "Color Primary",
+ "serverColorSecondary": "Color Secondary",
+ "serverLogo": "Logo Image",
+ "serverLogoInverted": "Inverted Logo Image",
+ "serverChangeDisable": "Disable Server Change",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "Kort",
+ "mapActive": "Active Maps",
+ "mapOverlay": "Map Overlay",
+ "mapOverlayCustom": "Custom Overlay",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API Key",
+ "mapOpenWeatherClouds": "OpenWeather Clouds",
+ "mapOpenWeatherPrecipitation": "OpenWeather Precipitation",
+ "mapOpenWeatherPressure": "OpenWeather Pressure",
+ "mapOpenWeatherWind": "OpenWeather Wind",
+ "mapOpenWeatherTemperature": "OpenWeather Temperature",
+ "mapLayer": "Kort opsætning",
+ "mapCustom": "Brugerdefineret (XYZ)",
+ "mapCustomArcgis": "Brugerdefineret (ArcGIS)",
+ "mapCustomLabel": "Brugerdefineret kort",
+ "mapCarto": "Carto Basemaps",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Bing Maps Key",
+ "mapBingRoad": "Bing Maps Road",
+ "mapBingAerial": "Bing Maps Aerial",
+ "mapBingHybrid": "Bing Maps Hybrid",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Yandex Kort",
+ "mapYandexSat": "Yandex Satellit",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox gader",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox udendørs",
+ "mapMapboxSatellite": "Mapbox satellit",
+ "mapMapboxKey": "Mapbox Access Token",
+ "mapMapTilerBasic": "MapTiler Basic",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "MapTiler API Key",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ Access Token",
+ "mapTomTomBasic": "TomTom Basic",
+ "mapTomTomFlow": "TomTom Traffic Flow",
+ "mapTomTomIncidents": "TomTom Traffic Incidents",
+ "mapTomTomKey": "TomTom API Key",
+ "mapHereBasic": "Here Basic",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satellite",
+ "mapHereFlow": "Here Traffic Flow",
+ "mapHereKey": "Here API Key",
+ "mapShapePolygon": "Polygon",
+ "mapShapeCircle": "Cirkel",
+ "mapShapePolyline": "Polyline",
+ "mapLiveRoutes": "Live ruter",
+ "mapDirection": "Show Direction",
+ "mapCurrentLocation": "Current Location",
+ "mapPoiLayer": "POI Lag",
+ "mapClustering": "Markers Clustering",
+ "mapOnSelect": "Show Map on Selection",
+ "mapDefault": "Default Map",
+ "stateTitle": "Status",
+ "stateName": "Parameter",
+ "stateValue": "Værdi",
+ "commandTitle": "Kommando",
+ "commandSend": "Send",
+ "commandSent": "Kommando sendt",
+ "commandQueued": "Kommando i kø",
+ "commandUnit": "Enhed",
+ "commandCustom": "Skræddersyet kommando",
+ "commandDeviceIdentification": "Enheds id",
+ "commandPositionSingle": "Enkel rapport",
+ "commandPositionPeriodic": "Periodisk Rapportering",
+ "commandPositionStop": "Stop Rapportering",
+ "commandEngineStop": "Stop motor",
+ "commandEngineResume": "Genstart motor",
+ "commandAlarmArm": "Armer alarm",
+ "commandAlarmDisarm": "Slå alarm fra",
+ "commandAlarmDismiss": "Afvis alarm",
+ "commandSetTimezone": "Sæt tidszone",
+ "commandRequestPhoto": "Tag billede",
+ "commandPowerOff": "Sluk for enhed",
+ "commandRebootDevice": "Genstart enhed",
+ "commandFactoryReset": "Gendan fabriksindstillinger",
+ "commandSendSms": "send SMS",
+ "commandSendUssd": "Send USSD",
+ "commandSosNumber": "Angiv SOS nummer",
+ "commandSilenceTime": "Angiv lydløs tid",
+ "commandSetPhonebook": "Angiv telefonbog",
+ "commandVoiceMessage": "Tale meddelelse",
+ "commandOutputControl": "Output kontrol",
+ "commandVoiceMonitoring": "Stemmeovervågning",
+ "commandSetAgps": "Konfigurer AGPS",
+ "commandSetIndicator": "Gem indikator",
+ "commandConfiguration": "Konfiguration",
+ "commandGetVersion": "Hent version",
+ "commandFirmwareUpdate": "Opdater irmware",
+ "commandSetConnection": "Konfigurer forbindelse",
+ "commandSetOdometer": "Konfigurer kilometertæller",
+ "commandGetModemStatus": "Hent Modem status",
+ "commandGetDeviceStatus": "Hent enheds status",
+ "commandSetSpeedLimit": "Indstil hastighedsgrænse",
+ "commandModePowerSaving": "Power Saving Mode",
+ "commandModeDeepSleep": "Deep Sleep Mode",
+ "commandAlarmGeofence": "Set Geofence Alarm",
+ "commandAlarmBattery": "Set Battery Alarm",
+ "commandAlarmSos": "Set SOS Alarm",
+ "commandAlarmRemove": "Set Remove Alarm",
+ "commandAlarmClock": "Set Clock Alarm",
+ "commandAlarmSpeed": "Set Speed Alarm",
+ "commandAlarmFall": "Set Fall Alarm",
+ "commandAlarmVibration": "Set Vibration Alarm",
+ "commandFrequency": "Frekvens",
+ "commandTimezone": "Tidszone forskel",
+ "commandMessage": "Meddelelse",
+ "commandRadius": "Radius",
+ "commandEnable": "aktiver",
+ "commandData": "Data",
+ "commandIndex": "Indeks",
+ "commandPhone": "Telefon nummer",
+ "commandServer": "Server",
+ "commandPort": "Port",
+ "eventAll": "Alle begivenheder",
+ "eventDeviceOnline": "Status online",
+ "eventDeviceUnknown": "Status ukendt",
+ "eventDeviceOffline": "Status offline",
+ "eventDeviceInactive": "Enhed inaktiv",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "Enheden bevæger sig",
+ "eventDeviceStopped": "Enheden stoppet",
+ "eventDeviceOverspeed": "Hastighedsgrænsen overskredet",
+ "eventDeviceFuelDrop": "Brændstoffald",
+ "eventDeviceFuelIncrease": "Fuel increase",
+ "eventCommandResult": "Resultat af kommando",
+ "eventGeofenceEnter": "Geofence indtastet",
+ "eventGeofenceExit": "Geofence forladt",
+ "eventAlarm": "Alarm",
+ "eventIgnitionOn": "Tænding tændt",
+ "eventIgnitionOff": "Tænding slukket",
+ "eventMaintenance": "Vedligeholdelse krævet",
+ "eventTextMessage": "Tekst besked modtaget",
+ "eventDriverChanged": "Chaufførskift",
+ "eventMedia": "Media",
+ "eventsScrollToLast": "Rul til sidste",
+ "eventsSoundEvents": "Sound Events",
+ "eventsSoundAlarms": "Sound Alarms",
+ "alarmGeneral": "Generel",
+ "alarmSos": "SOS",
+ "alarmVibration": "Vibration",
+ "alarmMovement": "Bevægelse",
+ "alarmLowspeed": "Lav hastighed",
+ "alarmOverspeed": "Hastighedsoverskridelse",
+ "alarmFallDown": "Fald",
+ "alarmLowPower": "Lav strøm",
+ "alarmLowBattery": "Lavt batteri",
+ "alarmFault": "Fejl",
+ "alarmPowerOff": "Sluk",
+ "alarmPowerOn": "Tænd",
+ "alarmDoor": "Dør",
+ "alarmLock": "Lås",
+ "alarmUnlock": "Lås op",
+ "alarmGeofence": "Geofence",
+ "alarmGeofenceEnter": "Geofence Enter",
+ "alarmGeofenceExit": "Geofence Exit",
+ "alarmGpsAntennaCut": "GPS-antennesnit",
+ "alarmAccident": "Ulykke",
+ "alarmTow": "Bugseret",
+ "alarmIdle": "Ledig",
+ "alarmHighRpm": "Høj RPM",
+ "alarmHardAcceleration": "Hård acceleration",
+ "alarmHardBraking": "Hård bremsning",
+ "alarmHardCornering": "Hård sving",
+ "alarmLaneChange": "Vognbaneskift",
+ "alarmFatigueDriving": "Træthedskørsel",
+ "alarmPowerCut": "Strømafbrydelse",
+ "alarmPowerRestored": "Strøm genoprettet",
+ "alarmJamming": "Fastklemning",
+ "alarmTemperature": "Temperatur",
+ "alarmParking": "Parkering",
+ "alarmBonnet": "Motorhjelm",
+ "alarmFootBrake": "Fodbremse",
+ "alarmFuelLeak": "Brændstoflækage",
+ "alarmTampering": "Manipulering",
+ "alarmRemoving": "Fjerner",
+ "notificationType": "Type af notifikation",
+ "notificationAlways": "Alle enheder",
+ "notificationNotificators": "Kanaler",
+ "notificatorCommand": "Command",
+ "notificatorWeb": "Web",
+ "notificatorMail": "Mail",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Genafspil",
+ "reportCombined": "Combined",
+ "reportRoute": "Rute",
+ "reportEvents": "Begivenheder",
+ "reportTrips": "Ture",
+ "reportStops": "Antal stop",
+ "reportSummary": "Resume",
+ "reportDaily": "Daglig opsummering",
+ "reportChart": "Graf",
+ "reportConfigure": "Konfigurer",
+ "reportEventTypes": "Begivenheds typer",
+ "reportChartType": "Graf type",
+ "reportShowMarkers": "Vis markeringer",
+ "reportExport": "Eksporter",
+ "reportEmail": "Email rapport",
+ "reportSchedule": "Schedule",
+ "reportPeriod": "Periode",
+ "reportCustom": "Brugerdefineret",
+ "reportToday": "I dag",
+ "reportYesterday": "I går",
+ "reportThisWeek": "Denne uge",
+ "reportPreviousWeek": "Forrige uge",
+ "reportThisMonth": "Denne måned",
+ "reportPreviousMonth": "Forrige måned",
+ "reportDeviceName": "Enheds navn",
+ "reportAverageSpeed": "Gennemsnits hastighed",
+ "reportMaximumSpeed": "Maximum hastighed",
+ "reportEngineHours": "Motor aktiv timer",
+ "reportDuration": "Varighed",
+ "reportStartDate": "Start dato",
+ "reportStartTime": "Start tidspunkt",
+ "reportStartAddress": "Start adresse",
+ "reportEndTime": "Slut tidspunkt",
+ "reportEndAddress": "Slut adresse",
+ "reportSpentFuel": "Brændstof forbrug",
+ "reportStartOdometer": "Kilometertæller start",
+ "reportEndOdometer": "Kilometertæller slut",
+ "statisticsTitle": "Statistik",
+ "statisticsCaptureTime": "Noter tid",
+ "statisticsActiveUsers": "Aktive brugere",
+ "statisticsActiveDevices": "Aktive enheder",
+ "statisticsRequests": "Anmodninger",
+ "statisticsMessagesReceived": "Meddelelser modtaget",
+ "statisticsMessagesStored": "Meddelelser gemt",
+ "statisticsGeocoder": "Geokoder opslag",
+ "statisticsGeolocation": "Geolocation opslag",
+ "categoryArrow": "Pil",
+ "categoryDefault": "Standard",
+ "categoryAnimal": "Dyr",
+ "categoryBicycle": "Cykel",
+ "categoryBoat": "Båd",
+ "categoryBus": "Bus",
+ "categoryCar": "Bil",
+ "categoryCamper": "Camper",
+ "categoryCrane": "Kran",
+ "categoryHelicopter": "Helikopter",
+ "categoryMotorcycle": "Motorcykel",
+ "categoryOffroad": "Off-road",
+ "categoryPerson": "Person",
+ "categoryPickup": "Pickup ",
+ "categoryPlane": "Fly",
+ "categoryShip": "Skib",
+ "categoryTractor": "Traktor",
+ "categoryTrain": "Tog",
+ "categoryTram": "Sporvogn",
+ "categoryTrolleybus": "Bybus",
+ "categoryTruck": "Lastbil",
+ "categoryVan": "Van",
+ "categoryScooter": "scooter",
+ "maintenanceStart": "start",
+ "maintenancePeriod": "Periode"
+} \ No newline at end of file
diff --git a/src/resources/l10n/de.json b/src/resources/l10n/de.json
new file mode 100644
index 00000000..3e937ea0
--- /dev/null
+++ b/src/resources/l10n/de.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "Lade...",
+ "sharedHide": "Ausblenden",
+ "sharedSave": "Speichern",
+ "sharedUpload": "Upload",
+ "sharedSet": "Speichern",
+ "sharedCancel": "Abbrechen",
+ "sharedCopy": "Kopieren",
+ "sharedAdd": "Hinzufügen",
+ "sharedEdit": "Bearbeiten",
+ "sharedRemove": "Entfernen",
+ "sharedRemoveConfirm": "Objekt entfernen?",
+ "sharedNoData": "Keine Daten",
+ "sharedSubject": "Betreff",
+ "sharedYes": "Ja",
+ "sharedNo": "Nein",
+ "sharedKm": "km",
+ "sharedMi": "mi",
+ "sharedNmi": "nmi",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "kn",
+ "sharedKmh": "km/h",
+ "sharedMph": "mph",
+ "sharedHour": "Stunde",
+ "sharedMinute": "Minute",
+ "sharedSecond": "Sekunde",
+ "sharedDays": "Tage",
+ "sharedHours": "Stunden",
+ "sharedMinutes": "Minuten",
+ "sharedDecimalDegrees": "Dezimalgrad",
+ "sharedDegreesDecimalMinutes": "Dezimalgrad Minuten",
+ "sharedDegreesMinutesSeconds": "Grad Minuten Sekunden",
+ "sharedName": "Name",
+ "sharedDescription": "Beschreibung",
+ "sharedSearch": "Suchen",
+ "sharedIconScale": "Icon-Skalierung",
+ "sharedGeofence": "Geo-Zaun",
+ "sharedGeofences": "Geo-Zäune",
+ "sharedCreateGeofence": "Geozaun erstellen",
+ "sharedNotifications": "Benachrichtigungen",
+ "sharedNotification": "Benachrichtigung",
+ "sharedAttributes": "Eigenschaften",
+ "sharedAttribute": "Eigenschaft",
+ "sharedDrivers": "Fahrer",
+ "sharedDriver": "Fahrer",
+ "sharedArea": "Gebiet",
+ "sharedSound": "Benachrichtigungston",
+ "sharedType": "Typ",
+ "sharedDistance": "Entfernung",
+ "sharedHourAbbreviation": "h",
+ "sharedMinuteAbbreviation": "m",
+ "sharedSecondAbbreviation": "s",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "l",
+ "sharedGallonAbbreviation": "gal",
+ "sharedLiter": "Liter",
+ "sharedImpGallon": "Imp. Gallonen",
+ "sharedUsGallon": "U.S. Gallonen",
+ "sharedLiterPerHourAbbreviation": "l/h",
+ "sharedGetMapState": "Aktuelle Position übernehmen",
+ "sharedComputedAttribute": "Berechneter Wert",
+ "sharedComputedAttributes": "Berechnete Werte",
+ "sharedCheckComputedAttribute": "Berechneten Wert prüfen",
+ "sharedExpression": "Ausdruck",
+ "sharedDevice": "Gerät",
+ "sharedTest": "Test",
+ "sharedTestNotification": "Testbenachrichtigung senden",
+ "sharedTestNotificators": "Kanäle testen",
+ "sharedTestExpression": "Ausdruck testen",
+ "sharedCalendar": "Kalender",
+ "sharedCalendars": "Kalender",
+ "sharedFile": "Datei",
+ "sharedSearchDevices": "Geräte suchen",
+ "sharedSortBy": "Sortieren nach",
+ "sharedFilterMap": "Auf Karte filtern",
+ "sharedSelectFile": "Datei auswählen",
+ "sharedPhone": "Telefon",
+ "sharedRequired": "Erforderlich",
+ "sharedPreferences": "Einstellungen",
+ "sharedPermissions": "Berechtigungen",
+ "sharedConnections": "Verbindungen",
+ "sharedExtra": "Extra",
+ "sharedPrimary": "Primär",
+ "sharedSecondary": "Sekundär",
+ "sharedTypeString": "Text",
+ "sharedTypeNumber": "Nummer",
+ "sharedTypeBoolean": "Boolesche Variable",
+ "sharedTimezone": "Zeitzone",
+ "sharedInfoTitle": "Info",
+ "sharedSavedCommand": "Gespeicherter Befehl",
+ "sharedSavedCommands": "Gespeicherte Befehle",
+ "sharedNew": "Neu...",
+ "sharedShowAddress": "Adresse anzeigen",
+ "sharedShowDetails": "Details",
+ "sharedDisabled": "Deaktiviert",
+ "sharedMaintenance": "Wartung",
+ "sharedDeviceAccumulators": "Zählerstände",
+ "sharedAlarms": "Alarme",
+ "sharedLocation": "Ort",
+ "sharedImport": "Import",
+ "sharedColumns": "Spalten",
+ "sharedDropzoneText": "Datei hierhin ziehen oder klicken",
+ "sharedLogs": "Protokolle",
+ "sharedLink": "Link",
+ "calendarSimple": "Einfach",
+ "calendarRecurrence": "Wiederholung",
+ "calendarOnce": "Einmalig",
+ "calendarDaily": "Täglich",
+ "calendarWeekly": "Wöchentlich",
+ "calendarMonthly": "Monatlich",
+ "calendarDays": "Tage",
+ "calendarSunday": "Sonntag",
+ "calendarMonday": "Montag",
+ "calendarTuesday": "Dienstag",
+ "calendarWednesday": "Mittwoch",
+ "calendarThursday": "Donnerstag",
+ "calendarFriday": "Freitag",
+ "calendarSaturday": "Samstag",
+ "attributeShowGeofences": "Geozäune anzeigen",
+ "attributeSpeedLimit": "Höchstgeschwindigkeit",
+ "attributeFuelDropThreshold": "Fuel Drop Schwelle",
+ "attributeFuelIncreaseThreshold": "Fuel Increase Schwelle",
+ "attributePolylineDistance": "Polyliniendistanz",
+ "attributeReportIgnoreOdometer": "Bericht: Kilometerzähler ignorieren",
+ "attributeWebReportColor": "Web: Berichtsfarbe",
+ "attributeDevicePassword": "Gerätepasswort",
+ "attributeDeviceImage": "Gerätebild",
+ "attributeDeviceInactivityStart": "Geräteinaktivität Start",
+ "attributeDeviceInactivityPeriod": "Geräteinaktivität Periode",
+ "attributeProcessingCopyAttributes": "Aktiv: Werte kopieren",
+ "attributeColor": "Farbe",
+ "attributeWebLiveRouteLength": "Web: Länge der Liveroute",
+ "attributeWebSelectZoom": "Web: Mit Auswahl vergrößern",
+ "attributeWebMaxZoom": "Web: Maximaler Zoom",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Pushover User Key",
+ "attributePushoverDeviceNames": "Pushover Gerätenamen",
+ "attributeMailSmtpHost": "Mail: SMTP Host",
+ "attributeMailSmtpPort": "Mail: SMTP Port",
+ "attributeMailSmtpStarttlsEnable": "Mail: SMTP STARTTLS aktivieren",
+ "attributeMailSmtpStarttlsRequired": "Mail: SMTP STARTTLS erforderlich",
+ "attributeMailSmtpSslEnable": "Mail: SMTP SSL aktivieren",
+ "attributeMailSmtpSslTrust": "Mail: SMTP SSL Vertrauensstufe",
+ "attributeMailSmtpSslProtocols": "Mail: SMTP SSL Protokolle",
+ "attributeMailSmtpFrom": "Mail: SMTP Absender",
+ "attributeMailSmtpAuth": "Mail: SMTP Authentifizierung aktivieren",
+ "attributeMailSmtpUsername": "Mail: SMTP Benutzername",
+ "attributeMailSmtpPassword": "Mail: SMTP Passwort",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: Attribute deaktivieren",
+ "attributeUiDisableGroups": "UI: Gruppen deaktivieren",
+ "attributeUiDisableEvents": "Oberfläche: Ereignisse deaktivieren",
+ "attributeUiDisableVehicleFeatures": "UI: Fahrzeugfunktionen deaktivieren",
+ "attributeUiDisableDrivers": "Oberfläche: Fahrer deaktivieren",
+ "attributeUiDisableComputedAttributes": "Oberfläche: Berechnete Werte deaktivieren",
+ "attributeUiDisableCalendars": "Oberfläche: Kalender deaktivieren",
+ "attributeUiDisableMaintenance": "Oberfläche: Wartung deaktivieren",
+ "attributeUiHidePositionAttributes": "Oberfläche: Positionsattribute ausblenden",
+ "attributeUiDisableLoginLanguage": "UI: Loginsprache deaktivieren",
+ "attributeNotificationTokens": "Benachrichtungsschlüssel",
+ "attributePopupInfo": "Popup Info",
+ "errorTitle": "Fehler",
+ "errorGeneral": "Ungültige Eingabe oder keine Berechtigung",
+ "errorConnection": "Verbindungsfehler",
+ "errorSocket": "Web Socket Verbindungsfehler",
+ "errorZero": "Darf nicht leer sein",
+ "userEmail": "Email",
+ "userPassword": "Passwort",
+ "userAdmin": "Admin",
+ "userRemember": "Erinnern",
+ "userExpirationTime": "Ablaufdatum",
+ "userDeviceLimit": "Gerätelimit",
+ "userUserLimit": "Benutzerlimit",
+ "userDeviceReadonly": "Gerät nur Betrachten",
+ "userLimitCommands": "Befehle begrenzen",
+ "userDisableReports": "Berichte deaktivieren",
+ "userFixedEmail": "Änderung der E-Mailadresse verbieten",
+ "userToken": "Schlüssel",
+ "userDeleteAccount": "Account löschen",
+ "userTemporary": "Temporär",
+ "loginTitle": "Anmeldung",
+ "loginLanguage": "Sprache",
+ "loginReset": "Passwort zurücksetzen",
+ "loginRegister": "Registrieren",
+ "loginLogin": "Anmelden",
+ "loginOpenId": "Login mit OpenID",
+ "loginFailed": "Falsche Emailadresse oder Passwort",
+ "loginCreated": "Neuer Benutzer wurde registriert",
+ "loginResetSuccess": "Prüfen Sie Ihre E-Mails",
+ "loginUpdateSuccess": "Neues Passwort gespeichert",
+ "loginLogout": "Abmelden",
+ "loginLogo": "Logo",
+ "loginTotpCode": "OTP-Code",
+ "loginTotpKey": "OTP-Schlüssel",
+ "devicesAndState": "Geräte und Status",
+ "deviceSelected": "Ausgewähltes Gerät",
+ "deviceTitle": "Geräte",
+ "devicePrimaryInfo": "Gerät Titel",
+ "deviceSecondaryInfo": "Gerät Detail",
+ "deviceIdentifier": "Kennung",
+ "deviceModel": "Modell",
+ "deviceContact": "Kontakt",
+ "deviceCategory": "Symbol",
+ "deviceLastUpdate": "Letzte Aktualisierung",
+ "deviceCommand": "Befehl",
+ "deviceFollow": "Folgen",
+ "deviceTotalDistance": "Gesamt Strecke",
+ "deviceStatus": "Status",
+ "deviceStatusOnline": "Online",
+ "deviceStatusOffline": "Offline",
+ "deviceStatusUnknown": "Unbekannt",
+ "deviceRegisterFirst": "Registriere dein erstes Gerät",
+ "deviceIdentifierHelp": "IMEI, Seriennummer or andere ID. Eingabe muss mit dem Identifier übereinstimmen, den das Gerät an den Server überträgt.",
+ "deviceShare": "Gerät freigeben",
+ "groupDialog": "Gruppe",
+ "groupParent": "Gruppe",
+ "groupNoGroup": "Keine Gruppe",
+ "settingsTitle": "Einstellungen",
+ "settingsUser": "Benutzerkonto",
+ "settingsGroups": "Gruppen",
+ "settingsServer": "Server",
+ "settingsUsers": "Benutzer",
+ "settingsDistanceUnit": "Entfernungseinheit",
+ "settingsAltitudeUnit": "Höhe Einheit",
+ "settingsSpeedUnit": "Geschwindigkeitseinheit",
+ "settingsVolumeUnit": "Volumeneinheit",
+ "settingsTwelveHourFormat": "12 Stunden Format",
+ "settingsCoordinateFormat": "Koordinatenformat",
+ "settingsServerVersion": "Server-Version",
+ "settingsAppVersion": "App-Version",
+ "settingsConnection": "Verbindung",
+ "settingsDarkMode": "Dunkelmodus",
+ "settingsTotpEnable": "OTP aktivieren",
+ "settingsTotpForce": "OTP erzwingen",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Intervall",
+ "settingsUpdateAvailable": "Es ist ein Update verfügbar.",
+ "settingsSupport": "Hilfe",
+ "reportTitle": "Berichte",
+ "reportScheduled": "Geplante Berichte",
+ "reportDevice": "Gerät",
+ "reportGroup": "Gruppe",
+ "reportFrom": "Von",
+ "reportTo": "Bis",
+ "reportShow": "Anzeigen",
+ "reportClear": "Leeren",
+ "linkGoogleMaps": "Google Maps",
+ "linkAppleMaps": "Apple Maps",
+ "linkStreetView": "Street View",
+ "positionFixTime": "Fix Time",
+ "positionDeviceTime": "Gerätezeit",
+ "positionServerTime": "Serverzeit",
+ "positionValid": "Gültig",
+ "positionAccuracy": "Genauigkeit",
+ "positionLatitude": "Breitengrad",
+ "positionLongitude": "Längengrad",
+ "positionAltitude": "Höhe",
+ "positionSpeed": "Geschwindigkeit",
+ "positionCourse": "Richtung",
+ "positionAddress": "Adresse",
+ "positionProtocol": "Protokoll",
+ "positionDistance": "Distanz",
+ "positionRpm": "Drehzahl",
+ "positionFuel": "Treibstoff",
+ "positionPower": "Versorgungsspannung",
+ "positionBattery": "Batterie",
+ "positionRaw": "Roh",
+ "positionIndex": "Index",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "Satelliten",
+ "positionSatVisible": "Sichtbare Satelliten",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "Roaming",
+ "positionEvent": "Ereignis",
+ "positionAlarm": "Alarm",
+ "positionStatus": "Status",
+ "positionOdometer": "Kilometerzähler",
+ "positionServiceOdometer": "Kilometerzähler - Service",
+ "positionTripOdometer": "Kilometerzähler - Trip",
+ "positionHours": "Stunden",
+ "positionSteps": "Schritte",
+ "positionInput": "Input",
+ "positionHeartRate": "Herzfrequenz",
+ "positionOutput": "Output",
+ "positionBatteryLevel": "Batteriestand",
+ "positionFuelConsumption": "Kraftstoffverbrauch",
+ "positionRfid": "RFID",
+ "positionVersionFw": "Firmware Version",
+ "positionVersionHw": "Hardware Version",
+ "positionIgnition": "Zündung",
+ "positionFlags": "Flags",
+ "positionCharge": "Laden",
+ "positionIp": "IP",
+ "positionArchive": "Archiv",
+ "positionVin": "VIN",
+ "positionApproximate": "Geschätzt",
+ "positionThrottle": "Gasstellung",
+ "positionMotion": "Bewegung",
+ "positionArmed": "Gesichert",
+ "positionAcceleration": "Beschleunigung",
+ "positionTemp": "Temperatur",
+ "positionDeviceTemp": "Gerätetemperatur",
+ "positionCoolantTemp": "Kühlmitteltemperatur",
+ "positionOperator": "Netz",
+ "positionCommand": "Befehl",
+ "positionBlocked": "Blockiert",
+ "positionDtcs": "DTCs",
+ "positionObdSpeed": "OBD Geschwindigkeit",
+ "positionObdOdometer": "OBD Kilometerzähler",
+ "positionDrivingTime": "Fahrzeit",
+ "positionDriverUniqueId": "Eindeutige ID des Fahrers",
+ "positionCard": "Karte",
+ "positionImage": "Bild",
+ "positionVideo": "Video",
+ "positionAudio": "Audio",
+ "serverTitle": "Server Einstellungen",
+ "serverZoom": "Zoomen",
+ "serverRegistration": "Registrierung zulassen",
+ "serverReadonly": "Nur Lesen",
+ "serverForceSettings": "Einstellungen erzwingen",
+ "serverAnnouncement": "Ankündigung",
+ "serverName": "Server Name",
+ "serverDescription": "Server Beschreibung",
+ "serverColorPrimary": "Primäre Farbe",
+ "serverColorSecondary": "Sekundäre Farbe",
+ "serverLogo": "Logo",
+ "serverLogoInverted": "Umgekehrte Logografik",
+ "serverChangeDisable": "Serverwechsel deaktivieren",
+ "serverDisableShare": "Gerätefreigabe deaktivieren",
+ "mapTitle": "Karte",
+ "mapActive": "Aktive Karte",
+ "mapOverlay": "Karten Overlay",
+ "mapOverlayCustom": "Benutzerdefiniertes Overlay",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API Key",
+ "mapOpenWeatherClouds": "OpenWeather Wolken",
+ "mapOpenWeatherPrecipitation": "OpenWeather Niederschlag",
+ "mapOpenWeatherPressure": "OpenWeather Luftdruck",
+ "mapOpenWeatherWind": "OpenWeather Wind",
+ "mapOpenWeatherTemperature": "OpenWeather Temperatur",
+ "mapLayer": "Karten Layer",
+ "mapCustom": "Benutzerdefiniert (XYZ)",
+ "mapCustomArcgis": "Benutzerdefiniert (ArcGIS)",
+ "mapCustomLabel": "Benutzerdefinierte Karte",
+ "mapCarto": "Carto Basemaps",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Straßen",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellit",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Bing Maps Key",
+ "mapBingRoad": "Bing Strassenkarte",
+ "mapBingAerial": "Bing Luftbilder",
+ "mapBingHybrid": "Bing Maps Hybrid",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Yandex Map",
+ "mapYandexSat": "Yandex Satellite",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Material-Überwachung",
+ "mapMapboxStreets": "Mapbox Streets",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox Outdoors",
+ "mapMapboxSatellite": "Mapbox Satellite",
+ "mapMapboxKey": "Mapbox Access Token",
+ "mapMapTilerBasic": "MapTiler Basic",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "MapTiler API Key",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dunkel",
+ "mapLocationIqKey": "LocationIQ Access Token",
+ "mapTomTomBasic": "TomTom Basic",
+ "mapTomTomFlow": "TomTom Traffic Flow",
+ "mapTomTomIncidents": "TomTom Traffic Incidents",
+ "mapTomTomKey": "TomTom API Key",
+ "mapHereBasic": "Here Basic",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satellite",
+ "mapHereFlow": "Here Traffic Flow",
+ "mapHereKey": "Here API Key",
+ "mapShapePolygon": "Polygon",
+ "mapShapeCircle": "Kreis",
+ "mapShapePolyline": "Polylinie",
+ "mapLiveRoutes": "Live Route",
+ "mapDirection": "Richtung anzeigen",
+ "mapCurrentLocation": "Momentaner Standort",
+ "mapPoiLayer": "POI Ebene",
+ "mapClustering": "Marker zusammenfassen",
+ "mapOnSelect": "Karte bei Auswahl anzeigen",
+ "mapDefault": "Standartkarte",
+ "stateTitle": "Status",
+ "stateName": "Parameter",
+ "stateValue": "Wert",
+ "commandTitle": "Befehl",
+ "commandSend": "Senden",
+ "commandSent": "Befehl gesendet",
+ "commandQueued": "Befehl eingereiht",
+ "commandUnit": "Einheit",
+ "commandCustom": "Benutzerdefinierter Befehl",
+ "commandDeviceIdentification": "Gerätekennung",
+ "commandPositionSingle": "Einzelne Meldung",
+ "commandPositionPeriodic": "Regelmäßige Meldungen",
+ "commandPositionStop": "Meldungen beenden",
+ "commandEngineStop": "Motor gestoppt",
+ "commandEngineResume": "Motor gestartet",
+ "commandAlarmArm": "Scharf schalten",
+ "commandAlarmDisarm": "Unscharf schalten",
+ "commandAlarmDismiss": "Alarm abstellen",
+ "commandSetTimezone": "Zeitzone festlegen",
+ "commandRequestPhoto": "Foto anfordern",
+ "commandPowerOff": "Gerät ausschalten",
+ "commandRebootDevice": "Gerät neustarten",
+ "commandFactoryReset": "Zurücksetzen auf Werkseinstellungen",
+ "commandSendSms": "SMS senden",
+ "commandSendUssd": "USSD senden",
+ "commandSosNumber": "SOS-Nummer festlegen",
+ "commandSilenceTime": "Ruhezeit festlegen",
+ "commandSetPhonebook": "Telefonbuch festlegen",
+ "commandVoiceMessage": "Sprachnachricht",
+ "commandOutputControl": "Berichtsteuerung",
+ "commandVoiceMonitoring": "Stimmenmonitor",
+ "commandSetAgps": "AGPS einstellen",
+ "commandSetIndicator": "Blinker setzen",
+ "commandConfiguration": "Konfiguration",
+ "commandGetVersion": "Version abfragen",
+ "commandFirmwareUpdate": "Firmware upgraden",
+ "commandSetConnection": "Verbindung aufbauen",
+ "commandSetOdometer": "Kilometerzähler einstellen",
+ "commandGetModemStatus": "Modemstatus abrufen",
+ "commandGetDeviceStatus": "Gerätestatus abrufen",
+ "commandSetSpeedLimit": "Tempolimit einstellen",
+ "commandModePowerSaving": "Energiesparmodus",
+ "commandModeDeepSleep": "Deep Sleep Mode",
+ "commandAlarmGeofence": "Geozaun-Alarm einstellen",
+ "commandAlarmBattery": "Batterie-Alarm einstellen",
+ "commandAlarmSos": "SOS-Alarm einstellen",
+ "commandAlarmRemove": "Demontage-Alarm einstellen",
+ "commandAlarmClock": "Uhr-Alarm einstellen",
+ "commandAlarmSpeed": "Geschwinidkeits-Alarm einstellen",
+ "commandAlarmFall": "Sturz-Alarm einstellen",
+ "commandAlarmVibration": "Vibrations-Alarm einstellen",
+ "commandFrequency": "Frequenz",
+ "commandTimezone": "Zeitzonendifferenz",
+ "commandMessage": "Nachricht",
+ "commandRadius": "Radius",
+ "commandEnable": "Aktivieren",
+ "commandData": "Daten",
+ "commandIndex": "Index",
+ "commandPhone": "Rufnummer",
+ "commandServer": "Server",
+ "commandPort": "Port",
+ "eventAll": "Alle Ereignisse",
+ "eventDeviceOnline": "Status online",
+ "eventDeviceUnknown": "Status unbekannt",
+ "eventDeviceOffline": "Status offline",
+ "eventDeviceInactive": "Gerät inaktiv",
+ "eventQueuedCommandSent": "Befehl in der Warteschlange senden",
+ "eventDeviceMoving": "Gerät in Bewegung",
+ "eventDeviceStopped": "Gerät gestoppt",
+ "eventDeviceOverspeed": "Tempolimit überschritten",
+ "eventDeviceFuelDrop": "Treibstoffleck",
+ "eventDeviceFuelIncrease": "Anstieg der Tankfüllung",
+ "eventCommandResult": "Ergebnis des Befehls",
+ "eventGeofenceEnter": "Geozaun betreten",
+ "eventGeofenceExit": "Geozaun verlassen",
+ "eventAlarm": "Alarm",
+ "eventIgnitionOn": "Zündung an",
+ "eventIgnitionOff": "Zündung aus",
+ "eventMaintenance": "Wartung erforderlich",
+ "eventTextMessage": "Textnachricht empfangen",
+ "eventDriverChanged": "Fahrer gewechselt",
+ "eventMedia": "Medien",
+ "eventsScrollToLast": "Zur Neuesten scrollen",
+ "eventsSoundEvents": "Sound Events",
+ "eventsSoundAlarms": "Sound Alarme",
+ "alarmGeneral": "Allgemein",
+ "alarmSos": "SOS",
+ "alarmVibration": "Vibration",
+ "alarmMovement": "Bewegung",
+ "alarmLowspeed": "Geringe Geschwindigkeit",
+ "alarmOverspeed": "Geschwindigkeitsüberschreitung",
+ "alarmFallDown": "Sturz",
+ "alarmLowPower": "Geringe Spannung",
+ "alarmLowBattery": "Geringer Batteriestand",
+ "alarmFault": "Fehler",
+ "alarmPowerOff": "Ausschalten",
+ "alarmPowerOn": "Einschalten",
+ "alarmDoor": "Tür",
+ "alarmLock": "Sperren",
+ "alarmUnlock": "Entsperren",
+ "alarmGeofence": "Geozaun",
+ "alarmGeofenceEnter": "Geozaun betreten",
+ "alarmGeofenceExit": "Geofence verlassen",
+ "alarmGpsAntennaCut": "GPS Antenne entfernt",
+ "alarmAccident": "Unfall",
+ "alarmTow": "Abschleppen",
+ "alarmIdle": "Ruhezustand",
+ "alarmHighRpm": "Hohe Drehzahl",
+ "alarmHardAcceleration": "Starke Beschleunigung",
+ "alarmHardBraking": "Starkes Bremsen",
+ "alarmHardCornering": "Scharfes Kurvenfahren",
+ "alarmLaneChange": "Spurwechsel",
+ "alarmFatigueDriving": "Müdigkeitserkennung",
+ "alarmPowerCut": "Stromversorgung unterbrochen",
+ "alarmPowerRestored": "Stromversorgung wiederhergestellt",
+ "alarmJamming": "Jamming",
+ "alarmTemperature": "Temperatur",
+ "alarmParking": "Parken",
+ "alarmBonnet": "Haube",
+ "alarmFootBrake": "Betriebsbremse",
+ "alarmFuelLeak": "Treibstoffleck",
+ "alarmTampering": "Manipulation",
+ "alarmRemoving": "Entfernen",
+ "notificationType": "Art der Benachrichtigung",
+ "notificationAlways": "Alle Geräte",
+ "notificationNotificators": "Kanäle",
+ "notificatorCommand": "Befehl",
+ "notificatorWeb": "Web",
+ "notificatorMail": "Mail",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Abspielen",
+ "reportCombined": "Kombiniert",
+ "reportRoute": "Route",
+ "reportEvents": "Ereignis",
+ "reportTrips": "Fahrten",
+ "reportStops": "Stopps",
+ "reportSummary": "Zusammenfassung",
+ "reportDaily": "tägliche Übersicht",
+ "reportChart": "Diagramm",
+ "reportConfigure": "Konfigurieren",
+ "reportEventTypes": "Ereignisarten",
+ "reportChartType": "Diagramm Typ",
+ "reportShowMarkers": "Zeige Markierungen",
+ "reportExport": "Export",
+ "reportEmail": "E-Mail Report",
+ "reportSchedule": "Planen",
+ "reportPeriod": "Zeitraum",
+ "reportCustom": "Eigene",
+ "reportToday": "Heute",
+ "reportYesterday": "Gestern",
+ "reportThisWeek": "Diese Woche",
+ "reportPreviousWeek": "Letzte Woche",
+ "reportThisMonth": "Dieser Monat",
+ "reportPreviousMonth": "Letzer Monat",
+ "reportDeviceName": "Gerätename",
+ "reportAverageSpeed": "Durchschnittsgeschwindigkeit",
+ "reportMaximumSpeed": "Höchstgeschwindigkeit",
+ "reportEngineHours": "Betriebsstunden",
+ "reportDuration": "Dauer",
+ "reportStartDate": "Beginn",
+ "reportStartTime": "Startzeit",
+ "reportStartAddress": "Startort",
+ "reportEndTime": "Zielzeit",
+ "reportEndAddress": "Zielort",
+ "reportSpentFuel": "Kraftstoffverbrauch",
+ "reportStartOdometer": "Kilometerstand Start",
+ "reportEndOdometer": "Kilometerstand Ende",
+ "statisticsTitle": "Statistiken",
+ "statisticsCaptureTime": "Zeitpunkt",
+ "statisticsActiveUsers": "Aktive Benutzer",
+ "statisticsActiveDevices": "Aktive Geräte",
+ "statisticsRequests": "Anfragen",
+ "statisticsMessagesReceived": "Empfangene Nachrichten",
+ "statisticsMessagesStored": "Gespeicherte Nachrichten",
+ "statisticsGeocoder": "Geocoder Anfragen",
+ "statisticsGeolocation": "Geolocation Anfragen",
+ "categoryArrow": "Pfeil",
+ "categoryDefault": "Standard",
+ "categoryAnimal": "Tier",
+ "categoryBicycle": "Fahrrad",
+ "categoryBoat": "Boot",
+ "categoryBus": "Bus",
+ "categoryCar": "Auto",
+ "categoryCamper": "Wohnmobil",
+ "categoryCrane": "Kran",
+ "categoryHelicopter": "Helikopter",
+ "categoryMotorcycle": "Motorrad",
+ "categoryOffroad": "Offroad",
+ "categoryPerson": "Person",
+ "categoryPickup": "Pickup",
+ "categoryPlane": "Flugzeug",
+ "categoryShip": "Schiff",
+ "categoryTractor": "Traktor",
+ "categoryTrain": "Zug",
+ "categoryTram": "Straßenbahn",
+ "categoryTrolleybus": "Oberleitungsbus",
+ "categoryTruck": "LKW",
+ "categoryVan": "Van",
+ "categoryScooter": "Roller",
+ "maintenanceStart": "Start",
+ "maintenancePeriod": "Periode"
+} \ No newline at end of file
diff --git a/src/resources/l10n/el.json b/src/resources/l10n/el.json
new file mode 100644
index 00000000..ad2f2cf1
--- /dev/null
+++ b/src/resources/l10n/el.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "Φόρτωση...",
+ "sharedHide": "Απόκρυψη",
+ "sharedSave": "Αποθήκευση",
+ "sharedUpload": "Upload",
+ "sharedSet": "Θέσε",
+ "sharedCancel": "Άκυρο",
+ "sharedCopy": "Copy",
+ "sharedAdd": "Προσθήκη",
+ "sharedEdit": "Επεξεργασία",
+ "sharedRemove": "Διαγραφή",
+ "sharedRemoveConfirm": "Διαγραφη στοιχείου;",
+ "sharedNoData": "Χωρίς δεδομένα",
+ "sharedSubject": "Subject",
+ "sharedYes": "Ναι",
+ "sharedNo": "Όχι",
+ "sharedKm": "χλμ",
+ "sharedMi": "μίλια",
+ "sharedNmi": "nmi",
+ "sharedMeters": "μ.",
+ "sharedFeet": "ft",
+ "sharedKn": "κόμβοι",
+ "sharedKmh": "χλμ/ώρα",
+ "sharedMph": "μίλια/ώρα",
+ "sharedHour": "Ώρα",
+ "sharedMinute": "Λεπτά",
+ "sharedSecond": "Δευτερόλεπτα",
+ "sharedDays": "ημέρες",
+ "sharedHours": "ώρες",
+ "sharedMinutes": "λεπτά",
+ "sharedDecimalDegrees": "Μοίρες",
+ "sharedDegreesDecimalMinutes": "Λεπτά μοίρας",
+ "sharedDegreesMinutesSeconds": "Δεύτερα μοίρας",
+ "sharedName": "Όνομα",
+ "sharedDescription": "Περιγραφή",
+ "sharedSearch": "Αναζήτηση",
+ "sharedIconScale": "Κλίμακα εικονιδίων",
+ "sharedGeofence": "Γεωφράχτης",
+ "sharedGeofences": "Γεωφράχτες",
+ "sharedCreateGeofence": "Δημιουργήστε Γεώφραγμα",
+ "sharedNotifications": "Ειδοποιήσεις",
+ "sharedNotification": "Ειδοποίηση",
+ "sharedAttributes": "Παράμετροι",
+ "sharedAttribute": "Παράμετρος",
+ "sharedDrivers": "Οδηγοί",
+ "sharedDriver": "Οδηγός",
+ "sharedArea": "Περιοχή",
+ "sharedSound": "Ήχος ειδοποίησης",
+ "sharedType": "Τύπος",
+ "sharedDistance": "Απόσταση",
+ "sharedHourAbbreviation": "ώ",
+ "sharedMinuteAbbreviation": "λ",
+ "sharedSecondAbbreviation": "δ",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "l",
+ "sharedGallonAbbreviation": "γαλόνι",
+ "sharedLiter": "Λίτρο",
+ "sharedImpGallon": "Γαλόνι Αγγλικό",
+ "sharedUsGallon": "Γαλόνι ΗΠΑ",
+ "sharedLiterPerHourAbbreviation": "λ/ώ",
+ "sharedGetMapState": "Λήψη κατάστασης χάρτη",
+ "sharedComputedAttribute": "Υπολογισμένο χαρακτηριστικό",
+ "sharedComputedAttributes": "Υπολογισμένα χαρακτηριστικά",
+ "sharedCheckComputedAttribute": "Ελέξτε την υπολογισμένη ιδιότητα",
+ "sharedExpression": "Έκφραση",
+ "sharedDevice": "Συσκευή",
+ "sharedTest": "Δοκιμή",
+ "sharedTestNotification": "Αποστολή δοκιμαστικής ειδοποίησης",
+ "sharedTestNotificators": "Δοκιμή καναλιών",
+ "sharedTestExpression": "Δοκιμή Διατύπωσης",
+ "sharedCalendar": "Ημερολόγιο",
+ "sharedCalendars": "Ημερολόγια",
+ "sharedFile": "Αρχείο",
+ "sharedSearchDevices": "Αναζήτηση συσκευών",
+ "sharedSortBy": "Ταξινόμηση κατά",
+ "sharedFilterMap": "Φίλτρο στον χάρτη",
+ "sharedSelectFile": "Επιλογή αρχείου",
+ "sharedPhone": "Τηλέφωνο",
+ "sharedRequired": "Απαραίτητο",
+ "sharedPreferences": "Προτιμήσεις",
+ "sharedPermissions": "Άδειες",
+ "sharedConnections": "Συνδέσεις",
+ "sharedExtra": "Επιπλέον",
+ "sharedPrimary": "Πρωτεύον",
+ "sharedSecondary": "Δευτερεύον",
+ "sharedTypeString": "Αλφαριθμητικό",
+ "sharedTypeNumber": "Αριθμός",
+ "sharedTypeBoolean": "Αληθές/Ψευδές",
+ "sharedTimezone": "Ζώνη ώρας",
+ "sharedInfoTitle": "Πληροφορίες",
+ "sharedSavedCommand": "Αποθηκευμένη Εντολή",
+ "sharedSavedCommands": "Αποθηκευμένες Εντολές",
+ "sharedNew": "Νέο...",
+ "sharedShowAddress": "Δείξε Διεύθυνση",
+ "sharedShowDetails": "Περισσότερες λεπτομέρειες",
+ "sharedDisabled": "Απενεργοποιημένο",
+ "sharedMaintenance": "Συντήρηση",
+ "sharedDeviceAccumulators": "Συσσωρευτές",
+ "sharedAlarms": "Συναγερμοί",
+ "sharedLocation": "Τοποθεσία",
+ "sharedImport": "Εισαγωγή",
+ "sharedColumns": "Στήλες",
+ "sharedDropzoneText": "Σύρετε και αποθέστε ένα αρχείο εδώ ή κάντε κλικ",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "Απλό",
+ "calendarRecurrence": "Επανάληψη",
+ "calendarOnce": "Μια φορά",
+ "calendarDaily": "Καθημερινά",
+ "calendarWeekly": "Εβδομαδιαία",
+ "calendarMonthly": "Μηνιαίο",
+ "calendarDays": "Ημέρες",
+ "calendarSunday": "Κυριακή",
+ "calendarMonday": "Δευτέρα",
+ "calendarTuesday": "Τρίτη",
+ "calendarWednesday": "Τετάρτη",
+ "calendarThursday": "Πέμπτη",
+ "calendarFriday": "Παρασκευή",
+ "calendarSaturday": "Σάββατο",
+ "attributeShowGeofences": "Εμφάνιση γεωφραγμάτων",
+ "attributeSpeedLimit": "Όριο ταχύτητας",
+ "attributeFuelDropThreshold": "Όριο πτώσης καυσίμου",
+ "attributeFuelIncreaseThreshold": "Όριο αύξησης καυσίμου",
+ "attributePolylineDistance": "Πολυγραμμική Απόσταση",
+ "attributeReportIgnoreOdometer": "Αναφορά: Αγνόηση οδομέτρου",
+ "attributeWebReportColor": "Διαδίκτυο: Χρώμα Αναφοράς",
+ "attributeDevicePassword": "Κωδικός συσκευής",
+ "attributeDeviceImage": "Εικόνα συσκευής",
+ "attributeDeviceInactivityStart": "Αρχή αδράνειας συσκευής",
+ "attributeDeviceInactivityPeriod": "Περίοδος αδράνειας συσκευής",
+ "attributeProcessingCopyAttributes": "Επεξεργασία: Αντιγραφή Ιδιοτήτων",
+ "attributeColor": "Χρώμα",
+ "attributeWebLiveRouteLength": "Ιστός: Μήκος Ζωντανής Διαδρομής",
+ "attributeWebSelectZoom": "Ιστός: Επιλογή μεγέθυνσης",
+ "attributeWebMaxZoom": "Διαδίκτυο: Μέγιστη Μεγέθυνση",
+ "attributeTelegramChatId": "Αναγνωριστικό συνομιλίας Telegram",
+ "attributePushoverUserKey": "Κλειδί χρήστη Pushover",
+ "attributePushoverDeviceNames": "Ονόματα συσκευών Pushover",
+ "attributeMailSmtpHost": "Αλληλογραφία: Εξυπηρετητής SMTP",
+ "attributeMailSmtpPort": "Αλληλογραφία: Θύρα SMTP",
+ "attributeMailSmtpStarttlsEnable": "Αλληλογραφία: Ενεργοποίηση SMTP STARTLLS",
+ "attributeMailSmtpStarttlsRequired": "Αλληλογραφία: Απαιτούμενο SMTP STARTLLS",
+ "attributeMailSmtpSslEnable": "Αλληλογραφία: Ενεργοποίηση SMTP SSL",
+ "attributeMailSmtpSslTrust": "Αλληλογραφία: SMTP SSL Trust",
+ "attributeMailSmtpSslProtocols": "Αλληλογραφία: SMTP SSL Πρωτόκολλα",
+ "attributeMailSmtpFrom": "Αλληλογραφία: SMTP Από",
+ "attributeMailSmtpAuth": "Αλληλογραφία: Ενεργοποίηση Πιστοποίησης SMTP",
+ "attributeMailSmtpUsername": "Αλληλογραφία: Κωδικός SMTP",
+ "attributeMailSmtpPassword": "Αλληλογραφία: SMTP Συνθηματικό",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "Διεπαφή: Απενεργοποίηση χαρακτηριστικών",
+ "attributeUiDisableGroups": "Διεπαφή: Απενεργοποίηση ομάδων",
+ "attributeUiDisableEvents": "Διεπαφή: Απενεργοποίηση Γεγονότων",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "Διεπαφή: Απενεργοποίηση Οδηγών",
+ "attributeUiDisableComputedAttributes": "Διεπαφή: Απενεργοποίηση Υπολογισμένων Ιδιοτήτων",
+ "attributeUiDisableCalendars": "Διεπαφή: Απενεργοποίηση Ημερολογίων",
+ "attributeUiDisableMaintenance": "Διεπαφή: Απενεργοποίηση συντήρησης",
+ "attributeUiHidePositionAttributes": "Διεπαφή: Απόκρυψη Ιδιοτήτων Θέσης",
+ "attributeUiDisableLoginLanguage": "Διεπαφή: Απενεργοποίηση γλώσσας σύνδεσης",
+ "attributeNotificationTokens": "Διακριτικά ειδοποίησης",
+ "attributePopupInfo": "Αναδυόμενη πληροφορία",
+ "errorTitle": "Σφάλμα",
+ "errorGeneral": "Μη έγκυρες παράμετροι ή παραβίαση περιορισμών",
+ "errorConnection": "Σφάλμα σύνδεσης",
+ "errorSocket": "Σφάλμα σύνδεσης στο διαδίκτυο",
+ "errorZero": "Δεν μπορεί να είναι μηδέν",
+ "userEmail": "Ηλ. διεύθυνση",
+ "userPassword": "Συνθηματικό",
+ "userAdmin": "Διαχειριστής",
+ "userRemember": "Απομνημόνευση",
+ "userExpirationTime": "Λήξη",
+ "userDeviceLimit": "Όριο συσκευής",
+ "userUserLimit": "Όριο χρήστη",
+ "userDeviceReadonly": "Συσκευή μόνο για Ανάγνωση",
+ "userLimitCommands": "Όριο Εντολών",
+ "userDisableReports": "Απανεργοποίηση αναφορών",
+ "userFixedEmail": "Δεν επιτρέπεται η αλλαγή email",
+ "userToken": "Λεκτικό",
+ "userDeleteAccount": "Διαγραφή λογαριασμού",
+ "userTemporary": "Temporary",
+ "loginTitle": "Σύνδεση",
+ "loginLanguage": "Γλώσσα",
+ "loginReset": "Επαναφορά κωδικού",
+ "loginRegister": "Εγγραφή",
+ "loginLogin": "Σύνδεση",
+ "loginOpenId": "Login with OpenID",
+ "loginFailed": "Εσφαλμένη διεύθυνση ή εσφαλμένο συνθηματικό",
+ "loginCreated": "Ο νέος χρήστης καταχωρήθηκε.",
+ "loginResetSuccess": "Ελέγξτε τα email σας",
+ "loginUpdateSuccess": "Το νέο συνθηματικό αποθηκεύθηκε",
+ "loginLogout": "Αποσύνδεση",
+ "loginLogo": "Λογότυπο",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "Κατάσταση συσκευών",
+ "deviceSelected": "Επιλεγμένη συσκευή",
+ "deviceTitle": "Συσκευές",
+ "devicePrimaryInfo": "Τίτλος συσκευής",
+ "deviceSecondaryInfo": "Λεπτομέρεια συσκευής",
+ "deviceIdentifier": "Αναγνωριστικό",
+ "deviceModel": "Μοντέλο",
+ "deviceContact": "Επαφή",
+ "deviceCategory": "Κατηγορία",
+ "deviceLastUpdate": "Τελευταία ενημέρωση",
+ "deviceCommand": "Εντολή",
+ "deviceFollow": "Ακολουθώ",
+ "deviceTotalDistance": "Συνολική απόσταση",
+ "deviceStatus": "Κατάσταση",
+ "deviceStatusOnline": "Σε σύνδεση",
+ "deviceStatusOffline": "Χωρίς σύνδεση",
+ "deviceStatusUnknown": "Άγνωστο",
+ "deviceRegisterFirst": "Καταχωρίστε την πρώτη σας συσκευή",
+ "deviceIdentifierHelp": "IMEI, σειριακός αριθμός ή άλλο αναγνωριστικό. Πρέπει να ταιριάζει με τις αναφορές της συσκευής στον διακομιστή.",
+ "deviceShare": "Share Device",
+ "groupDialog": "Ομάδα",
+ "groupParent": "Ομάδα",
+ "groupNoGroup": "Χωρίς Ομάδα",
+ "settingsTitle": "Ρυθμίσεις",
+ "settingsUser": "Λογαριασμός",
+ "settingsGroups": "Ομάδες",
+ "settingsServer": "Εξυπηρετητής",
+ "settingsUsers": "Χρήστες",
+ "settingsDistanceUnit": "Μονάδα Απόστασης",
+ "settingsAltitudeUnit": "Υψομετρική Μονάδα",
+ "settingsSpeedUnit": "Μονάδα Ταχύτητας",
+ "settingsVolumeUnit": "Μονάδα Όγκου",
+ "settingsTwelveHourFormat": "12ώρη μορφή",
+ "settingsCoordinateFormat": "Τύπος Συντεταγμένων",
+ "settingsServerVersion": "Έκδοση διακομιστή",
+ "settingsAppVersion": "Έκδοση εφαρμογής",
+ "settingsConnection": "Connection",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "Αναφορές",
+ "reportScheduled": "Προγραμματισμένες αναφορές",
+ "reportDevice": "Συσκευή",
+ "reportGroup": "Ομάδα",
+ "reportFrom": "Από",
+ "reportTo": "Έως",
+ "reportShow": "Προβολή",
+ "reportClear": "Καθαρισμός",
+ "linkGoogleMaps": "Χάρτες Google",
+ "linkAppleMaps": "Χάρτες Apple",
+ "linkStreetView": "Street View",
+ "positionFixTime": "Διόρθωση χρόνου",
+ "positionDeviceTime": "Χρόνος συσκευής",
+ "positionServerTime": "Χρόνος Διακομιστή",
+ "positionValid": "Έγκυρο",
+ "positionAccuracy": "Ακρίβεια",
+ "positionLatitude": "Γ. πλάτος",
+ "positionLongitude": "Γ. μήκος",
+ "positionAltitude": "Υψόμετρο",
+ "positionSpeed": "Ταχύτητα",
+ "positionCourse": "Πορεία",
+ "positionAddress": "Διεύθυνση",
+ "positionProtocol": "Πρωτόκολλο",
+ "positionDistance": "Απόσταση",
+ "positionRpm": "ΣΑΛ",
+ "positionFuel": "Καύσιμο",
+ "positionPower": "Ισχύς",
+ "positionBattery": "Συσσωρευτής",
+ "positionRaw": "Ακατέργαστο",
+ "positionIndex": "Δείκτης",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "Δορυφόροι",
+ "positionSatVisible": "Ορατοί Δορυφόροι",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "Περιαγωγή",
+ "positionEvent": "Γεγονός",
+ "positionAlarm": "Συναγερμός",
+ "positionStatus": "Κατάσταση",
+ "positionOdometer": "Οδόμετρο",
+ "positionServiceOdometer": "Οδόμετρο Σέρβις",
+ "positionTripOdometer": "Οδόμετρο Διαδρομής",
+ "positionHours": "Ώρες",
+ "positionSteps": "Βήματα",
+ "positionInput": "Είσοδος",
+ "positionHeartRate": "Heart Rate",
+ "positionOutput": "Έξοδος",
+ "positionBatteryLevel": "Επίπεδο Συσσωρευτή",
+ "positionFuelConsumption": "Κατανάλωση καυσίμου",
+ "positionRfid": "RFID",
+ "positionVersionFw": "Έκδοση λογισμικού",
+ "positionVersionHw": "Έκδοση υλικού",
+ "positionIgnition": "Ανάφλεξη",
+ "positionFlags": "Σημαίες",
+ "positionCharge": "Φόρτιση",
+ "positionIp": "IP",
+ "positionArchive": "Αρχείο",
+ "positionVin": "VIN",
+ "positionApproximate": "Κατά προσέγγιση",
+ "positionThrottle": "Γκάζι",
+ "positionMotion": "Κίνηση",
+ "positionArmed": "Οπλισμένο",
+ "positionAcceleration": "Επιτάχυνση",
+ "positionTemp": "Θερμοκρασία",
+ "positionDeviceTemp": "Θερμοκρασία συσκευής",
+ "positionCoolantTemp": "Coolant Temperature",
+ "positionOperator": "Χειριστής",
+ "positionCommand": "Εντολή",
+ "positionBlocked": "Αποκλεισμένο",
+ "positionDtcs": "DTCs",
+ "positionObdSpeed": "Ταχύτητα OBD",
+ "positionObdOdometer": "Χιλιομετρητής OBD",
+ "positionDrivingTime": "Driving Time",
+ "positionDriverUniqueId": "Αναγνωριστικό οδηγού",
+ "positionCard": "Card",
+ "positionImage": "Εικόνα",
+ "positionVideo": "Βίντεο",
+ "positionAudio": "Ήχος",
+ "serverTitle": "Ρυθμίσεις εξυπηρετητή",
+ "serverZoom": "Εστίαση",
+ "serverRegistration": "Εγγραφή",
+ "serverReadonly": "Μόνο για ανάγνωση",
+ "serverForceSettings": "Επιβολή ρυθμίσεων",
+ "serverAnnouncement": "Αναγγελία",
+ "serverName": "Server Name",
+ "serverDescription": "Server Description",
+ "serverColorPrimary": "Color Primary",
+ "serverColorSecondary": "Color Secondary",
+ "serverLogo": "Logo Image",
+ "serverLogoInverted": "Inverted Logo Image",
+ "serverChangeDisable": "Disable Server Change",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "Χάρτης",
+ "mapActive": "Ενεργοί Χάρτες",
+ "mapOverlay": "Υπέρθεση Χάρτη",
+ "mapOverlayCustom": "Προσαρμοσμένη Υπέρθεση",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API Key",
+ "mapOpenWeatherClouds": "OpenWeather Clouds",
+ "mapOpenWeatherPrecipitation": "OpenWeather Precipitation",
+ "mapOpenWeatherPressure": "OpenWeather Pressure",
+ "mapOpenWeatherWind": "OpenWeather Wind",
+ "mapOpenWeatherTemperature": "OpenWeather Temperature",
+ "mapLayer": "Επιλογή χάρτη",
+ "mapCustom": "Προσαρμοσμένο (XYZ)",
+ "mapCustomArcgis": "Προσαρμοσμένο (ArcGIS)",
+ "mapCustomLabel": "Προσαρμοσμένος χάρτης",
+ "mapCarto": "Carto Basemaps",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "OpenTopoMap",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Κλειδί Bing Maps",
+ "mapBingRoad": "Bing Maps Road",
+ "mapBingAerial": "Bing Maps Aerial",
+ "mapBingHybrid": "Bing Maps Hybrid",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Yandex Map",
+ "mapYandexSat": "Yandex Satellite",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Χαρτογραφική υπηρεσία",
+ "mapMapboxStreets": "Οδικός Mapbox",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Υπαίθριος Mapbox",
+ "mapMapboxSatellite": "Δορυφορικός Mapbox",
+ "mapMapboxKey": "Mapbox Διακριτικό Πρόσβασης",
+ "mapMapTilerBasic": "Βασικός MapTiler",
+ "mapMapTilerHybrid": "Υβριδικός MapTiler",
+ "mapMapTilerKey": "MapTiler API Key",
+ "mapLocationIqStreets": "Οδική ΤοποθεσίαIQ",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ Access Token",
+ "mapTomTomBasic": "TomTom Basic",
+ "mapTomTomFlow": "TomTom Traffic Flow",
+ "mapTomTomIncidents": "TomTom Traffic Incidents",
+ "mapTomTomKey": "TomTom API Key",
+ "mapHereBasic": "Here Basic",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satellite",
+ "mapHereFlow": "Here Traffic Flow",
+ "mapHereKey": "Here API Key",
+ "mapShapePolygon": "Πολύγωνο",
+ "mapShapeCircle": "Κύκλος",
+ "mapShapePolyline": "Πολύγραμμο",
+ "mapLiveRoutes": "Ζωντανές διαδρομές",
+ "mapDirection": "Εμφάνιση κατεύθυνσης",
+ "mapCurrentLocation": "Τρέχουσα τοποθεσία",
+ "mapPoiLayer": "Επίπεδο POI",
+ "mapClustering": "Ομαδοποίηση δεικτών",
+ "mapOnSelect": "Εμφάνιση χάρτη κατά την επιλογή",
+ "mapDefault": "Προεπιλεγμένος χάρτης",
+ "stateTitle": "Κατάσταση",
+ "stateName": "Παράμετρος",
+ "stateValue": "Τιμή",
+ "commandTitle": "Εντολή",
+ "commandSend": "Αποστολή",
+ "commandSent": "Εντολή εστάλη",
+ "commandQueued": "Εντολή σε ουρά",
+ "commandUnit": "Μονάδα",
+ "commandCustom": "Προσαρμοσμένη εντολή",
+ "commandDeviceIdentification": "Αναγνωριστικό συσκευής",
+ "commandPositionSingle": "Ενιαία αναφορά",
+ "commandPositionPeriodic": "Περιοδικές αναφορές",
+ "commandPositionStop": "Λήξη αναφορών",
+ "commandEngineStop": "Κλείσιμο",
+ "commandEngineResume": "Επανεκκίνηση",
+ "commandAlarmArm": "Ενεργοποίηση συναγερμού",
+ "commandAlarmDisarm": "Απενεργοποίηση συναγερμού",
+ "commandAlarmDismiss": "Απενεργοποίηση συναγερμού",
+ "commandSetTimezone": "Καθορισμός ζώνης ώρας",
+ "commandRequestPhoto": "Αίτημα για φωτογραφία",
+ "commandPowerOff": "Απενεργοποίηση συσκευής",
+ "commandRebootDevice": "Επανεκκίνηση συσκευής",
+ "commandFactoryReset": "Επαναφορά εργοστασιακών ρυθμίσεων",
+ "commandSendSms": "Αποστολή γραπτού μηνύματος (SMS)",
+ "commandSendUssd": "Αποστολή USSD",
+ "commandSosNumber": "Καθορισμός αριθμού SOS",
+ "commandSilenceTime": "Καθορισμός χρόνου σιωπής",
+ "commandSetPhonebook": "Καθορισμός τηλεφωνικού καταλόγου",
+ "commandVoiceMessage": "Φωνητικό μήνυμα",
+ "commandOutputControl": "Έλεγχος αποτελεσμάτων",
+ "commandVoiceMonitoring": "Παρακολούθηση Φωνής",
+ "commandSetAgps": "Ρύθμιση AGPS",
+ "commandSetIndicator": "Θέσε Δείκτη",
+ "commandConfiguration": "Διαμόρφωση",
+ "commandGetVersion": "Λήψη Έκδοσης",
+ "commandFirmwareUpdate": "Ενημέρωση λογισμικού",
+ "commandSetConnection": "Θέσε Σύνδεση",
+ "commandSetOdometer": "Θέσε Οδόμετρο",
+ "commandGetModemStatus": "Λήψη Κατάσταση Αποδιαμορφωτή",
+ "commandGetDeviceStatus": "Λήψη Κατάστασης Συσκευής",
+ "commandSetSpeedLimit": "Ρύθμιση ορίου ταχύτητας",
+ "commandModePowerSaving": "Λειτουργία εξοικονόμησης ενέργειας",
+ "commandModeDeepSleep": "Λειτουργία βαθύ ύπνου",
+ "commandAlarmGeofence": "Ρύθμιση συναγερμού Geofence",
+ "commandAlarmBattery": "Ρύθμιση συναγερμού μπαταρίας",
+ "commandAlarmSos": "Ρύθμιση συναγερμού SOS",
+ "commandAlarmRemove": "Ρύθμιση αφαίρεσης συναγερμού",
+ "commandAlarmClock": "Ρύθμιση ξυπνητηριού",
+ "commandAlarmSpeed": "Ρύθμιση συναγερμού ταχύτητας",
+ "commandAlarmFall": "Ρύθμιση συναγερμού πτώσης",
+ "commandAlarmVibration": "Ρύθμιση συναγερμού κραδασμών",
+ "commandFrequency": "Συχνότητα",
+ "commandTimezone": "Μετατόπιση ζώνης ώρας",
+ "commandMessage": "Μήνυμα",
+ "commandRadius": "Ακτίνα",
+ "commandEnable": "Ενεργοποίηση",
+ "commandData": "Δεδομένα",
+ "commandIndex": "Δείκτης",
+ "commandPhone": "Τηλεφωνικός αριθμός",
+ "commandServer": "Εξυπηρετητής",
+ "commandPort": "Θύρα",
+ "eventAll": "Όλα τα γεγονότα",
+ "eventDeviceOnline": "Κατάσταση σε σύνδεση",
+ "eventDeviceUnknown": "Άγνωστη κατάσταση",
+ "eventDeviceOffline": "Κατάσταση εκτός σύνδεσης",
+ "eventDeviceInactive": "Αδρανής συσκευή",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "Συσκευή εν κινήσει",
+ "eventDeviceStopped": "Συσκευή σε στάση",
+ "eventDeviceOverspeed": "Υπέρβαση ορίου ταχύτητας",
+ "eventDeviceFuelDrop": "Πτώση καυσίμων",
+ "eventDeviceFuelIncrease": "Αύξηση καυσίμου",
+ "eventCommandResult": "Αποτέλεσμα εντολής",
+ "eventGeofenceEnter": "Είσοδος σε γεωφράκτη",
+ "eventGeofenceExit": "Έξοδος από γεωφράκτη",
+ "eventAlarm": "Συναγερμός",
+ "eventIgnitionOn": "Μηχανή αναμμένη",
+ "eventIgnitionOff": "Μηχανή σβηστή",
+ "eventMaintenance": "Απαιτείται συντήρηση",
+ "eventTextMessage": "Ελήφθη γραπτό μήνυμα",
+ "eventDriverChanged": "Ο οδηγός άλλαξε",
+ "eventMedia": "Media",
+ "eventsScrollToLast": "Κύλιση στο τέλος",
+ "eventsSoundEvents": "Ήχοι Ειδοποιήσεων",
+ "eventsSoundAlarms": "Ήχοι Συναγερμού",
+ "alarmGeneral": "Γενικά",
+ "alarmSos": "SOS",
+ "alarmVibration": "Δόνηση",
+ "alarmMovement": "Κίνηση",
+ "alarmLowspeed": "Χαμηλή ταχύτητα",
+ "alarmOverspeed": "Υπέρβαση ορίου ταχύτητας",
+ "alarmFallDown": "Πτώση",
+ "alarmLowPower": "Χαμηλή ισχύς",
+ "alarmLowBattery": "Χαμηλή μπαταρία",
+ "alarmFault": "Σφάλμα",
+ "alarmPowerOff": "Απενεργοποίηση",
+ "alarmPowerOn": "Ενεργοποίηση",
+ "alarmDoor": "Πόρτα",
+ "alarmLock": "Κλείδωμα",
+ "alarmUnlock": "Ξεκλείδωμα",
+ "alarmGeofence": "Γεωφράκτης",
+ "alarmGeofenceEnter": "Είσοδος σε γεωφράκτη",
+ "alarmGeofenceExit": "Είσοδος από γεωφράκτη",
+ "alarmGpsAntennaCut": "Αποκοπή κεραίας GPS",
+ "alarmAccident": "Ατύχημα",
+ "alarmTow": "Ρυμούλκηση",
+ "alarmIdle": "Αδρανές",
+ "alarmHighRpm": "Υψηλές στροφές μηχανής",
+ "alarmHardAcceleration": "Απότομη επιτάχυνση",
+ "alarmHardBraking": "Απότομο φρενάρισμα",
+ "alarmHardCornering": "Απότομο στρίψιμο",
+ "alarmLaneChange": "Αλλαγή λωρίδας",
+ "alarmFatigueDriving": "Κόπωση Οδήγησης",
+ "alarmPowerCut": "Απώλεια ενέργειας",
+ "alarmPowerRestored": "Ισχύς αποκαταστάθηκε",
+ "alarmJamming": "Παρεμβολές",
+ "alarmTemperature": "Θερμοκρασία",
+ "alarmParking": "Στάθμευση",
+ "alarmBonnet": "Καπό",
+ "alarmFootBrake": "Ποδόφρενο",
+ "alarmFuelLeak": "Διαρροή καυσίμου",
+ "alarmTampering": "Παραποίηση",
+ "alarmRemoving": "Αφαίρεση",
+ "notificationType": "Τύπος ειδοποίησης",
+ "notificationAlways": "Όλες οι συσκευές",
+ "notificationNotificators": "Κανάλια",
+ "notificatorCommand": "Εντολή",
+ "notificatorWeb": "Web",
+ "notificatorMail": "Mail",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Επανάληψη",
+ "reportCombined": "Σε συνδυασμό",
+ "reportRoute": "Διαδρομή",
+ "reportEvents": "Γεγονότα",
+ "reportTrips": "Ταξίδια",
+ "reportStops": "Στάσεις",
+ "reportSummary": "Περίληψη",
+ "reportDaily": "Επισκόπιση ημέρας",
+ "reportChart": "Διάγραμμα",
+ "reportConfigure": "Διαμόρφωση",
+ "reportEventTypes": "Tύποι γεγονότος",
+ "reportChartType": "Τύπος διαγράμματος",
+ "reportShowMarkers": "Δείξε Δείκτες",
+ "reportExport": "Εξαγωγή",
+ "reportEmail": "Αποστολή αναφοράς με email",
+ "reportSchedule": "Πρόγραμμα",
+ "reportPeriod": "Περίοδος",
+ "reportCustom": "Προσαρμοσμένος",
+ "reportToday": "Σήμερα",
+ "reportYesterday": "Χθες",
+ "reportThisWeek": "Τρέχουσα Εβδομάδα",
+ "reportPreviousWeek": "Προηγούμενη Εβδομάδα",
+ "reportThisMonth": "Τρέχων Μήνας",
+ "reportPreviousMonth": "Προηγούμενος Μήνας",
+ "reportDeviceName": "Όνομα συσκευής",
+ "reportAverageSpeed": "Μέση ταχύτητα",
+ "reportMaximumSpeed": "Μέγιστη ταχύτητα",
+ "reportEngineHours": "Ώρες Μηχανής",
+ "reportDuration": "Διάρκεια",
+ "reportStartDate": "Ημερομηνία έναρξης",
+ "reportStartTime": "Ώρα έναρξης",
+ "reportStartAddress": "Διεύθυνση αφετηρίας",
+ "reportEndTime": "Ώρα λήξης",
+ "reportEndAddress": "Διεύθυνση τερματισμού",
+ "reportSpentFuel": "Κατανάλωση καυσίμων",
+ "reportStartOdometer": "Έναρξη χιλιομετρητή",
+ "reportEndOdometer": "Τέλος χιλιομετρητή",
+ "statisticsTitle": "Στατιστικά",
+ "statisticsCaptureTime": "Ώρα Καταγραφής",
+ "statisticsActiveUsers": "Ενεργοί Χρήστες",
+ "statisticsActiveDevices": "Ενεργές Συσκευές",
+ "statisticsRequests": "Αιτήματα",
+ "statisticsMessagesReceived": "Εισερχόμενα Μυνήματα",
+ "statisticsMessagesStored": "Αποθηκευμένα Μυνήματα",
+ "statisticsGeocoder": "Αιτήματα Γεωκωδικοποιητή",
+ "statisticsGeolocation": "Αιτήματα Γεωτοποθέτησης",
+ "categoryArrow": "Βέλος",
+ "categoryDefault": "Προκαθορισμένο",
+ "categoryAnimal": "Ζώο",
+ "categoryBicycle": "Ποδήλατο",
+ "categoryBoat": "Πλοίο",
+ "categoryBus": "Λεωφορείο",
+ "categoryCar": "Αυτοκίνητο",
+ "categoryCamper": "Camper",
+ "categoryCrane": "Γερανός",
+ "categoryHelicopter": "Ελικόπτερο",
+ "categoryMotorcycle": "Μοτοσικλέτα",
+ "categoryOffroad": "Εκτός δρόμου",
+ "categoryPerson": "Άτομο",
+ "categoryPickup": "Αγροτικό",
+ "categoryPlane": "Αεροπλάνο",
+ "categoryShip": "Πλοίο",
+ "categoryTractor": "Ρυμουλκό",
+ "categoryTrain": "Τραίνο",
+ "categoryTram": "Τραμ",
+ "categoryTrolleybus": "Τρόλεϊ",
+ "categoryTruck": "Φορτηγό",
+ "categoryVan": "Κλειστό Φορτηγό",
+ "categoryScooter": "Σκούτερ",
+ "maintenanceStart": "Εκκίνηση",
+ "maintenancePeriod": "Περίοδος"
+} \ No newline at end of file
diff --git a/src/resources/l10n/en.json b/src/resources/l10n/en.json
new file mode 100644
index 00000000..a9303f63
--- /dev/null
+++ b/src/resources/l10n/en.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "Loading...",
+ "sharedHide": "Hide",
+ "sharedSave": "Save",
+ "sharedUpload": "Upload",
+ "sharedSet": "Set",
+ "sharedCancel": "Cancel",
+ "sharedCopy": "Copy",
+ "sharedAdd": "Add",
+ "sharedEdit": "Edit",
+ "sharedRemove": "Remove",
+ "sharedRemoveConfirm": "Remove item?",
+ "sharedNoData": "No data",
+ "sharedSubject": "Subject",
+ "sharedYes": "Yes",
+ "sharedNo": "No",
+ "sharedKm": "km",
+ "sharedMi": "mi",
+ "sharedNmi": "nmi",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "kn",
+ "sharedKmh": "km/h",
+ "sharedMph": "mph",
+ "sharedHour": "Hour",
+ "sharedMinute": "Minute",
+ "sharedSecond": "Second",
+ "sharedDays": "days",
+ "sharedHours": "hours",
+ "sharedMinutes": "minutes",
+ "sharedDecimalDegrees": "Decimal Degrees",
+ "sharedDegreesDecimalMinutes": "Degrees Decimal Minutes",
+ "sharedDegreesMinutesSeconds": "Degrees Minutes Seconds",
+ "sharedName": "Name",
+ "sharedDescription": "Description",
+ "sharedSearch": "Search",
+ "sharedIconScale": "Icon Scale",
+ "sharedGeofence": "Geofence",
+ "sharedGeofences": "Geofences",
+ "sharedCreateGeofence": "Create Geofence",
+ "sharedNotifications": "Notifications",
+ "sharedNotification": "Notification",
+ "sharedAttributes": "Attributes",
+ "sharedAttribute": "Attribute",
+ "sharedDrivers": "Drivers",
+ "sharedDriver": "Driver",
+ "sharedArea": "Area",
+ "sharedSound": "Notification Sound",
+ "sharedType": "Type",
+ "sharedDistance": "Distance",
+ "sharedHourAbbreviation": "h",
+ "sharedMinuteAbbreviation": "m",
+ "sharedSecondAbbreviation": "s",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "l",
+ "sharedGallonAbbreviation": "gal",
+ "sharedLiter": "Liter",
+ "sharedImpGallon": "Imp. Gallon",
+ "sharedUsGallon": "U.S. Gallon",
+ "sharedLiterPerHourAbbreviation": "l/h",
+ "sharedGetMapState": "Get Map State",
+ "sharedComputedAttribute": "Computed Attribute",
+ "sharedComputedAttributes": "Computed Attributes",
+ "sharedCheckComputedAttribute": "Check Computed Attribute",
+ "sharedExpression": "Expression",
+ "sharedDevice": "Device",
+ "sharedTest": "Test",
+ "sharedTestNotification": "Send Test Notification",
+ "sharedTestNotificators": "Test Channels",
+ "sharedTestExpression": "Test Expression",
+ "sharedCalendar": "Calendar",
+ "sharedCalendars": "Calendars",
+ "sharedFile": "File",
+ "sharedSearchDevices": "Search Devices",
+ "sharedSortBy": "Sort By",
+ "sharedFilterMap": "Filter on Map",
+ "sharedSelectFile": "Select File",
+ "sharedPhone": "Phone",
+ "sharedRequired": "Required",
+ "sharedPreferences": "Preferences",
+ "sharedPermissions": "Permissions",
+ "sharedConnections": "Connections",
+ "sharedExtra": "Extra",
+ "sharedPrimary": "Primary",
+ "sharedSecondary": "Secondary",
+ "sharedTypeString": "String",
+ "sharedTypeNumber": "Number",
+ "sharedTypeBoolean": "Boolean",
+ "sharedTimezone": "Timezone",
+ "sharedInfoTitle": "Info",
+ "sharedSavedCommand": "Saved Command",
+ "sharedSavedCommands": "Saved Commands",
+ "sharedNew": "New…",
+ "sharedShowAddress": "Show Address",
+ "sharedShowDetails": "More Details",
+ "sharedDisabled": "Disabled",
+ "sharedMaintenance": "Maintenance",
+ "sharedDeviceAccumulators": "Accumulators",
+ "sharedAlarms": "Alarms",
+ "sharedLocation": "Location",
+ "sharedImport": "Import",
+ "sharedColumns": "Columns",
+ "sharedDropzoneText": "Drag and drop a file here or click",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "Simple",
+ "calendarRecurrence": "Recurrence",
+ "calendarOnce": "Once",
+ "calendarDaily": "Daily",
+ "calendarWeekly": "Weekly",
+ "calendarMonthly": "Monthly",
+ "calendarDays": "Days",
+ "calendarSunday": "Sunday",
+ "calendarMonday": "Monday",
+ "calendarTuesday": "Tuesday",
+ "calendarWednesday": "Wednesday",
+ "calendarThursday": "Thursday",
+ "calendarFriday": "Friday",
+ "calendarSaturday": "Saturday",
+ "attributeShowGeofences": "Show Geofences",
+ "attributeSpeedLimit": "Speed Limit",
+ "attributeFuelDropThreshold": "Fuel Drop Threshold",
+ "attributeFuelIncreaseThreshold": "Fuel Increase Threshold",
+ "attributePolylineDistance": "Polyline Distance",
+ "attributeReportIgnoreOdometer": "Report: Ignore Odometer",
+ "attributeWebReportColor": "Web: Report Color",
+ "attributeDevicePassword": "Device Password",
+ "attributeDeviceImage": "Device Image",
+ "attributeDeviceInactivityStart": "Device Inactivity Start",
+ "attributeDeviceInactivityPeriod": "Device Inactivity Period",
+ "attributeProcessingCopyAttributes": "Processing: Copy Attributes",
+ "attributeColor": "Color",
+ "attributeWebLiveRouteLength": "Web: Live Route Length",
+ "attributeWebSelectZoom": "Web: Zoom On Select",
+ "attributeWebMaxZoom": "Web: Maximum Zoom",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Pushover User Key",
+ "attributePushoverDeviceNames": "Pushover Device Names",
+ "attributeMailSmtpHost": "Mail: SMTP Host",
+ "attributeMailSmtpPort": "Mail: SMTP Port",
+ "attributeMailSmtpStarttlsEnable": "Mail: SMTP STARTTLS Enable",
+ "attributeMailSmtpStarttlsRequired": "Mail: SMTP STARTTLS Required",
+ "attributeMailSmtpSslEnable": "Mail: SMTP SSL Enable",
+ "attributeMailSmtpSslTrust": "Mail: SMTP SSL Trust",
+ "attributeMailSmtpSslProtocols": "Mail: SMTP SSL Protocols",
+ "attributeMailSmtpFrom": "Mail: SMTP From",
+ "attributeMailSmtpAuth": "Mail: SMTP Auth Enable",
+ "attributeMailSmtpUsername": "Mail: SMTP Username",
+ "attributeMailSmtpPassword": "Mail: SMTP Password",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: Disable Attributes",
+ "attributeUiDisableGroups": "UI: Disable Groups",
+ "attributeUiDisableEvents": "UI: Disable Events",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "UI: Disable Drivers",
+ "attributeUiDisableComputedAttributes": "UI: Disable Computed Attributes",
+ "attributeUiDisableCalendars": "UI: Disable Calendars",
+ "attributeUiDisableMaintenance": "UI: Disable Maintenance",
+ "attributeUiHidePositionAttributes": "UI: Hide Position Attributes",
+ "attributeUiDisableLoginLanguage": "UI: Disable Login Language",
+ "attributeNotificationTokens": "Notification Tokens",
+ "attributePopupInfo": "Popup Info",
+ "errorTitle": "Error",
+ "errorGeneral": "Invalid parameters or constraints violation",
+ "errorConnection": "Connection error",
+ "errorSocket": "Web socket connection error",
+ "errorZero": "Can't be zero",
+ "userEmail": "Email",
+ "userPassword": "Password",
+ "userAdmin": "Admin",
+ "userRemember": "Remember",
+ "userExpirationTime": "Expiration",
+ "userDeviceLimit": "Device Limit",
+ "userUserLimit": "User Limit",
+ "userDeviceReadonly": "Device Readonly",
+ "userLimitCommands": "Limit Commands",
+ "userDisableReports": "Disable Reports",
+ "userFixedEmail": "No Email Change",
+ "userToken": "Token",
+ "userDeleteAccount": "Delete Account",
+ "userTemporary": "Temporary",
+ "loginTitle": "Login",
+ "loginLanguage": "Language",
+ "loginReset": "Reset Password",
+ "loginRegister": "Register",
+ "loginLogin": "Login",
+ "loginOpenId": "Login with OpenID",
+ "loginFailed": "Incorrect email address or password",
+ "loginCreated": "New user has been registered",
+ "loginResetSuccess": "Check your email",
+ "loginUpdateSuccess": "New password is set",
+ "loginLogout": "Logout",
+ "loginLogo": "Logo",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "Devices and State",
+ "deviceSelected": "Selected Device",
+ "deviceTitle": "Devices",
+ "devicePrimaryInfo": "Device Title",
+ "deviceSecondaryInfo": "Device Detail",
+ "deviceIdentifier": "Identifier",
+ "deviceModel": "Model",
+ "deviceContact": "Contact",
+ "deviceCategory": "Category",
+ "deviceLastUpdate": "Last Update",
+ "deviceCommand": "Command",
+ "deviceFollow": "Follow",
+ "deviceTotalDistance": "Total Distance",
+ "deviceStatus": "Status",
+ "deviceStatusOnline": "Online",
+ "deviceStatusOffline": "Offline",
+ "deviceStatusUnknown": "Unknown",
+ "deviceRegisterFirst": "Register your first device",
+ "deviceIdentifierHelp": "IMEI, serial number or other id. It has to match the identifier device reports to the server.",
+ "deviceShare": "Share Device",
+ "groupDialog": "Group",
+ "groupParent": "Group",
+ "groupNoGroup": "No Group",
+ "settingsTitle": "Settings",
+ "settingsUser": "Account",
+ "settingsGroups": "Groups",
+ "settingsServer": "Server",
+ "settingsUsers": "Users",
+ "settingsDistanceUnit": "Distance Unit",
+ "settingsAltitudeUnit": "Altitude Unit",
+ "settingsSpeedUnit": "Speed Unit",
+ "settingsVolumeUnit": "Volume Unit",
+ "settingsTwelveHourFormat": "12-hour Format",
+ "settingsCoordinateFormat": "Coordinates Format",
+ "settingsServerVersion": "Server Version",
+ "settingsAppVersion": "App Version",
+ "settingsConnection": "Connection",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "Reports",
+ "reportScheduled": "Scheduled Reports",
+ "reportDevice": "Device",
+ "reportGroup": "Group",
+ "reportFrom": "From",
+ "reportTo": "To",
+ "reportShow": "Show",
+ "reportClear": "Clear",
+ "linkGoogleMaps": "Google Maps",
+ "linkAppleMaps": "Apple Maps",
+ "linkStreetView": "Street View",
+ "positionFixTime": "Fix Time",
+ "positionDeviceTime": "Device Time",
+ "positionServerTime": "Server Time",
+ "positionValid": "Valid",
+ "positionAccuracy": "Accuracy",
+ "positionLatitude": "Latitude",
+ "positionLongitude": "Longitude",
+ "positionAltitude": "Altitude",
+ "positionSpeed": "Speed",
+ "positionCourse": "Course",
+ "positionAddress": "Address",
+ "positionProtocol": "Protocol",
+ "positionDistance": "Distance",
+ "positionRpm": "RPM",
+ "positionFuel": "Fuel",
+ "positionPower": "Power",
+ "positionBattery": "Battery",
+ "positionRaw": "Raw",
+ "positionIndex": "Index",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "Satellites",
+ "positionSatVisible": "Visible Satellites",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "Roaming",
+ "positionEvent": "Event",
+ "positionAlarm": "Alarm",
+ "positionStatus": "Status",
+ "positionOdometer": "Odometer",
+ "positionServiceOdometer": "Service Odometer",
+ "positionTripOdometer": "Trip Odometer",
+ "positionHours": "Hours",
+ "positionSteps": "Steps",
+ "positionInput": "Input",
+ "positionHeartRate": "Heart Rate",
+ "positionOutput": "Output",
+ "positionBatteryLevel": "Battery Level",
+ "positionFuelConsumption": "Fuel Consumption",
+ "positionRfid": "RFID",
+ "positionVersionFw": "Firmware Version",
+ "positionVersionHw": "Hardware Version",
+ "positionIgnition": "Ignition",
+ "positionFlags": "Flags",
+ "positionCharge": "Charge",
+ "positionIp": "IP",
+ "positionArchive": "Archive",
+ "positionVin": "VIN",
+ "positionApproximate": "Approximate",
+ "positionThrottle": "Throttle",
+ "positionMotion": "Motion",
+ "positionArmed": "Armed",
+ "positionAcceleration": "Acceleration",
+ "positionTemp": "Temperature",
+ "positionDeviceTemp": "Device Temperature",
+ "positionCoolantTemp": "Coolant Temperature",
+ "positionOperator": "Operator",
+ "positionCommand": "Command",
+ "positionBlocked": "Blocked",
+ "positionDtcs": "DTCs",
+ "positionObdSpeed": "OBD Speed",
+ "positionObdOdometer": "OBD Odometer",
+ "positionDrivingTime": "Driving Time",
+ "positionDriverUniqueId": "Driver Unique Id",
+ "positionCard": "Card",
+ "positionImage": "Image",
+ "positionVideo": "Video",
+ "positionAudio": "Audio",
+ "serverTitle": "Server Settings",
+ "serverZoom": "Zoom",
+ "serverRegistration": "Registration",
+ "serverReadonly": "Readonly",
+ "serverForceSettings": "Force Settings",
+ "serverAnnouncement": "Announcement",
+ "serverName": "Server Name",
+ "serverDescription": "Server Description",
+ "serverColorPrimary": "Color Primary",
+ "serverColorSecondary": "Color Secondary",
+ "serverLogo": "Logo Image",
+ "serverLogoInverted": "Inverted Logo Image",
+ "serverChangeDisable": "Disable Server Change",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "Map",
+ "mapActive": "Active Maps",
+ "mapOverlay": "Map Overlay",
+ "mapOverlayCustom": "Custom Overlay",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API Key",
+ "mapOpenWeatherClouds": "OpenWeather Clouds",
+ "mapOpenWeatherPrecipitation": "OpenWeather Precipitation",
+ "mapOpenWeatherPressure": "OpenWeather Pressure",
+ "mapOpenWeatherWind": "OpenWeather Wind",
+ "mapOpenWeatherTemperature": "OpenWeather Temperature",
+ "mapLayer": "Map Layer",
+ "mapCustom": "Custom (XYZ)",
+ "mapCustomArcgis": "Custom (ArcGIS)",
+ "mapCustomLabel": "Custom map",
+ "mapCarto": "Carto Basemaps",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Bing Maps Key",
+ "mapBingRoad": "Bing Maps Road",
+ "mapBingAerial": "Bing Maps Aerial",
+ "mapBingHybrid": "Bing Maps Hybrid",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Yandex Map",
+ "mapYandexSat": "Yandex Satellite",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox Streets",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox Outdoors",
+ "mapMapboxSatellite": "Mapbox Satellite",
+ "mapMapboxKey": "Mapbox Access Token",
+ "mapMapTilerBasic": "MapTiler Basic",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "MapTiler API Key",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ Access Token",
+ "mapTomTomBasic": "TomTom Basic",
+ "mapTomTomFlow": "TomTom Traffic Flow",
+ "mapTomTomIncidents": "TomTom Traffic Incidents",
+ "mapTomTomKey": "TomTom API Key",
+ "mapHereBasic": "Here Basic",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satellite",
+ "mapHereFlow": "Here Traffic Flow",
+ "mapHereKey": "Here API Key",
+ "mapShapePolygon": "Polygon",
+ "mapShapeCircle": "Circle",
+ "mapShapePolyline": "Polyline",
+ "mapLiveRoutes": "Live Routes",
+ "mapDirection": "Show Direction",
+ "mapCurrentLocation": "Current Location",
+ "mapPoiLayer": "POI Layer",
+ "mapClustering": "Markers Clustering",
+ "mapOnSelect": "Show Map on Selection",
+ "mapDefault": "Default Map",
+ "stateTitle": "State",
+ "stateName": "Attribute",
+ "stateValue": "Value",
+ "commandTitle": "Command",
+ "commandSend": "Send",
+ "commandSent": "Command sent",
+ "commandQueued": "Command queued",
+ "commandUnit": "Unit",
+ "commandCustom": "Custom command",
+ "commandDeviceIdentification": "Device Identification",
+ "commandPositionSingle": "Single Reporting",
+ "commandPositionPeriodic": "Periodic Reporting",
+ "commandPositionStop": "Stop Reporting",
+ "commandEngineStop": "Engine Stop",
+ "commandEngineResume": "Engine Resume",
+ "commandAlarmArm": "Arm Alarm",
+ "commandAlarmDisarm": "Disarm Alarm",
+ "commandAlarmDismiss": "Dismiss Alarm",
+ "commandSetTimezone": "Set Timezone",
+ "commandRequestPhoto": "Request Photo",
+ "commandPowerOff": "Power Off Device",
+ "commandRebootDevice": "Reboot Device",
+ "commandFactoryReset": "Factory Reset",
+ "commandSendSms": "Send SMS",
+ "commandSendUssd": "Send USSD",
+ "commandSosNumber": "Set SOS Number",
+ "commandSilenceTime": "Set Silence Time",
+ "commandSetPhonebook": "Set Phonebook",
+ "commandVoiceMessage": "Voice Message",
+ "commandOutputControl": "Output Control",
+ "commandVoiceMonitoring": "Voice Monitoring",
+ "commandSetAgps": "Set AGPS",
+ "commandSetIndicator": "Set Indicator",
+ "commandConfiguration": "Configuration",
+ "commandGetVersion": "Get Version",
+ "commandFirmwareUpdate": "Update Firmware",
+ "commandSetConnection": "Set Connection",
+ "commandSetOdometer": "Set Odometer",
+ "commandGetModemStatus": "Get Modem Status",
+ "commandGetDeviceStatus": "Get Device Status",
+ "commandSetSpeedLimit": "Set Speed Limit",
+ "commandModePowerSaving": "Power Saving Mode",
+ "commandModeDeepSleep": "Deep Sleep Mode",
+ "commandAlarmGeofence": "Set Geofence Alarm",
+ "commandAlarmBattery": "Set Battery Alarm",
+ "commandAlarmSos": "Set SOS Alarm",
+ "commandAlarmRemove": "Set Remove Alarm",
+ "commandAlarmClock": "Set Clock Alarm",
+ "commandAlarmSpeed": "Set Speed Alarm",
+ "commandAlarmFall": "Set Fall Alarm",
+ "commandAlarmVibration": "Set Vibration Alarm",
+ "commandFrequency": "Frequency",
+ "commandTimezone": "Timezone Offset",
+ "commandMessage": "Message",
+ "commandRadius": "Radius",
+ "commandEnable": "Enable",
+ "commandData": "Data",
+ "commandIndex": "Index",
+ "commandPhone": "Phone Number",
+ "commandServer": "Server",
+ "commandPort": "Port",
+ "eventAll": "All Events",
+ "eventDeviceOnline": "Status online",
+ "eventDeviceUnknown": "Status unknown",
+ "eventDeviceOffline": "Status offline",
+ "eventDeviceInactive": "Device inactive",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "Device moving",
+ "eventDeviceStopped": "Device stopped",
+ "eventDeviceOverspeed": "Speed limit exceeded",
+ "eventDeviceFuelDrop": "Fuel drop",
+ "eventDeviceFuelIncrease": "Fuel increase",
+ "eventCommandResult": "Command result",
+ "eventGeofenceEnter": "Geofence entered",
+ "eventGeofenceExit": "Geofence exited",
+ "eventAlarm": "Alarm",
+ "eventIgnitionOn": "Ignition on",
+ "eventIgnitionOff": "Ignition off",
+ "eventMaintenance": "Maintenance required",
+ "eventTextMessage": "Text message received",
+ "eventDriverChanged": "Driver changed",
+ "eventMedia": "Media",
+ "eventsScrollToLast": "Scroll To Last",
+ "eventsSoundEvents": "Sound Events",
+ "eventsSoundAlarms": "Sound Alarms",
+ "alarmGeneral": "General",
+ "alarmSos": "SOS",
+ "alarmVibration": "Vibration",
+ "alarmMovement": "Movement",
+ "alarmLowspeed": "Low Speed",
+ "alarmOverspeed": "Overspeed",
+ "alarmFallDown": "Fall Down",
+ "alarmLowPower": "Low Power",
+ "alarmLowBattery": "Low Battery",
+ "alarmFault": "Fault",
+ "alarmPowerOff": "Power Off",
+ "alarmPowerOn": "Power On",
+ "alarmDoor": "Door",
+ "alarmLock": "Lock",
+ "alarmUnlock": "Unlock",
+ "alarmGeofence": "Geofence",
+ "alarmGeofenceEnter": "Geofence Enter",
+ "alarmGeofenceExit": "Geofence Exit",
+ "alarmGpsAntennaCut": "GPS Antenna Cut",
+ "alarmAccident": "Accident",
+ "alarmTow": "Tow",
+ "alarmIdle": "Idle",
+ "alarmHighRpm": "High RPM",
+ "alarmHardAcceleration": "Hard Acceleration",
+ "alarmHardBraking": "Hard Braking",
+ "alarmHardCornering": "Hard Cornering",
+ "alarmLaneChange": "Lane Change",
+ "alarmFatigueDriving": "Fatigue Driving",
+ "alarmPowerCut": "Power Cut",
+ "alarmPowerRestored": "Power Restored",
+ "alarmJamming": "Jamming",
+ "alarmTemperature": "Temperature",
+ "alarmParking": "Parking",
+ "alarmBonnet": "Bonnet",
+ "alarmFootBrake": "Foot Brake",
+ "alarmFuelLeak": "Fuel Leak",
+ "alarmTampering": "Tampering",
+ "alarmRemoving": "Removing",
+ "notificationType": "Type of Notification",
+ "notificationAlways": "All Devices",
+ "notificationNotificators": "Channels",
+ "notificatorCommand": "Command",
+ "notificatorWeb": "Web",
+ "notificatorMail": "Mail",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Replay",
+ "reportCombined": "Combined",
+ "reportRoute": "Route",
+ "reportEvents": "Events",
+ "reportTrips": "Trips",
+ "reportStops": "Stops",
+ "reportSummary": "Summary",
+ "reportDaily": "Daily Summary",
+ "reportChart": "Chart",
+ "reportConfigure": "Configure",
+ "reportEventTypes": "Event Types",
+ "reportChartType": "Chart Type",
+ "reportShowMarkers": "Show Markers",
+ "reportExport": "Export",
+ "reportEmail": "Email Report",
+ "reportSchedule": "Schedule",
+ "reportPeriod": "Period",
+ "reportCustom": "Custom",
+ "reportToday": "Today",
+ "reportYesterday": "Yesterday",
+ "reportThisWeek": "This Week",
+ "reportPreviousWeek": "Previous Week",
+ "reportThisMonth": "This Month",
+ "reportPreviousMonth": "Previous Month",
+ "reportDeviceName": "Device Name",
+ "reportAverageSpeed": "Average Speed",
+ "reportMaximumSpeed": "Maximum Speed",
+ "reportEngineHours": "Engine Hours",
+ "reportDuration": "Duration",
+ "reportStartDate": "Start Date",
+ "reportStartTime": "Start Time",
+ "reportStartAddress": "Start Address",
+ "reportEndTime": "End Time",
+ "reportEndAddress": "End Address",
+ "reportSpentFuel": "Spent Fuel",
+ "reportStartOdometer": "Odometer Start",
+ "reportEndOdometer": "Odometer End",
+ "statisticsTitle": "Statistics",
+ "statisticsCaptureTime": "Capture Time",
+ "statisticsActiveUsers": "Active Users",
+ "statisticsActiveDevices": "Active Devices",
+ "statisticsRequests": "Requests",
+ "statisticsMessagesReceived": "Messages Received",
+ "statisticsMessagesStored": "Messages Stored",
+ "statisticsGeocoder": "Geocoder Requests",
+ "statisticsGeolocation": "Geolocation Requests",
+ "categoryArrow": "Arrow",
+ "categoryDefault": "Default",
+ "categoryAnimal": "Animal",
+ "categoryBicycle": "Bicycle",
+ "categoryBoat": "Boat",
+ "categoryBus": "Bus",
+ "categoryCar": "Car",
+ "categoryCamper": "Camper",
+ "categoryCrane": "Crane",
+ "categoryHelicopter": "Helicopter",
+ "categoryMotorcycle": "Motorcycle",
+ "categoryOffroad": "Offroad",
+ "categoryPerson": "Person",
+ "categoryPickup": "Pickup",
+ "categoryPlane": "Plane",
+ "categoryShip": "Ship",
+ "categoryTractor": "Tractor",
+ "categoryTrain": "Train",
+ "categoryTram": "Tram",
+ "categoryTrolleybus": "Trolleybus",
+ "categoryTruck": "Truck",
+ "categoryVan": "Van",
+ "categoryScooter": "Scooter",
+ "maintenanceStart": "Start",
+ "maintenancePeriod": "Period"
+}
diff --git a/src/resources/l10n/es.json b/src/resources/l10n/es.json
new file mode 100644
index 00000000..a0405eb1
--- /dev/null
+++ b/src/resources/l10n/es.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "Cargando…",
+ "sharedHide": "Ocultar",
+ "sharedSave": "Guardar",
+ "sharedUpload": "Cargar",
+ "sharedSet": "Establecer",
+ "sharedCancel": "Cancelar",
+ "sharedCopy": "Copiar",
+ "sharedAdd": "Añadir",
+ "sharedEdit": "Editar",
+ "sharedRemove": "Eliminar",
+ "sharedRemoveConfirm": "¿Eliminar elemento?",
+ "sharedNoData": "Sin datos",
+ "sharedSubject": "Asunto",
+ "sharedYes": "Si",
+ "sharedNo": "No",
+ "sharedKm": "Km",
+ "sharedMi": "MI",
+ "sharedNmi": "nmi",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "Nudos",
+ "sharedKmh": "km/h",
+ "sharedMph": "mph",
+ "sharedHour": "Hora",
+ "sharedMinute": "Minuto",
+ "sharedSecond": "Segundo",
+ "sharedDays": "Días",
+ "sharedHours": "Horas",
+ "sharedMinutes": "minutos",
+ "sharedDecimalDegrees": "Grados",
+ "sharedDegreesDecimalMinutes": "Minutos",
+ "sharedDegreesMinutesSeconds": "Segundos",
+ "sharedName": "Nombre",
+ "sharedDescription": "Descripción",
+ "sharedSearch": "Buscar",
+ "sharedIconScale": "Escala de los iconos",
+ "sharedGeofence": "Geo-Zona",
+ "sharedGeofences": "Geo-Zonas",
+ "sharedCreateGeofence": "Crear Geo-Zona",
+ "sharedNotifications": "Notificaciones",
+ "sharedNotification": "Notificación",
+ "sharedAttributes": "Atributos",
+ "sharedAttribute": "Atributo",
+ "sharedDrivers": "Conductores",
+ "sharedDriver": "Conductor",
+ "sharedArea": "Área",
+ "sharedSound": "Sonido de notificación",
+ "sharedType": "Tipo",
+ "sharedDistance": "Distancia",
+ "sharedHourAbbreviation": "h",
+ "sharedMinuteAbbreviation": "m",
+ "sharedSecondAbbreviation": "s",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "I",
+ "sharedGallonAbbreviation": "Galón",
+ "sharedLiter": "Litro",
+ "sharedImpGallon": "Galón Imp.",
+ "sharedUsGallon": "Galón U.S.",
+ "sharedLiterPerHourAbbreviation": "L/h",
+ "sharedGetMapState": "Obtener Estado del Mapa",
+ "sharedComputedAttribute": "Atributo calculado",
+ "sharedComputedAttributes": "Atributos calculados",
+ "sharedCheckComputedAttribute": "Revisar atributo calculado",
+ "sharedExpression": "Expresión",
+ "sharedDevice": "Dispositivo",
+ "sharedTest": "Test",
+ "sharedTestNotification": "Enviar notificación de prueba",
+ "sharedTestNotificators": "Canales Test",
+ "sharedTestExpression": "Expresión Test",
+ "sharedCalendar": "Calendario",
+ "sharedCalendars": "Calendarios",
+ "sharedFile": "Archivo",
+ "sharedSearchDevices": "Buscar Dispositivos",
+ "sharedSortBy": "Ordenar por",
+ "sharedFilterMap": "Filtrar en Mapa",
+ "sharedSelectFile": "Seleccione archivo",
+ "sharedPhone": "Teléfono",
+ "sharedRequired": "Obligatorio",
+ "sharedPreferences": "Preferencias",
+ "sharedPermissions": "Permisos",
+ "sharedConnections": "Conexiones",
+ "sharedExtra": "Extra",
+ "sharedPrimary": "Principal",
+ "sharedSecondary": "Secundario",
+ "sharedTypeString": "Cadena",
+ "sharedTypeNumber": "Número",
+ "sharedTypeBoolean": "Booleano",
+ "sharedTimezone": "Zona Horaria",
+ "sharedInfoTitle": "Información",
+ "sharedSavedCommand": "Comando guardado",
+ "sharedSavedCommands": "Comandos guardados",
+ "sharedNew": "Nuevo…",
+ "sharedShowAddress": "Mostrar calle",
+ "sharedShowDetails": "Más detalles",
+ "sharedDisabled": "Deshabilitado",
+ "sharedMaintenance": "Mantenimientos",
+ "sharedDeviceAccumulators": "Acumulador",
+ "sharedAlarms": "Alarmas",
+ "sharedLocation": "Ubicación",
+ "sharedImport": "Importar",
+ "sharedColumns": "Columnas",
+ "sharedDropzoneText": "Arrastra y suelta el archivo aquí o pulsa",
+ "sharedLogs": "Registros",
+ "sharedLink": "Enlace",
+ "calendarSimple": "Simple",
+ "calendarRecurrence": "Recurrente",
+ "calendarOnce": "Una vez",
+ "calendarDaily": "Diario",
+ "calendarWeekly": "Semanal",
+ "calendarMonthly": "Mensual",
+ "calendarDays": "Días",
+ "calendarSunday": "Domingo",
+ "calendarMonday": "Lunes",
+ "calendarTuesday": "Martes",
+ "calendarWednesday": "Miércoles",
+ "calendarThursday": "Jueves",
+ "calendarFriday": "Viernes",
+ "calendarSaturday": "Sábado",
+ "attributeShowGeofences": "Mostrar Geo-Zonas",
+ "attributeSpeedLimit": "Límite de velocidad",
+ "attributeFuelDropThreshold": "Umbral de pérdida de combustible",
+ "attributeFuelIncreaseThreshold": "Umbral del subida de combustible",
+ "attributePolylineDistance": "Distancia de polilínea",
+ "attributeReportIgnoreOdometer": "Informe: Ignorar el odómetro",
+ "attributeWebReportColor": "Web: Color de informe",
+ "attributeDevicePassword": "Contraseña de dispositivo",
+ "attributeDeviceImage": "Imagen del dispositivo",
+ "attributeDeviceInactivityStart": "Inicio de inactividad del dispositivo",
+ "attributeDeviceInactivityPeriod": "Periodo de inactividad del dispositivo",
+ "attributeProcessingCopyAttributes": "Procesando: Copia de atributos",
+ "attributeColor": "Color",
+ "attributeWebLiveRouteLength": "Web: Longitud de la ruta en directo",
+ "attributeWebSelectZoom": "Web: hacer zoom al seleccionar",
+ "attributeWebMaxZoom": "Web: Zoom máximo",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Pushover User Key",
+ "attributePushoverDeviceNames": "Pushover Nombres de Dispositivo",
+ "attributeMailSmtpHost": "Correo: Servidor SMTP",
+ "attributeMailSmtpPort": "Correo: Puerto SMTP",
+ "attributeMailSmtpStarttlsEnable": "Correo: Activar SMTP STARTTLS",
+ "attributeMailSmtpStarttlsRequired": "Correo: SMTP STARTTLS requerido",
+ "attributeMailSmtpSslEnable": "Correo: Activar SMTP SSL",
+ "attributeMailSmtpSslTrust": "Correo: SMTP SSL de confianza",
+ "attributeMailSmtpSslProtocols": "Correo: protocolos SMTP SSL",
+ "attributeMailSmtpFrom": "Correo: SMTP desde",
+ "attributeMailSmtpAuth": "Correo: Activar autenticación SMTP",
+ "attributeMailSmtpUsername": "Correo: Nombre de usuario SMTP",
+ "attributeMailSmtpPassword": "Correo: Contraseña SMTP",
+ "attributeUiDisableSavedCommands": "UI: Deshabilitar Comandos guardados",
+ "attributeUiDisableAttributes": "UI: Deshabilitar Atributos",
+ "attributeUiDisableGroups": "UI: Deshabilitar Grupos",
+ "attributeUiDisableEvents": "UI: Deshabilitar panel Eventos",
+ "attributeUiDisableVehicleFeatures": "UI: Desactivar características del vehículo",
+ "attributeUiDisableDrivers": "UI: Desactivar Conductores",
+ "attributeUiDisableComputedAttributes": "UI: Desactivar Atributos calculados",
+ "attributeUiDisableCalendars": "UI: Desactivar Calendario",
+ "attributeUiDisableMaintenance": "UI: Deshabilitar Mantenimiento",
+ "attributeUiHidePositionAttributes": "UI: Ocultar Atributos de Posición",
+ "attributeUiDisableLoginLanguage": "UI: Deshabilitar Idioma de inicio de sesión",
+ "attributeNotificationTokens": "Notification Tokens",
+ "attributePopupInfo": "Información popup",
+ "errorTitle": "Error",
+ "errorGeneral": "Parámetros no válidos o fuera de los límites",
+ "errorConnection": "Error en la conexión",
+ "errorSocket": "Error del Web-Socket",
+ "errorZero": "No puede ser cero",
+ "userEmail": "Email",
+ "userPassword": "Contraseña",
+ "userAdmin": "Administrador",
+ "userRemember": "Recordar",
+ "userExpirationTime": "Caducidad",
+ "userDeviceLimit": "Límite de dispositivos",
+ "userUserLimit": "Límite de usuarios",
+ "userDeviceReadonly": "Dispositivo de solo lectura",
+ "userLimitCommands": "Limitar Comandos",
+ "userDisableReports": "Deshabilitar Informes",
+ "userFixedEmail": "No cambiar email",
+ "userToken": "Token Acceso",
+ "userDeleteAccount": "Borrar cuenta",
+ "userTemporary": "Temporal",
+ "loginTitle": "Iniciar sesión",
+ "loginLanguage": "Idioma",
+ "loginReset": "Reiniciar contraseña",
+ "loginRegister": "Registrarse",
+ "loginLogin": "Iniciar sesión",
+ "loginOpenId": "Iniciar sesión con OpenID",
+ "loginFailed": "Email o contraseña incorrecta",
+ "loginCreated": "Se ha registrado un nuevo usuario",
+ "loginResetSuccess": "Comprueba tu email",
+ "loginUpdateSuccess": "Se ha creado la nueva contraseña",
+ "loginLogout": "Cerrar Sesión",
+ "loginLogo": "Logotipo",
+ "loginTotpCode": "Código de un solo uso",
+ "loginTotpKey": "Llave de un solo uso",
+ "devicesAndState": "Dispositivos y Estado",
+ "deviceSelected": "Dispositivo seleccionado",
+ "deviceTitle": "Dispositivos",
+ "devicePrimaryInfo": "Título del dispositivo",
+ "deviceSecondaryInfo": "Detalles del dispositivo",
+ "deviceIdentifier": "Identificador",
+ "deviceModel": "Modelo",
+ "deviceContact": "Contacto",
+ "deviceCategory": "Categoría",
+ "deviceLastUpdate": "Última Actualización",
+ "deviceCommand": "Comando",
+ "deviceFollow": "Seguir",
+ "deviceTotalDistance": "Distancia Total",
+ "deviceStatus": "Estado",
+ "deviceStatusOnline": "En línea",
+ "deviceStatusOffline": "Fuera de línea",
+ "deviceStatusUnknown": "Desconocido",
+ "deviceRegisterFirst": "Registre su primer dispositivo",
+ "deviceIdentifierHelp": "El IMEI, numero de serie u otro id, tiene que ser igual que el identificador del dispositivo que reporta al servidor.",
+ "deviceShare": "Compartir dispositivo",
+ "groupDialog": "Grupo",
+ "groupParent": "Grupo",
+ "groupNoGroup": "Sin grupo",
+ "settingsTitle": "Ajustes",
+ "settingsUser": "Cuenta",
+ "settingsGroups": "Grupos",
+ "settingsServer": "Servidor",
+ "settingsUsers": "Usuarios",
+ "settingsDistanceUnit": "Unidad de Distancia",
+ "settingsAltitudeUnit": "Unidad de Altitud",
+ "settingsSpeedUnit": "Unidad de Velocidad",
+ "settingsVolumeUnit": "Unidad de Volumen",
+ "settingsTwelveHourFormat": "Formato de 12h.",
+ "settingsCoordinateFormat": "Formato de Coordenadas",
+ "settingsServerVersion": "Versión del servidor",
+ "settingsAppVersion": "Versión de la app",
+ "settingsConnection": "Conexión",
+ "settingsDarkMode": "Modo Oscuro",
+ "settingsTotpEnable": "Activar contraseña de un solo uso",
+ "settingsTotpForce": "Forzar contraseña de un solo uso",
+ "settingsServiceWorkerUpdateInterval": "Intervalo de actualización del ServiceWorker",
+ "settingsUpdateAvailable": "Hay una actualización disponible.",
+ "settingsSupport": "Soporte",
+ "reportTitle": "Reportes",
+ "reportScheduled": "Reportes Programados",
+ "reportDevice": "Dispositivos",
+ "reportGroup": "Grupo",
+ "reportFrom": "Desde",
+ "reportTo": "Hasta",
+ "reportShow": "Mostrar",
+ "reportClear": "Limpiar",
+ "linkGoogleMaps": "Google Maps",
+ "linkAppleMaps": "Apple Maps",
+ "linkStreetView": "Street View",
+ "positionFixTime": "Hora ajustada",
+ "positionDeviceTime": "Hora del dispositivo",
+ "positionServerTime": "Hora del Servidor",
+ "positionValid": "Válida",
+ "positionAccuracy": "Precisión",
+ "positionLatitude": "Latitud",
+ "positionLongitude": "Longitud",
+ "positionAltitude": "Altitud",
+ "positionSpeed": "Velocidad",
+ "positionCourse": "Rumbo",
+ "positionAddress": "Dirección Calle",
+ "positionProtocol": "Protocolo",
+ "positionDistance": "Distancia",
+ "positionRpm": "RPM",
+ "positionFuel": "Combustible",
+ "positionPower": "Energía",
+ "positionBattery": "Batería",
+ "positionRaw": "Crudo",
+ "positionIndex": "Índice / Index",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "Satélites",
+ "positionSatVisible": "Satélites Visibles",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "Roaming",
+ "positionEvent": "Evento",
+ "positionAlarm": "Alarma",
+ "positionStatus": "Estado",
+ "positionOdometer": "Odómetro",
+ "positionServiceOdometer": "Odómetro de mantenimiento",
+ "positionTripOdometer": "Odómetro de viaje",
+ "positionHours": "Horas",
+ "positionSteps": "Pasos",
+ "positionInput": "Entrada",
+ "positionHeartRate": "Ritmo cardiaco",
+ "positionOutput": "Salida",
+ "positionBatteryLevel": "Nivel de batería",
+ "positionFuelConsumption": "Consumo de combustible",
+ "positionRfid": "RFDI",
+ "positionVersionFw": "Versión de firmware",
+ "positionVersionHw": "Versión de hardware",
+ "positionIgnition": "Encendido",
+ "positionFlags": "Banderas",
+ "positionCharge": "Carga",
+ "positionIp": "IP",
+ "positionArchive": "Archivo",
+ "positionVin": "VIN",
+ "positionApproximate": "Aproximado",
+ "positionThrottle": "Acelerador",
+ "positionMotion": "Movimiento",
+ "positionArmed": "Armado",
+ "positionAcceleration": "Aceleración",
+ "positionTemp": "Temperatura",
+ "positionDeviceTemp": "Temperatura del dispositivo",
+ "positionCoolantTemp": "Temperatura del Refrigerante",
+ "positionOperator": "Operador",
+ "positionCommand": "Comando",
+ "positionBlocked": "Bloqueado",
+ "positionDtcs": "DTCs",
+ "positionObdSpeed": "Velocidad OBD",
+ "positionObdOdometer": "Odómetro OBD",
+ "positionDrivingTime": "Tiempo Conduciendo",
+ "positionDriverUniqueId": "ID única del conductor",
+ "positionCard": "Tarjeta",
+ "positionImage": "Imagen",
+ "positionVideo": "Video",
+ "positionAudio": "Audio",
+ "serverTitle": "Ajustes del servidor",
+ "serverZoom": "Zoom",
+ "serverRegistration": "Registro",
+ "serverReadonly": "Solo lectura",
+ "serverForceSettings": "Forzar estos ajustes",
+ "serverAnnouncement": "Mostrar anuncio",
+ "serverName": "Nombre del Servidor",
+ "serverDescription": "Descripción del Servidor",
+ "serverColorPrimary": "Color Primario",
+ "serverColorSecondary": "Color Secundario",
+ "serverLogo": "Imagen del Logo",
+ "serverLogoInverted": "Imagen de logo invertida",
+ "serverChangeDisable": "Deshabilitar el cambio de servidor",
+ "serverDisableShare": "Deshabilitar poder Compartir Dispositivos",
+ "mapTitle": "Mapa",
+ "mapActive": "Mapas activos",
+ "mapOverlay": "Capa sobre el mapa",
+ "mapOverlayCustom": "Capa personalizada",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API Key",
+ "mapOpenWeatherClouds": "OpenWeather Clouds",
+ "mapOpenWeatherPrecipitation": "OpenWeather Precipitation",
+ "mapOpenWeatherPressure": "OpenWeather Pressure",
+ "mapOpenWeatherWind": "OpenWeather Wind",
+ "mapOpenWeatherTemperature": "OpenWeather Temperature",
+ "mapLayer": "Capas del Mapa",
+ "mapCustom": "Personalizado (XYZ)",
+ "mapCustomArcgis": "ArcGIS Personalizado",
+ "mapCustomLabel": "Mapa Personalizado",
+ "mapCarto": "Carto",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Carreteras",
+ "mapGoogleHybrid": "Google Híbrido",
+ "mapGoogleSatellite": "Google Satélite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Bing Maps Key",
+ "mapBingRoad": "Bing Maps - Carretera",
+ "mapBingAerial": "Bing Maps - Aéreo",
+ "mapBingHybrid": "Bing Maps - Híbrido",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Yandex",
+ "mapYandexSat": "Yandex Satélite",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox Streets",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox Outdoors",
+ "mapMapboxSatellite": "Mapbox Satellite",
+ "mapMapboxKey": "Mapbox Access Token",
+ "mapMapTilerBasic": "MapTiler Basic",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "MapTiler API Key",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ Access Token",
+ "mapTomTomBasic": "TomTom Basic",
+ "mapTomTomFlow": "TomTom Traffic Flow",
+ "mapTomTomIncidents": "TomTom Traffic Incidents",
+ "mapTomTomKey": "TomTom API Key",
+ "mapHereBasic": "Here Basic",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satellite",
+ "mapHereFlow": "Here Traffic Flow",
+ "mapHereKey": "Here API Key",
+ "mapShapePolygon": "Polígono",
+ "mapShapeCircle": "Círculo",
+ "mapShapePolyline": "Polilínea",
+ "mapLiveRoutes": "Rutas en Directo",
+ "mapDirection": "Mostrar dirección",
+ "mapCurrentLocation": "Ubicación Actual",
+ "mapPoiLayer": "Capa POI",
+ "mapClustering": "Agrupar Marcadores",
+ "mapOnSelect": "Mostrar Mapas en la selección",
+ "mapDefault": "Mapa por defecto",
+ "stateTitle": "Estado",
+ "stateName": "Parámetro",
+ "stateValue": "Valor",
+ "commandTitle": "Comando",
+ "commandSend": "Enviar",
+ "commandSent": "Comando enviado",
+ "commandQueued": "Comando en cola",
+ "commandUnit": "Unidad",
+ "commandCustom": "Comando personalizado",
+ "commandDeviceIdentification": "Identificación de Dispositivo",
+ "commandPositionSingle": "Informe Único",
+ "commandPositionPeriodic": "Informe Periódico",
+ "commandPositionStop": "Detener Informe",
+ "commandEngineStop": "Apagar motor",
+ "commandEngineResume": "Desbloquear Encendido de Motor",
+ "commandAlarmArm": "Armar Alarma",
+ "commandAlarmDisarm": "Desarmar Alarma",
+ "commandAlarmDismiss": "Descartar Alarma",
+ "commandSetTimezone": "Establecer Zona Horaria",
+ "commandRequestPhoto": "Solicitar Foto",
+ "commandPowerOff": "Apagar dispositivo",
+ "commandRebootDevice": "Reiniciar dispositivo",
+ "commandFactoryReset": "Valores de fábrica",
+ "commandSendSms": "Enviar SMS",
+ "commandSendUssd": "Enviar USSD",
+ "commandSosNumber": "Establecer el número SOS",
+ "commandSilenceTime": "Establecer horario de silencio",
+ "commandSetPhonebook": "Establecer contacto",
+ "commandVoiceMessage": "Mensaje de voz",
+ "commandOutputControl": "Control de Salidas",
+ "commandVoiceMonitoring": "Monitoreo de Voz",
+ "commandSetAgps": "Establecer AGPS",
+ "commandSetIndicator": "Establecer indicador",
+ "commandConfiguration": "Configuración",
+ "commandGetVersion": "Obtener Versión",
+ "commandFirmwareUpdate": "Actualizar Firmware",
+ "commandSetConnection": "Establecer Conexión",
+ "commandSetOdometer": "Establecer Odómetro",
+ "commandGetModemStatus": "Obtener Estatus de Módem",
+ "commandGetDeviceStatus": "Obtener Estatus de Dispositivo",
+ "commandSetSpeedLimit": "Establecer Límite de Velocidad",
+ "commandModePowerSaving": "Modo Ahorro de Energía",
+ "commandModeDeepSleep": "Modo Ahorro de Energía Profundo",
+ "commandAlarmGeofence": "Establecer Alarma de Geo-Zona",
+ "commandAlarmBattery": "Establecer Alarma de Batería",
+ "commandAlarmSos": "Establecer Alarma de SOS",
+ "commandAlarmRemove": "Establecer Alarma de Desmontaje",
+ "commandAlarmClock": "Establecer Alarma de Hora",
+ "commandAlarmSpeed": "Establecer Alarma de Velocidad",
+ "commandAlarmFall": "Establecer Alarma de Caída",
+ "commandAlarmVibration": "Establecer Alarma de Vibración",
+ "commandFrequency": "Frequencia",
+ "commandTimezone": "Compensación de zona horaria",
+ "commandMessage": "Mensaje",
+ "commandRadius": "Radio",
+ "commandEnable": "Activado",
+ "commandData": "Datos",
+ "commandIndex": "Índice",
+ "commandPhone": "Número de Teléfono",
+ "commandServer": "Servidor",
+ "commandPort": "Puerto",
+ "eventAll": "Todos los eventos",
+ "eventDeviceOnline": "Dispositivo en Línea",
+ "eventDeviceUnknown": "Dispositivo en estado Desconocido",
+ "eventDeviceOffline": "Dispositivo Fuera de Línea",
+ "eventDeviceInactive": "Dispositivo Inactivo",
+ "eventQueuedCommandSent": "Comando en cola enviado",
+ "eventDeviceMoving": "Dispositivo en Movimiento",
+ "eventDeviceStopped": "Dispositivo Detenido",
+ "eventDeviceOverspeed": "Excedido el límite de Velocidad",
+ "eventDeviceFuelDrop": "Perdida de Combustible ",
+ "eventDeviceFuelIncrease": "Incremento de combustible",
+ "eventCommandResult": "Resultado del comando",
+ "eventGeofenceEnter": "Entrada en la Geo-Zona",
+ "eventGeofenceExit": "Salida de la Geo-Zona",
+ "eventAlarm": "Alarma ",
+ "eventIgnitionOn": "Llave encendido ON",
+ "eventIgnitionOff": "Llave encendido OFF",
+ "eventMaintenance": "Se requiere mantenimiento",
+ "eventTextMessage": "Mensaje de texto recibido",
+ "eventDriverChanged": "El conductor ha cambiado",
+ "eventMedia": "Media",
+ "eventsScrollToLast": "Desplazarse hasta el último",
+ "eventsSoundEvents": "Sonido para eventos",
+ "eventsSoundAlarms": "Sonido para alarmas",
+ "alarmGeneral": "General",
+ "alarmSos": "SOS",
+ "alarmVibration": "Vibración",
+ "alarmMovement": "Movimiento",
+ "alarmLowspeed": "Baja Velocidad",
+ "alarmOverspeed": "Exceso de Velocidad ",
+ "alarmFallDown": "Alarma de caída",
+ "alarmLowPower": "Energía baja",
+ "alarmLowBattery": "Batería Baja",
+ "alarmFault": "Alarma de fallo",
+ "alarmPowerOff": "Apagado",
+ "alarmPowerOn": "Encendido",
+ "alarmDoor": "Puerta",
+ "alarmLock": "Bloqueado",
+ "alarmUnlock": "Desbloquear",
+ "alarmGeofence": "Geo-Zona",
+ "alarmGeofenceEnter": "El Dispositivo ha entrado a la Geo-Zona",
+ "alarmGeofenceExit": "El Dispositivo ha salido de la Geo-Zona",
+ "alarmGpsAntennaCut": "Antena del GPS Cortada ",
+ "alarmAccident": "Accidente",
+ "alarmTow": "Grúa de arrastre",
+ "alarmIdle": "Reposo",
+ "alarmHighRpm": "Altas revoluciones",
+ "alarmHardAcceleration": "Aceleración brusca",
+ "alarmHardBraking": "Frenada extrema",
+ "alarmHardCornering": "Giro brusco",
+ "alarmLaneChange": "Cambio de carril",
+ "alarmFatigueDriving": "Conducción fatigosa",
+ "alarmPowerCut": "Energía desconectada",
+ "alarmPowerRestored": "Energía restaurada",
+ "alarmJamming": "Interferencia",
+ "alarmTemperature": "Temperatura",
+ "alarmParking": "Aparcamiento",
+ "alarmBonnet": "Capó",
+ "alarmFootBrake": "Freno de pie",
+ "alarmFuelLeak": "Fuga de combustible",
+ "alarmTampering": "Manipulación",
+ "alarmRemoving": "Eliminando",
+ "notificationType": "Tipo de Notificación",
+ "notificationAlways": "Todos los dispositivos",
+ "notificationNotificators": "Canales",
+ "notificatorCommand": "Comando",
+ "notificatorWeb": "Web",
+ "notificatorMail": "Correo",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Repetición Ruta",
+ "reportCombined": "Combinado",
+ "reportRoute": "Ruta",
+ "reportEvents": "Eventos",
+ "reportTrips": "Viajes",
+ "reportStops": "Paradas",
+ "reportSummary": "Resumen",
+ "reportDaily": "Resumen diario",
+ "reportChart": "Gráfica",
+ "reportConfigure": "Configurar",
+ "reportEventTypes": "Tipos de evento",
+ "reportChartType": "Tipo de gráfica",
+ "reportShowMarkers": "Mostrar marcadores",
+ "reportExport": "Exportar",
+ "reportEmail": "Informe por correo",
+ "reportSchedule": "Planificación",
+ "reportPeriod": "Período",
+ "reportCustom": "Personalizado",
+ "reportToday": "Hoy",
+ "reportYesterday": "Ayer",
+ "reportThisWeek": "Semana Actual",
+ "reportPreviousWeek": "Semana Anterior",
+ "reportThisMonth": "Mes Actual",
+ "reportPreviousMonth": "Mes Anterior",
+ "reportDeviceName": "Nombre",
+ "reportAverageSpeed": "Velocidad media",
+ "reportMaximumSpeed": "Velocidad máxima",
+ "reportEngineHours": "Horas motor",
+ "reportDuration": "Duración",
+ "reportStartDate": "Fecha de inicio",
+ "reportStartTime": "Hora de Inicio",
+ "reportStartAddress": "Dirección de Inicio",
+ "reportEndTime": "Hora de Fin",
+ "reportEndAddress": "Dirección de Fin",
+ "reportSpentFuel": "Combustible consumido",
+ "reportStartOdometer": "Odómetro inicial",
+ "reportEndOdometer": "Odómetro final",
+ "statisticsTitle": "Estadísticas",
+ "statisticsCaptureTime": "Fecha de captura",
+ "statisticsActiveUsers": "Usuarios activos",
+ "statisticsActiveDevices": "Dispositivos activos",
+ "statisticsRequests": "Peticiones",
+ "statisticsMessagesReceived": "Mensajes recibidos",
+ "statisticsMessagesStored": "Mensajes almacenados",
+ "statisticsGeocoder": "Solicitudes al codificador geográfico",
+ "statisticsGeolocation": "Solicitudes de geolocalización",
+ "categoryArrow": "Flecha",
+ "categoryDefault": "Prederminado",
+ "categoryAnimal": "Animal",
+ "categoryBicycle": "Bicicleta",
+ "categoryBoat": "Barco",
+ "categoryBus": "Autobús",
+ "categoryCar": "Automóvil",
+ "categoryCamper": "Camper",
+ "categoryCrane": "Grúa",
+ "categoryHelicopter": "Helicóptero",
+ "categoryMotorcycle": "Motocicleta",
+ "categoryOffroad": "Todoterreno",
+ "categoryPerson": "Persona",
+ "categoryPickup": "Pickup",
+ "categoryPlane": "Avión",
+ "categoryShip": "Barco",
+ "categoryTractor": "Tractor",
+ "categoryTrain": "Trén",
+ "categoryTram": "Tranvía",
+ "categoryTrolleybus": "Trolebús",
+ "categoryTruck": "Camión",
+ "categoryVan": "Furgoneta",
+ "categoryScooter": "Moto",
+ "maintenanceStart": "Iniciar",
+ "maintenancePeriod": "Período"
+} \ No newline at end of file
diff --git a/src/resources/l10n/fa.json b/src/resources/l10n/fa.json
new file mode 100644
index 00000000..3507681c
--- /dev/null
+++ b/src/resources/l10n/fa.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "در حال بارگزارى ...",
+ "sharedHide": "مخفی",
+ "sharedSave": "ذخيره",
+ "sharedUpload": "Upload",
+ "sharedSet": "تنظیم",
+ "sharedCancel": "انصراف",
+ "sharedCopy": "Copy",
+ "sharedAdd": "اضافه كردن",
+ "sharedEdit": "ویرایش",
+ "sharedRemove": "پاک کردن",
+ "sharedRemoveConfirm": "پاک کردن آیتم",
+ "sharedNoData": "داده ای نیست",
+ "sharedSubject": "Subject",
+ "sharedYes": "بلی",
+ "sharedNo": "خیر",
+ "sharedKm": "Km",
+ "sharedMi": "Mile",
+ "sharedNmi": "مایل دریایی",
+ "sharedMeters": "متر",
+ "sharedFeet": "فوت",
+ "sharedKn": "گره دریایی",
+ "sharedKmh": "Km/h",
+ "sharedMph": "M/h",
+ "sharedHour": "ساعت",
+ "sharedMinute": "دقيقه",
+ "sharedSecond": "ثانيه",
+ "sharedDays": "روز",
+ "sharedHours": "ساعت",
+ "sharedMinutes": "دقیقه",
+ "sharedDecimalDegrees": "درجه اعشار",
+ "sharedDegreesDecimalMinutes": "درجه اعشار دقیقه",
+ "sharedDegreesMinutesSeconds": "درجه اعشار ثانیه",
+ "sharedName": "نام",
+ "sharedDescription": "توضیحات",
+ "sharedSearch": "جستجو",
+ "sharedIconScale": "اندازه آیکن",
+ "sharedGeofence": "حصار جغرافیایی",
+ "sharedGeofences": "حصارهای جغرافیایی",
+ "sharedCreateGeofence": "ایجاد جغرافیا",
+ "sharedNotifications": "رویدادها",
+ "sharedNotification": "اطلاعیه",
+ "sharedAttributes": "ویژگی ها",
+ "sharedAttribute": "ویژگی",
+ "sharedDrivers": "رانندگان",
+ "sharedDriver": "راننده",
+ "sharedArea": "محدوده",
+ "sharedSound": "صدای هشدار",
+ "sharedType": "نوع خط",
+ "sharedDistance": "طول مسیر",
+ "sharedHourAbbreviation": "h",
+ "sharedMinuteAbbreviation": "m",
+ "sharedSecondAbbreviation": "s",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "l",
+ "sharedGallonAbbreviation": "گالن",
+ "sharedLiter": "لیتر",
+ "sharedImpGallon": "گالن UK",
+ "sharedUsGallon": "گالن US",
+ "sharedLiterPerHourAbbreviation": "l/h",
+ "sharedGetMapState": "دریافت نقشه وضعیت",
+ "sharedComputedAttribute": "ویژگی محاسبه شده",
+ "sharedComputedAttributes": "ویژگیهای محاسبه شده",
+ "sharedCheckComputedAttribute": "بررسی ویژگی محاسبه شده",
+ "sharedExpression": "اصطلاح",
+ "sharedDevice": "دستگاه",
+ "sharedTest": "آزمایش",
+ "sharedTestNotification": "ارسال تست اخطار",
+ "sharedTestNotificators": "آزمایش کانال ها",
+ "sharedTestExpression": "تست اصطلاح",
+ "sharedCalendar": "تقویم",
+ "sharedCalendars": "تقویمها",
+ "sharedFile": "فایل",
+ "sharedSearchDevices": "جستجوی دستگاهها",
+ "sharedSortBy": "مرتب سازی",
+ "sharedFilterMap": "فیلتر بر روی نقشه",
+ "sharedSelectFile": "انتخاب فایل",
+ "sharedPhone": "تلفن",
+ "sharedRequired": "ضروری",
+ "sharedPreferences": "تنظیمات",
+ "sharedPermissions": "دسترسی ها",
+ "sharedConnections": "اتصالات",
+ "sharedExtra": "بیشتر",
+ "sharedPrimary": "اولیه",
+ "sharedSecondary": "ثانویه",
+ "sharedTypeString": "رشته",
+ "sharedTypeNumber": "عدد",
+ "sharedTypeBoolean": "True/False",
+ "sharedTimezone": "منطقه زمانی",
+ "sharedInfoTitle": "اطلاعات",
+ "sharedSavedCommand": "دستور ذخیره شده",
+ "sharedSavedCommands": "دستورات ذخیره شده",
+ "sharedNew": "جدید...",
+ "sharedShowAddress": "نمایش آدرس",
+ "sharedShowDetails": "جزئیات بیشتر",
+ "sharedDisabled": "غیرفعال شده",
+ "sharedMaintenance": "تعمیر و نگهداری",
+ "sharedDeviceAccumulators": "باطری",
+ "sharedAlarms": "هشدارها",
+ "sharedLocation": "مکان",
+ "sharedImport": "وارد كردن",
+ "sharedColumns": "ستونها",
+ "sharedDropzoneText": "یک فایل را در اینجا بکشید و رها کنید یا کلیک کنید",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "ساده",
+ "calendarRecurrence": "عکس العمل",
+ "calendarOnce": "یکبار",
+ "calendarDaily": "روزانه",
+ "calendarWeekly": "هفتگی",
+ "calendarMonthly": "ماهانه",
+ "calendarDays": "روزها",
+ "calendarSunday": "یکشنبه",
+ "calendarMonday": "دوشنبه",
+ "calendarTuesday": "سه شنبه",
+ "calendarWednesday": "چهارشنبه",
+ "calendarThursday": "پنجشنبه",
+ "calendarFriday": "جمعه",
+ "calendarSaturday": "شنبه",
+ "attributeShowGeofences": "نمایش حصار مجازی",
+ "attributeSpeedLimit": "محدودیت سرعت",
+ "attributeFuelDropThreshold": "حد اتمام سوخت",
+ "attributeFuelIncreaseThreshold": "حد تکمیل سوخت",
+ "attributePolylineDistance": "مسافت چند مسیری",
+ "attributeReportIgnoreOdometer": "گزارش : بدون کیلومتر شمار",
+ "attributeWebReportColor": "وب : گزارش رنگ",
+ "attributeDevicePassword": "رمز عبور ردیاب",
+ "attributeDeviceImage": "عکس دستگاه",
+ "attributeDeviceInactivityStart": "آغاز عدم فعالیت دستگاه",
+ "attributeDeviceInactivityPeriod": "دوره عدم فعالیت دستگاه",
+ "attributeProcessingCopyAttributes": "در حال پردازش : کپی ویژگیها",
+ "attributeColor": "رنگ",
+ "attributeWebLiveRouteLength": "وب : طول جاده آنلاین",
+ "attributeWebSelectZoom": "وب : زوم با انتخاب",
+ "attributeWebMaxZoom": "بیشترین زوم",
+ "attributeTelegramChatId": "آی دی تلگرام",
+ "attributePushoverUserKey": "Pushover کلید کاربری",
+ "attributePushoverDeviceNames": "Pushover نام دستگاه",
+ "attributeMailSmtpHost": "ایمیل : هاست SMTP",
+ "attributeMailSmtpPort": "ایمیل : پورت SMTP",
+ "attributeMailSmtpStarttlsEnable": "ایمیل : SMTP STARTTLS فعال",
+ "attributeMailSmtpStarttlsRequired": "ایمیل : SMTP STARTTLS ضروری است",
+ "attributeMailSmtpSslEnable": "ایمیل : SMTP SSL فعال",
+ "attributeMailSmtpSslTrust": "ایمیل : SMTP SSL معتبر",
+ "attributeMailSmtpSslProtocols": "ایمیل : پروتکلهای SMTP SSL",
+ "attributeMailSmtpFrom": "ایمیل : SMTP ازطرف",
+ "attributeMailSmtpAuth": "ایمیل : معتبر سازی SMTP فعال",
+ "attributeMailSmtpUsername": "ایمیل : نام کاربری SMTP",
+ "attributeMailSmtpPassword": "ایمیل : رمز عبور SMTP",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: ویژگی ها را غیرفعال کنید",
+ "attributeUiDisableGroups": "UI: غیر فعال کردن گروه ها",
+ "attributeUiDisableEvents": "UI: غیر فعال کردن رویدادها",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "غیرفعالسازی رانندگان",
+ "attributeUiDisableComputedAttributes": "غیرفعالسازی محاسبات",
+ "attributeUiDisableCalendars": "غیرفعالسازی تقویم",
+ "attributeUiDisableMaintenance": "غیر فعال کردن تعمییرات",
+ "attributeUiHidePositionAttributes": "پنهان کردن ویژگی های موقعیت",
+ "attributeUiDisableLoginLanguage": "UI: انتخاب زبان در هنگام ورود را غیرفعال کنید",
+ "attributeNotificationTokens": "اخطار توکن",
+ "attributePopupInfo": "اطلاعات Popup",
+ "errorTitle": "خطا",
+ "errorGeneral": "نقض پارامترها یا محدودیت ها نامعتبر است",
+ "errorConnection": "خطا در اتصال",
+ "errorSocket": "ایراد اتصال سوکت وب",
+ "errorZero": "نمیتواند صفر باشد",
+ "userEmail": "ایمیل",
+ "userPassword": "رمز عبور",
+ "userAdmin": "مدیر",
+ "userRemember": "به یاد آوردن",
+ "userExpirationTime": "انقضاء",
+ "userDeviceLimit": "محدودیت دستگاه",
+ "userUserLimit": "محدودیت کاربر",
+ "userDeviceReadonly": "دستگاه فقط خواندنی",
+ "userLimitCommands": "دستورات محدود",
+ "userDisableReports": "گزارشات غیرفعال",
+ "userFixedEmail": "عدم تغییر ایمیل",
+ "userToken": "رمز یکبار",
+ "userDeleteAccount": "حذف اکانت",
+ "userTemporary": "Temporary",
+ "loginTitle": "ورود",
+ "loginLanguage": "انتخاب زبان",
+ "loginReset": "بازیابی رمز عبور",
+ "loginRegister": "ثبت نام",
+ "loginLogin": "ورود",
+ "loginOpenId": "با OpenID وارد شوید",
+ "loginFailed": "نام كاربرى يا گذرواژه اشتباه است",
+ "loginCreated": "ثبت نام با موفقيت انجام شد",
+ "loginResetSuccess": "ایمیلتان را چک کنید",
+ "loginUpdateSuccess": "رمز جدید اعمال شد",
+ "loginLogout": "خروج",
+ "loginLogo": "لوگو",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "دستگاه ها و وضعیت",
+ "deviceSelected": "دستگاه انتخاب شده",
+ "deviceTitle": "دستگاه ها",
+ "devicePrimaryInfo": "عنوان دستگاه",
+ "deviceSecondaryInfo": "جزئیات دستگاه",
+ "deviceIdentifier": "سريال دستگاه",
+ "deviceModel": "مدل",
+ "deviceContact": "تماس",
+ "deviceCategory": "دسته بندی",
+ "deviceLastUpdate": "آخرين بروزرسانى",
+ "deviceCommand": "فرمان",
+ "deviceFollow": "تعقیب",
+ "deviceTotalDistance": "کل مسافت",
+ "deviceStatus": "وضعیت",
+ "deviceStatusOnline": "آنلاین",
+ "deviceStatusOffline": "آفلاین",
+ "deviceStatusUnknown": "نامعلوم",
+ "deviceRegisterFirst": "اولین دستگاه خود را ثبت کنید",
+ "deviceIdentifierHelp": "IEMI ، شماره سریال یا شناسه دیگر باید با گزارش های دستگاه شناسه به سرور مطابقت داشته باشد.",
+ "deviceShare": "Share Device",
+ "groupDialog": "گروه",
+ "groupParent": "گروه",
+ "groupNoGroup": "بدون گروه",
+ "settingsTitle": "تنظيمات",
+ "settingsUser": "حساب كاربرى",
+ "settingsGroups": "گروه ها",
+ "settingsServer": "سرور",
+ "settingsUsers": "کاربر",
+ "settingsDistanceUnit": "واحد مسافت",
+ "settingsAltitudeUnit": "واحد ارتفاع",
+ "settingsSpeedUnit": "واحد سرعت",
+ "settingsVolumeUnit": "واحد حجم",
+ "settingsTwelveHourFormat": "فرمت ساعت : 12 ساعتی",
+ "settingsCoordinateFormat": "فرمت مختصاتی",
+ "settingsServerVersion": "نسخه سرور",
+ "settingsAppVersion": "نسخه برنامه",
+ "settingsConnection": "ارتباط",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "گزارشات ",
+ "reportScheduled": "گزارش های برنامه ریزی شده",
+ "reportDevice": "دستگاه",
+ "reportGroup": "گروه",
+ "reportFrom": "از",
+ "reportTo": "تا",
+ "reportShow": "نمایش",
+ "reportClear": "خالی کردن",
+ "linkGoogleMaps": "نقشه گوگل",
+ "linkAppleMaps": "نقشه اپل",
+ "linkStreetView": "نمای خیابان",
+ "positionFixTime": "زمان ثابت",
+ "positionDeviceTime": "زمان دستگاه",
+ "positionServerTime": "زمان سرور",
+ "positionValid": "معتبر",
+ "positionAccuracy": "دقت",
+ "positionLatitude": "عرض جغرافيايى",
+ "positionLongitude": "طول جغرافيايى",
+ "positionAltitude": "ارتفاع",
+ "positionSpeed": "سرعت",
+ "positionCourse": "دوره",
+ "positionAddress": "آدرس",
+ "positionProtocol": "پروتکل",
+ "positionDistance": "مسافت",
+ "positionRpm": "دور / دقیقه",
+ "positionFuel": "سوخت",
+ "positionPower": "روشن بودن",
+ "positionBattery": "باطری",
+ "positionRaw": "خالص",
+ "positionIndex": "شاخص",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "ماهواره ها",
+ "positionSatVisible": "ماهواره های قابل مشاهده",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "رومینگ",
+ "positionEvent": "رویداد",
+ "positionAlarm": "هشدار",
+ "positionStatus": "وضعیت",
+ "positionOdometer": "کیلومتر شمار",
+ "positionServiceOdometer": "سرویس کیلومتر شمار",
+ "positionTripOdometer": "کیلومتر شمار مسافت",
+ "positionHours": "ساعت",
+ "positionSteps": "مراحل",
+ "positionInput": "ورود",
+ "positionHeartRate": "ضربان قلب",
+ "positionOutput": "خروج",
+ "positionBatteryLevel": "وضعیت باطری",
+ "positionFuelConsumption": "مصرف سوخت",
+ "positionRfid": "RFID",
+ "positionVersionFw": "ورژن فلش",
+ "positionVersionHw": "ورژن سخت افزار",
+ "positionIgnition": "روشن بودن",
+ "positionFlags": "نشانها",
+ "positionCharge": "شارژ",
+ "positionIp": "IP",
+ "positionArchive": "آرشیو",
+ "positionVin": "VIN",
+ "positionApproximate": "تقریبی",
+ "positionThrottle": "دریچه گاز",
+ "positionMotion": "حرکت",
+ "positionArmed": "مسلح",
+ "positionAcceleration": "شتاب",
+ "positionTemp": "درجه حرارت",
+ "positionDeviceTemp": "دمای دستگاه",
+ "positionCoolantTemp": "دمای مایع خنک کننده",
+ "positionOperator": "اپراتور",
+ "positionCommand": "کد دستوری",
+ "positionBlocked": "بلوکه",
+ "positionDtcs": "DTCs",
+ "positionObdSpeed": "سرعت OBD",
+ "positionObdOdometer": "کیلومتر شمار ODB",
+ "positionDrivingTime": "زمان رانندگی",
+ "positionDriverUniqueId": "کد شناسایی راننده",
+ "positionCard": "کارت",
+ "positionImage": "تصویر",
+ "positionVideo": "ویدیو",
+ "positionAudio": "صدا",
+ "serverTitle": "تنظیمات سرور",
+ "serverZoom": "بزرگنمایی",
+ "serverRegistration": "ثبت نام",
+ "serverReadonly": "فقط خواندنی",
+ "serverForceSettings": "تنظیمات اجباری",
+ "serverAnnouncement": "اطلاعیه",
+ "serverName": "نام سرور",
+ "serverDescription": "توضیحات سرور",
+ "serverColorPrimary": "رنگ اصلی",
+ "serverColorSecondary": "رنگ ثانویه",
+ "serverLogo": "عکس لوگو",
+ "serverLogoInverted": "معکوس سازی رنگ لوگو",
+ "serverChangeDisable": "غیرفعال کردن تغییر سرور",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "نقشه",
+ "mapActive": "نقشه های فعال",
+ "mapOverlay": "لایه نقشه",
+ "mapOverlayCustom": "لایه سفارشی",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API کلید",
+ "mapOpenWeatherClouds": "OpenWeather Clouds",
+ "mapOpenWeatherPrecipitation": "OpenWeather Precipitation",
+ "mapOpenWeatherPressure": "OpenWeather فشار",
+ "mapOpenWeatherWind": "OpenWeather باد",
+ "mapOpenWeatherTemperature": "OpenWeather Temperature",
+ "mapLayer": "لایه های نقشه",
+ "mapCustom": "سفارشی (XYZ)",
+ "mapCustomArcgis": "سفارشی (ArcGIS)",
+ "mapCustomLabel": "نقشه اختیاری",
+ "mapCarto": "نقشه کارتو",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google جاده",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google ماهواره",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Bing کلید نقشه",
+ "mapBingRoad": "Bing نقشه جاده",
+ "mapBingAerial": "Bing نقشه هوایی",
+ "mapBingHybrid": "Bing نقشه هیبریدی",
+ "mapBaidu": "Baidu چین",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Yandex نقشه",
+ "mapYandexSat": "Yandex ماهواره",
+ "mapWikimedia": "ویکی مدیا",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox Streets",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox Outdoors",
+ "mapMapboxSatellite": "Mapbox Satellite",
+ "mapMapboxKey": "Mapbox توکن دسترسی",
+ "mapMapTilerBasic": "MapTiler Basic",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "MapTiler کلید API",
+ "mapLocationIqStreets": "LocationIQ Streets نقشه",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ توکن دسترسی",
+ "mapTomTomBasic": "TomTom Basic",
+ "mapTomTomFlow": "TomTom جریان ترافیک",
+ "mapTomTomIncidents": "TomTom Traffic Incidents",
+ "mapTomTomKey": "TomTom کلید API",
+ "mapHereBasic": "Here Basic",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satellite نقشه",
+ "mapHereFlow": "Here Traffic Flow",
+ "mapHereKey": "Here API Key",
+ "mapShapePolygon": "چند ضلعی",
+ "mapShapeCircle": "دایره ",
+ "mapShapePolyline": "چند خطی",
+ "mapLiveRoutes": "تعقیب مسیر",
+ "mapDirection": "نمایش جهت",
+ "mapCurrentLocation": "موقعیت فعلی",
+ "mapPoiLayer": "لایه POI",
+ "mapClustering": "خوشه بندی نشانگرها",
+ "mapOnSelect": "نمایش نقشه در انتخاب",
+ "mapDefault": "نقشه پیش فرض",
+ "stateTitle": "وضعیت",
+ "stateName": "ویژگی",
+ "stateValue": "مقدار",
+ "commandTitle": "ارسال دستور به دستگاه",
+ "commandSend": "ارسال",
+ "commandSent": "ارسال دستور",
+ "commandQueued": "دستور در صف",
+ "commandUnit": "واحد",
+ "commandCustom": "دستور سفارشی",
+ "commandDeviceIdentification": "شناسایی دستگاه",
+ "commandPositionSingle": "گزارش ساده",
+ "commandPositionPeriodic": "گزارشات چندوضعیتی",
+ "commandPositionStop": "توقف گزارش",
+ "commandEngineStop": "توقف موتور",
+ "commandEngineResume": "ادامه موتور",
+ "commandAlarmArm": "آلارم فعال",
+ "commandAlarmDisarm": "آلارم غیر فعال",
+ "commandAlarmDismiss": "رد کردن هشدار",
+ "commandSetTimezone": "تنظیم منطقه زمانی",
+ "commandRequestPhoto": "درخواست عکس",
+ "commandPowerOff": "خاموش شدن دستگاه",
+ "commandRebootDevice": "ریست کردن دستگاه",
+ "commandFactoryReset": "ریست تنظیم کارخانه",
+ "commandSendSms": "ارسال پیام کوتاه",
+ "commandSendUssd": "USSD ارسال کد",
+ "commandSosNumber": "شماره SOS را تنظیم کنید",
+ "commandSilenceTime": "تنظیم زمان سکوت",
+ "commandSetPhonebook": "تنظیم دفترچه تلفن",
+ "commandVoiceMessage": "پیام صوتی",
+ "commandOutputControl": "تنظیمات خروجی ",
+ "commandVoiceMonitoring": "کنترل صوتی",
+ "commandSetAgps": "تنظیم AGPS",
+ "commandSetIndicator": "ایجاد شاخص",
+ "commandConfiguration": "پیکر بندی",
+ "commandGetVersion": "دریافت نگارش",
+ "commandFirmwareUpdate": "به روز رسانی فریمور",
+ "commandSetConnection": "تنظیم ارتباط",
+ "commandSetOdometer": "تنظیم کیلومتر شمار",
+ "commandGetModemStatus": "دریافت وضعیت مودم",
+ "commandGetDeviceStatus": "دریافت وضعیت دستگاه",
+ "commandSetSpeedLimit": "تنظیم محدودیت سرعت",
+ "commandModePowerSaving": "حالت کم مصرف",
+ "commandModeDeepSleep": "حالت خواب عمیق",
+ "commandAlarmGeofence": "تنظیم هشدار محدوده جغرافیایی",
+ "commandAlarmBattery": "تنظیم هشدار باتری",
+ "commandAlarmSos": "تنظیم هشدار کمک",
+ "commandAlarmRemove": "تنظیم حذف هشدار",
+ "commandAlarmClock": "تنظیم ساعت هشدار",
+ "commandAlarmSpeed": "تنظیم هشدار سرعت",
+ "commandAlarmFall": "Set Fall Alarm",
+ "commandAlarmVibration": "Set Vibration Alarm",
+ "commandFrequency": "تاخیر ارسال",
+ "commandTimezone": "فاصله زمانی مبدا",
+ "commandMessage": "پیام",
+ "commandRadius": "شعاع",
+ "commandEnable": "فعال",
+ "commandData": "دیتا",
+ "commandIndex": "فهرست",
+ "commandPhone": "شماره تلفن",
+ "commandServer": "سرور",
+ "commandPort": "پورت",
+ "eventAll": "همه رویدادها",
+ "eventDeviceOnline": "وضعیت آنلاین",
+ "eventDeviceUnknown": "وضعیت نامعلوم",
+ "eventDeviceOffline": "وضعیت آفلاین",
+ "eventDeviceInactive": "دستگاه غیرفعال",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "حرکت دستگاه",
+ "eventDeviceStopped": "دستگاه متوقف شد",
+ "eventDeviceOverspeed": "سرعت از حد مجاز فراتر رفت",
+ "eventDeviceFuelDrop": "افت سوخت",
+ "eventDeviceFuelIncrease": "افزایش سوخت",
+ "eventCommandResult": "نتیجه ارسال دستور",
+ "eventGeofenceEnter": "ورود محدوده جغرافیایی",
+ "eventGeofenceExit": "خروج محدوده جغرافیایی",
+ "eventAlarm": "هشدار",
+ "eventIgnitionOn": "سویچ روشن",
+ "eventIgnitionOff": "سوئیچ خاموش",
+ "eventMaintenance": "نیاز به تعمیر",
+ "eventTextMessage": "پیامک دریافت شد",
+ "eventDriverChanged": "تعویض راننده",
+ "eventMedia": "مدیا",
+ "eventsScrollToLast": "اسکرول تا آخر",
+ "eventsSoundEvents": "رویدادهای صدا",
+ "eventsSoundAlarms": "هشدار صوتی",
+ "alarmGeneral": "اصلی",
+ "alarmSos": "درخواست کمک",
+ "alarmVibration": "لرزش",
+ "alarmMovement": "جابجایی",
+ "alarmLowspeed": "سرعت کم",
+ "alarmOverspeed": "سرعت بالا",
+ "alarmFallDown": "سقوط",
+ "alarmLowPower": "انرژی کم",
+ "alarmLowBattery": "باطری کم",
+ "alarmFault": "نقص",
+ "alarmPowerOff": "خاموش",
+ "alarmPowerOn": "روشن",
+ "alarmDoor": "درب",
+ "alarmLock": "قفل",
+ "alarmUnlock": "باز کردن",
+ "alarmGeofence": "محدوده جغرافیایی",
+ "alarmGeofenceEnter": "ورود محدوده جغرافیایی",
+ "alarmGeofenceExit": "خروج محدوده جغرافیایی",
+ "alarmGpsAntennaCut": "قطع آنتن GPS",
+ "alarmAccident": "تصادف",
+ "alarmTow": "یدک کش",
+ "alarmIdle": "بیکار",
+ "alarmHighRpm": "دورموتور بالا",
+ "alarmHardAcceleration": "شتاب تند",
+ "alarmHardBraking": "ترمز تند",
+ "alarmHardCornering": "زاویه شدید",
+ "alarmLaneChange": "تغییر مسیر",
+ "alarmFatigueDriving": "رانندگی خسته",
+ "alarmPowerCut": "قطع ولتاژ",
+ "alarmPowerRestored": "برگشت ولتاژ",
+ "alarmJamming": "مسدود",
+ "alarmTemperature": "دما",
+ "alarmParking": "پارکینگ",
+ "alarmBonnet": "صندوق",
+ "alarmFootBrake": "پدال ترمز",
+ "alarmFuelLeak": "نشت باک",
+ "alarmTampering": "دستکاری",
+ "alarmRemoving": "حذف",
+ "notificationType": "تعیین نوع رویداد ",
+ "notificationAlways": "همه ردیابها",
+ "notificationNotificators": "کانال ها",
+ "notificatorCommand": "فرمان",
+ "notificatorWeb": "وب",
+ "notificatorMail": "ایمیل",
+ "notificatorSms": "پیامک",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "تلگرام",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "بازپخش",
+ "reportCombined": "ترکیب شده",
+ "reportRoute": "مسیر های پیموده شده ",
+ "reportEvents": "رویداد ها",
+ "reportTrips": "مسافرتها",
+ "reportStops": "توقفها",
+ "reportSummary": "خلاصه وضعیت ",
+ "reportDaily": "وقایع روزانه",
+ "reportChart": "نمودار",
+ "reportConfigure": "تنظیمات",
+ "reportEventTypes": "نوع رویدادها",
+ "reportChartType": "نوع نمودار",
+ "reportShowMarkers": "نمایش علامتها",
+ "reportExport": "خروجی فایل",
+ "reportEmail": "گزارش پست الکترونیکی",
+ "reportSchedule": "برنامه زمانی",
+ "reportPeriod": "بازه",
+ "reportCustom": "سفارشی",
+ "reportToday": "امروز",
+ "reportYesterday": "دیروز",
+ "reportThisWeek": "هفته جاری",
+ "reportPreviousWeek": "هفته قبلی",
+ "reportThisMonth": "ماه جاری",
+ "reportPreviousMonth": "ماه قبلی",
+ "reportDeviceName": "نام دستگاه ",
+ "reportAverageSpeed": "سرعت میانگین",
+ "reportMaximumSpeed": "حداکثر سرعت",
+ "reportEngineHours": "مدت زمان روشن بودن وسیله",
+ "reportDuration": "مسافت",
+ "reportStartDate": "تاریخ آغاز",
+ "reportStartTime": "زمان شروع",
+ "reportStartAddress": "آدرس شروع",
+ "reportEndTime": "زمان پایانی",
+ "reportEndAddress": "آدرس پایانی",
+ "reportSpentFuel": "مصرف سوخت",
+ "reportStartOdometer": "شروع کیلومترشمار",
+ "reportEndOdometer": "پایان کیلومتر شمار",
+ "statisticsTitle": "گزارشات",
+ "statisticsCaptureTime": "زمان ضبط",
+ "statisticsActiveUsers": "کاربران فعال",
+ "statisticsActiveDevices": "ردیابهای فعال",
+ "statisticsRequests": "درخواستها",
+ "statisticsMessagesReceived": "پیامهای دریافتی",
+ "statisticsMessagesStored": "پیامها ذخیره",
+ "statisticsGeocoder": "درخواست محدوده",
+ "statisticsGeolocation": "درخواستهای محدوده",
+ "categoryArrow": "فلش",
+ "categoryDefault": "پیش فرض",
+ "categoryAnimal": "حیوان",
+ "categoryBicycle": "دوچرخه",
+ "categoryBoat": "قایق",
+ "categoryBus": "اتوبوس",
+ "categoryCar": "خودرو",
+ "categoryCamper": "Camper",
+ "categoryCrane": "جرثقیل",
+ "categoryHelicopter": "هلی کوپتر",
+ "categoryMotorcycle": "موتورسیکلت",
+ "categoryOffroad": "شاسی بلند",
+ "categoryPerson": "شخص",
+ "categoryPickup": "وانت",
+ "categoryPlane": "هواپیما",
+ "categoryShip": "کشتی",
+ "categoryTractor": "تراکتور",
+ "categoryTrain": "قطار",
+ "categoryTram": "تراموا",
+ "categoryTrolleybus": "تریلر",
+ "categoryTruck": "تریلی",
+ "categoryVan": "ون",
+ "categoryScooter": "اسکوتر",
+ "maintenanceStart": "شروع",
+ "maintenancePeriod": "بازه"
+} \ No newline at end of file
diff --git a/src/resources/l10n/fi.json b/src/resources/l10n/fi.json
new file mode 100644
index 00000000..f5459166
--- /dev/null
+++ b/src/resources/l10n/fi.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "Ladataan...",
+ "sharedHide": "Piilota",
+ "sharedSave": "Tallenna",
+ "sharedUpload": "Lataa palvelimelle",
+ "sharedSet": "Aseta",
+ "sharedCancel": "Peruuta",
+ "sharedCopy": "Kopioi",
+ "sharedAdd": "Lisää",
+ "sharedEdit": "Muokkaa",
+ "sharedRemove": "Poista",
+ "sharedRemoveConfirm": "Poista kohde?",
+ "sharedNoData": "Ei dataa",
+ "sharedSubject": "Subject",
+ "sharedYes": "Kyllä",
+ "sharedNo": "Ei",
+ "sharedKm": "km",
+ "sharedMi": "mi",
+ "sharedNmi": "nmi",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "kn",
+ "sharedKmh": "km/h",
+ "sharedMph": "mph",
+ "sharedHour": "Tunti",
+ "sharedMinute": "Minuutti",
+ "sharedSecond": "Sekunti",
+ "sharedDays": "päivää",
+ "sharedHours": "tuntia",
+ "sharedMinutes": "minuuttia",
+ "sharedDecimalDegrees": "Desimaaliasteet",
+ "sharedDegreesDecimalMinutes": "Asteet ja desimaaliminuutit",
+ "sharedDegreesMinutesSeconds": "Asteet, minuutit ja sekunnit",
+ "sharedName": "Nimi",
+ "sharedDescription": "Kuvaus",
+ "sharedSearch": "Haku",
+ "sharedIconScale": "Kuvakkeiden skaalaus",
+ "sharedGeofence": "Geoaita",
+ "sharedGeofences": "Geoaidat",
+ "sharedCreateGeofence": "Luo geoaita",
+ "sharedNotifications": "Ilmoitukset",
+ "sharedNotification": "Ilmoitus",
+ "sharedAttributes": "Ominaisuudet",
+ "sharedAttribute": "Ominaisuus",
+ "sharedDrivers": "Kuljettajat",
+ "sharedDriver": "Kuljettaja",
+ "sharedArea": "Alue",
+ "sharedSound": "Ilmoitusääni",
+ "sharedType": "Tyyppi",
+ "sharedDistance": "Matka",
+ "sharedHourAbbreviation": "h",
+ "sharedMinuteAbbreviation": "m",
+ "sharedSecondAbbreviation": "s",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "l",
+ "sharedGallonAbbreviation": "gallona",
+ "sharedLiter": "Litra",
+ "sharedImpGallon": "Brittiläinen gallona",
+ "sharedUsGallon": "Amerikkalainen gallona",
+ "sharedLiterPerHourAbbreviation": "l/h",
+ "sharedGetMapState": "Hae kartan tila",
+ "sharedComputedAttribute": "Laskettu ominaisuus",
+ "sharedComputedAttributes": "Lasketut ominaisuudet",
+ "sharedCheckComputedAttribute": "Tarkista laskettu ominaisuus",
+ "sharedExpression": "Lauseke",
+ "sharedDevice": "Laite",
+ "sharedTest": "Testaa",
+ "sharedTestNotification": "Lähetä testi-ilmoitus",
+ "sharedTestNotificators": "Testaa kanavia",
+ "sharedTestExpression": "Testaa lauseke",
+ "sharedCalendar": "Kalenteri",
+ "sharedCalendars": "Kalenterit",
+ "sharedFile": "Tiedosto",
+ "sharedSearchDevices": "Etsi laitteita",
+ "sharedSortBy": "Lajittele",
+ "sharedFilterMap": "Suodata kartalla",
+ "sharedSelectFile": "Valitse tiedosto",
+ "sharedPhone": "Puhelin",
+ "sharedRequired": "Vaadittavat",
+ "sharedPreferences": "Asetukset",
+ "sharedPermissions": "Käyttöoikeudet",
+ "sharedConnections": "Yhteydet",
+ "sharedExtra": "Ekstra",
+ "sharedPrimary": "Ensisijainen",
+ "sharedSecondary": "Toissijainen",
+ "sharedTypeString": "Merkkijono",
+ "sharedTypeNumber": "Numero",
+ "sharedTypeBoolean": "Totuusarvo",
+ "sharedTimezone": "Aikavyöhyke",
+ "sharedInfoTitle": "Info",
+ "sharedSavedCommand": "Tallennettu komento",
+ "sharedSavedCommands": "Tallennetut komennot",
+ "sharedNew": "Uusi...",
+ "sharedShowAddress": "Näytä osoite",
+ "sharedShowDetails": "Lisätietoja",
+ "sharedDisabled": "Poistettu käytöstä",
+ "sharedMaintenance": "Huolto",
+ "sharedDeviceAccumulators": "Laskurit",
+ "sharedAlarms": "Hälytykset",
+ "sharedLocation": "Sijainti",
+ "sharedImport": "Tuo",
+ "sharedColumns": "Sarakkeet",
+ "sharedDropzoneText": "Vedä ja pudota tiedosto tähän tai klikkaa",
+ "sharedLogs": "Lokit",
+ "sharedLink": "Link",
+ "calendarSimple": "Yksinkertainen",
+ "calendarRecurrence": "Toisto",
+ "calendarOnce": "Kerran",
+ "calendarDaily": "Päivittäin",
+ "calendarWeekly": "Viikoittain",
+ "calendarMonthly": "Kuukausittain",
+ "calendarDays": "Päivät",
+ "calendarSunday": "Sunnuntai",
+ "calendarMonday": "Maanantai",
+ "calendarTuesday": "Tiistai",
+ "calendarWednesday": "Keskiviikko",
+ "calendarThursday": "Torstai",
+ "calendarFriday": "Perjantai",
+ "calendarSaturday": "Lauantai",
+ "attributeShowGeofences": "Näytä geoaita",
+ "attributeSpeedLimit": "Nopeusrajoitus",
+ "attributeFuelDropThreshold": "Polttoaineen vähenemisen raja-arvo",
+ "attributeFuelIncreaseThreshold": "Polttoaineen lisäämisen raja-arvo",
+ "attributePolylineDistance": "Etäisyys murtoviivaan",
+ "attributeReportIgnoreOdometer": "Raportti: Älä huomioi matkamittaria",
+ "attributeWebReportColor": "Web: Raportin väri",
+ "attributeDevicePassword": "Laitteen salasana",
+ "attributeDeviceImage": "Laitteen kuva",
+ "attributeDeviceInactivityStart": "Laitteen epäaktiivisuuden alku",
+ "attributeDeviceInactivityPeriod": "Laitteen epäaktiivisuuden jakso",
+ "attributeProcessingCopyAttributes": "Laskenta: Kopioi ominaisuudet",
+ "attributeColor": "Väri",
+ "attributeWebLiveRouteLength": "Web: Live-reitin pituus",
+ "attributeWebSelectZoom": "Web: Lähennys valittaessa",
+ "attributeWebMaxZoom": "Web: Suurin lähennys",
+ "attributeTelegramChatId": "Telegram keskustelu ID",
+ "attributePushoverUserKey": "Pushover käyttäjäavain",
+ "attributePushoverDeviceNames": "Pushover laitteiden nimet",
+ "attributeMailSmtpHost": "Email: SMTP-isäntäpalvelin",
+ "attributeMailSmtpPort": "Email: SMTP-portti",
+ "attributeMailSmtpStarttlsEnable": "Email: SMTP STARTTLS:n aktivointi",
+ "attributeMailSmtpStarttlsRequired": "Email: SMTP STARTTLS vaaditaan",
+ "attributeMailSmtpSslEnable": "Email: SMTP SSL:n aktivointi",
+ "attributeMailSmtpSslTrust": "Email: SMTP SSL Trust",
+ "attributeMailSmtpSslProtocols": "Email: SMTP SSL -protokollat",
+ "attributeMailSmtpFrom": "Email: SMTP-lähettäjä",
+ "attributeMailSmtpAuth": "Email: SMTP-tunnistautumisen aktivointi",
+ "attributeMailSmtpUsername": "Email: SMTP-käyttäjätunnus",
+ "attributeMailSmtpPassword": "Email: SMTP-salasana",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: Piilota ominaisuudet",
+ "attributeUiDisableGroups": "UI: Piilota ryhmät",
+ "attributeUiDisableEvents": "UI: Piilota tapahtumat",
+ "attributeUiDisableVehicleFeatures": "UI: Piilota ajoneuvon omainaisuudet",
+ "attributeUiDisableDrivers": "UI: Piilota kuljettajat",
+ "attributeUiDisableComputedAttributes": "UI: Piilota lasketut ominaisuudet",
+ "attributeUiDisableCalendars": "UI: Piilota kalenterit",
+ "attributeUiDisableMaintenance": "UI: Piilota huolto",
+ "attributeUiHidePositionAttributes": "UI: Piilota sijainnin ominaisuudet",
+ "attributeUiDisableLoginLanguage": "UI: Piilota kielivalinta kirjautumisesta",
+ "attributeNotificationTokens": "Ilmoitustunnukset",
+ "attributePopupInfo": "Popup tiedot",
+ "errorTitle": "Virhe",
+ "errorGeneral": "Epäkelvot parametrit tai rajoitteiden rikkomus",
+ "errorConnection": "Yhteysvirhe",
+ "errorSocket": "WebSocket-yhteysvirhe",
+ "errorZero": "Ei voi olla nolla",
+ "userEmail": "Sähköposti",
+ "userPassword": "Salasana",
+ "userAdmin": "Ylläpito",
+ "userRemember": "Muista",
+ "userExpirationTime": "Vanheneminen",
+ "userDeviceLimit": "Laiteraja",
+ "userUserLimit": "Käyttäjäraja",
+ "userDeviceReadonly": "Laitteet vain luettavissa",
+ "userLimitCommands": "Rajoita komentoja",
+ "userDisableReports": "Poista raportit käytöstä",
+ "userFixedEmail": "Ei sähköpostiosoitteen vaihtoa",
+ "userToken": "Todennustunnus",
+ "userDeleteAccount": "Poista tili",
+ "userTemporary": "Temporary",
+ "loginTitle": "Kirjaudu",
+ "loginLanguage": "Kieli",
+ "loginReset": "Nollaa salasana",
+ "loginRegister": "Rekisteröidy",
+ "loginLogin": "Kirjaudu",
+ "loginOpenId": "Kirjaudu OpenID:llä",
+ "loginFailed": "Virheellinen sähköposti tai salasana",
+ "loginCreated": "Uusi käyttäjä on rekisteröity",
+ "loginResetSuccess": "Tarkista sähköpostisi",
+ "loginUpdateSuccess": "Uusi salasana asetettu",
+ "loginLogout": "Kirjaudu ulos",
+ "loginLogo": "Logo",
+ "loginTotpCode": "Kertakäyttö salasana koodi",
+ "loginTotpKey": "Kertakäyttö salasanan avain",
+ "devicesAndState": "Laitteet ja Tilat",
+ "deviceSelected": "Valitse laite",
+ "deviceTitle": "Laitteet",
+ "devicePrimaryInfo": "Laitteen nimi",
+ "deviceSecondaryInfo": "Laitteen tiedot",
+ "deviceIdentifier": "Tunniste",
+ "deviceModel": "Malli",
+ "deviceContact": "Yhteyshenkilö",
+ "deviceCategory": "Kategoria",
+ "deviceLastUpdate": "Viimeisin päivitys",
+ "deviceCommand": "Komento",
+ "deviceFollow": "Seuraa",
+ "deviceTotalDistance": "Kokonaismatka",
+ "deviceStatus": "Tila",
+ "deviceStatusOnline": "Online",
+ "deviceStatusOffline": "Offline",
+ "deviceStatusUnknown": "Tuntematon",
+ "deviceRegisterFirst": "Rekisteröi ensimmäinen laitteesi",
+ "deviceIdentifierHelp": "IMEI, sarjanumeo tai muu id. ID:n pitää täsmätä laitteen lähettämään tunnisteeseen.",
+ "deviceShare": "Jaa laitteen sijainti",
+ "groupDialog": "Ryhmä",
+ "groupParent": "Ryhmä",
+ "groupNoGroup": "Ei ryhmää",
+ "settingsTitle": "Asetukset",
+ "settingsUser": "Tili",
+ "settingsGroups": "Ryhmät",
+ "settingsServer": "Palvelin",
+ "settingsUsers": "Käyttäjät",
+ "settingsDistanceUnit": "Matkayksikkö",
+ "settingsAltitudeUnit": "Korkeusyksikkö",
+ "settingsSpeedUnit": "Nopeusyksikkö",
+ "settingsVolumeUnit": "Tilavuusyksikkö",
+ "settingsTwelveHourFormat": "12-tunnin formaatti",
+ "settingsCoordinateFormat": "Koordinaattien formaatti",
+ "settingsServerVersion": "Palvelin versio",
+ "settingsAppVersion": "Sovellus versio",
+ "settingsConnection": "Yhteys",
+ "settingsDarkMode": "Tumma tila",
+ "settingsTotpEnable": "Ota käyttöön kertakäyttö salasana",
+ "settingsTotpForce": "Pakota kertakäyttöinen salasana",
+ "settingsServiceWorkerUpdateInterval": "Palvelun päivitysväli",
+ "settingsUpdateAvailable": "Päivitys saatavilla",
+ "settingsSupport": "Tuki",
+ "reportTitle": "Raportit",
+ "reportScheduled": "Ajastetut raportit",
+ "reportDevice": "Laite",
+ "reportGroup": "Ryhmä",
+ "reportFrom": "Mistä",
+ "reportTo": "Mihin",
+ "reportShow": "Näytä",
+ "reportClear": "Tyhjennä",
+ "linkGoogleMaps": "Google kartat",
+ "linkAppleMaps": "Apple kartat",
+ "linkStreetView": "Katunäkymä",
+ "positionFixTime": "Sijaintiaika",
+ "positionDeviceTime": "Laitteen aika",
+ "positionServerTime": "Palvelimen aika",
+ "positionValid": "Kelvollinen",
+ "positionAccuracy": "Tarkkuus",
+ "positionLatitude": "Leveysaste",
+ "positionLongitude": "Pituusaste",
+ "positionAltitude": "Korkeus",
+ "positionSpeed": "Nopeus",
+ "positionCourse": "Suunta",
+ "positionAddress": "Osoite",
+ "positionProtocol": "Protokolla",
+ "positionDistance": "Matka",
+ "positionRpm": "RPM",
+ "positionFuel": "Polttoaine",
+ "positionPower": "Virta",
+ "positionBattery": "Akku",
+ "positionRaw": "Raaka",
+ "positionIndex": "Indeksi",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "Satelliitteja",
+ "positionSatVisible": "Näkyviä satelliitteja",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "Verkkovierailu",
+ "positionEvent": "Tapahtuma",
+ "positionAlarm": "Hälytys",
+ "positionStatus": "Tila",
+ "positionOdometer": "Matkamittari",
+ "positionServiceOdometer": "Huollon matkamittari",
+ "positionTripOdometer": "Trippimittari",
+ "positionHours": "Tuntia",
+ "positionSteps": "Askeleita",
+ "positionInput": "Sisääntulo",
+ "positionHeartRate": "Syke",
+ "positionOutput": "Ulostulo",
+ "positionBatteryLevel": "Akun varaus",
+ "positionFuelConsumption": "Polttoaineen kulutus",
+ "positionRfid": "RFID",
+ "positionVersionFw": "Laiteohjelmiston versio",
+ "positionVersionHw": "Laitteiston versio",
+ "positionIgnition": "Virta (sytytys)",
+ "positionFlags": "Liput",
+ "positionCharge": "Lataus",
+ "positionIp": "IP-osoite",
+ "positionArchive": "Arkisto",
+ "positionVin": "VIN",
+ "positionApproximate": "Likimääräinen",
+ "positionThrottle": "Kaasupoljin",
+ "positionMotion": "Liike",
+ "positionArmed": "Hälytys kytketty",
+ "positionAcceleration": "Kiihtyvyys",
+ "positionTemp": "Lämpötila",
+ "positionDeviceTemp": "Laitteen lämpötila",
+ "positionCoolantTemp": "Jäähdytysnesteen lämpötila",
+ "positionOperator": "Operaattori",
+ "positionCommand": "Komento",
+ "positionBlocked": "Estetty",
+ "positionDtcs": "DTC:t",
+ "positionObdSpeed": "OBD-nopeus",
+ "positionObdOdometer": "OBD-matkamittari",
+ "positionDrivingTime": "Ajoaika",
+ "positionDriverUniqueId": "Kuljettajan ID",
+ "positionCard": "Kortti",
+ "positionImage": "Kuva",
+ "positionVideo": "Video",
+ "positionAudio": "Ääni",
+ "serverTitle": "Palvelinasetukset",
+ "serverZoom": "Lähennys",
+ "serverRegistration": "Rekisteröinti",
+ "serverReadonly": "Vain luku",
+ "serverForceSettings": "Pakota asetukset",
+ "serverAnnouncement": "Ilmoitus",
+ "serverName": "Palvelimen nimi",
+ "serverDescription": "Palvelimen kuvaus",
+ "serverColorPrimary": "Ensisijainen väri",
+ "serverColorSecondary": "Toissijainen väri",
+ "serverLogo": "Logo kuva",
+ "serverLogoInverted": "Logo kuva negatiivinen",
+ "serverChangeDisable": "Estä palvelimen vaihto",
+ "serverDisableShare": "Estä laitteen jakaminen",
+ "mapTitle": "Kartta",
+ "mapActive": "Aktiiviset kartat",
+ "mapOverlay": "Karttapäällys",
+ "mapOverlayCustom": "Oma karttapäällys",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API-avain",
+ "mapOpenWeatherClouds": "OpenWeather pilvet",
+ "mapOpenWeatherPrecipitation": "OpenWeather sade",
+ "mapOpenWeatherPressure": "OpenWeather ilmanpaine",
+ "mapOpenWeatherWind": "OpenWeather tuuli",
+ "mapOpenWeatherTemperature": "OpenWeather lämpötila",
+ "mapLayer": "Karttataso",
+ "mapCustom": "Oma kartta (XYZ)",
+ "mapCustomArcgis": "Oma kartta (ArcGIS)",
+ "mapCustomLabel": "Oma kartta",
+ "mapCarto": "Carto-pohjakartat",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google katukartta",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google ilmakuva",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Bing Maps -avain",
+ "mapBingRoad": "Bing Maps -tiet",
+ "mapBingAerial": "Bing Maps -ilmakuva",
+ "mapBingHybrid": "Bing Maps -hybridi",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Yandex-kartta",
+ "mapYandexSat": "Yandex-satelliittikuva",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox kadut",
+ "mapMapboxStreetsDark": "Mapbox Streets tumma",
+ "mapMapboxOutdoors": "Mapbox ulkoilu",
+ "mapMapboxSatellite": "Mapbox satelliitti",
+ "mapMapboxKey": "Mapbox pääsyavain",
+ "mapMapTilerBasic": "MapTile perus",
+ "mapMapTilerHybrid": "MapTiler hybrid",
+ "mapMapTilerKey": "MapTiler API-avain",
+ "mapLocationIqStreets": "LocationIQ kadut",
+ "mapLocationIqDark": "LocationIQ tumma",
+ "mapLocationIqKey": "LocationIQ pääsyavain",
+ "mapTomTomBasic": "TomTom perus",
+ "mapTomTomFlow": "TomTom liikenne",
+ "mapTomTomIncidents": "TomTom liikenneonnettomuudet",
+ "mapTomTomKey": "TomTom API-avain",
+ "mapHereBasic": "Here perus",
+ "mapHereHybrid": "Here hybrid",
+ "mapHereSatellite": "Here satellitti",
+ "mapHereFlow": "Here liikenne",
+ "mapHereKey": "Here API-avain",
+ "mapShapePolygon": "Monikulmio",
+ "mapShapeCircle": "Ympyrä",
+ "mapShapePolyline": "Murtoviiva",
+ "mapLiveRoutes": "Live-reitit",
+ "mapDirection": "Näytä suunta",
+ "mapCurrentLocation": "Nykyinen sijainti",
+ "mapPoiLayer": "Kohdepistetaso",
+ "mapClustering": "Merkkien ryvästäminen",
+ "mapOnSelect": "Näytä kartta valittaessa",
+ "mapDefault": "Oletus kartta",
+ "stateTitle": "Tila",
+ "stateName": "Ominaisuus",
+ "stateValue": "Arvo",
+ "commandTitle": "Komento",
+ "commandSend": "Lähetä",
+ "commandSent": "Komento lähetetty",
+ "commandQueued": "Komento jonossa",
+ "commandUnit": "Yksikkö",
+ "commandCustom": "Oma komento",
+ "commandDeviceIdentification": "Laitteen tunnistus",
+ "commandPositionSingle": "Yksittäinen raportointi",
+ "commandPositionPeriodic": "Määräaikaisraportointi",
+ "commandPositionStop": "Lopeta raportointi",
+ "commandEngineStop": "Sammuta moottori",
+ "commandEngineResume": "Palauta moottori",
+ "commandAlarmArm": "Hälytys päälle",
+ "commandAlarmDisarm": "Hälytys pois",
+ "commandAlarmDismiss": "Hylkää hälytys",
+ "commandSetTimezone": "Aseta aikavyöhyke",
+ "commandRequestPhoto": "Pyydä kuva",
+ "commandPowerOff": "Sammuta laite",
+ "commandRebootDevice": "Käynnistä laite uudelleen",
+ "commandFactoryReset": "Tehdasasetusten palautus",
+ "commandSendSms": "Lähetä tekstiviesti",
+ "commandSendUssd": "Lähetä USSD",
+ "commandSosNumber": "Aseta SOS-numero",
+ "commandSilenceTime": "Aseta hiljainen aika",
+ "commandSetPhonebook": "Aseta osoitekirja",
+ "commandVoiceMessage": "Ääniviesti",
+ "commandOutputControl": "Ulostulon ohjaus",
+ "commandVoiceMonitoring": "Äänitarkkailu",
+ "commandSetAgps": "Aseta AGPS",
+ "commandSetIndicator": "Aseta indikaattori",
+ "commandConfiguration": "Konfigurointi",
+ "commandGetVersion": "Pyydä versio",
+ "commandFirmwareUpdate": "Päivitä laiteohjelmisto",
+ "commandSetConnection": "Aseta yhteys",
+ "commandSetOdometer": "Aseta matkamittari",
+ "commandGetModemStatus": "Pyydä modeemin tila",
+ "commandGetDeviceStatus": "Pyydä laitteen tila",
+ "commandSetSpeedLimit": "Aseta nopeusrajoitus",
+ "commandModePowerSaving": "Virransäästötila",
+ "commandModeDeepSleep": "Syväunitila",
+ "commandAlarmGeofence": "Aseta geoaitahälytys",
+ "commandAlarmBattery": "Aseta akkuhälytys",
+ "commandAlarmSos": "Aseta SOS-hälytys",
+ "commandAlarmRemove": "Aseta poistettu hälytys",
+ "commandAlarmClock": "Aseta aikahälytys",
+ "commandAlarmSpeed": "Aseta nopeushälytys",
+ "commandAlarmFall": "Aseta kaatumishälytys",
+ "commandAlarmVibration": "Aseta tärinähälytys",
+ "commandFrequency": "Taajuus",
+ "commandTimezone": "Aikavyöhykkeen poikkeama",
+ "commandMessage": "Viesti",
+ "commandRadius": "Säde",
+ "commandEnable": "Ota käyttöön",
+ "commandData": "Tieto",
+ "commandIndex": "Indeksi",
+ "commandPhone": "Puhelinnumero",
+ "commandServer": "Palvelin",
+ "commandPort": "Portti",
+ "eventAll": "Kaikki tapahtumat",
+ "eventDeviceOnline": "Tila online",
+ "eventDeviceUnknown": "Tila tuntematon",
+ "eventDeviceOffline": "Tila offline",
+ "eventDeviceInactive": "Laite epäaktiivinen",
+ "eventQueuedCommandSent": "Jonossa oleva komento lähetetty",
+ "eventDeviceMoving": "Laite liikkuu",
+ "eventDeviceStopped": "Laite pysähtynyt",
+ "eventDeviceOverspeed": "Nopeusrajoitus ylitetty",
+ "eventDeviceFuelDrop": "Polttoaineen väheneminen",
+ "eventDeviceFuelIncrease": "Polttoaineen lisääminen",
+ "eventCommandResult": "Komennon tulos",
+ "eventGeofenceEnter": "Laite saapui geoaitaan",
+ "eventGeofenceExit": "Laite poistui geoaidasta",
+ "eventAlarm": "Hälytykset",
+ "eventIgnitionOn": "Sytytysvirta päällä",
+ "eventIgnitionOff": "Sytytysvirta pois päältä",
+ "eventMaintenance": "Huoltoa tarvitaan",
+ "eventTextMessage": "Tekstiviesti vastaanotettu",
+ "eventDriverChanged": "Kuljettaja vaihtunut",
+ "eventMedia": "Media",
+ "eventsScrollToLast": "Vieritä viimeiseen",
+ "eventsSoundEvents": "Ääni tapahtumat",
+ "eventsSoundAlarms": "Ääni hälytykset",
+ "alarmGeneral": "Yleinen",
+ "alarmSos": "SOS",
+ "alarmVibration": "Tärinä",
+ "alarmMovement": "Liike",
+ "alarmLowspeed": "Alhainen nopeus",
+ "alarmOverspeed": "Ylinopeus",
+ "alarmFallDown": "Kaatuminen",
+ "alarmLowPower": "Alhainen virta",
+ "alarmLowBattery": "Alhainen akun varaus",
+ "alarmFault": "Vikaantuminen",
+ "alarmPowerOff": "Virran sammutus",
+ "alarmPowerOn": "Virran käynnistys",
+ "alarmDoor": "Ovi",
+ "alarmLock": "Lukittu",
+ "alarmUnlock": "Lukitus avattu",
+ "alarmGeofence": "Geoaita",
+ "alarmGeofenceEnter": "Geoaitaan saapuminen",
+ "alarmGeofenceExit": "Geoaidasta poistuminen",
+ "alarmGpsAntennaCut": "GPS antennivika",
+ "alarmAccident": "Onnettomuus",
+ "alarmTow": "Hinaus",
+ "alarmIdle": "Tyhjäkäynti",
+ "alarmHighRpm": "Korkea kierrosluku",
+ "alarmHardAcceleration": "Nopea kiihdytys",
+ "alarmHardBraking": "Nopea jarrutus",
+ "alarmHardCornering": "Kova käännös",
+ "alarmLaneChange": "Kaistan vaihto",
+ "alarmFatigueDriving": "Väsyneenä ajaminen",
+ "alarmPowerCut": "Sähkökatko",
+ "alarmPowerRestored": "Sähkökatkon loppuminen",
+ "alarmJamming": "Häirintä",
+ "alarmTemperature": "Lämpötila",
+ "alarmParking": "Pysäköinti",
+ "alarmBonnet": "Konepelti",
+ "alarmFootBrake": "Jalkajarru",
+ "alarmFuelLeak": "Polttoainevuoto",
+ "alarmTampering": "Peukalointi",
+ "alarmRemoving": "Siirto",
+ "notificationType": "Ilmoituksen tyyppi",
+ "notificationAlways": "Kaikki laitteet",
+ "notificationNotificators": "Kanavat",
+ "notificatorCommand": "Komento",
+ "notificatorWeb": "Web",
+ "notificatorMail": "Sähköposti",
+ "notificatorSms": "Tekstiviesti",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Toista",
+ "reportCombined": "Yhdistelmä",
+ "reportRoute": "Reitti",
+ "reportEvents": "Tapahtumat",
+ "reportTrips": "Matkat",
+ "reportStops": "Pysähdykset",
+ "reportSummary": "Yhteenveto",
+ "reportDaily": "Päivittäinen yhteenveto",
+ "reportChart": "Kuvaaja",
+ "reportConfigure": "Konfiguroi",
+ "reportEventTypes": "Tapahtumatyypit",
+ "reportChartType": "Kuvaajatyypit",
+ "reportShowMarkers": "Näytä merkit",
+ "reportExport": "Vie",
+ "reportEmail": "Sähköpostiraportti",
+ "reportSchedule": "Ajastettu",
+ "reportPeriod": "Jakso",
+ "reportCustom": "Oma",
+ "reportToday": "Tänään",
+ "reportYesterday": "Eilen",
+ "reportThisWeek": "Tällä viikolla",
+ "reportPreviousWeek": "Viime viikolla",
+ "reportThisMonth": "Tässä kuussa",
+ "reportPreviousMonth": "Viime kuussa",
+ "reportDeviceName": "Laitteen nimi",
+ "reportAverageSpeed": "Keskinopeus",
+ "reportMaximumSpeed": "Suurin nopeus",
+ "reportEngineHours": "Käyttötunnit",
+ "reportDuration": "Kesto",
+ "reportStartDate": "Aloituspäivä",
+ "reportStartTime": "Aloitusaika",
+ "reportStartAddress": "Aloitusosoite",
+ "reportEndTime": "Lopetusaika",
+ "reportEndAddress": "Lopetusosoite",
+ "reportSpentFuel": "Käytetty polttoaine",
+ "reportStartOdometer": "Matkamittari alussa",
+ "reportEndOdometer": "Matkamittari lopussa",
+ "statisticsTitle": "Tilastot",
+ "statisticsCaptureTime": "Tarkasteluaika",
+ "statisticsActiveUsers": "Aktiivisia käyttäjiä",
+ "statisticsActiveDevices": "Aktiivisia laitteita",
+ "statisticsRequests": "Pyyntöjä",
+ "statisticsMessagesReceived": "Viestejä vastaanotettu",
+ "statisticsMessagesStored": "Viestejä tallennettu",
+ "statisticsGeocoder": "Geokoodauspyyntöjä",
+ "statisticsGeolocation": "Sijaintipyyntöjä",
+ "categoryArrow": "Nuoli",
+ "categoryDefault": "Oletus",
+ "categoryAnimal": "Eläin",
+ "categoryBicycle": "Polkupyörä",
+ "categoryBoat": "Vene",
+ "categoryBus": "Bussi",
+ "categoryCar": "Auto",
+ "categoryCamper": "Matkailuajoneuvo",
+ "categoryCrane": "Nosturi",
+ "categoryHelicopter": "Helikopteri",
+ "categoryMotorcycle": "Moottoripyörä",
+ "categoryOffroad": "Maastoajoneuvo",
+ "categoryPerson": "Henkilö",
+ "categoryPickup": "Lava-auto",
+ "categoryPlane": "Lentokone",
+ "categoryShip": "Laiva",
+ "categoryTractor": "Traktori",
+ "categoryTrain": "Juna",
+ "categoryTram": "Raitiovaunu",
+ "categoryTrolleybus": "Johdinauto",
+ "categoryTruck": "Kuorma-auto",
+ "categoryVan": "Pakettiauto",
+ "categoryScooter": "Potkulauta",
+ "maintenanceStart": "Alku",
+ "maintenancePeriod": "Huoltoväli"
+} \ No newline at end of file
diff --git a/src/resources/l10n/fr.json b/src/resources/l10n/fr.json
new file mode 100644
index 00000000..1e9147ae
--- /dev/null
+++ b/src/resources/l10n/fr.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "Chargement...",
+ "sharedHide": "Cacher",
+ "sharedSave": "Enregistrer",
+ "sharedUpload": "Upload",
+ "sharedSet": "Régler",
+ "sharedCancel": "Annuler",
+ "sharedCopy": "Copy",
+ "sharedAdd": "Ajouter",
+ "sharedEdit": "Editer",
+ "sharedRemove": "Effacer",
+ "sharedRemoveConfirm": "Effacer objet?",
+ "sharedNoData": "Aucune donnée",
+ "sharedSubject": "Subject",
+ "sharedYes": "Oui",
+ "sharedNo": "Non",
+ "sharedKm": "km",
+ "sharedMi": "mi",
+ "sharedNmi": "nmi",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "nd",
+ "sharedKmh": "km/h",
+ "sharedMph": "mph",
+ "sharedHour": "Heure",
+ "sharedMinute": "Minute",
+ "sharedSecond": "Seconde",
+ "sharedDays": "jours",
+ "sharedHours": "heures",
+ "sharedMinutes": "minutes",
+ "sharedDecimalDegrees": "Degrés Décimaux",
+ "sharedDegreesDecimalMinutes": "Degrés Décimaux Minutes",
+ "sharedDegreesMinutesSeconds": "Degrés Minutes Secondes",
+ "sharedName": "Nom",
+ "sharedDescription": "Description",
+ "sharedSearch": "Recherche",
+ "sharedIconScale": "Echelle des icônes",
+ "sharedGeofence": "Périmètre virtuel",
+ "sharedGeofences": "Périmètres virtuels",
+ "sharedCreateGeofence": "Créé un périmètre virtuel",
+ "sharedNotifications": "Notifications",
+ "sharedNotification": "Notification",
+ "sharedAttributes": "Attributs",
+ "sharedAttribute": "Attribut",
+ "sharedDrivers": "Pilotes",
+ "sharedDriver": "Pilote",
+ "sharedArea": "Aire",
+ "sharedSound": "Son de notification",
+ "sharedType": "Type",
+ "sharedDistance": "Distance",
+ "sharedHourAbbreviation": "h",
+ "sharedMinuteAbbreviation": "m",
+ "sharedSecondAbbreviation": "s",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "l",
+ "sharedGallonAbbreviation": "gal",
+ "sharedLiter": "Litre",
+ "sharedImpGallon": "Gallon impérial",
+ "sharedUsGallon": "Gallon US",
+ "sharedLiterPerHourAbbreviation": "l/h",
+ "sharedGetMapState": "État de la carte",
+ "sharedComputedAttribute": "Attribut calculé",
+ "sharedComputedAttributes": "Attributs calculés",
+ "sharedCheckComputedAttribute": "Verifier l'attribut calculé",
+ "sharedExpression": "Expression",
+ "sharedDevice": "Appareil",
+ "sharedTest": "Test",
+ "sharedTestNotification": "Test d'envoi de notification",
+ "sharedTestNotificators": "Test canaux",
+ "sharedTestExpression": "Test expression",
+ "sharedCalendar": "Calendrier",
+ "sharedCalendars": "Calendriers",
+ "sharedFile": "Fichier",
+ "sharedSearchDevices": "Recherche appareils",
+ "sharedSortBy": "Trier par",
+ "sharedFilterMap": "Filtrer sur la carte",
+ "sharedSelectFile": "Sélectionner un fichier",
+ "sharedPhone": "Téléphone",
+ "sharedRequired": "Requis",
+ "sharedPreferences": "Préférences",
+ "sharedPermissions": "Permissions",
+ "sharedConnections": "Connexions",
+ "sharedExtra": "Extra",
+ "sharedPrimary": "Primaire",
+ "sharedSecondary": "Secondaire",
+ "sharedTypeString": "Chaîne de caractères",
+ "sharedTypeNumber": "Nombre",
+ "sharedTypeBoolean": "Booléen",
+ "sharedTimezone": "Fuseau horaire",
+ "sharedInfoTitle": "Information",
+ "sharedSavedCommand": "Commande sauvegardée",
+ "sharedSavedCommands": "Commandes sauvegardées",
+ "sharedNew": "Nouveau",
+ "sharedShowAddress": "Montrer adresse",
+ "sharedShowDetails": "Plus de détails",
+ "sharedDisabled": "Désactivé",
+ "sharedMaintenance": "Entretien",
+ "sharedDeviceAccumulators": "Accumulateurs",
+ "sharedAlarms": "Alarmes",
+ "sharedLocation": "Localisation",
+ "sharedImport": "Importer",
+ "sharedColumns": "Colonnes",
+ "sharedDropzoneText": "Glissez et déposez un fichier ici ou cliquez",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "Simple",
+ "calendarRecurrence": "Récurrence",
+ "calendarOnce": "Une fois",
+ "calendarDaily": "Journalier",
+ "calendarWeekly": "Hebdomadaire",
+ "calendarMonthly": "Mensuel",
+ "calendarDays": "Jours",
+ "calendarSunday": "Dimanche",
+ "calendarMonday": "Lundi",
+ "calendarTuesday": "Mardi",
+ "calendarWednesday": "Mercredi",
+ "calendarThursday": "Jeudi",
+ "calendarFriday": "Vendredi",
+ "calendarSaturday": "Samedi",
+ "attributeShowGeofences": "Afficher les périmètres virtuels",
+ "attributeSpeedLimit": "Limite de vitesse",
+ "attributeFuelDropThreshold": "Seuil perte de carburant",
+ "attributeFuelIncreaseThreshold": "Seuil ajout de carburant",
+ "attributePolylineDistance": "Distance polyligne",
+ "attributeReportIgnoreOdometer": "Rapport: Ignorer l'odomètre",
+ "attributeWebReportColor": "Web: couleur du rapport",
+ "attributeDevicePassword": "Mot de passe de l'appareil",
+ "attributeDeviceImage": "Image de l'appareil",
+ "attributeDeviceInactivityStart": "Début d'inactivité de l'appareil",
+ "attributeDeviceInactivityPeriod": "Période d'inactivité de l'appareil",
+ "attributeProcessingCopyAttributes": "En cours: Copie des attributs",
+ "attributeColor": "Couleur",
+ "attributeWebLiveRouteLength": "Web: Longueur de route en temps réel",
+ "attributeWebSelectZoom": "Web: Zoomer à la sélection",
+ "attributeWebMaxZoom": "Web: Zoom maximum",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Pushover Clé Utilisateur",
+ "attributePushoverDeviceNames": "Pushover Noms d'appareils",
+ "attributeMailSmtpHost": "Mail: Hôte SMTP",
+ "attributeMailSmtpPort": "Mail: Port SMTP",
+ "attributeMailSmtpStarttlsEnable": "Mail: Activer SMTP STARTTLS",
+ "attributeMailSmtpStarttlsRequired": "Mail: Requérir SMTP STARTTLS",
+ "attributeMailSmtpSslEnable": "Mail: Activer SMTP SSL",
+ "attributeMailSmtpSslTrust": "Mail: Trust SMTP SSL",
+ "attributeMailSmtpSslProtocols": "Mail: Protocoles SMTP SSL",
+ "attributeMailSmtpFrom": "Mail: SMTP From",
+ "attributeMailSmtpAuth": "Mail: Activer l'authentification",
+ "attributeMailSmtpUsername": "Mail: Nom d'utilisateur",
+ "attributeMailSmtpPassword": "Mail: Mot de passe",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: Désactiver les attributs",
+ "attributeUiDisableGroups": "UI: Désactiver groupes",
+ "attributeUiDisableEvents": "UI: Désactiver évènements",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "UI: Désactiver les conducteurs",
+ "attributeUiDisableComputedAttributes": "UI: Désactiver les attributs calculés",
+ "attributeUiDisableCalendars": "UI: Désactiver les calendiers",
+ "attributeUiDisableMaintenance": "UI: Désactiver l'entretien",
+ "attributeUiHidePositionAttributes": "UI: Cacher Attributs de Position",
+ "attributeUiDisableLoginLanguage": "UI: Désactiver choix des languges",
+ "attributeNotificationTokens": "Jeton de notification",
+ "attributePopupInfo": "Informations dans popup",
+ "errorTitle": "Erreur",
+ "errorGeneral": "Paramètres invalides ou violation de contrainte",
+ "errorConnection": "Erreur de connexion",
+ "errorSocket": "Erreur de connexion au socket web",
+ "errorZero": "Ne peut être zéro",
+ "userEmail": "Email",
+ "userPassword": "Mot de Passe",
+ "userAdmin": "Admin",
+ "userRemember": "Rappel",
+ "userExpirationTime": "Expiration",
+ "userDeviceLimit": "Limite de l'appareil",
+ "userUserLimit": "Limite d'utilisateurs",
+ "userDeviceReadonly": "Appareil - Lecture seule",
+ "userLimitCommands": "Commandes de limite",
+ "userDisableReports": "Désactiver les rapports",
+ "userFixedEmail": "Changement d'email bloqué",
+ "userToken": "Jeton",
+ "userDeleteAccount": "Supprimer le compte",
+ "userTemporary": "Temporary",
+ "loginTitle": "Identification",
+ "loginLanguage": "Langue",
+ "loginReset": "Réinitialiser le mot de passe",
+ "loginRegister": "Inscription",
+ "loginLogin": "Se connecter",
+ "loginOpenId": "Login with OpenID",
+ "loginFailed": "Adresse email ou mot de passe incorrect",
+ "loginCreated": "Nouvel utilisateur enregistré",
+ "loginResetSuccess": "Vérifiez vos emails",
+ "loginUpdateSuccess": "Le nouveau mot de passe est défini",
+ "loginLogout": "Déconnexion",
+ "loginLogo": "Logo",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "Appareils et État",
+ "deviceSelected": "Appareil sélectionné",
+ "deviceTitle": "Appareils",
+ "devicePrimaryInfo": "Titre de l'appareil",
+ "deviceSecondaryInfo": "Détail de l'appareil",
+ "deviceIdentifier": "Identifiant",
+ "deviceModel": "Modèle",
+ "deviceContact": "Contact",
+ "deviceCategory": "Catégorie",
+ "deviceLastUpdate": "Dernière mise à jour",
+ "deviceCommand": "Commande",
+ "deviceFollow": "Suivre",
+ "deviceTotalDistance": "Distance totale",
+ "deviceStatus": "Statut",
+ "deviceStatusOnline": "En ligne",
+ "deviceStatusOffline": "Hors ligne",
+ "deviceStatusUnknown": "Inconnu",
+ "deviceRegisterFirst": "Enregistrez votre premier appareil",
+ "deviceIdentifierHelp": "IMEI, numéro de série ou autre id. Cela doit correspondre à l'identifiant avec lequel l'appareil envoie ses données au serveur.",
+ "deviceShare": "Share Device",
+ "groupDialog": "Groupe",
+ "groupParent": "Groupe",
+ "groupNoGroup": "Sans groupe",
+ "settingsTitle": "Paramètres",
+ "settingsUser": "Compte",
+ "settingsGroups": "Groupes",
+ "settingsServer": "Serveur",
+ "settingsUsers": "Utilisateurs",
+ "settingsDistanceUnit": "Unité de distance",
+ "settingsAltitudeUnit": "Unité d'altitude",
+ "settingsSpeedUnit": "Unité de vitesse",
+ "settingsVolumeUnit": "Unité de volume",
+ "settingsTwelveHourFormat": "Format d'heure - 12 heures",
+ "settingsCoordinateFormat": "Format des coordonées",
+ "settingsServerVersion": "Version serveur",
+ "settingsAppVersion": "Version application",
+ "settingsConnection": "Connexion",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "Rapports",
+ "reportScheduled": "Rapports programmés",
+ "reportDevice": "Appareil",
+ "reportGroup": "Groupe",
+ "reportFrom": "De",
+ "reportTo": "À",
+ "reportShow": "Afficher",
+ "reportClear": "Effacer",
+ "linkGoogleMaps": "Google Maps",
+ "linkAppleMaps": "Apple Plans",
+ "linkStreetView": "Street View",
+ "positionFixTime": "Horodatage",
+ "positionDeviceTime": "Heure de l'appareil",
+ "positionServerTime": "Heure du serveur",
+ "positionValid": "Valide",
+ "positionAccuracy": "Précision",
+ "positionLatitude": "Latitude",
+ "positionLongitude": "Longitude",
+ "positionAltitude": "Altitude",
+ "positionSpeed": "Vitesse",
+ "positionCourse": "Orientation",
+ "positionAddress": "Adresse",
+ "positionProtocol": "Protocole",
+ "positionDistance": "Distance",
+ "positionRpm": "t/m",
+ "positionFuel": "Carburant",
+ "positionPower": "Courant",
+ "positionBattery": "Batterie",
+ "positionRaw": "Brut",
+ "positionIndex": "Index",
+ "positionHdop": "Dilution de précision horizontale",
+ "positionVdop": "Dilution de précision verticale",
+ "positionPdop": "Dilution de précision de position",
+ "positionSat": "Satellites",
+ "positionSatVisible": "Satellites visibles",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "Itinérance",
+ "positionEvent": "Évènement",
+ "positionAlarm": "Alarme",
+ "positionStatus": "Statut",
+ "positionOdometer": "Odomètre",
+ "positionServiceOdometer": "Odomètre d'entretien",
+ "positionTripOdometer": "Odomètre de voyage",
+ "positionHours": "Heures",
+ "positionSteps": "Etapes",
+ "positionInput": "Entrée de données",
+ "positionHeartRate": "Heart Rate",
+ "positionOutput": "Sortie de données",
+ "positionBatteryLevel": "Niveau de batterie",
+ "positionFuelConsumption": "Consommation de carburant",
+ "positionRfid": "RFID",
+ "positionVersionFw": "Version du micrologiciel",
+ "positionVersionHw": "Version du matériel",
+ "positionIgnition": "Allumage",
+ "positionFlags": "Drapeau",
+ "positionCharge": "Charge",
+ "positionIp": "IP",
+ "positionArchive": "Archive",
+ "positionVin": "Numéro de chassis",
+ "positionApproximate": "Approximatif",
+ "positionThrottle": "Accélérateur",
+ "positionMotion": "Mouvement",
+ "positionArmed": "Armé",
+ "positionAcceleration": "Accélération",
+ "positionTemp": "Température",
+ "positionDeviceTemp": "Température de l'appareil",
+ "positionCoolantTemp": "Coolant Temperature",
+ "positionOperator": "Opérateur",
+ "positionCommand": "Commande",
+ "positionBlocked": "Bloqué",
+ "positionDtcs": "Code erreur",
+ "positionObdSpeed": "Vitesse OBD",
+ "positionObdOdometer": "Odomètre OBD",
+ "positionDrivingTime": "Driving Time",
+ "positionDriverUniqueId": "ID unique du pilote",
+ "positionCard": "Card",
+ "positionImage": "Image",
+ "positionVideo": "Vidéo",
+ "positionAudio": "Audio",
+ "serverTitle": "Paramètres de serveur",
+ "serverZoom": "Zoom",
+ "serverRegistration": "Inscription",
+ "serverReadonly": "Lecture seule",
+ "serverForceSettings": "Forcer les paramètres",
+ "serverAnnouncement": "Avis",
+ "serverName": "Server Name",
+ "serverDescription": "Server Description",
+ "serverColorPrimary": "Color Primary",
+ "serverColorSecondary": "Color Secondary",
+ "serverLogo": "Logo Image",
+ "serverLogoInverted": "Inverted Logo Image",
+ "serverChangeDisable": "Disable Server Change",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "Carte",
+ "mapActive": "Cartes actives",
+ "mapOverlay": "Superposition de cartes",
+ "mapOverlayCustom": "Superposition personnalisée",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather Clé d'API",
+ "mapOpenWeatherClouds": "OpenWeather Clouds",
+ "mapOpenWeatherPrecipitation": "OpenWeather Précipitations",
+ "mapOpenWeatherPressure": "OpenWeather Pression",
+ "mapOpenWeatherWind": "OpenWeather Vents",
+ "mapOpenWeatherTemperature": "OpenWeather Températures",
+ "mapLayer": "Couche cartographique",
+ "mapCustom": "Personnalisé (XYZ)",
+ "mapCustomArcgis": "Personnalisé (ArcGIS)",
+ "mapCustomLabel": "Carte Personnalisée",
+ "mapCarto": "Cartographie Basemaps",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Bing Maps Clé d'API",
+ "mapBingRoad": "Bing Maps Road",
+ "mapBingAerial": "Bing Maps Aerial",
+ "mapBingHybrid": "Bing Maps Hybride",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Yandex Map",
+ "mapYandexSat": "Yandex Satelitte",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox Streets",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox Outdoors",
+ "mapMapboxSatellite": "Mapbox Satellite",
+ "mapMapboxKey": "Mapbox Jeton d'Accès",
+ "mapMapTilerBasic": "MapTiler Basic",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "MapTiler Clé d'API",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ Jeton d'Accès",
+ "mapTomTomBasic": "TomTom Basic",
+ "mapTomTomFlow": "TomTom Traffic Flow",
+ "mapTomTomIncidents": "TomTom Traffic Incidents",
+ "mapTomTomKey": "TomTom Clé d'API",
+ "mapHereBasic": "Here Basic",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satellite",
+ "mapHereFlow": "Here Traffic Flow",
+ "mapHereKey": "Here Clé d'API",
+ "mapShapePolygon": "Polygone",
+ "mapShapeCircle": "Cercle",
+ "mapShapePolyline": "Polyligne",
+ "mapLiveRoutes": "Routes en direct",
+ "mapDirection": "Montre la direction",
+ "mapCurrentLocation": "Localisation actuelle",
+ "mapPoiLayer": "Couche Point d'intérêt",
+ "mapClustering": "Regroupement des marqueurs",
+ "mapOnSelect": "Afficher la carte lors de la sélection",
+ "mapDefault": "Carte par défaut",
+ "stateTitle": "État",
+ "stateName": "Paramètre",
+ "stateValue": "Valeur",
+ "commandTitle": "Commande",
+ "commandSend": "Envoyer",
+ "commandSent": "Commande envoyée",
+ "commandQueued": "Commande mise en queue",
+ "commandUnit": "Unité",
+ "commandCustom": "Commande personnalisée",
+ "commandDeviceIdentification": "Identification de l'appareil",
+ "commandPositionSingle": "Rapport de position unique",
+ "commandPositionPeriodic": "Périodicité du rapport de position",
+ "commandPositionStop": "Arrêt du rapport de position",
+ "commandEngineStop": "Arrêt moteur",
+ "commandEngineResume": "Démarrage moteur",
+ "commandAlarmArm": "Activer l'alarme",
+ "commandAlarmDisarm": "Désactiver l'alarme",
+ "commandAlarmDismiss": "Ignorer l'alarme",
+ "commandSetTimezone": "Régler le fuseau horaire",
+ "commandRequestPhoto": "Demander une photo",
+ "commandPowerOff": "Eteindre l'appareil",
+ "commandRebootDevice": "Redémarrer l'appareil",
+ "commandFactoryReset": "Réinitialisation d'usine",
+ "commandSendSms": "Envoyer un SMS",
+ "commandSendUssd": "Envoyer un USSD",
+ "commandSosNumber": "Régler le n° SOS",
+ "commandSilenceTime": "Définir le temps de silence",
+ "commandSetPhonebook": "Définir l'annuaire",
+ "commandVoiceMessage": "Message vocal",
+ "commandOutputControl": "Contrôle de la sortie",
+ "commandVoiceMonitoring": "Moniteur vocal",
+ "commandSetAgps": "Fixer l'AGPS",
+ "commandSetIndicator": "Définir l'indicateur",
+ "commandConfiguration": "Configuration",
+ "commandGetVersion": "Acquérir la version",
+ "commandFirmwareUpdate": "Mettre à jour le firmware",
+ "commandSetConnection": "Fixer la connexion",
+ "commandSetOdometer": "Fixer l'odomètre",
+ "commandGetModemStatus": "Acquérir le statut du modem",
+ "commandGetDeviceStatus": "Acquérir le statut du périphérique",
+ "commandSetSpeedLimit": "Définir la limite de vitesse",
+ "commandModePowerSaving": "Mode économie d'énergie",
+ "commandModeDeepSleep": "Mode veille profonde",
+ "commandAlarmGeofence": "Définir l'alarme périmètre virtuel",
+ "commandAlarmBattery": "Définir l'alarme de batterie",
+ "commandAlarmSos": "Définir l'alarme SOS",
+ "commandAlarmRemove": "Définir l'effacement des alarmes",
+ "commandAlarmClock": "Définir l'alarme de l'horloge",
+ "commandAlarmSpeed": "Définir l'alarme de vitesse",
+ "commandAlarmFall": "Définir l'alarme de chute",
+ "commandAlarmVibration": "Définir l'alarme de vibration",
+ "commandFrequency": "Fréquence",
+ "commandTimezone": "Décalage horaire",
+ "commandMessage": "Message",
+ "commandRadius": "Rayon",
+ "commandEnable": "Activer",
+ "commandData": "Données",
+ "commandIndex": "Index",
+ "commandPhone": "Numéro de téléphone",
+ "commandServer": "Serveur",
+ "commandPort": "Port",
+ "eventAll": "Tous les événements",
+ "eventDeviceOnline": "Statut: en ligne",
+ "eventDeviceUnknown": "Statut: inconnu",
+ "eventDeviceOffline": "Statut: hors ligne",
+ "eventDeviceInactive": "Appareil inactif",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "Appareil en mouvement",
+ "eventDeviceStopped": "Appareil arrêté",
+ "eventDeviceOverspeed": "Vitesse limite dépassée",
+ "eventDeviceFuelDrop": "Perte de carburant",
+ "eventDeviceFuelIncrease": "Ajout de carburant",
+ "eventCommandResult": "Résultat de la commande",
+ "eventGeofenceEnter": "Entrée dans le périmètre virtuel",
+ "eventGeofenceExit": "Sortie du périmètre virtuel",
+ "eventAlarm": "Alarme",
+ "eventIgnitionOn": "Contact mis",
+ "eventIgnitionOff": "Contact coupé",
+ "eventMaintenance": "Entretien requis",
+ "eventTextMessage": "Message textuel reçu",
+ "eventDriverChanged": "Changement de conducteur",
+ "eventMedia": "Média",
+ "eventsScrollToLast": "Faire défiler jusqu'à la fin",
+ "eventsSoundEvents": "Son d'événements",
+ "eventsSoundAlarms": "Son d'alarmes",
+ "alarmGeneral": "Général",
+ "alarmSos": "SOS",
+ "alarmVibration": "Vibration",
+ "alarmMovement": "Mouvement",
+ "alarmLowspeed": "Basse vitesse",
+ "alarmOverspeed": "Vitesse trop haute",
+ "alarmFallDown": "Chute",
+ "alarmLowPower": "Tension basse",
+ "alarmLowBattery": "Batterie basse",
+ "alarmFault": "Défaut",
+ "alarmPowerOff": "Eteindre",
+ "alarmPowerOn": "Allumer",
+ "alarmDoor": "Porte",
+ "alarmLock": "Verrouiller",
+ "alarmUnlock": "Déverrouiller",
+ "alarmGeofence": "Périmètre virtuel",
+ "alarmGeofenceEnter": "Entrée dans le périmètre virtuel",
+ "alarmGeofenceExit": "Sortie du périmètre virtuel",
+ "alarmGpsAntennaCut": "Antenne GPS coupée",
+ "alarmAccident": "Accident",
+ "alarmTow": "Remorquage",
+ "alarmIdle": "Au repos",
+ "alarmHighRpm": "Tours par minute élevés",
+ "alarmHardAcceleration": "Accélération brusque",
+ "alarmHardBraking": "Freinage brusque",
+ "alarmHardCornering": "Virage sec",
+ "alarmLaneChange": "Changement de bande",
+ "alarmFatigueDriving": "Conduite en état de fatigue",
+ "alarmPowerCut": "Courant coupé",
+ "alarmPowerRestored": "Courant restauré",
+ "alarmJamming": "Brouillage",
+ "alarmTemperature": "Température",
+ "alarmParking": "Parking",
+ "alarmBonnet": "Capot",
+ "alarmFootBrake": "Frein à pied",
+ "alarmFuelLeak": "Fuite de carburant",
+ "alarmTampering": "Altération",
+ "alarmRemoving": "Retrait",
+ "notificationType": "Type de notification",
+ "notificationAlways": "Tous les appareils",
+ "notificationNotificators": "Canaux",
+ "notificatorCommand": "Command",
+ "notificatorWeb": "Web",
+ "notificatorMail": "Mail",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Replay",
+ "reportCombined": "Combinés",
+ "reportRoute": "Route",
+ "reportEvents": "Évènements",
+ "reportTrips": "Trajets",
+ "reportStops": "Arrêts",
+ "reportSummary": "Résumé",
+ "reportDaily": "Rapport journalier",
+ "reportChart": "Graphique",
+ "reportConfigure": "Configurer",
+ "reportEventTypes": "Types d'évènements",
+ "reportChartType": "Type de graphique",
+ "reportShowMarkers": "Montrer les marqueurs",
+ "reportExport": "Exporter",
+ "reportEmail": "Rapport par email",
+ "reportSchedule": "Programme",
+ "reportPeriod": "Période",
+ "reportCustom": "Personnalisé",
+ "reportToday": "Aujourd'hui",
+ "reportYesterday": "Hier",
+ "reportThisWeek": "Cette semaine",
+ "reportPreviousWeek": "La semaine dernière",
+ "reportThisMonth": "Ce mois-ci",
+ "reportPreviousMonth": "Le mois dernier",
+ "reportDeviceName": "Nom de l'appareil",
+ "reportAverageSpeed": "Vitesse moyenne",
+ "reportMaximumSpeed": "Vitesse maximum",
+ "reportEngineHours": "Heures du moteur",
+ "reportDuration": "Durée",
+ "reportStartDate": "Date de départ",
+ "reportStartTime": "Heure de départ",
+ "reportStartAddress": "Adresse de départ",
+ "reportEndTime": "Date de fin",
+ "reportEndAddress": "Adresse de fin",
+ "reportSpentFuel": "Consommation de carburant",
+ "reportStartOdometer": "Départ de l'odomètre",
+ "reportEndOdometer": "Arrêt de l'odomètre",
+ "statisticsTitle": "Statistiques",
+ "statisticsCaptureTime": "Heure de capture",
+ "statisticsActiveUsers": "Utilisateurs actifs",
+ "statisticsActiveDevices": "Appareils actifs",
+ "statisticsRequests": "Requêtes",
+ "statisticsMessagesReceived": "Messages reçus",
+ "statisticsMessagesStored": "Messages stockés",
+ "statisticsGeocoder": "Requêtes de géocodage",
+ "statisticsGeolocation": "Requêtes de géolocalisation",
+ "categoryArrow": "Flèche",
+ "categoryDefault": "Par défaut",
+ "categoryAnimal": "Animal",
+ "categoryBicycle": "Vélo",
+ "categoryBoat": "Bateau",
+ "categoryBus": "Bus",
+ "categoryCar": "Voiture",
+ "categoryCamper": "Camper",
+ "categoryCrane": "Grue",
+ "categoryHelicopter": "Hélicoptère",
+ "categoryMotorcycle": "Moto",
+ "categoryOffroad": "Véhicule tout-terrain",
+ "categoryPerson": "Personne",
+ "categoryPickup": "Pickup",
+ "categoryPlane": "Avion",
+ "categoryShip": "Bateau",
+ "categoryTractor": "Tracteur",
+ "categoryTrain": "Train",
+ "categoryTram": "Tram",
+ "categoryTrolleybus": "Trolleybus",
+ "categoryTruck": "Camion",
+ "categoryVan": "Van",
+ "categoryScooter": "Scooter",
+ "maintenanceStart": "Départ",
+ "maintenancePeriod": "Période"
+} \ No newline at end of file
diff --git a/src/resources/l10n/gl.json b/src/resources/l10n/gl.json
new file mode 100644
index 00000000..8464e7bb
--- /dev/null
+++ b/src/resources/l10n/gl.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "Cargando...",
+ "sharedHide": "Agochar",
+ "sharedSave": "Gardar",
+ "sharedUpload": "Upload",
+ "sharedSet": "Establecer",
+ "sharedCancel": "Cancelar",
+ "sharedCopy": "Copy",
+ "sharedAdd": "Engadir",
+ "sharedEdit": "Editar",
+ "sharedRemove": "Eliminar",
+ "sharedRemoveConfirm": "Eliminar elemento?",
+ "sharedNoData": "Sen datos",
+ "sharedSubject": "Subject",
+ "sharedYes": "Si",
+ "sharedNo": "Non",
+ "sharedKm": "Km",
+ "sharedMi": "mi",
+ "sharedNmi": "nmi",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "Nós",
+ "sharedKmh": "Km/h",
+ "sharedMph": "mph",
+ "sharedHour": "Hora",
+ "sharedMinute": "Minuto",
+ "sharedSecond": "Segundo",
+ "sharedDays": "días",
+ "sharedHours": "horas",
+ "sharedMinutes": "minutos",
+ "sharedDecimalDegrees": "Graos",
+ "sharedDegreesDecimalMinutes": "Minutos",
+ "sharedDegreesMinutesSeconds": "Segundos",
+ "sharedName": "Nome",
+ "sharedDescription": "Descrición",
+ "sharedSearch": "Buscar",
+ "sharedIconScale": "Escala das iconas",
+ "sharedGeofence": "Xeocerca",
+ "sharedGeofences": "Xeocercas",
+ "sharedCreateGeofence": "Crear xeocerca",
+ "sharedNotifications": "Notificacións",
+ "sharedNotification": "Notificación",
+ "sharedAttributes": "Atributos",
+ "sharedAttribute": "Atributo",
+ "sharedDrivers": "Condutores",
+ "sharedDriver": "Condutor",
+ "sharedArea": "Área",
+ "sharedSound": "Son de notificación",
+ "sharedType": "Tipo",
+ "sharedDistance": "Distancia",
+ "sharedHourAbbreviation": "h",
+ "sharedMinuteAbbreviation": "m",
+ "sharedSecondAbbreviation": "s",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "l",
+ "sharedGallonAbbreviation": "gal",
+ "sharedLiter": "Litro",
+ "sharedImpGallon": "Imp. Galón",
+ "sharedUsGallon": "Galón dos EUA",
+ "sharedLiterPerHourAbbreviation": "l/h",
+ "sharedGetMapState": "Obter o estado do mapa",
+ "sharedComputedAttribute": "Atributo calculado",
+ "sharedComputedAttributes": "Atributos calculados",
+ "sharedCheckComputedAttribute": "Revisar atributo calculado",
+ "sharedExpression": "Expresión",
+ "sharedDevice": "Dispositivo",
+ "sharedTest": "Probar",
+ "sharedTestNotification": "Enviar notificación de proba",
+ "sharedTestNotificators": "Probar Canles",
+ "sharedTestExpression": "Probar expresión",
+ "sharedCalendar": "Calendario",
+ "sharedCalendars": "Calendarios",
+ "sharedFile": "Arquivo",
+ "sharedSearchDevices": "Buscar dispositivos",
+ "sharedSortBy": "Ordenar por",
+ "sharedFilterMap": "Filtrar no mapa",
+ "sharedSelectFile": "Seleccione Arquivo",
+ "sharedPhone": "Teléfono",
+ "sharedRequired": "Obrigatorio",
+ "sharedPreferences": "Preferencias",
+ "sharedPermissions": "Permisos",
+ "sharedConnections": "Conexións",
+ "sharedExtra": "Extra",
+ "sharedPrimary": "Principal",
+ "sharedSecondary": "Secundario",
+ "sharedTypeString": "Cadena",
+ "sharedTypeNumber": "Número",
+ "sharedTypeBoolean": "Booleano",
+ "sharedTimezone": "Fuso horario",
+ "sharedInfoTitle": "Información",
+ "sharedSavedCommand": "Comando gardado",
+ "sharedSavedCommands": "Comandos gardados",
+ "sharedNew": "Novo…",
+ "sharedShowAddress": "Mostrar enderezo",
+ "sharedShowDetails": "Máis detalles",
+ "sharedDisabled": "Desactivado",
+ "sharedMaintenance": "Mantemento",
+ "sharedDeviceAccumulators": "Acumuladores",
+ "sharedAlarms": "Alarmas",
+ "sharedLocation": "Localización",
+ "sharedImport": "Importar",
+ "sharedColumns": "Columnas",
+ "sharedDropzoneText": "Arrastra e solta un arquivo aquí ou fai clic",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "Simple",
+ "calendarRecurrence": "Recorrencia",
+ "calendarOnce": "Unha vez",
+ "calendarDaily": "A diario",
+ "calendarWeekly": "Semanal",
+ "calendarMonthly": "Mensual",
+ "calendarDays": "Días",
+ "calendarSunday": "Domingo",
+ "calendarMonday": "Luns",
+ "calendarTuesday": "martes",
+ "calendarWednesday": "Mércores",
+ "calendarThursday": "Xoves",
+ "calendarFriday": "Venres",
+ "calendarSaturday": "Sábado",
+ "attributeShowGeofences": "Mostrar xeocercas",
+ "attributeSpeedLimit": "Límite de velocidade",
+ "attributeFuelDropThreshold": "Limiar de perda de combustible",
+ "attributeFuelIncreaseThreshold": "Umbral de subida de combustible",
+ "attributePolylineDistance": "Distancia de poliliña",
+ "attributeReportIgnoreOdometer": "Informe: ignorar o odómetro",
+ "attributeWebReportColor": "Web: Cor de informe",
+ "attributeDevicePassword": "Contrasinal do dispositivo",
+ "attributeDeviceImage": "Imaxe do dispositivo",
+ "attributeDeviceInactivityStart": "Inicio da inactividade do dispositivo",
+ "attributeDeviceInactivityPeriod": "Período de inactividade do dispositivo",
+ "attributeProcessingCopyAttributes": "Procesamento: Copia de atributos",
+ "attributeColor": "Cor",
+ "attributeWebLiveRouteLength": "Web: Lonxitude da ruta en directo",
+ "attributeWebSelectZoom": "Web: Zoom na selección",
+ "attributeWebMaxZoom": "Web: Zoom máximo",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Pushover User Key",
+ "attributePushoverDeviceNames": "Pushover nomes de dispositivos",
+ "attributeMailSmtpHost": "Correo: Servidor SMTP",
+ "attributeMailSmtpPort": "Correo: porto SMTP",
+ "attributeMailSmtpStarttlsEnable": "Correo: Activar SMTP STARTTLS",
+ "attributeMailSmtpStarttlsRequired": "Correo: SMTP STARTTLS necesario",
+ "attributeMailSmtpSslEnable": "Correo: Activar SMTP SSL",
+ "attributeMailSmtpSslTrust": "Correo: SMTP SSL de confianza",
+ "attributeMailSmtpSslProtocols": "Correo: Protocolos SMTP SSL",
+ "attributeMailSmtpFrom": "Correo: SMTP De",
+ "attributeMailSmtpAuth": "Correo: Activar autenticación SMTP",
+ "attributeMailSmtpUsername": "Correo: Nome de usuario SMTP",
+ "attributeMailSmtpPassword": "Correo: Contrasinal SMTP",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "IU: Desactivar atributos",
+ "attributeUiDisableGroups": "IU: Desactivar grupos",
+ "attributeUiDisableEvents": "IU: Desactivar eventos",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "IU: Desactivar condutores",
+ "attributeUiDisableComputedAttributes": "IU: Desactivar atributos calculados",
+ "attributeUiDisableCalendars": "IU: desactivar os calendarios",
+ "attributeUiDisableMaintenance": "IU: Desactivar o mantemento",
+ "attributeUiHidePositionAttributes": "IU: ocultar atributos de posición",
+ "attributeUiDisableLoginLanguage": "IU: desactivar o idioma de inicio de sesión",
+ "attributeNotificationTokens": "Tokens de notificación",
+ "attributePopupInfo": "Información emerxente",
+ "errorTitle": "Erro",
+ "errorGeneral": "Parámetros non válidos ou violación das restricións",
+ "errorConnection": "Erro de conexión",
+ "errorSocket": "Erro de conexión do socket web",
+ "errorZero": "Non pode ser cero",
+ "userEmail": "Email",
+ "userPassword": "Contrasinal",
+ "userAdmin": "Administrador",
+ "userRemember": "Recordar",
+ "userExpirationTime": "Caducidade",
+ "userDeviceLimit": "Límite de dispositivos",
+ "userUserLimit": "Límite de usuarios",
+ "userDeviceReadonly": "Dispositivo só de lectura",
+ "userLimitCommands": "Limitar comandos",
+ "userDisableReports": "Desactivar informes",
+ "userFixedEmail": "Sen cambio de correo electrónico",
+ "userToken": "Token Acceso",
+ "userDeleteAccount": "Eliminar conta",
+ "userTemporary": "Temporary",
+ "loginTitle": "Iniciar sesión",
+ "loginLanguage": "Idioma",
+ "loginReset": "Restablecer o contrasinal",
+ "loginRegister": "Rexistrarse",
+ "loginLogin": "Iniciar sesión",
+ "loginOpenId": "Iniciar sesión con OpenID",
+ "loginFailed": "Enderezo de correo electrónico ou contrasinal incorrectos",
+ "loginCreated": "Rexistrouse un novo usuario",
+ "loginResetSuccess": "Comprobe o seu correo electrónico",
+ "loginUpdateSuccess": "Establécese un novo contrasinal",
+ "loginLogout": "Pechar sesión",
+ "loginLogo": "Logotipo",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "Dispositivos e estado",
+ "deviceSelected": "Dispositivo seleccionado",
+ "deviceTitle": "Dispositivos",
+ "devicePrimaryInfo": "Título do dispositivo",
+ "deviceSecondaryInfo": "Detalle do dispositivo",
+ "deviceIdentifier": "Identificador",
+ "deviceModel": "Modelo",
+ "deviceContact": "Contacto",
+ "deviceCategory": "Categoría",
+ "deviceLastUpdate": "Última actualización",
+ "deviceCommand": "Comando",
+ "deviceFollow": "Seguir",
+ "deviceTotalDistance": "Distancia total",
+ "deviceStatus": "Estado",
+ "deviceStatusOnline": "En liña",
+ "deviceStatusOffline": "Sen conexión",
+ "deviceStatusUnknown": "Descoñecido",
+ "deviceRegisterFirst": "Rexistra o teu primeiro dispositivo",
+ "deviceIdentifierHelp": " O IMEI, número de serie ou outro id., ten que ser igual que o identificador do dispositivo que se meteu no servidor",
+ "deviceShare": "Share Device",
+ "groupDialog": "Grupo",
+ "groupParent": "Grupo",
+ "groupNoGroup": "Sen grupo",
+ "settingsTitle": "Axustes",
+ "settingsUser": "Conta",
+ "settingsGroups": "Grupos",
+ "settingsServer": "Servidor",
+ "settingsUsers": "Usuarios",
+ "settingsDistanceUnit": "Unidade de Distancia",
+ "settingsAltitudeUnit": "Unidade de altitude",
+ "settingsSpeedUnit": "Unidade de velocidade",
+ "settingsVolumeUnit": "Unidad de volumen",
+ "settingsTwelveHourFormat": "Formato de 12 horas",
+ "settingsCoordinateFormat": "Formato de coordenadas",
+ "settingsServerVersion": "Versión do servidor",
+ "settingsAppVersion": "Versión da aplicación",
+ "settingsConnection": "Conexión",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "Informes",
+ "reportScheduled": "Informes programados",
+ "reportDevice": "Dispositivo",
+ "reportGroup": "Grupo",
+ "reportFrom": "Dende",
+ "reportTo": "Ata",
+ "reportShow": "Mostrar",
+ "reportClear": "Limpar",
+ "linkGoogleMaps": "Google Maps",
+ "linkAppleMaps": "Apple Maps",
+ "linkStreetView": "Street View",
+ "positionFixTime": "Hora axustada",
+ "positionDeviceTime": "Hora do dispositivo",
+ "positionServerTime": "Hora do servidor",
+ "positionValid": "Válida",
+ "positionAccuracy": "Precisión",
+ "positionLatitude": "Latitude",
+ "positionLongitude": "Lonxitude",
+ "positionAltitude": "Altitude",
+ "positionSpeed": "Velocidade",
+ "positionCourse": "Rumbo",
+ "positionAddress": "Enderezo",
+ "positionProtocol": "Protocolo",
+ "positionDistance": "Distancia",
+ "positionRpm": "RPM",
+ "positionFuel": "Combustible",
+ "positionPower": "Enerxía",
+ "positionBattery": "Batería",
+ "positionRaw": "Crudo",
+ "positionIndex": "Índice",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "Satélites",
+ "positionSatVisible": "Satélites visibles",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "Itinerancia",
+ "positionEvent": "Evento",
+ "positionAlarm": "Alarma",
+ "positionStatus": "Estado",
+ "positionOdometer": "Odómetro",
+ "positionServiceOdometer": "Odómetro de mantemento",
+ "positionTripOdometer": "Odómetro de viaxe",
+ "positionHours": "Horas",
+ "positionSteps": "Pasos",
+ "positionInput": "Entrada",
+ "positionHeartRate": "Ritmo cardíaco",
+ "positionOutput": "Saída",
+ "positionBatteryLevel": "Nivel de batería",
+ "positionFuelConsumption": "Consumo de combustible",
+ "positionRfid": "RFID",
+ "positionVersionFw": "Versión de firmware",
+ "positionVersionHw": "Versión de hardware",
+ "positionIgnition": "Encendido",
+ "positionFlags": "Bandeiras",
+ "positionCharge": "Carga",
+ "positionIp": "IP",
+ "positionArchive": "Arquivo",
+ "positionVin": "VIN",
+ "positionApproximate": "Aproximado",
+ "positionThrottle": "Acelerador",
+ "positionMotion": "Movemento",
+ "positionArmed": "Armado",
+ "positionAcceleration": "Aceleración",
+ "positionTemp": "Temperatura",
+ "positionDeviceTemp": "Temperatura do dispositivo",
+ "positionCoolantTemp": "Temperatura do Refrixerante",
+ "positionOperator": "Operador",
+ "positionCommand": "Comando",
+ "positionBlocked": "Bloqueado",
+ "positionDtcs": "DTC",
+ "positionObdSpeed": "Velocidade OBD",
+ "positionObdOdometer": "Odómetro OBD",
+ "positionDrivingTime": "Tempo conducindo",
+ "positionDriverUniqueId": "ID única do condutor",
+ "positionCard": "Tarxeta",
+ "positionImage": "Imaxe",
+ "positionVideo": "Vídeo",
+ "positionAudio": "Audio",
+ "serverTitle": "Axustes do servidor",
+ "serverZoom": "Zoom",
+ "serverRegistration": "Rexistro",
+ "serverReadonly": "Só lectura",
+ "serverForceSettings": "Forzar axustes",
+ "serverAnnouncement": "Anuncio",
+ "serverName": "Nome do servidor",
+ "serverDescription": "Descrición do servidor",
+ "serverColorPrimary": "Color primario",
+ "serverColorSecondary": "Color secundario",
+ "serverLogo": "Imaxe do logotipo",
+ "serverLogoInverted": "Imaxe do logotipo con cores inversos",
+ "serverChangeDisable": "Desactivar o cambio de servidor",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "Mapa",
+ "mapActive": "Mapas activos",
+ "mapOverlay": "Superposición de mapas",
+ "mapOverlayCustom": "Superposición personalizada",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API Key",
+ "mapOpenWeatherClouds": "OpenWeather Clouds",
+ "mapOpenWeatherPrecipitation": "OpenWeather Precipitation",
+ "mapOpenWeatherPressure": "OpenWeather Pressure",
+ "mapOpenWeatherWind": "OpenWeather Wind",
+ "mapOpenWeatherTemperature": "OpenWeather Temperature",
+ "mapLayer": "Capa do mapa",
+ "mapCustom": "Personalizado (XYZ)",
+ "mapCustomArcgis": "Personalizado (ArcGIS)",
+ "mapCustomLabel": "Mapa personalizado",
+ "mapCarto": "Carto",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Bing Maps - Clave",
+ "mapBingRoad": "Bing Maps - Carreteras",
+ "mapBingAerial": "Bing Maps - Aéreo",
+ "mapBingHybrid": "Bing Maps - Híbrido",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Yandex",
+ "mapYandexSat": "Yandex Satélite",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox Streets",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox Outdoors",
+ "mapMapboxSatellite": "Mapbox Satellite",
+ "mapMapboxKey": "Mapbox Access Token",
+ "mapMapTilerBasic": "MapTiler Basic",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "MapTiler API Key",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ Access Token",
+ "mapTomTomBasic": "TomTom Basic",
+ "mapTomTomFlow": "TomTom Traffic Flow",
+ "mapTomTomIncidents": "TomTom Traffic Incidents",
+ "mapTomTomKey": "TomTom API Key",
+ "mapHereBasic": "Here Basic",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satellite",
+ "mapHereFlow": "Here Traffic Flow",
+ "mapHereKey": "Here API Key",
+ "mapShapePolygon": "Polígono",
+ "mapShapeCircle": "Círculo",
+ "mapShapePolyline": "Poliliña",
+ "mapLiveRoutes": "Rutas en directo",
+ "mapDirection": "Mostrar dirección",
+ "mapCurrentLocation": "Localización actual",
+ "mapPoiLayer": "Capa de PDI",
+ "mapClustering": "Agrupar marcadores",
+ "mapOnSelect": "Mostrar mapa na selección",
+ "mapDefault": "Mapa por defecto",
+ "stateTitle": "Estado",
+ "stateName": "Atributo",
+ "stateValue": "Valor",
+ "commandTitle": "Comando",
+ "commandSend": "Enviar",
+ "commandSent": "Comando enviado",
+ "commandQueued": "Comando en cola",
+ "commandUnit": "Unidade",
+ "commandCustom": "Comando personalizado",
+ "commandDeviceIdentification": "Identificador do dispositivo",
+ "commandPositionSingle": "Informe único",
+ "commandPositionPeriodic": "Informes periódicos",
+ "commandPositionStop": "Deixar de informar",
+ "commandEngineStop": "Apagar motor",
+ "commandEngineResume": "Desbloquear aceso de motor",
+ "commandAlarmArm": "Armar alarma",
+ "commandAlarmDisarm": "Desarmar alarma",
+ "commandAlarmDismiss": "Descartar alarma",
+ "commandSetTimezone": "Establecer fuso horario",
+ "commandRequestPhoto": "Solicitar foto",
+ "commandPowerOff": "Apagar o dispositivo",
+ "commandRebootDevice": "Reiniciar dispositivo",
+ "commandFactoryReset": "Restablecemento de fábrica",
+ "commandSendSms": "Enviar SMS",
+ "commandSendUssd": "Enviar USSD",
+ "commandSosNumber": "Establece o número de SOS",
+ "commandSilenceTime": "Establecer o horario de silencio",
+ "commandSetPhonebook": "Establecer axenda telefónica",
+ "commandVoiceMessage": "Mensaxe de voz",
+ "commandOutputControl": "Control de saídas",
+ "commandVoiceMonitoring": "Monitorización de voz",
+ "commandSetAgps": "Establecer AGPS",
+ "commandSetIndicator": "Establecer indicador",
+ "commandConfiguration": "Configuración",
+ "commandGetVersion": "Obter a versión",
+ "commandFirmwareUpdate": "Actualizar o firmware",
+ "commandSetConnection": "Establecer conexión",
+ "commandSetOdometer": "Establecer odómetro",
+ "commandGetModemStatus": "Obter o estado do módem",
+ "commandGetDeviceStatus": "Obter o estado do dispositivo",
+ "commandSetSpeedLimit": "Establecer límite de velocidade",
+ "commandModePowerSaving": "Modo de aforro de enerxía",
+ "commandModeDeepSleep": "Modo de aforro de enerxía profundo",
+ "commandAlarmGeofence": "Establecer alarma de xeocerca",
+ "commandAlarmBattery": "Establecer alarma de batería",
+ "commandAlarmSos": "Establecer alarma de SOS",
+ "commandAlarmRemove": "Establecer alarma de desmontaje",
+ "commandAlarmClock": "Establecer alarma de hora",
+ "commandAlarmSpeed": "Establecer alarma de velocidade",
+ "commandAlarmFall": "Establecer alarma de caída",
+ "commandAlarmVibration": "Establecer alarma de vibración",
+ "commandFrequency": "Frecuencia",
+ "commandTimezone": "Desfase de fuso horario",
+ "commandMessage": "Mensaxe",
+ "commandRadius": "Radio",
+ "commandEnable": "Activar",
+ "commandData": "Datos",
+ "commandIndex": "Índice",
+ "commandPhone": "Número de teléfono",
+ "commandServer": "Servidor",
+ "commandPort": "Porto",
+ "eventAll": "Todos os eventos",
+ "eventDeviceOnline": "Estado en liña",
+ "eventDeviceUnknown": "Estado descoñecido",
+ "eventDeviceOffline": "Estado sen conexión",
+ "eventDeviceInactive": "Dispositivo inactivo",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "Dispositivo en movemento",
+ "eventDeviceStopped": "Dispositivo detido",
+ "eventDeviceOverspeed": "Superouse o límite de velocidade",
+ "eventDeviceFuelDrop": "Perda de combustible",
+ "eventDeviceFuelIncrease": "Aumento de combustible",
+ "eventCommandResult": "Resultado do comando",
+ "eventGeofenceEnter": "Entrada na xeocerca",
+ "eventGeofenceExit": "Saída da cerca xeocerca",
+ "eventAlarm": "Alarma",
+ "eventIgnitionOn": "Chave Encendida - ON",
+ "eventIgnitionOff": "Chave apagada - OFF",
+ "eventMaintenance": "Necesítase mantemento",
+ "eventTextMessage": "Mensaxe de texto recibida",
+ "eventDriverChanged": "O condutor cambiou",
+ "eventMedia": "Medios",
+ "eventsScrollToLast": "Desprazarse ata o último",
+ "eventsSoundEvents": "Sonido para eventos",
+ "eventsSoundAlarms": "Sonido para alarmas",
+ "alarmGeneral": "General",
+ "alarmSos": "SOS",
+ "alarmVibration": "Vibración",
+ "alarmMovement": "Movemento",
+ "alarmLowspeed": "Baixa velocidade",
+ "alarmOverspeed": "Exceso de velocidade",
+ "alarmFallDown": "Alarma de caída",
+ "alarmLowPower": "Enerxía baixa",
+ "alarmLowBattery": "Batería baixa",
+ "alarmFault": "Alarma de fallo",
+ "alarmPowerOff": "Apagado",
+ "alarmPowerOn": "Encendido",
+ "alarmDoor": "Porta",
+ "alarmLock": "Bloquear",
+ "alarmUnlock": "Desbloquear",
+ "alarmGeofence": "Xeocerca",
+ "alarmGeofenceEnter": "Entrou na xeocerca",
+ "alarmGeofenceExit": "Saíu da xeocerca",
+ "alarmGpsAntennaCut": "Corte de antena GPS",
+ "alarmAccident": "Accidente",
+ "alarmTow": "Guindastre de arrastre",
+ "alarmIdle": "Inactivo",
+ "alarmHighRpm": "Altas RPM",
+ "alarmHardAcceleration": "Aceleración brusca",
+ "alarmHardBraking": "Freada brusca",
+ "alarmHardCornering": "Xiro brusco",
+ "alarmLaneChange": "Cambio de carril",
+ "alarmFatigueDriving": "Condución con cansazo",
+ "alarmPowerCut": "Corte de alimentación",
+ "alarmPowerRestored": "Enerxía restablecida",
+ "alarmJamming": "Interferencia",
+ "alarmTemperature": "Temperatura",
+ "alarmParking": "Aparcadoiro",
+ "alarmBonnet": "Capó",
+ "alarmFootBrake": "Freo de pé",
+ "alarmFuelLeak": "Fuga de combustible",
+ "alarmTampering": "Manipulación",
+ "alarmRemoving": "Eliminando",
+ "notificationType": "Tipo de notificación",
+ "notificationAlways": "Todos os dispositivos",
+ "notificationNotificators": "Canles",
+ "notificatorCommand": "Comando",
+ "notificatorWeb": "Web",
+ "notificatorMail": "Correo",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Repetición ruta",
+ "reportCombined": "Combinado \n ",
+ "reportRoute": "Ruta",
+ "reportEvents": "Eventos",
+ "reportTrips": "Viaxes",
+ "reportStops": "Paradas",
+ "reportSummary": "Resumo",
+ "reportDaily": "Resumo diario",
+ "reportChart": "Gráfica",
+ "reportConfigure": "Configurar",
+ "reportEventTypes": "Tipos de eventos",
+ "reportChartType": "Tipo de gráfica",
+ "reportShowMarkers": "Amosar marcadores",
+ "reportExport": "Exportar",
+ "reportEmail": "Informe por correo",
+ "reportSchedule": "Planificación",
+ "reportPeriod": "Período",
+ "reportCustom": "Personalizado",
+ "reportToday": "Hoxe",
+ "reportYesterday": "Onte",
+ "reportThisWeek": "Esta semana",
+ "reportPreviousWeek": "Semana anterior",
+ "reportThisMonth": "Este mes",
+ "reportPreviousMonth": "Mes anterior",
+ "reportDeviceName": "Nome",
+ "reportAverageSpeed": "Velocidade media",
+ "reportMaximumSpeed": "Velocidade máxima",
+ "reportEngineHours": "Horas do motor",
+ "reportDuration": "Duración",
+ "reportStartDate": "Data de inicio",
+ "reportStartTime": "Hora de inicio",
+ "reportStartAddress": "Enderezo de inicio",
+ "reportEndTime": "Hora de fin",
+ "reportEndAddress": "Enderezo de fin",
+ "reportSpentFuel": "Combustible gastado",
+ "reportStartOdometer": "Odómetro inicial",
+ "reportEndOdometer": "Odómetro final",
+ "statisticsTitle": "Estatísticas",
+ "statisticsCaptureTime": "Fecha de captura",
+ "statisticsActiveUsers": "Usuarios activos",
+ "statisticsActiveDevices": "Dispositivos activos",
+ "statisticsRequests": "Peticións",
+ "statisticsMessagesReceived": "Mensaxes recibidas",
+ "statisticsMessagesStored": "Mensaxes almacenadas",
+ "statisticsGeocoder": "Solicitudes ao xeocodificador",
+ "statisticsGeolocation": "Solicitudes de xeolocalización",
+ "categoryArrow": "Frecha",
+ "categoryDefault": "Por defecto",
+ "categoryAnimal": "Animal",
+ "categoryBicycle": "Bicicleta",
+ "categoryBoat": "Barco",
+ "categoryBus": "Autobús",
+ "categoryCar": "Coche",
+ "categoryCamper": "Camper",
+ "categoryCrane": "Grúa",
+ "categoryHelicopter": "Helicóptero",
+ "categoryMotorcycle": "Motocicleta",
+ "categoryOffroad": "Todoterreo",
+ "categoryPerson": "Persoa",
+ "categoryPickup": "Pickup",
+ "categoryPlane": "Avión",
+ "categoryShip": "Barco",
+ "categoryTractor": "Tractor",
+ "categoryTrain": "Tren",
+ "categoryTram": "Tranvía",
+ "categoryTrolleybus": "Trolebús",
+ "categoryTruck": "Camión",
+ "categoryVan": "Furgoneta",
+ "categoryScooter": "Scooter",
+ "maintenanceStart": "Comezar",
+ "maintenancePeriod": "Período"
+} \ No newline at end of file
diff --git a/src/resources/l10n/he.json b/src/resources/l10n/he.json
new file mode 100644
index 00000000..8a2a4eed
--- /dev/null
+++ b/src/resources/l10n/he.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "טוען...",
+ "sharedHide": "הסתר",
+ "sharedSave": "שמירה",
+ "sharedUpload": "Upload",
+ "sharedSet": "הגדרה",
+ "sharedCancel": "ביטול",
+ "sharedCopy": "העתקה",
+ "sharedAdd": "הוספה",
+ "sharedEdit": "עריכה",
+ "sharedRemove": "הסרה",
+ "sharedRemoveConfirm": "הסרת פריט",
+ "sharedNoData": "No data",
+ "sharedSubject": "נושא",
+ "sharedYes": "כן",
+ "sharedNo": "לא",
+ "sharedKm": "ק\"מ",
+ "sharedMi": "מייל",
+ "sharedNmi": "nmi",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "קשר",
+ "sharedKmh": "ק\"מ/שעה",
+ "sharedMph": "מייל/שעה",
+ "sharedHour": "שעה",
+ "sharedMinute": "דקה",
+ "sharedSecond": "שנייה",
+ "sharedDays": "ימים",
+ "sharedHours": "שעות",
+ "sharedMinutes": "דקות",
+ "sharedDecimalDegrees": "מעלות עשרוניות",
+ "sharedDegreesDecimalMinutes": "מעלות ודקות עשרוניות",
+ "sharedDegreesMinutesSeconds": "מעלות, דקות ושניות",
+ "sharedName": "שם",
+ "sharedDescription": "תיאור",
+ "sharedSearch": "חיפוש",
+ "sharedIconScale": "Icon Scale",
+ "sharedGeofence": "גדר וירטואלית",
+ "sharedGeofences": "גדרות וירטואליות",
+ "sharedCreateGeofence": "צרו איזור מגודר",
+ "sharedNotifications": "התראות",
+ "sharedNotification": "התראה",
+ "sharedAttributes": "תכונות",
+ "sharedAttribute": "תכונה",
+ "sharedDrivers": "נהגים",
+ "sharedDriver": "נהג",
+ "sharedArea": "איזור",
+ "sharedSound": "צליל התראה",
+ "sharedType": "סוג",
+ "sharedDistance": "מרחק",
+ "sharedHourAbbreviation": "ש'",
+ "sharedMinuteAbbreviation": "דק'",
+ "sharedSecondAbbreviation": "שנ'",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "ל",
+ "sharedGallonAbbreviation": "גלון",
+ "sharedLiter": "ליטר",
+ "sharedImpGallon": "גלון",
+ "sharedUsGallon": "גלון",
+ "sharedLiterPerHourAbbreviation": "ליטר/ שעה",
+ "sharedGetMapState": "הגדר מהמפה",
+ "sharedComputedAttribute": "תכונה ממוחשבת",
+ "sharedComputedAttributes": "תכונה ממוחשבת",
+ "sharedCheckComputedAttribute": "בדוק תכונות ממוחשבות",
+ "sharedExpression": "ביטוי",
+ "sharedDevice": "התקן",
+ "sharedTest": "בדיקה",
+ "sharedTestNotification": "שלח הודעת בדיקה",
+ "sharedTestNotificators": "Test Channels",
+ "sharedTestExpression": "Test Expression",
+ "sharedCalendar": "יומן",
+ "sharedCalendars": "יומנים",
+ "sharedFile": "קובץ",
+ "sharedSearchDevices": "חיפוש מכשירים",
+ "sharedSortBy": "מיון לפי",
+ "sharedFilterMap": "Filter on Map",
+ "sharedSelectFile": "בחר קובץ",
+ "sharedPhone": "טלפון",
+ "sharedRequired": "נדרש",
+ "sharedPreferences": "עדיפויות",
+ "sharedPermissions": "הרשאות",
+ "sharedConnections": "Connections",
+ "sharedExtra": "תוספת",
+ "sharedPrimary": "Primary",
+ "sharedSecondary": "Secondary",
+ "sharedTypeString": "מחרוזת",
+ "sharedTypeNumber": "מספר",
+ "sharedTypeBoolean": "בוליאני",
+ "sharedTimezone": "אזור זמן",
+ "sharedInfoTitle": "פרטים",
+ "sharedSavedCommand": "פקודות שמורות",
+ "sharedSavedCommands": "פקודות שמורות",
+ "sharedNew": "חדש",
+ "sharedShowAddress": "הצג כתובת",
+ "sharedShowDetails": "יותר פרטים",
+ "sharedDisabled": "מושבת",
+ "sharedMaintenance": "תחזוקה",
+ "sharedDeviceAccumulators": "מצברים",
+ "sharedAlarms": "אזעקות",
+ "sharedLocation": "מיקום",
+ "sharedImport": "ייבוא",
+ "sharedColumns": "עמודות",
+ "sharedDropzoneText": "Drag and drop a file here or click",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "Simple",
+ "calendarRecurrence": "Recurrence",
+ "calendarOnce": "פעם אחת",
+ "calendarDaily": "יומי",
+ "calendarWeekly": "שבועי",
+ "calendarMonthly": "חודשי",
+ "calendarDays": "Days",
+ "calendarSunday": "יום ראשון",
+ "calendarMonday": "יום שני",
+ "calendarTuesday": "יום שלישי",
+ "calendarWednesday": "יום רביעי",
+ "calendarThursday": "יום חמישי",
+ "calendarFriday": "יום שישי",
+ "calendarSaturday": "שבת",
+ "attributeShowGeofences": "הצג איזורים מגודרים",
+ "attributeSpeedLimit": "הגבלת מהירות",
+ "attributeFuelDropThreshold": "Fuel Drop Threshold",
+ "attributeFuelIncreaseThreshold": "Fuel Increase Threshold",
+ "attributePolylineDistance": "מרחק מצולע",
+ "attributeReportIgnoreOdometer": "דוח : התעלם ממד המרחק",
+ "attributeWebReportColor": "אתר : בחר צבע",
+ "attributeDevicePassword": "סיסמת התקן ",
+ "attributeDeviceImage": "תמונת רכב",
+ "attributeDeviceInactivityStart": "Device Inactivity Start",
+ "attributeDeviceInactivityPeriod": "Device Inactivity Period",
+ "attributeProcessingCopyAttributes": "עיבוד: העתקת תכונות",
+ "attributeColor": "צבע",
+ "attributeWebLiveRouteLength": "ממשק : אורך מסלול זמן אמת",
+ "attributeWebSelectZoom": "אתר : בחר הפעלת זום",
+ "attributeWebMaxZoom": "אתר : זום מקסימלי",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Pushover User Key",
+ "attributePushoverDeviceNames": "Pushover Device Names",
+ "attributeMailSmtpHost": "כתובת שרת דואר SMTP",
+ "attributeMailSmtpPort": "שרת דואר: פורט גישה ",
+ "attributeMailSmtpStarttlsEnable": "אפשר מייל מאובטח באמצעות TLS",
+ "attributeMailSmtpStarttlsRequired": "נדרש SMTP STARTTLS ",
+ "attributeMailSmtpSslEnable": "אפשר SSL לשרת דואר",
+ "attributeMailSmtpSslTrust": "דואר:SMTP SSL Trust",
+ "attributeMailSmtpSslProtocols": "פרוטוקול חיבור SSL לשרת דואר",
+ "attributeMailSmtpFrom": "כתובת השולח ",
+ "attributeMailSmtpAuth": "אפשר אימות לSMTP",
+ "attributeMailSmtpUsername": "שם משתמש SMTP ",
+ "attributeMailSmtpPassword": "סיסמת SMTP",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: Disable Attributes",
+ "attributeUiDisableGroups": "UI: Disable Groups",
+ "attributeUiDisableEvents": "ממשק: בטל ארועים",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "ממשק : בטל נהגים ",
+ "attributeUiDisableComputedAttributes": "ממשק : בטל תכונה ממוחשבת",
+ "attributeUiDisableCalendars": "ממשק : בטל יומנים",
+ "attributeUiDisableMaintenance": "אל תציג :תחזוקה",
+ "attributeUiHidePositionAttributes": "ממשק : הסתר מיקום תכונות",
+ "attributeUiDisableLoginLanguage": "UI: Disable Login Language",
+ "attributeNotificationTokens": "Notification Tokens",
+ "attributePopupInfo": "Popup Info",
+ "errorTitle": "שגיאה",
+ "errorGeneral": "גורם לא בתוקף או הפרת אילוצים ",
+ "errorConnection": "בעייה בחיבור",
+ "errorSocket": "בעיית רשת",
+ "errorZero": "ערך לא יכול להיות אפס",
+ "userEmail": "אימייל",
+ "userPassword": "סיסמה",
+ "userAdmin": "מנהל",
+ "userRemember": "זכור אותי",
+ "userExpirationTime": "תוקף",
+ "userDeviceLimit": "מגבלת מכשירים",
+ "userUserLimit": "מגבלת משתמשים",
+ "userDeviceReadonly": "מכשירים - לקריאה בלבד",
+ "userLimitCommands": "הגבל פקודות ",
+ "userDisableReports": "Disable Reports",
+ "userFixedEmail": "No Email Change",
+ "userToken": "אסימון",
+ "userDeleteAccount": "Delete Account",
+ "userTemporary": "Temporary",
+ "loginTitle": "כניסה",
+ "loginLanguage": "שפה",
+ "loginReset": "איפוס סיסמה",
+ "loginRegister": "הרשם",
+ "loginLogin": "כניסה",
+ "loginOpenId": "Login with OpenID",
+ "loginFailed": "אימייל או סיסמה שגויים",
+ "loginCreated": "משתמש חדש נרשם",
+ "loginResetSuccess": "Check your email",
+ "loginUpdateSuccess": "הסיסמה החדשה הוגדרה",
+ "loginLogout": "יציאה",
+ "loginLogo": "לוגו",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "מכשירים וסטטוס",
+ "deviceSelected": "Selected Device",
+ "deviceTitle": "מכשירים",
+ "devicePrimaryInfo": "Device Title",
+ "deviceSecondaryInfo": "Device Detail",
+ "deviceIdentifier": "מזהה",
+ "deviceModel": "דגם",
+ "deviceContact": "איש קשר",
+ "deviceCategory": "קטגוריה",
+ "deviceLastUpdate": "עודכן לאחרונה",
+ "deviceCommand": "פקודה",
+ "deviceFollow": "עקוב",
+ "deviceTotalDistance": "מרחק כולל",
+ "deviceStatus": "סטטוס",
+ "deviceStatusOnline": "מחובר",
+ "deviceStatusOffline": "מנותק",
+ "deviceStatusUnknown": "מצבו אינו ידוע",
+ "deviceRegisterFirst": "Register your first device",
+ "deviceIdentifierHelp": "IMEI, serial number or other id. It has to match the identifier device reports to the server.",
+ "deviceShare": "Share Device",
+ "groupDialog": "קבוצה",
+ "groupParent": "קבוצה",
+ "groupNoGroup": "ללא קבוצה",
+ "settingsTitle": "הגדרות",
+ "settingsUser": "חשבון",
+ "settingsGroups": "קבוצות",
+ "settingsServer": "שרת",
+ "settingsUsers": "משתמשים",
+ "settingsDistanceUnit": "יח מרחק",
+ "settingsAltitudeUnit": "יחידת גובה",
+ "settingsSpeedUnit": "יח מהירות ",
+ "settingsVolumeUnit": "יחידת נפח",
+ "settingsTwelveHourFormat": "פורמט של 12 שעות",
+ "settingsCoordinateFormat": "פורמט קואורדינטות",
+ "settingsServerVersion": "גרסת השרת",
+ "settingsAppVersion": "גרסת האפליקציה",
+ "settingsConnection": "Connection",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "דו\"חות",
+ "reportScheduled": "דוחות מתוזמנים",
+ "reportDevice": "מכשיר",
+ "reportGroup": "קבוצה",
+ "reportFrom": "מ-",
+ "reportTo": "עד",
+ "reportShow": "הצג",
+ "reportClear": "נקה",
+ "linkGoogleMaps": "מפות גוגל",
+ "linkAppleMaps": "מפות אפל",
+ "linkStreetView": "תצוגת רחוב",
+ "positionFixTime": "זמן עדכון GPS",
+ "positionDeviceTime": "עדכון אחרון",
+ "positionServerTime": "זמן השרת",
+ "positionValid": "תקין",
+ "positionAccuracy": "דיוק",
+ "positionLatitude": "קו רוחב",
+ "positionLongitude": "קו אורך",
+ "positionAltitude": "גובה",
+ "positionSpeed": "מהירות",
+ "positionCourse": "מסלול",
+ "positionAddress": "כתובת",
+ "positionProtocol": "פרוטוקול",
+ "positionDistance": "מרחק",
+ "positionRpm": "סל\"ד ",
+ "positionFuel": "דלק",
+ "positionPower": "מצבר",
+ "positionBattery": "סוללה",
+ "positionRaw": "גלם",
+ "positionIndex": "אינדקס",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "לווינים",
+ "positionSatVisible": "לווניינים בשימוש ",
+ "positionRssi": "RSSI",
+ "positionGps": "מערכת איתור לוונית ",
+ "positionRoaming": "נדידה",
+ "positionEvent": "אירוע",
+ "positionAlarm": "התראה",
+ "positionStatus": "סטטוס",
+ "positionOdometer": "מד מרחק",
+ "positionServiceOdometer": "טיפול לפי ספידומטר",
+ "positionTripOdometer": "מד מרחק נסיעה",
+ "positionHours": "שעות",
+ "positionSteps": "צעדים",
+ "positionInput": "כניסה",
+ "positionHeartRate": "Heart Rate",
+ "positionOutput": "יציאה",
+ "positionBatteryLevel": "רמת הסוללה",
+ "positionFuelConsumption": "צריכת דלק",
+ "positionRfid": "RFID",
+ "positionVersionFw": "גרסת קושחה",
+ "positionVersionHw": "גרסת חומרה",
+ "positionIgnition": "הצתה",
+ "positionFlags": "איתות -סימון",
+ "positionCharge": "טעינה",
+ "positionIp": "כתובת IP",
+ "positionArchive": "ארכיון",
+ "positionVin": "מס זיהוי רכב יצרן",
+ "positionApproximate": "משוער",
+ "positionThrottle": "מצערת",
+ "positionMotion": "תנועה",
+ "positionArmed": "דרוך",
+ "positionAcceleration": "תאוצה",
+ "positionTemp": "טמפרטורה",
+ "positionDeviceTemp": "טמפרטורת המכשיר",
+ "positionCoolantTemp": "Coolant Temperature",
+ "positionOperator": "מפעיל",
+ "positionCommand": "פקודה",
+ "positionBlocked": "חסום",
+ "positionDtcs": "מערכת תקשורת טקטית מבוזרת",
+ "positionObdSpeed": "מהירות לפי OBD",
+ "positionObdOdometer": "מד מרחק OBD",
+ "positionDrivingTime": "Driving Time",
+ "positionDriverUniqueId": "מזהה נהג יחודי",
+ "positionCard": "Card",
+ "positionImage": "תמונה",
+ "positionVideo": "Video",
+ "positionAudio": "קול",
+ "serverTitle": "הגדרות שרת",
+ "serverZoom": "זום",
+ "serverRegistration": "הרשמה",
+ "serverReadonly": "לקריאה בלבד",
+ "serverForceSettings": "כפה הגדרות",
+ "serverAnnouncement": "Announcement",
+ "serverName": "Server Name",
+ "serverDescription": "Server Description",
+ "serverColorPrimary": "Color Primary",
+ "serverColorSecondary": "Color Secondary",
+ "serverLogo": "Logo Image",
+ "serverLogoInverted": "Inverted Logo Image",
+ "serverChangeDisable": "Disable Server Change",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "מפה",
+ "mapActive": "Active Maps",
+ "mapOverlay": "Map Overlay",
+ "mapOverlayCustom": "Custom Overlay",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API Key",
+ "mapOpenWeatherClouds": "OpenWeather Clouds",
+ "mapOpenWeatherPrecipitation": "OpenWeather Precipitation",
+ "mapOpenWeatherPressure": "OpenWeather Pressure",
+ "mapOpenWeatherWind": "OpenWeather Wind",
+ "mapOpenWeatherTemperature": "OpenWeather Temperature",
+ "mapLayer": "שכבת מפה",
+ "mapCustom": "מותאם (XYZ)",
+ "mapCustomArcgis": "מפה מותאמת (ArcGIS)",
+ "mapCustomLabel": "מפה מותאמת ",
+ "mapCarto": "Carto Basemaps\n",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "מפתח מפות בינג",
+ "mapBingRoad": "מפות כביש בינג",
+ "mapBingAerial": "מפות אוויריות בינג",
+ "mapBingHybrid": "מפות היברדיות בינג",
+ "mapBaidu": "מפות סיניות ביידו",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "מפות רוסיות Yandex",
+ "mapYandexSat": "מפות רוסיות לווין Yandex",
+ "mapWikimedia": "ויקימדיה",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox Streets",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox Outdoors",
+ "mapMapboxSatellite": "Mapbox Satellite",
+ "mapMapboxKey": "Mapbox Access Token",
+ "mapMapTilerBasic": "MapTiler Basic",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "MapTiler API Key",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ Access Token",
+ "mapTomTomBasic": "TomTom Basic",
+ "mapTomTomFlow": "TomTom Traffic Flow",
+ "mapTomTomIncidents": "TomTom Traffic Incidents",
+ "mapTomTomKey": "TomTom API Key",
+ "mapHereBasic": "Here Basic",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satellite",
+ "mapHereFlow": "Here Traffic Flow",
+ "mapHereKey": "Here API Key",
+ "mapShapePolygon": "פוליגון",
+ "mapShapeCircle": "מעגל",
+ "mapShapePolyline": "צלע",
+ "mapLiveRoutes": "מסלול נסיעה",
+ "mapDirection": "Show Direction",
+ "mapCurrentLocation": "Current Location",
+ "mapPoiLayer": "שכבת נק' עניין",
+ "mapClustering": "Markers Clustering",
+ "mapOnSelect": "Show Map on Selection",
+ "mapDefault": "Default Map",
+ "stateTitle": "מצב",
+ "stateName": "תכונה",
+ "stateValue": "ערך",
+ "commandTitle": "פקודה",
+ "commandSend": "שליחה",
+ "commandSent": "פקודה נשלחה",
+ "commandQueued": "הפקודה ממתינה ",
+ "commandUnit": "יחידה",
+ "commandCustom": "פקודה בהתאמה אישית",
+ "commandDeviceIdentification": "זיהוי מכשיר",
+ "commandPositionSingle": "דו\"ח יחיד",
+ "commandPositionPeriodic": "דיווח תקופתי",
+ "commandPositionStop": "עצור דיווח",
+ "commandEngineStop": "דומם מנוע",
+ "commandEngineResume": "הפעל מנוע",
+ "commandAlarmArm": "הפעלת אזעקה",
+ "commandAlarmDisarm": "נטרול אזעקה",
+ "commandAlarmDismiss": "Dismiss Alarm",
+ "commandSetTimezone": "קבע איזור זמן",
+ "commandRequestPhoto": "בקשה לתמונה",
+ "commandPowerOff": "כיבוי התקן ",
+ "commandRebootDevice": "איתחול המכשיר",
+ "commandFactoryReset": "Factory Reset",
+ "commandSendSms": "שלח סמס",
+ "commandSendUssd": "שלח נתוני שירות משלימים לא מובנים",
+ "commandSosNumber": "קבע מספר חירום",
+ "commandSilenceTime": "קבע משך זמן הדממה",
+ "commandSetPhonebook": "הגדר ספר טלפונים",
+ "commandVoiceMessage": "הודעה קולית",
+ "commandOutputControl": "בקרת פלט",
+ "commandVoiceMonitoring": "האזנה ",
+ "commandSetAgps": "הפעל AGPS",
+ "commandSetIndicator": "הגדר מד ",
+ "commandConfiguration": "תצורה",
+ "commandGetVersion": "קבל גרסה",
+ "commandFirmwareUpdate": "עדכן גרסת חומרה",
+ "commandSetConnection": "הגדר תקשורת",
+ "commandSetOdometer": "הגדר מד מרחק",
+ "commandGetModemStatus": "הבא מצב מודם",
+ "commandGetDeviceStatus": "הבא מצב התקן ",
+ "commandSetSpeedLimit": "Set Speed Limit",
+ "commandModePowerSaving": "מצב חיסכון בחשמל",
+ "commandModeDeepSleep": "Deep Sleep Mode",
+ "commandAlarmGeofence": "Set Geofence Alarm",
+ "commandAlarmBattery": "Set Battery Alarm",
+ "commandAlarmSos": "Set SOS Alarm",
+ "commandAlarmRemove": "Set Remove Alarm",
+ "commandAlarmClock": "Set Clock Alarm",
+ "commandAlarmSpeed": "Set Speed Alarm",
+ "commandAlarmFall": "Set Fall Alarm",
+ "commandAlarmVibration": "Set Vibration Alarm",
+ "commandFrequency": "תדירות",
+ "commandTimezone": "איזון אזור זמן ",
+ "commandMessage": "הודעה",
+ "commandRadius": "רדיוס",
+ "commandEnable": "מאופשר",
+ "commandData": "נתונים",
+ "commandIndex": "אינדקס",
+ "commandPhone": "טלפון",
+ "commandServer": "שרת",
+ "commandPort": "פורט",
+ "eventAll": "כל ההתראות",
+ "eventDeviceOnline": "מצב אונליין",
+ "eventDeviceUnknown": "מצב לא ידוע",
+ "eventDeviceOffline": "מצב מנותק",
+ "eventDeviceInactive": "Device inactive",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "ההתקן בתנועה",
+ "eventDeviceStopped": "ההתקן עצר",
+ "eventDeviceOverspeed": "חרג מהמהירות המותרת ",
+ "eventDeviceFuelDrop": "שחרור מיכל דלק",
+ "eventDeviceFuelIncrease": "Fuel increase",
+ "eventCommandResult": "תוצאות הפקודה",
+ "eventGeofenceEnter": "נכנס אל האזור המתוחם",
+ "eventGeofenceExit": "יצא מהאזור המתוחם",
+ "eventAlarm": "התראה",
+ "eventIgnitionOn": "התנעה",
+ "eventIgnitionOff": "מנוע נכבה",
+ "eventMaintenance": "תחזוקה נדרשת",
+ "eventTextMessage": "התקבלה הודעת טקסט",
+ "eventDriverChanged": "נהג הוחלף",
+ "eventMedia": "Media",
+ "eventsScrollToLast": "גלול אל האחרון",
+ "eventsSoundEvents": "Sound Events",
+ "eventsSoundAlarms": "Sound Alarms",
+ "alarmGeneral": "כללי",
+ "alarmSos": "חירום",
+ "alarmVibration": "ויברציה",
+ "alarmMovement": "תנועה",
+ "alarmLowspeed": "מהירות נמוכה",
+ "alarmOverspeed": "עבר מהירות מותרת",
+ "alarmFallDown": "ליפול",
+ "alarmLowPower": "מצבר חלש",
+ "alarmLowBattery": "סוללה חלשה",
+ "alarmFault": "תקלה",
+ "alarmPowerOff": "כיבוי",
+ "alarmPowerOn": "הדלקה",
+ "alarmDoor": "דלת",
+ "alarmLock": "נעילה",
+ "alarmUnlock": "פתיחה",
+ "alarmGeofence": "גדר וירטואלית ",
+ "alarmGeofenceEnter": "נכנס לתחום המוגדר",
+ "alarmGeofenceExit": "יצא מהתחום המוגדר",
+ "alarmGpsAntennaCut": "ניתוק GPS",
+ "alarmAccident": "תאונה",
+ "alarmTow": "גרירה",
+ "alarmIdle": "מצב מנוחה",
+ "alarmHighRpm": "סלד גבוה",
+ "alarmHardAcceleration": "האצה חדה",
+ "alarmHardBraking": "בלימת חירום",
+ "alarmHardCornering": "פניה חדה",
+ "alarmLaneChange": "סטיה מהנתיב",
+ "alarmFatigueDriving": "עייפות נהיגה",
+ "alarmPowerCut": "ניתוק",
+ "alarmPowerRestored": "חשמל חזר",
+ "alarmJamming": "שיבוש",
+ "alarmTemperature": "טמפרטורה",
+ "alarmParking": "חניה",
+ "alarmBonnet": "מכסה מנוע",
+ "alarmFootBrake": "בלם ",
+ "alarmFuelLeak": "דליפת דלק",
+ "alarmTampering": "מפריע",
+ "alarmRemoving": "מסיר",
+ "notificationType": "סוג ההתראה",
+ "notificationAlways": "כל ההתקנים",
+ "notificationNotificators": "ערוצים",
+ "notificatorCommand": "Command",
+ "notificatorWeb": "אתר",
+ "notificatorMail": "מייל",
+ "notificatorSms": "סמס",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Replay",
+ "reportCombined": "Combined",
+ "reportRoute": "מסלול",
+ "reportEvents": "אירועים",
+ "reportTrips": "נסיעות",
+ "reportStops": "עצירות",
+ "reportSummary": "סיכום",
+ "reportDaily": "Daily Summary",
+ "reportChart": "תרשים",
+ "reportConfigure": "הגדר",
+ "reportEventTypes": "סוגי אירועים",
+ "reportChartType": "סוג תרשים",
+ "reportShowMarkers": "הצג חצים",
+ "reportExport": "יצוא",
+ "reportEmail": "דוח במייל",
+ "reportSchedule": "Schedule",
+ "reportPeriod": "תקופה",
+ "reportCustom": "מותאם אישית",
+ "reportToday": "היום",
+ "reportYesterday": "אתמול",
+ "reportThisWeek": "השבוע",
+ "reportPreviousWeek": "שבוע שעבר",
+ "reportThisMonth": "חודש נוכחי",
+ "reportPreviousMonth": "חודש קודם",
+ "reportDeviceName": "שם המכשיר",
+ "reportAverageSpeed": "מהירות ממוצעת",
+ "reportMaximumSpeed": "מהירות מירבית",
+ "reportEngineHours": "שעות מנוע",
+ "reportDuration": "משך הנסיעה",
+ "reportStartDate": "Start Date",
+ "reportStartTime": "זמן התחלה",
+ "reportStartAddress": "נקודת מוצא",
+ "reportEndTime": "זמן סיום",
+ "reportEndAddress": "נקודת יעד",
+ "reportSpentFuel": "צריכת דלק",
+ "reportStartOdometer": "ספידומטר קמ התחלתי",
+ "reportEndOdometer": "ספידומטר קמ סוף ",
+ "statisticsTitle": "סטטיסטיקה",
+ "statisticsCaptureTime": "פרק זמן",
+ "statisticsActiveUsers": "משתמשים פעילים",
+ "statisticsActiveDevices": "מכשירים פעילים",
+ "statisticsRequests": "בקשות",
+ "statisticsMessagesReceived": "הודעות שהתקבלו",
+ "statisticsMessagesStored": "הודעות שנשמרו",
+ "statisticsGeocoder": "בקשת מיקום גיאוגרפי\n",
+ "statisticsGeolocation": "בקשות מיקום גיאוגרפי",
+ "categoryArrow": "חץ",
+ "categoryDefault": "ברירת מחדל",
+ "categoryAnimal": "חיה",
+ "categoryBicycle": "אופניים",
+ "categoryBoat": "סירה",
+ "categoryBus": "אוטובוס",
+ "categoryCar": "רכב פרטי",
+ "categoryCamper": "Camper",
+ "categoryCrane": "מנוף",
+ "categoryHelicopter": "מסוק",
+ "categoryMotorcycle": "אופנוע",
+ "categoryOffroad": "רכב שטח",
+ "categoryPerson": "אדם",
+ "categoryPickup": "משאית קלה",
+ "categoryPlane": "מטוס",
+ "categoryShip": "ספינה",
+ "categoryTractor": "טרקטור",
+ "categoryTrain": "רכבת",
+ "categoryTram": "חשמלית ",
+ "categoryTrolleybus": "אוטובוס",
+ "categoryTruck": "משאית",
+ "categoryVan": "מסחרית",
+ "categoryScooter": "קטנוע",
+ "maintenanceStart": "התחל",
+ "maintenancePeriod": "פרק זמן "
+} \ No newline at end of file
diff --git a/src/resources/l10n/hi.json b/src/resources/l10n/hi.json
new file mode 100644
index 00000000..1716dc99
--- /dev/null
+++ b/src/resources/l10n/hi.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "लोड हो रहा है...",
+ "sharedHide": "छिपाना",
+ "sharedSave": "सुरक्षित करें",
+ "sharedUpload": "Upload",
+ "sharedSet": "स्वीकृति दें",
+ "sharedCancel": "रद्द करें",
+ "sharedCopy": "Copy",
+ "sharedAdd": "जोड़ें",
+ "sharedEdit": "संपादित करें",
+ "sharedRemove": "हटाएं",
+ "sharedRemoveConfirm": "आइटम हटाएं ?",
+ "sharedNoData": "No data",
+ "sharedSubject": "Subject",
+ "sharedYes": "हाँ",
+ "sharedNo": "नहीं",
+ "sharedKm": "किमी / किलोमीटर",
+ "sharedMi": "एम आई",
+ "sharedNmi": "nmi",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "के.एन.",
+ "sharedKmh": "किमी / घंटा",
+ "sharedMph": "मील प्रति घंटा",
+ "sharedHour": "घंटा",
+ "sharedMinute": "मिनट",
+ "sharedSecond": "सैकंड",
+ "sharedDays": "दिन",
+ "sharedHours": "घंटे",
+ "sharedMinutes": "मिनट",
+ "sharedDecimalDegrees": "दशमलव डिग्री",
+ "sharedDegreesDecimalMinutes": "डिग्री दशमलव मिनट",
+ "sharedDegreesMinutesSeconds": "डिग्री मिनट सेकेंड",
+ "sharedName": "नाम",
+ "sharedDescription": "विवरण",
+ "sharedSearch": "खोजें",
+ "sharedIconScale": "Icon Scale",
+ "sharedGeofence": "जिओफेंस / भूगौलिक परिधि",
+ "sharedGeofences": "जिओफेंसस / भूगौलिक परिधियां",
+ "sharedCreateGeofence": "Create Geofence",
+ "sharedNotifications": "सूचनाएं",
+ "sharedNotification": "अधिसूचना",
+ "sharedAttributes": "एक से अधिक गुण",
+ "sharedAttribute": "गुण",
+ "sharedDrivers": "ड्राइवर",
+ "sharedDriver": "ड्राइवर",
+ "sharedArea": "क्षेत्र",
+ "sharedSound": "ध्वनि",
+ "sharedType": "टाइप/प्रकार",
+ "sharedDistance": "दूरी",
+ "sharedHourAbbreviation": "एच",
+ "sharedMinuteAbbreviation": "एम",
+ "sharedSecondAbbreviation": "s",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "l",
+ "sharedGallonAbbreviation": "गैल",
+ "sharedLiter": "लीटर",
+ "sharedImpGallon": "Imp. गैलन",
+ "sharedUsGallon": "U.S. गैलन",
+ "sharedLiterPerHourAbbreviation": "l/h",
+ "sharedGetMapState": "मानचित्र की स्तिति प्रदान करें",
+ "sharedComputedAttribute": "गणना गुण",
+ "sharedComputedAttributes": "गणना गुण",
+ "sharedCheckComputedAttribute": "गणना गुण की जांच करें",
+ "sharedExpression": "अभिव्यक्ति",
+ "sharedDevice": "उपकरण",
+ "sharedTest": "Test",
+ "sharedTestNotification": "कसौटी अधिसूचना भेजें",
+ "sharedTestNotificators": "Test Channels",
+ "sharedTestExpression": "Test Expression",
+ "sharedCalendar": "कैलेंडर",
+ "sharedCalendars": "कैलेंडर",
+ "sharedFile": "फ़ाइल",
+ "sharedSearchDevices": "Search Devices",
+ "sharedSortBy": "Sort By",
+ "sharedFilterMap": "Filter on Map",
+ "sharedSelectFile": "फ़ाइल का चयन करें",
+ "sharedPhone": "फ़ोन",
+ "sharedRequired": "अपेक्षित",
+ "sharedPreferences": "प्राथमिकताएं",
+ "sharedPermissions": "अनुमतियां",
+ "sharedConnections": "Connections",
+ "sharedExtra": "अतिरिक्त",
+ "sharedPrimary": "Primary",
+ "sharedSecondary": "Secondary",
+ "sharedTypeString": "String",
+ "sharedTypeNumber": "Number",
+ "sharedTypeBoolean": "Boolean",
+ "sharedTimezone": "समय क्षेत्र",
+ "sharedInfoTitle": "जानकारी",
+ "sharedSavedCommand": "संग्रहीत आदेश",
+ "sharedSavedCommands": "संग्रहीत आदेश",
+ "sharedNew": "नई…",
+ "sharedShowAddress": "पता दिखाएं",
+ "sharedShowDetails": "More Details",
+ "sharedDisabled": "अक्षम करें",
+ "sharedMaintenance": "रखरखाव",
+ "sharedDeviceAccumulators": "Accumulators",
+ "sharedAlarms": "Alarms",
+ "sharedLocation": "Location",
+ "sharedImport": "Import",
+ "sharedColumns": "Columns",
+ "sharedDropzoneText": "Drag and drop a file here or click",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "Simple",
+ "calendarRecurrence": "Recurrence",
+ "calendarOnce": "Once",
+ "calendarDaily": "Daily",
+ "calendarWeekly": "Weekly",
+ "calendarMonthly": "Monthly",
+ "calendarDays": "Days",
+ "calendarSunday": "Sunday",
+ "calendarMonday": "Monday",
+ "calendarTuesday": "Tuesday",
+ "calendarWednesday": "Wednesday",
+ "calendarThursday": "Thursday",
+ "calendarFriday": "Friday",
+ "calendarSaturday": "Saturday",
+ "attributeShowGeofences": "Show Geofences",
+ "attributeSpeedLimit": "गति सीमा",
+ "attributeFuelDropThreshold": "Fuel Drop Threshold",
+ "attributeFuelIncreaseThreshold": "Fuel Increase Threshold",
+ "attributePolylineDistance": "पॉलीलाइन दूरी",
+ "attributeReportIgnoreOdometer": "रिपोर्ट: ओडोमीटर को अनदेखा करें",
+ "attributeWebReportColor": "वेब: रिपोर्ट रंग",
+ "attributeDevicePassword": "उपकरण पासवर्ड",
+ "attributeDeviceImage": "Device Image",
+ "attributeDeviceInactivityStart": "Device Inactivity Start",
+ "attributeDeviceInactivityPeriod": "Device Inactivity Period",
+ "attributeProcessingCopyAttributes": "प्रसंस्करण: गुण कॉपी करें",
+ "attributeColor": "रंग",
+ "attributeWebLiveRouteLength": "वेब: लाइव मार्ग लंबाई",
+ "attributeWebSelectZoom": "वेब: ज़ूम पर चुनना",
+ "attributeWebMaxZoom": "वेब: अधिकतम ज़ूम",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Pushover User Key",
+ "attributePushoverDeviceNames": "Pushover Device Names",
+ "attributeMailSmtpHost": "मेल: एसएमटीपी होस्ट",
+ "attributeMailSmtpPort": "मेल: एसएमटीपी पोर्ट",
+ "attributeMailSmtpStarttlsEnable": "मेल: एसएमटीपी स्टार्ट्स सक्षम करें",
+ "attributeMailSmtpStarttlsRequired": "मेल: एसएमटीपी स्टार्टल्स आवश्यक है",
+ "attributeMailSmtpSslEnable": "मेल: एसएमटीपी एसएसएल सक्षम करें",
+ "attributeMailSmtpSslTrust": "मेल: एसएमटीपी एसएसएल ट्रस्ट",
+ "attributeMailSmtpSslProtocols": "मेल: एसएमटीपी एसएसएल प्रोटोकॉल",
+ "attributeMailSmtpFrom": "मेल: एसएमटीपी से",
+ "attributeMailSmtpAuth": "मेल: एसएमटीपी प्रमाण सक्षम करें",
+ "attributeMailSmtpUsername": "मेल: एसएमटीपी उपयोगकर्ता नाम",
+ "attributeMailSmtpPassword": "मेल: एसएमटीपी पासवर्ड",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: Disable Attributes",
+ "attributeUiDisableGroups": "UI: Disable Groups",
+ "attributeUiDisableEvents": "यूआई: घटनाओं को अक्षम करें",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "यूआई: ड्राइवर्स को अक्षम करें",
+ "attributeUiDisableComputedAttributes": "यूआई: गणना गुणों को अक्षम करें",
+ "attributeUiDisableCalendars": "यूआई: कैलेंडर अक्षम करें",
+ "attributeUiDisableMaintenance": "UI: Disable Maintenance",
+ "attributeUiHidePositionAttributes": "यूआई: स्थिति गुण छुपाएं",
+ "attributeUiDisableLoginLanguage": "UI: Disable Login Language",
+ "attributeNotificationTokens": "Notification Tokens",
+ "attributePopupInfo": "Popup Info",
+ "errorTitle": "त्रुटि",
+ "errorGeneral": "अमान्य पैरामीटर या बाधाओं का उल्लंघन",
+ "errorConnection": "कनेक्शन त्रुटि",
+ "errorSocket": "वेब सॉकेट कनेक्शन त्रुटि",
+ "errorZero": "शून्य नहीं हो सकता",
+ "userEmail": "ईमेल",
+ "userPassword": "पासवर्ड / गोपनीय शब्द",
+ "userAdmin": "एडमिन / व्यवस्थापक",
+ "userRemember": "याद रखें",
+ "userExpirationTime": "समय सीमा समाप्ति",
+ "userDeviceLimit": "उपकरण सीमा",
+ "userUserLimit": "उपयोगकर्ता सीमा",
+ "userDeviceReadonly": "उपकरण केवल पढ़ने के लिए",
+ "userLimitCommands": "सीमा कमांड",
+ "userDisableReports": "Disable Reports",
+ "userFixedEmail": "No Email Change",
+ "userToken": "टोकन",
+ "userDeleteAccount": "Delete Account",
+ "userTemporary": "Temporary",
+ "loginTitle": "लॉगिन / प्रवेश करें",
+ "loginLanguage": "भाषा",
+ "loginReset": "Reset Password",
+ "loginRegister": "रजिस्टर / पंजीकृत करें",
+ "loginLogin": "लॉगिन / प्रवेश करें",
+ "loginOpenId": "Login with OpenID",
+ "loginFailed": "ई-मेल पता या पासवर्ड गलत है",
+ "loginCreated": "नया उपयोगकर्ता पंजीकृत हो गया है",
+ "loginResetSuccess": "Check your email",
+ "loginUpdateSuccess": "New password is set",
+ "loginLogout": "लॉगआउट / निष्कासन करें",
+ "loginLogo": "लोगो",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "उपकरण एवम स्तिति",
+ "deviceSelected": "Selected Device",
+ "deviceTitle": "उपकरण",
+ "devicePrimaryInfo": "Device Title",
+ "deviceSecondaryInfo": "Device Detail",
+ "deviceIdentifier": "पहचानकर्ता",
+ "deviceModel": "आदर्श",
+ "deviceContact": "संपर्क",
+ "deviceCategory": "श्रेणी",
+ "deviceLastUpdate": "आखिरी अद्यतन / ताज़ा जानकारी",
+ "deviceCommand": "आदेश",
+ "deviceFollow": "पालन / अनुकरण करें",
+ "deviceTotalDistance": "कुल दूरी",
+ "deviceStatus": "स्थिति",
+ "deviceStatusOnline": "ऑनलाइन",
+ "deviceStatusOffline": "ऑफ़लाइन",
+ "deviceStatusUnknown": "अज्ञात",
+ "deviceRegisterFirst": "Register your first device",
+ "deviceIdentifierHelp": "IMEI, serial number or other id. It has to match the identifier device reports to the server.",
+ "deviceShare": "Share Device",
+ "groupDialog": "समूह",
+ "groupParent": "समूह",
+ "groupNoGroup": "कोई समूह नहीं",
+ "settingsTitle": "सेटिंग्स",
+ "settingsUser": "अकाउंट / लेखा",
+ "settingsGroups": "ग्रुप्स / एक से अधिक समूह",
+ "settingsServer": "सर्वर",
+ "settingsUsers": "एक से अधिक उपयोगकर्ता",
+ "settingsDistanceUnit": "दूरी इकाई",
+ "settingsAltitudeUnit": "Altitude Unit",
+ "settingsSpeedUnit": "स्पीड यूनिट",
+ "settingsVolumeUnit": "वॉल्यूम यूनिट",
+ "settingsTwelveHourFormat": "12 - घंटा का प्रारूप",
+ "settingsCoordinateFormat": "समन्वय प्रारूप",
+ "settingsServerVersion": "Server Version",
+ "settingsAppVersion": "App Version",
+ "settingsConnection": "Connection",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "रिपोर्ट्स / एक से अधिक रिपोर्ट / विवरण",
+ "reportScheduled": "Scheduled Reports",
+ "reportDevice": "उपकरण",
+ "reportGroup": "समूह",
+ "reportFrom": "से",
+ "reportTo": "तक",
+ "reportShow": "दिखाएं",
+ "reportClear": "स्पष्ट / साफ़",
+ "linkGoogleMaps": "Google Maps",
+ "linkAppleMaps": "Apple Maps",
+ "linkStreetView": "Street View",
+ "positionFixTime": "Fix Time",
+ "positionDeviceTime": "Device Time",
+ "positionServerTime": "Server Time",
+ "positionValid": "मान्य / वैध",
+ "positionAccuracy": "शुद्धता",
+ "positionLatitude": "अक्षांश / अक्षरेखा",
+ "positionLongitude": "देशान्तर",
+ "positionAltitude": "ऊंचाई",
+ "positionSpeed": "गति",
+ "positionCourse": "मार्ग",
+ "positionAddress": "पता",
+ "positionProtocol": "प्रोटोकॉल",
+ "positionDistance": "दूरी",
+ "positionRpm": "आरपीएम",
+ "positionFuel": "ईंधन",
+ "positionPower": "शक्ति",
+ "positionBattery": "बैटरी",
+ "positionRaw": "कच्चा",
+ "positionIndex": "सूची",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "उपग्रहों",
+ "positionSatVisible": "दृश्यमान उपग्रह",
+ "positionRssi": "RSSI",
+ "positionGps": "जीपीएस",
+ "positionRoaming": "रोमिंग",
+ "positionEvent": "घटना",
+ "positionAlarm": "अलार्म",
+ "positionStatus": "स्थिति",
+ "positionOdometer": "ओडोमीटर",
+ "positionServiceOdometer": "सेवा ओडोमीटर",
+ "positionTripOdometer": "ट्रिप ओडोमीटर",
+ "positionHours": "घंटे",
+ "positionSteps": "कदम",
+ "positionInput": "इनपुट",
+ "positionHeartRate": "Heart Rate",
+ "positionOutput": "उत्पादन",
+ "positionBatteryLevel": "बैटरी स्तर",
+ "positionFuelConsumption": "ईंधन उपभोग",
+ "positionRfid": "आरएफआईडी",
+ "positionVersionFw": "फर्मवेयर संस्करण",
+ "positionVersionHw": "हार्डवेयर संस्करण",
+ "positionIgnition": "इग्निशन",
+ "positionFlags": "झंडे",
+ "positionCharge": "चार्ज",
+ "positionIp": "आईपी",
+ "positionArchive": "पुरालेख",
+ "positionVin": "VIN",
+ "positionApproximate": "लगभग",
+ "positionThrottle": "Throttle",
+ "positionMotion": "चलती",
+ "positionArmed": "हथियारबंद",
+ "positionAcceleration": "त्वरण",
+ "positionTemp": "Temperature",
+ "positionDeviceTemp": "उपकरण तापमान",
+ "positionCoolantTemp": "Coolant Temperature",
+ "positionOperator": "ऑपरेटर",
+ "positionCommand": "आदेश",
+ "positionBlocked": "अवरुद्ध",
+ "positionDtcs": "DTCs",
+ "positionObdSpeed": "ओबीडी गति",
+ "positionObdOdometer": "ओबीडी ओडोमीटर",
+ "positionDrivingTime": "Driving Time",
+ "positionDriverUniqueId": "चालक अद्वितीय आईडी",
+ "positionCard": "Card",
+ "positionImage": "छवि",
+ "positionVideo": "Video",
+ "positionAudio": "ऑडियो",
+ "serverTitle": "सर्वर की सेटिंग्स",
+ "serverZoom": "ज़ूम",
+ "serverRegistration": "पंजीकरण",
+ "serverReadonly": "केवल पठीय / पड़ने के लिए",
+ "serverForceSettings": "बल सेटिंग्स",
+ "serverAnnouncement": "Announcement",
+ "serverName": "Server Name",
+ "serverDescription": "Server Description",
+ "serverColorPrimary": "Color Primary",
+ "serverColorSecondary": "Color Secondary",
+ "serverLogo": "Logo Image",
+ "serverLogoInverted": "Inverted Logo Image",
+ "serverChangeDisable": "Disable Server Change",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "मानचित्र",
+ "mapActive": "Active Maps",
+ "mapOverlay": "Map Overlay",
+ "mapOverlayCustom": "Custom Overlay",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API Key",
+ "mapOpenWeatherClouds": "OpenWeather Clouds",
+ "mapOpenWeatherPrecipitation": "OpenWeather Precipitation",
+ "mapOpenWeatherPressure": "OpenWeather Pressure",
+ "mapOpenWeatherWind": "OpenWeather Wind",
+ "mapOpenWeatherTemperature": "OpenWeather Temperature",
+ "mapLayer": "मानचित्र की परत",
+ "mapCustom": "Custom (XYZ)",
+ "mapCustomArcgis": "Custom (ArcGIS)",
+ "mapCustomLabel": "Custom map",
+ "mapCarto": "Carto Basemaps",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "बिंग मैप्स की कुंजी",
+ "mapBingRoad": "बिंग मैप्स रोड / सड़क",
+ "mapBingAerial": "बिंग मैप्स एरियल",
+ "mapBingHybrid": "बिंग मैप्स हाइब्रिड",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "यांडेक्स मानचित्र",
+ "mapYandexSat": "यांडेक्स सैटेलाइट",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox Streets",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox Outdoors",
+ "mapMapboxSatellite": "Mapbox Satellite",
+ "mapMapboxKey": "Mapbox Access Token",
+ "mapMapTilerBasic": "MapTiler Basic",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "MapTiler API Key",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ Access Token",
+ "mapTomTomBasic": "TomTom Basic",
+ "mapTomTomFlow": "TomTom Traffic Flow",
+ "mapTomTomIncidents": "TomTom Traffic Incidents",
+ "mapTomTomKey": "TomTom API Key",
+ "mapHereBasic": "Here Basic",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satellite",
+ "mapHereFlow": "Here Traffic Flow",
+ "mapHereKey": "Here API Key",
+ "mapShapePolygon": "बहुभुज",
+ "mapShapeCircle": "वृत्त",
+ "mapShapePolyline": "पाली लाइन",
+ "mapLiveRoutes": "लाइव मार्ग",
+ "mapDirection": "Show Direction",
+ "mapCurrentLocation": "Current Location",
+ "mapPoiLayer": "पीओआई परत",
+ "mapClustering": "Markers Clustering",
+ "mapOnSelect": "Show Map on Selection",
+ "mapDefault": "Default Map",
+ "stateTitle": "स्थिति / अवस्ता",
+ "stateName": "गुण",
+ "stateValue": "मान / वेल्यु",
+ "commandTitle": "आदेश",
+ "commandSend": "भेजें / प्रेषित करें",
+ "commandSent": "Command sent",
+ "commandQueued": "Command queued",
+ "commandUnit": "इकाई",
+ "commandCustom": "विशिष्ट रूप से निर्मित मेसेज / संदेश",
+ "commandDeviceIdentification": "उपकरण की पहचान",
+ "commandPositionSingle": "एक बार की रिपोर्टिंग",
+ "commandPositionPeriodic": "आवधिक रिपोर्टिंग",
+ "commandPositionStop": "रिपोर्टिंग रोकें",
+ "commandEngineStop": "इंजन बंद",
+ "commandEngineResume": "इंजन फिर से शुरू",
+ "commandAlarmArm": "अलार्म लगाएं",
+ "commandAlarmDisarm": "अलार्म हटाएं",
+ "commandAlarmDismiss": "Dismiss Alarm",
+ "commandSetTimezone": "समय क्षेत्र सेट करें",
+ "commandRequestPhoto": "फोटो मँगवाएं",
+ "commandPowerOff": "Power Off Device",
+ "commandRebootDevice": "उपकरण बंद करके पुन्ह आरंभ करें",
+ "commandFactoryReset": "Factory Reset",
+ "commandSendSms": "एसएमएस भेजें / प्रेषित करें",
+ "commandSendUssd": "यूएसएसडी कोड भेजें / प्रेषित करें",
+ "commandSosNumber": "एसओएस / एमर्जेन्सी फोन नंबर सेट करें",
+ "commandSilenceTime": "खामोशी का समय सेट करें",
+ "commandSetPhonebook": "फोनबुक सेट करें",
+ "commandVoiceMessage": "ध्वनि संदेश / वाय्स मेसेज",
+ "commandOutputControl": "आउटपुट नियंत्रण",
+ "commandVoiceMonitoring": "आवाज निगरानी",
+ "commandSetAgps": "एजीपीएस सेट करें",
+ "commandSetIndicator": "संकेतक सेट करें",
+ "commandConfiguration": "विन्यास",
+ "commandGetVersion": "संस्करण प्राप्त करें",
+ "commandFirmwareUpdate": "फर्मवेयर अपडेट करें",
+ "commandSetConnection": "कनेक्शन सेट करें",
+ "commandSetOdometer": "ओडोमीटर सेट करें",
+ "commandGetModemStatus": "मोडेम स्थिति प्राप्त करें",
+ "commandGetDeviceStatus": "उपकरण स्थिति प्राप्त करें",
+ "commandSetSpeedLimit": "Set Speed Limit",
+ "commandModePowerSaving": "Power Saving Mode",
+ "commandModeDeepSleep": "Deep Sleep Mode",
+ "commandAlarmGeofence": "Set Geofence Alarm",
+ "commandAlarmBattery": "Set Battery Alarm",
+ "commandAlarmSos": "Set SOS Alarm",
+ "commandAlarmRemove": "Set Remove Alarm",
+ "commandAlarmClock": "Set Clock Alarm",
+ "commandAlarmSpeed": "Set Speed Alarm",
+ "commandAlarmFall": "Set Fall Alarm",
+ "commandAlarmVibration": "Set Vibration Alarm",
+ "commandFrequency": "फ्रीक्वेंसी / आवृत्ति",
+ "commandTimezone": "टाइमज़ोन ऑफ़सेट",
+ "commandMessage": "संदेश / मेसेज",
+ "commandRadius": "त्रिज्या",
+ "commandEnable": "सक्षम करें",
+ "commandData": "डाटा / आंकड़े",
+ "commandIndex": "अनुक्रमणिका",
+ "commandPhone": "फोन नंबर",
+ "commandServer": "सर्वर",
+ "commandPort": "पोर्ट",
+ "eventAll": "सभी घटनाएँ / इवेंट्स",
+ "eventDeviceOnline": "Status online",
+ "eventDeviceUnknown": "Status unknown",
+ "eventDeviceOffline": "Status offline",
+ "eventDeviceInactive": "Device inactive",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "Device moving",
+ "eventDeviceStopped": "Device stopped",
+ "eventDeviceOverspeed": "Speed limit exceeded",
+ "eventDeviceFuelDrop": "Fuel drop",
+ "eventDeviceFuelIncrease": "Fuel increase",
+ "eventCommandResult": "आदेश / क्मांड का परिणाम",
+ "eventGeofenceEnter": "Geofence entered",
+ "eventGeofenceExit": "Geofence exited",
+ "eventAlarm": "Alarm",
+ "eventIgnitionOn": "Ignition on",
+ "eventIgnitionOff": "Ignition off",
+ "eventMaintenance": "रखरखाव की आवश्यकता है",
+ "eventTextMessage": "पाठ संदेश प्राप्त हुआ",
+ "eventDriverChanged": "Driver changed",
+ "eventMedia": "Media",
+ "eventsScrollToLast": "अंतिम तक स्क्रॉल करें",
+ "eventsSoundEvents": "Sound Events",
+ "eventsSoundAlarms": "Sound Alarms",
+ "alarmGeneral": "General",
+ "alarmSos": "SOS",
+ "alarmVibration": "Vibration",
+ "alarmMovement": "Movement",
+ "alarmLowspeed": "Low Speed",
+ "alarmOverspeed": "Overspeed",
+ "alarmFallDown": "Fall Down",
+ "alarmLowPower": "Low Power",
+ "alarmLowBattery": "Low Battery",
+ "alarmFault": "Fault",
+ "alarmPowerOff": "Power Off",
+ "alarmPowerOn": "Power On",
+ "alarmDoor": "Door",
+ "alarmLock": "Lock",
+ "alarmUnlock": "Unlock",
+ "alarmGeofence": "Geofence",
+ "alarmGeofenceEnter": "Geofence Enter",
+ "alarmGeofenceExit": "Geofence Exit",
+ "alarmGpsAntennaCut": "GPS Antenna Cut",
+ "alarmAccident": "Accident",
+ "alarmTow": "Tow",
+ "alarmIdle": "Idle",
+ "alarmHighRpm": "High RPM",
+ "alarmHardAcceleration": "Hard Acceleration",
+ "alarmHardBraking": "Hard Braking",
+ "alarmHardCornering": "Hard Cornering",
+ "alarmLaneChange": "Lane Change",
+ "alarmFatigueDriving": "Fatigue Driving",
+ "alarmPowerCut": "Power Cut",
+ "alarmPowerRestored": "Power Restored",
+ "alarmJamming": "Jamming",
+ "alarmTemperature": "Temperature",
+ "alarmParking": "Parking",
+ "alarmBonnet": "Bonnet",
+ "alarmFootBrake": "Foot Brake",
+ "alarmFuelLeak": "Fuel Leak",
+ "alarmTampering": "Tampering",
+ "alarmRemoving": "Removing",
+ "notificationType": "सूचना का प्रकार",
+ "notificationAlways": "सभी उपकरण",
+ "notificationNotificators": "Channels",
+ "notificatorCommand": "Command",
+ "notificatorWeb": "Web",
+ "notificatorMail": "Mail",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Replay",
+ "reportCombined": "Combined",
+ "reportRoute": "मार्ग",
+ "reportEvents": "घटनाएँ / इवेंट्स",
+ "reportTrips": "यात्राएँ / ट्रिप्स",
+ "reportStops": "Stops",
+ "reportSummary": "सारांश",
+ "reportDaily": "Daily Summary",
+ "reportChart": "चार्ट",
+ "reportConfigure": "समनुरूप / कन्फिगर करें",
+ "reportEventTypes": "घटनाओं / इवेंट्स के प्रकार",
+ "reportChartType": "चार्ट टाइप",
+ "reportShowMarkers": "मार्कर दिखाएं",
+ "reportExport": "निर्यात",
+ "reportEmail": "Email Report",
+ "reportSchedule": "Schedule",
+ "reportPeriod": "अवधि",
+ "reportCustom": "Custom",
+ "reportToday": "आज",
+ "reportYesterday": "कल",
+ "reportThisWeek": "इस सप्ताह",
+ "reportPreviousWeek": "पिछले सप्ताह",
+ "reportThisMonth": "इस महीने",
+ "reportPreviousMonth": "पिछले महीने",
+ "reportDeviceName": "उपकरण / उपकरण का नाम",
+ "reportAverageSpeed": "औसत गति",
+ "reportMaximumSpeed": "अधिकतम गति",
+ "reportEngineHours": "इंजन के घंटे",
+ "reportDuration": "अवधि",
+ "reportStartDate": "Start Date",
+ "reportStartTime": "समय का आरंभ",
+ "reportStartAddress": "प्रारंभ पता",
+ "reportEndTime": "अंतिम समय",
+ "reportEndAddress": "अंत पता",
+ "reportSpentFuel": "ईंधन खर्च",
+ "reportStartOdometer": "Odometer Start",
+ "reportEndOdometer": "Odometer End",
+ "statisticsTitle": "आंकड़े",
+ "statisticsCaptureTime": "समय कैप्चर करें",
+ "statisticsActiveUsers": "सक्रिय उपयोगकर्ता",
+ "statisticsActiveDevices": "सक्रिय उपकरण",
+ "statisticsRequests": "अनुरोध",
+ "statisticsMessagesReceived": "संदेश प्राप्त हुए",
+ "statisticsMessagesStored": "संदेश संग्रहीत",
+ "statisticsGeocoder": "जियोकोडर अनुरोध",
+ "statisticsGeolocation": "जियोलोकेशन अनुरोध",
+ "categoryArrow": "तीर",
+ "categoryDefault": "Default",
+ "categoryAnimal": "पशु",
+ "categoryBicycle": "साइकिल",
+ "categoryBoat": "नाव",
+ "categoryBus": "बस",
+ "categoryCar": "कार",
+ "categoryCamper": "Camper",
+ "categoryCrane": "क्रेन",
+ "categoryHelicopter": "हेलीकाप्टर",
+ "categoryMotorcycle": "मोटरसाइकिल",
+ "categoryOffroad": "Offroad",
+ "categoryPerson": "व्यक्ति",
+ "categoryPickup": "Pickup",
+ "categoryPlane": "विमान",
+ "categoryShip": "जहाज़",
+ "categoryTractor": "ट्रैक्टर",
+ "categoryTrain": "Train",
+ "categoryTram": "Tram",
+ "categoryTrolleybus": "Trolleybus",
+ "categoryTruck": "ट्रक",
+ "categoryVan": "वैन",
+ "categoryScooter": "Scooter",
+ "maintenanceStart": "प्रारंभ",
+ "maintenancePeriod": "अवधि"
+} \ No newline at end of file
diff --git a/src/resources/l10n/hr.json b/src/resources/l10n/hr.json
new file mode 100644
index 00000000..f64cdb04
--- /dev/null
+++ b/src/resources/l10n/hr.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "Učitavanje...",
+ "sharedHide": "Sakrij",
+ "sharedSave": "Spremi",
+ "sharedUpload": "Upload",
+ "sharedSet": "Postavi",
+ "sharedCancel": "Poništi",
+ "sharedCopy": "Copy",
+ "sharedAdd": "Dodaj",
+ "sharedEdit": "Uredi",
+ "sharedRemove": "Ukloni",
+ "sharedRemoveConfirm": "Ukloniti stavku?",
+ "sharedNoData": "Nema podataka",
+ "sharedSubject": "Subject",
+ "sharedYes": "Da",
+ "sharedNo": "Ne",
+ "sharedKm": "km",
+ "sharedMi": "mi",
+ "sharedNmi": "nmi",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "čv",
+ "sharedKmh": "km/h",
+ "sharedMph": "mph",
+ "sharedHour": "Sat",
+ "sharedMinute": "Minuta",
+ "sharedSecond": "Sekunda",
+ "sharedDays": "dani",
+ "sharedHours": "sati",
+ "sharedMinutes": "minuta",
+ "sharedDecimalDegrees": "Decimalni stupnjevi",
+ "sharedDegreesDecimalMinutes": "Stupnjevi u decimalnim minutama",
+ "sharedDegreesMinutesSeconds": "Stupnjevi u minutama i sekundama",
+ "sharedName": "Ime",
+ "sharedDescription": "Opis",
+ "sharedSearch": "Pretraži",
+ "sharedIconScale": "Skaliraj ikonu",
+ "sharedGeofence": "Geo-ograda",
+ "sharedGeofences": "Geo-ograde",
+ "sharedCreateGeofence": "Kreiraj geo-ogradu",
+ "sharedNotifications": "Obavijesti",
+ "sharedNotification": "Obavijest",
+ "sharedAttributes": "Atributi",
+ "sharedAttribute": "Atribut",
+ "sharedDrivers": "Vozači",
+ "sharedDriver": "Vozač",
+ "sharedArea": "Područje",
+ "sharedSound": "Zvuk obavijesti",
+ "sharedType": "Tip",
+ "sharedDistance": "Udaljenost",
+ "sharedHourAbbreviation": "h",
+ "sharedMinuteAbbreviation": "m",
+ "sharedSecondAbbreviation": "s",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "l",
+ "sharedGallonAbbreviation": "galon",
+ "sharedLiter": "Litra",
+ "sharedImpGallon": "Imperijalni galon",
+ "sharedUsGallon": "američki galon",
+ "sharedLiterPerHourAbbreviation": "l/h",
+ "sharedGetMapState": "Dohvati stanje karte",
+ "sharedComputedAttribute": "Izračunati atribut",
+ "sharedComputedAttributes": "Izračunati atributi",
+ "sharedCheckComputedAttribute": "Provjeri izračunati atribut",
+ "sharedExpression": "Izraz",
+ "sharedDevice": "Uređaj",
+ "sharedTest": "Test",
+ "sharedTestNotification": "Pošalji testnu obavijest",
+ "sharedTestNotificators": "Testiraj kanale",
+ "sharedTestExpression": "Testiraj izraz",
+ "sharedCalendar": "Kalendar",
+ "sharedCalendars": "Kalendari",
+ "sharedFile": "Datoteka",
+ "sharedSearchDevices": "Traži uređaje",
+ "sharedSortBy": "Sortiraj po",
+ "sharedFilterMap": "Filtriraj na karti",
+ "sharedSelectFile": "Odaberi datoteku",
+ "sharedPhone": "Telefon",
+ "sharedRequired": "Potrebno",
+ "sharedPreferences": "Postavke",
+ "sharedPermissions": "Dozvole",
+ "sharedConnections": "Veze",
+ "sharedExtra": "Extra",
+ "sharedPrimary": "Primarno",
+ "sharedSecondary": "Sekundarno",
+ "sharedTypeString": "String",
+ "sharedTypeNumber": "Broj",
+ "sharedTypeBoolean": "Boolean",
+ "sharedTimezone": "Vremenska zona",
+ "sharedInfoTitle": "Info",
+ "sharedSavedCommand": "Snimljena naredba",
+ "sharedSavedCommands": "Snimljene naredbe",
+ "sharedNew": "Novo",
+ "sharedShowAddress": "Prikaži adresu",
+ "sharedShowDetails": "Više detalja",
+ "sharedDisabled": "Isključeno",
+ "sharedMaintenance": "Održavanje",
+ "sharedDeviceAccumulators": "Varijable",
+ "sharedAlarms": "Alarmi",
+ "sharedLocation": "Lokacija",
+ "sharedImport": "Uvoz",
+ "sharedColumns": "Stupac",
+ "sharedDropzoneText": "Dovuci i pusti datoteku ovdje ili klikni",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "Jednostavno",
+ "calendarRecurrence": "Ponavljajuće",
+ "calendarOnce": "Jednom",
+ "calendarDaily": "Dnevno",
+ "calendarWeekly": "Tjedno",
+ "calendarMonthly": "Mjesečno",
+ "calendarDays": "Dani",
+ "calendarSunday": "Nedjelja",
+ "calendarMonday": "Ponedjeljak",
+ "calendarTuesday": "Utorak",
+ "calendarWednesday": "Srijeda",
+ "calendarThursday": "Četvrtak",
+ "calendarFriday": "Petak",
+ "calendarSaturday": "Subota",
+ "attributeShowGeofences": "Pokaži geo-ograde",
+ "attributeSpeedLimit": "Ograničenje brzine",
+ "attributeFuelDropThreshold": "Gorivo je ispod zadane vrijednosti",
+ "attributeFuelIncreaseThreshold": "Gorivo je iznad zadane vrijednosti",
+ "attributePolylineDistance": "Udaljenost linijom",
+ "attributeReportIgnoreOdometer": "Izvještaj: ignoriraj kilometražu",
+ "attributeWebReportColor": "Web: Boja izvještaja",
+ "attributeDevicePassword": "Lozinka uređaja",
+ "attributeDeviceImage": "Slika uređaja",
+ "attributeDeviceInactivityStart": "Neaktivnost uređaja",
+ "attributeDeviceInactivityPeriod": "Vrijeme neaktivnosti uređaja",
+ "attributeProcessingCopyAttributes": "Obrada: Kopiraj atribute",
+ "attributeColor": "Boja",
+ "attributeWebLiveRouteLength": "Web: Udaljenost rute uživo",
+ "attributeWebSelectZoom": "Web: Približi kod odabira",
+ "attributeWebMaxZoom": "Web: Maksimalni zoom",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Proslijedi ključ korisnika",
+ "attributePushoverDeviceNames": "Proslijedi imena uređaja",
+ "attributeMailSmtpHost": "Mail: SMTP poslužitelj",
+ "attributeMailSmtpPort": "Mail: SMTP port",
+ "attributeMailSmtpStarttlsEnable": "Mail: SMTP STARTTLS uključen",
+ "attributeMailSmtpStarttlsRequired": "Mail: SMTP STARTTLS potreban",
+ "attributeMailSmtpSslEnable": "Mail: SMTP SSL uključen",
+ "attributeMailSmtpSslTrust": "Mail: SMTP SSL Trust",
+ "attributeMailSmtpSslProtocols": "Mail: SMTP SSL protokoli",
+ "attributeMailSmtpFrom": "Mail: SMTP pošiljatelj",
+ "attributeMailSmtpAuth": "Mail: SMTP uključi autentikaciju",
+ "attributeMailSmtpUsername": "Mail: SMTP korisničko ime",
+ "attributeMailSmtpPassword": "Mail: SMTP lozinka",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: Onemogući atribute",
+ "attributeUiDisableGroups": "UI: Onemogući grupe",
+ "attributeUiDisableEvents": "UI: Isključi događaje",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "UI: Isključi vozače",
+ "attributeUiDisableComputedAttributes": "UI: Isključi izračunate atribute",
+ "attributeUiDisableCalendars": "UI: Isključi kalendare",
+ "attributeUiDisableMaintenance": "UI: Isključi održavanje",
+ "attributeUiHidePositionAttributes": "UI: Sakrij atribute pozicioniranja",
+ "attributeUiDisableLoginLanguage": "UI: Onemogući jezik prilikom logina",
+ "attributeNotificationTokens": "Token za notifikacije",
+ "attributePopupInfo": "Popup info",
+ "errorTitle": "Greška",
+ "errorGeneral": "Nevažeći parametri ili kršenje ograničenja",
+ "errorConnection": "Greška konekcije",
+ "errorSocket": "WebSocket konekcijska greška",
+ "errorZero": "Ne može biti nula.",
+ "userEmail": "E-mail",
+ "userPassword": "Lozinka",
+ "userAdmin": "Administrator",
+ "userRemember": "Zapamti",
+ "userExpirationTime": "Vrijeme isteka",
+ "userDeviceLimit": "Maksimalni broj uređaja",
+ "userUserLimit": "Maksimalni broj korisnika",
+ "userDeviceReadonly": "Onemogući dodavanje uređaja",
+ "userLimitCommands": "Ograniči komande",
+ "userDisableReports": "Onemogući izvještaje",
+ "userFixedEmail": "Zabrani promjenu e-pošte",
+ "userToken": "Token",
+ "userDeleteAccount": "Obriši račun",
+ "userTemporary": "Temporary",
+ "loginTitle": "Prijava",
+ "loginLanguage": "Jezik",
+ "loginReset": "Ponovo postavi lozinku",
+ "loginRegister": "Registracija",
+ "loginLogin": "Prijava",
+ "loginOpenId": "Prijavi se sa OpenID-om",
+ "loginFailed": "Netočna e-mail adresa ili password",
+ "loginCreated": "Registiran je novi korisnik",
+ "loginResetSuccess": "Provjeri e-poštu",
+ "loginUpdateSuccess": "Postavljena je nova lozinka",
+ "loginLogout": "Odjava",
+ "loginLogo": "Logo",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "Uređaji i stanje",
+ "deviceSelected": "Odabrani uređaj",
+ "deviceTitle": "Uređaji",
+ "devicePrimaryInfo": "Ime uređaja",
+ "deviceSecondaryInfo": "Detalji uređaja",
+ "deviceIdentifier": "Identifikator",
+ "deviceModel": "Model",
+ "deviceContact": "Kontakt",
+ "deviceCategory": "Kategorija",
+ "deviceLastUpdate": "Zadnje ažuriranje",
+ "deviceCommand": "Naredba",
+ "deviceFollow": "Prati",
+ "deviceTotalDistance": "Ukupna udaljenost",
+ "deviceStatus": "Status",
+ "deviceStatusOnline": "Online",
+ "deviceStatusOffline": "Offline",
+ "deviceStatusUnknown": "Nepoznato",
+ "deviceRegisterFirst": "Prijavi svoj prvi uređaj",
+ "deviceIdentifierHelp": "IMEI, serijski broj ili drugi ID mora biti jednak identifikatoru uređaja koji se prijavljuje na poslužitelj",
+ "deviceShare": "Share Device",
+ "groupDialog": "Grupa",
+ "groupParent": "Grupa",
+ "groupNoGroup": "Bez grupe",
+ "settingsTitle": "Postavke",
+ "settingsUser": "Račun",
+ "settingsGroups": "Grupe",
+ "settingsServer": "Server",
+ "settingsUsers": "Korisnici",
+ "settingsDistanceUnit": "Jedinica udaljenosti",
+ "settingsAltitudeUnit": "Mjerna jedinica za visinu",
+ "settingsSpeedUnit": "Jedinica brzine",
+ "settingsVolumeUnit": "Mjerna jedinica za volumen",
+ "settingsTwelveHourFormat": "12-satno vrijeme",
+ "settingsCoordinateFormat": "Format koordinata",
+ "settingsServerVersion": "Verzija poslužitelja",
+ "settingsAppVersion": "Verzija aplikacije",
+ "settingsConnection": "Veza",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "Izvještaji",
+ "reportScheduled": "Redovni izvještaj",
+ "reportDevice": "Uređaj",
+ "reportGroup": "Grupa",
+ "reportFrom": "Od",
+ "reportTo": "Za",
+ "reportShow": "Prikaži",
+ "reportClear": "Očisti",
+ "linkGoogleMaps": "Google Maps",
+ "linkAppleMaps": "Apple Maps",
+ "linkStreetView": "Street View",
+ "positionFixTime": "Fiksno vrijeme",
+ "positionDeviceTime": "Vrijeme na uređaju",
+ "positionServerTime": "Vrijeme na poslužitelju",
+ "positionValid": "Važeće",
+ "positionAccuracy": "Preciznost",
+ "positionLatitude": "Širina",
+ "positionLongitude": "Dužina",
+ "positionAltitude": "Visina",
+ "positionSpeed": "Brzina",
+ "positionCourse": "Kurs",
+ "positionAddress": "Adresa",
+ "positionProtocol": "Protokol",
+ "positionDistance": "Udaljenost",
+ "positionRpm": "O/min",
+ "positionFuel": "Gorivo",
+ "positionPower": "Napajanje",
+ "positionBattery": "Baterija",
+ "positionRaw": "Sirovi podaci",
+ "positionIndex": "Index",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "Sateliti",
+ "positionSatVisible": "Vidljivi sateliti",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "Roming",
+ "positionEvent": "Događaj",
+ "positionAlarm": "Alarm",
+ "positionStatus": "Stanje",
+ "positionOdometer": "Kilometraža",
+ "positionServiceOdometer": "Kilometara do servisa",
+ "positionTripOdometer": "Kilometraža puta",
+ "positionHours": "Sati",
+ "positionSteps": "Koraci",
+ "positionInput": "Ulaz",
+ "positionHeartRate": "Otkucaji srca",
+ "positionOutput": "Izlaz",
+ "positionBatteryLevel": "Razina baterije",
+ "positionFuelConsumption": "Potrošnja goriva",
+ "positionRfid": "RFID",
+ "positionVersionFw": "Verzija firmwarea",
+ "positionVersionHw": "Verzija hadrvera",
+ "positionIgnition": "Paljenje",
+ "positionFlags": "Zastavice",
+ "positionCharge": "Punjenje",
+ "positionIp": "IP",
+ "positionArchive": "Arhiva",
+ "positionVin": "VIN",
+ "positionApproximate": "Približno",
+ "positionThrottle": "Gas",
+ "positionMotion": "Kretanje",
+ "positionArmed": "Aktivirano",
+ "positionAcceleration": "Ubrzavanje",
+ "positionTemp": "Temperatura",
+ "positionDeviceTemp": "Temperatura uređaja",
+ "positionCoolantTemp": "Temperatura rashladne tekućine",
+ "positionOperator": "Operater",
+ "positionCommand": "Naredba",
+ "positionBlocked": "Blokirano",
+ "positionDtcs": "DTC kodovi",
+ "positionObdSpeed": "OBD brzina",
+ "positionObdOdometer": "OBD kilometraža",
+ "positionDrivingTime": "Vrijeme vožnje",
+ "positionDriverUniqueId": "Vozačev jedinstveni ID",
+ "positionCard": "Kartica",
+ "positionImage": "Slika",
+ "positionVideo": "Video",
+ "positionAudio": "Audio",
+ "serverTitle": "Postavke poslužitelja",
+ "serverZoom": "Povećanje",
+ "serverRegistration": "Registracija",
+ "serverReadonly": "Samo pregledavanje",
+ "serverForceSettings": "Nametni postavke",
+ "serverAnnouncement": "Najava",
+ "serverName": "Ime poslužitelja",
+ "serverDescription": "Opis poslužitelja",
+ "serverColorPrimary": "Primarna boja",
+ "serverColorSecondary": "Sekundarna boja",
+ "serverLogo": "Logo",
+ "serverLogoInverted": "Inverted Logo Image",
+ "serverChangeDisable": "Disable Server Change",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "Karta",
+ "mapActive": "Aktivna karta",
+ "mapOverlay": "Prekrivajuća karta",
+ "mapOverlayCustom": "Prilagođeno prekrivanje",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API ključ",
+ "mapOpenWeatherClouds": "OpenWeather oblaci",
+ "mapOpenWeatherPrecipitation": "OpenWeather oborine",
+ "mapOpenWeatherPressure": "OpenWeather tlak",
+ "mapOpenWeatherWind": "OpenWeather vjetar",
+ "mapOpenWeatherTemperature": "OpenWeather temperatura",
+ "mapLayer": "Sloj karte",
+ "mapCustom": "Prilagođeno (XYZ)",
+ "mapCustomArcgis": "Prilagođeno (ArcGIS)",
+ "mapCustomLabel": "Prilagođena karta",
+ "mapCarto": "Carto Basemaps",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Bing Maps Key",
+ "mapBingRoad": "Bing Maps Road",
+ "mapBingAerial": "Bing Maps Aerial",
+ "mapBingHybrid": "Bing Maps Hybrid",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Yandex Map",
+ "mapYandexSat": "Yandex Satellite",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox ulice",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox izvana",
+ "mapMapboxSatellite": "Mapbox satelit",
+ "mapMapboxKey": "Mapbox pristupni ključ",
+ "mapMapTilerBasic": "MapTiler osnovno",
+ "mapMapTilerHybrid": "MapTiler hibrid",
+ "mapMapTilerKey": "MapTiler API ključ",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ pristupni ključ",
+ "mapTomTomBasic": "TomTom osnovno",
+ "mapTomTomFlow": "TomTom prikaz prometa",
+ "mapTomTomIncidents": "TomTom prometne nesreće",
+ "mapTomTomKey": "TomTom API ključ",
+ "mapHereBasic": "Here osnovno",
+ "mapHereHybrid": "Here hibridno",
+ "mapHereSatellite": "Here satelit",
+ "mapHereFlow": "Here prikaz prometa",
+ "mapHereKey": "Here API ključ",
+ "mapShapePolygon": "Mnogokut",
+ "mapShapeCircle": "Krug",
+ "mapShapePolyline": "Razlomljena linija",
+ "mapLiveRoutes": "Rute uživo",
+ "mapDirection": "Pokaži smjer",
+ "mapCurrentLocation": "Trenutna lokacija",
+ "mapPoiLayer": "Točke interesa sloj",
+ "mapClustering": "Organizacija markera",
+ "mapOnSelect": "Prikaži kartu na odabranom",
+ "mapDefault": "Zadana karta",
+ "stateTitle": "Stanje",
+ "stateName": "Atribut",
+ "stateValue": "Vrijednost",
+ "commandTitle": "Naredba",
+ "commandSend": "Pošalji",
+ "commandSent": "Naredba poslana",
+ "commandQueued": "Naredba u redu čekanja",
+ "commandUnit": "Jedinica",
+ "commandCustom": "Prilagođena naredba",
+ "commandDeviceIdentification": "Identifikacija uređaja",
+ "commandPositionSingle": "Jedan izvještaj",
+ "commandPositionPeriodic": "Periodično izvješćivanje",
+ "commandPositionStop": "Zaustavi izvješćivanje",
+ "commandEngineStop": "Zaustavi motor",
+ "commandEngineResume": "Pokreni motor",
+ "commandAlarmArm": "Aktiviraj alarm",
+ "commandAlarmDisarm": "Deaktiviraj alarm",
+ "commandAlarmDismiss": "Zanemari alarm",
+ "commandSetTimezone": "Postavi vremensku zonu",
+ "commandRequestPhoto": "Zatraži sliku",
+ "commandPowerOff": "Isključi uređaj",
+ "commandRebootDevice": "Ponovno pokretanje uređaja",
+ "commandFactoryReset": "Tvorničko postavljanje",
+ "commandSendSms": "Pošalji SMS",
+ "commandSendUssd": "Pošalji USSD",
+ "commandSosNumber": "Postavi SOS broj",
+ "commandSilenceTime": "Postavi vrijeme tišine",
+ "commandSetPhonebook": "Postavi imenik",
+ "commandVoiceMessage": "Glasovna poruka",
+ "commandOutputControl": "Kontrola izlaza",
+ "commandVoiceMonitoring": "Nadzor glasa",
+ "commandSetAgps": "Postavi AGPS",
+ "commandSetIndicator": "Postavi indikator",
+ "commandConfiguration": "Konfiguracija",
+ "commandGetVersion": "Provjeri verzij",
+ "commandFirmwareUpdate": "Nadogradi firmware",
+ "commandSetConnection": "Postavi vezu",
+ "commandSetOdometer": "Postavi odometar",
+ "commandGetModemStatus": "Dohvati status modema",
+ "commandGetDeviceStatus": "Dohvati status uređaja",
+ "commandSetSpeedLimit": "Postavi ograničenje brzine",
+ "commandModePowerSaving": "Način uštede energije",
+ "commandModeDeepSleep": "Način dubokog sna",
+ "commandAlarmGeofence": "Postavi alarm za geo-ogradu",
+ "commandAlarmBattery": "Postavi alarm za bateriju",
+ "commandAlarmSos": "Postavi SOS alarm",
+ "commandAlarmRemove": "Ukloni alarm",
+ "commandAlarmClock": "Postavi alarm na vrijeme",
+ "commandAlarmSpeed": "Postavi alarm za brzinu",
+ "commandAlarmFall": "Postavi alarm za pad",
+ "commandAlarmVibration": "Postavi alarm za vribracije",
+ "commandFrequency": "Učestalost",
+ "commandTimezone": "Korekcija vremenske zone",
+ "commandMessage": "Poruka",
+ "commandRadius": "Radius",
+ "commandEnable": "Uključ",
+ "commandData": "Podaci",
+ "commandIndex": "Indeks",
+ "commandPhone": "Telefonski broj",
+ "commandServer": "Poslužitelj",
+ "commandPort": "Port",
+ "eventAll": "Svi događaji",
+ "eventDeviceOnline": "Status online",
+ "eventDeviceUnknown": "Status nepoznat",
+ "eventDeviceOffline": "Status offline",
+ "eventDeviceInactive": "Uređaj neaktivan",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "Uređaj se pomiče",
+ "eventDeviceStopped": "Uređaj zaustavljen",
+ "eventDeviceOverspeed": "Prekoračeno ograničenje brzine",
+ "eventDeviceFuelDrop": "Pad količine goriva",
+ "eventDeviceFuelIncrease": "Porast goriva",
+ "eventCommandResult": "Rezultat naredbe",
+ "eventGeofenceEnter": "Ulaz u geo-ogradu",
+ "eventGeofenceExit": "Izlaz iz geo-ograde",
+ "eventAlarm": "Alarm",
+ "eventIgnitionOn": "Vozilo upaljeno",
+ "eventIgnitionOff": "Vozilo isključeno",
+ "eventMaintenance": "Potrebno održavanje",
+ "eventTextMessage": "Primljena tekstualna poruka",
+ "eventDriverChanged": "Vozač promijenjen",
+ "eventMedia": "Media",
+ "eventsScrollToLast": "Pomakni na zadnji",
+ "eventsSoundEvents": "Zvučni događaji",
+ "eventsSoundAlarms": "Zvučni alarm",
+ "alarmGeneral": "Općenito",
+ "alarmSos": "SOS",
+ "alarmVibration": "Vibracija",
+ "alarmMovement": "Pokret",
+ "alarmLowspeed": "Niska brzina",
+ "alarmOverspeed": "Prekoračena brzina",
+ "alarmFallDown": "Pad",
+ "alarmLowPower": "Niska energija",
+ "alarmLowBattery": "Nizak napon baterije",
+ "alarmFault": "Greška",
+ "alarmPowerOff": "Gašenje vozila",
+ "alarmPowerOn": "Paljenje vozila",
+ "alarmDoor": "Vrata",
+ "alarmLock": "Zaključavanje",
+ "alarmUnlock": "Otključavanje",
+ "alarmGeofence": "Geo-ograda",
+ "alarmGeofenceEnter": "Ulaz u geo-ogradu",
+ "alarmGeofenceExit": "Izlaz iz geo-ograde",
+ "alarmGpsAntennaCut": "GPS antena uklonjena",
+ "alarmAccident": "Nesreća",
+ "alarmTow": "Vuča",
+ "alarmIdle": "Prazan hod",
+ "alarmHighRpm": "Visoki okretaji",
+ "alarmHardAcceleration": "Naglo kretanje",
+ "alarmHardBraking": "Naglo kočenje",
+ "alarmHardCornering": "Nagli ulaz u zavoj",
+ "alarmLaneChange": "Promjena trake",
+ "alarmFatigueDriving": "Umorna vožnja",
+ "alarmPowerCut": "Napajanje uklonjeno",
+ "alarmPowerRestored": "Napajanje vraćeno",
+ "alarmJamming": "Ometanje",
+ "alarmTemperature": "Temperatura",
+ "alarmParking": "Parking",
+ "alarmBonnet": "Hauba",
+ "alarmFootBrake": "Kočnica",
+ "alarmFuelLeak": "Curenje goriva",
+ "alarmTampering": "Petljanje",
+ "alarmRemoving": "Uklanjanje",
+ "notificationType": "Tip obavijesti",
+ "notificationAlways": "Svi uređaji",
+ "notificationNotificators": "Kanali",
+ "notificatorCommand": "Naredba",
+ "notificatorWeb": "Web",
+ "notificatorMail": "Email",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pošalji",
+ "reportReplay": "Ponovi",
+ "reportCombined": "Kombinirano",
+ "reportRoute": "Ruta",
+ "reportEvents": "Događaji",
+ "reportTrips": "Putovanja",
+ "reportStops": "Zaustavljanja",
+ "reportSummary": "Sažetak",
+ "reportDaily": "Dnevni izvještaj",
+ "reportChart": "Graf",
+ "reportConfigure": "Konfiguriraj",
+ "reportEventTypes": "Vrste događaja",
+ "reportChartType": "Vrsta grafa",
+ "reportShowMarkers": "Pokaži markere",
+ "reportExport": "Izvoz",
+ "reportEmail": "Izvještaj emailom",
+ "reportSchedule": "Raspored",
+ "reportPeriod": "Period",
+ "reportCustom": "Prilagođeno",
+ "reportToday": "Danas",
+ "reportYesterday": "Jučer",
+ "reportThisWeek": "Ovaj tjedan",
+ "reportPreviousWeek": "Prošli tjedan",
+ "reportThisMonth": "Ovaj mjesec",
+ "reportPreviousMonth": "Prošli mjesec",
+ "reportDeviceName": "Ime uređaja",
+ "reportAverageSpeed": "Prosječna brzina",
+ "reportMaximumSpeed": "Maksimalna brzina",
+ "reportEngineHours": "Sati rada motora",
+ "reportDuration": "Trajanje",
+ "reportStartDate": "Početni datum",
+ "reportStartTime": "Početno vrijeme",
+ "reportStartAddress": "Početna adresa",
+ "reportEndTime": "Vrijeme završetka",
+ "reportEndAddress": "Završna adresa",
+ "reportSpentFuel": "Potrošeno goriva",
+ "reportStartOdometer": "Početna kilometraža",
+ "reportEndOdometer": "Završna kilometraža",
+ "statisticsTitle": "Statistike",
+ "statisticsCaptureTime": "Vrijeme dohvaćanja",
+ "statisticsActiveUsers": "Aktivnih korisnika",
+ "statisticsActiveDevices": "Aktivnih uređaja",
+ "statisticsRequests": "Zahtjeva",
+ "statisticsMessagesReceived": "Poruka primljeno",
+ "statisticsMessagesStored": "Poruka spremljeno",
+ "statisticsGeocoder": "Geocoder zahtjevi",
+ "statisticsGeolocation": "Geolokacijski zahtjevi",
+ "categoryArrow": "Strelica",
+ "categoryDefault": "Zadano",
+ "categoryAnimal": "Životinja",
+ "categoryBicycle": "Bicikl",
+ "categoryBoat": "Brod",
+ "categoryBus": "Autobus",
+ "categoryCar": "Automobil",
+ "categoryCamper": "Camper",
+ "categoryCrane": "Dizalica",
+ "categoryHelicopter": "Helikopter",
+ "categoryMotorcycle": "Motocikl",
+ "categoryOffroad": "Offroad",
+ "categoryPerson": "Osoba",
+ "categoryPickup": "Pickup",
+ "categoryPlane": "Avion",
+ "categoryShip": "Brod",
+ "categoryTractor": "Traktor",
+ "categoryTrain": "Vlak",
+ "categoryTram": "Tramvaj",
+ "categoryTrolleybus": "Trolejbus",
+ "categoryTruck": "Kamion",
+ "categoryVan": "Kombi",
+ "categoryScooter": "Skuter",
+ "maintenanceStart": "Start",
+ "maintenancePeriod": "Period"
+} \ No newline at end of file
diff --git a/src/resources/l10n/hu.json b/src/resources/l10n/hu.json
new file mode 100644
index 00000000..f50b329c
--- /dev/null
+++ b/src/resources/l10n/hu.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "Betöltés...",
+ "sharedHide": "Elrejt",
+ "sharedSave": "Mentés",
+ "sharedUpload": "Upload",
+ "sharedSet": "Beállít",
+ "sharedCancel": "Mégse",
+ "sharedCopy": "Copy",
+ "sharedAdd": "Hozzáadás",
+ "sharedEdit": "Szerkesztés",
+ "sharedRemove": "Törlés",
+ "sharedRemoveConfirm": "Biztosan törli?",
+ "sharedNoData": "Nincs adat",
+ "sharedSubject": "Subject",
+ "sharedYes": "Igen",
+ "sharedNo": "Nem",
+ "sharedKm": "km",
+ "sharedMi": "mi",
+ "sharedNmi": "nmi",
+ "sharedMeters": "m",
+ "sharedFeet": "láb",
+ "sharedKn": "csomó",
+ "sharedKmh": "km/h",
+ "sharedMph": "mph",
+ "sharedHour": "Óra",
+ "sharedMinute": "Perc",
+ "sharedSecond": "Másodperc",
+ "sharedDays": "nap",
+ "sharedHours": "óra",
+ "sharedMinutes": "perc",
+ "sharedDecimalDegrees": "Decimális fok",
+ "sharedDegreesDecimalMinutes": "Fok decimális percben",
+ "sharedDegreesMinutesSeconds": "Fok decimális másodpercben",
+ "sharedName": "Név",
+ "sharedDescription": "Leírás",
+ "sharedSearch": "Keresés",
+ "sharedIconScale": "Icon Scale",
+ "sharedGeofence": "Geokerítés",
+ "sharedGeofences": "Geokerítések",
+ "sharedCreateGeofence": "Create Geofence",
+ "sharedNotifications": "Értesítések",
+ "sharedNotification": "Értesítés",
+ "sharedAttributes": "Tulajdonságok",
+ "sharedAttribute": "Tulajdonság",
+ "sharedDrivers": "Sofőrök",
+ "sharedDriver": "Sofőr",
+ "sharedArea": "Terület",
+ "sharedSound": "Értesítési hang",
+ "sharedType": "Típus",
+ "sharedDistance": "Távolság",
+ "sharedHourAbbreviation": "ó",
+ "sharedMinuteAbbreviation": "p",
+ "sharedSecondAbbreviation": "mp",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "l",
+ "sharedGallonAbbreviation": "gallon",
+ "sharedLiter": "Liter",
+ "sharedImpGallon": "Angolszász gallon",
+ "sharedUsGallon": "Amerikai Gallon",
+ "sharedLiterPerHourAbbreviation": "l/h",
+ "sharedGetMapState": "Térkép állapot lekérés",
+ "sharedComputedAttribute": "Számított attribútum",
+ "sharedComputedAttributes": "Számított attribútumok",
+ "sharedCheckComputedAttribute": "Számított attribútum ellenőrzése",
+ "sharedExpression": "Kifejezés",
+ "sharedDevice": "Eszköz",
+ "sharedTest": "Teszt",
+ "sharedTestNotification": "Próba értesítés küldése",
+ "sharedTestNotificators": "Teszt csatornák",
+ "sharedTestExpression": "Test kifejezés",
+ "sharedCalendar": "Naptár",
+ "sharedCalendars": "Naptárak",
+ "sharedFile": "Fájl",
+ "sharedSearchDevices": "Eszközök keresése",
+ "sharedSortBy": "Rendezés",
+ "sharedFilterMap": "Szűrés a térképen",
+ "sharedSelectFile": "Fájl kiválasztása",
+ "sharedPhone": "Telefon",
+ "sharedRequired": "Kötelező",
+ "sharedPreferences": "Preferenciák",
+ "sharedPermissions": "Jogosultságok",
+ "sharedConnections": "Kapcsolatok",
+ "sharedExtra": "Extra",
+ "sharedPrimary": "Primary",
+ "sharedSecondary": "Secondary",
+ "sharedTypeString": "Szöveg",
+ "sharedTypeNumber": "Szám",
+ "sharedTypeBoolean": "Logikai",
+ "sharedTimezone": "Időzóna",
+ "sharedInfoTitle": "Info",
+ "sharedSavedCommand": "Elmentett parancs",
+ "sharedSavedCommands": "Elmentett Parancsok",
+ "sharedNew": "Új...",
+ "sharedShowAddress": "Cím megjelenítése",
+ "sharedShowDetails": "További részletek",
+ "sharedDisabled": "Letiltva",
+ "sharedMaintenance": "Karbantartás",
+ "sharedDeviceAccumulators": "Akkumulátorok",
+ "sharedAlarms": "Riasztások",
+ "sharedLocation": "Lokáció",
+ "sharedImport": "Importálás",
+ "sharedColumns": "Oszlopok",
+ "sharedDropzoneText": "Drag and drop a file here or click",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "Simple",
+ "calendarRecurrence": "Recurrence",
+ "calendarOnce": "Once",
+ "calendarDaily": "Daily",
+ "calendarWeekly": "Weekly",
+ "calendarMonthly": "Monthly",
+ "calendarDays": "Days",
+ "calendarSunday": "Sunday",
+ "calendarMonday": "Monday",
+ "calendarTuesday": "Tuesday",
+ "calendarWednesday": "Wednesday",
+ "calendarThursday": "Thursday",
+ "calendarFriday": "Friday",
+ "calendarSaturday": "Saturday",
+ "attributeShowGeofences": "Show Geofences",
+ "attributeSpeedLimit": "Sebesség határ",
+ "attributeFuelDropThreshold": "Fuel Drop Threshold",
+ "attributeFuelIncreaseThreshold": "Fuel Increase Threshold",
+ "attributePolylineDistance": "Vonaltávolság",
+ "attributeReportIgnoreOdometer": "Jelentés: Odométer figyelmen kívül hagyása",
+ "attributeWebReportColor": "Web: Jelentés színe",
+ "attributeDevicePassword": "Eszköz jelszó",
+ "attributeDeviceImage": "Eszköz kép",
+ "attributeDeviceInactivityStart": "Eszköz inaktivitás kezdete",
+ "attributeDeviceInactivityPeriod": "Eszköz inaktivitás periódus",
+ "attributeProcessingCopyAttributes": "Feldolgozás: Attribútumok másolása",
+ "attributeColor": "Szín",
+ "attributeWebLiveRouteLength": "Web: Élő útvonal hossz",
+ "attributeWebSelectZoom": "Web: közelítés a kiválasztáson",
+ "attributeWebMaxZoom": "Web: Maximális Zoom",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Pushover User Key",
+ "attributePushoverDeviceNames": "Pushover Device Names",
+ "attributeMailSmtpHost": "SMTP kiszolgáló",
+ "attributeMailSmtpPort": "SMTP port",
+ "attributeMailSmtpStarttlsEnable": "SMTP STARTTLS engedélyezése",
+ "attributeMailSmtpStarttlsRequired": "SMTP STARTTLS szükséges",
+ "attributeMailSmtpSslEnable": "SMTP SSL engedélyezése",
+ "attributeMailSmtpSslTrust": "SMTP SSL Trust",
+ "attributeMailSmtpSslProtocols": "SMTP SSL Protokollok",
+ "attributeMailSmtpFrom": "SMTP Feladó",
+ "attributeMailSmtpAuth": "SMTP autentikáció engedélyezése",
+ "attributeMailSmtpUsername": "SMTP felhasználónév",
+ "attributeMailSmtpPassword": "SMTP jelszó",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: Disable Attributes",
+ "attributeUiDisableGroups": "UI: Csoportok letiltása",
+ "attributeUiDisableEvents": "UI: Események letiltása",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "UI: Sofőrök letiltása",
+ "attributeUiDisableComputedAttributes": "UI: Számított attribútumok letiltása",
+ "attributeUiDisableCalendars": "UI: Naptárak letiltása",
+ "attributeUiDisableMaintenance": "UI: Karbantartás letiltása",
+ "attributeUiHidePositionAttributes": "UI: Pozíciós attribútumok elrejtése",
+ "attributeUiDisableLoginLanguage": "UI: Bejelentkezés nyelv letiltás",
+ "attributeNotificationTokens": "Értesítés tokenek",
+ "attributePopupInfo": "Popup Info",
+ "errorTitle": "Hiba",
+ "errorGeneral": "Érvénytelen paraméterek vagy integritási szabályok megsértése",
+ "errorConnection": "Kapcsolódási hiba",
+ "errorSocket": "Kapcsolódási hiba",
+ "errorZero": "Nem lehet nulla",
+ "userEmail": "Email",
+ "userPassword": "Jelszó",
+ "userAdmin": "Adminisztrátor",
+ "userRemember": "Megjegyez",
+ "userExpirationTime": "Lejárat",
+ "userDeviceLimit": "Eszköz limit",
+ "userUserLimit": "Felhasználólimit",
+ "userDeviceReadonly": "Eszköz csak olvasható",
+ "userLimitCommands": "Parancsok listázása",
+ "userDisableReports": "Riportok letiltása",
+ "userFixedEmail": "No Email Change",
+ "userToken": "Token",
+ "userDeleteAccount": "Delete Account",
+ "userTemporary": "Temporary",
+ "loginTitle": "Bejelentkezés",
+ "loginLanguage": "Nyelv",
+ "loginReset": "Jelszó reset",
+ "loginRegister": "Regisztráció",
+ "loginLogin": "Bejelentkezés",
+ "loginOpenId": "Login with OpenID",
+ "loginFailed": "Hibás email vagy jelszó",
+ "loginCreated": "Az új felhasználó sikeresen létrehozva",
+ "loginResetSuccess": "Ellenőrizd az emailedet",
+ "loginUpdateSuccess": "Új jelszó beállítva",
+ "loginLogout": "Kilépés",
+ "loginLogo": "Logo",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "Eszközök és állapotuk",
+ "deviceSelected": "Selected Device",
+ "deviceTitle": "Eszközök",
+ "devicePrimaryInfo": "Device Title",
+ "deviceSecondaryInfo": "Device Detail",
+ "deviceIdentifier": "Azonosító",
+ "deviceModel": "Modell",
+ "deviceContact": "Kapcsolat",
+ "deviceCategory": "Kategória",
+ "deviceLastUpdate": "Utolsó frissítés",
+ "deviceCommand": "Parancs",
+ "deviceFollow": "Követ",
+ "deviceTotalDistance": "Teljes távolság",
+ "deviceStatus": "Státusz",
+ "deviceStatusOnline": "Elérhető",
+ "deviceStatusOffline": "Offline",
+ "deviceStatusUnknown": "Ismeretlen",
+ "deviceRegisterFirst": "Register your first device",
+ "deviceIdentifierHelp": "IMEI, serial number or other id. It has to match the identifier device reports to the server.",
+ "deviceShare": "Share Device",
+ "groupDialog": "Csoport",
+ "groupParent": "Csoport",
+ "groupNoGroup": "Nincs Csoport",
+ "settingsTitle": "Beállítások",
+ "settingsUser": "Fiók",
+ "settingsGroups": "Csoportok",
+ "settingsServer": "Szerver",
+ "settingsUsers": "Felhasználók",
+ "settingsDistanceUnit": "Távolság egysége",
+ "settingsAltitudeUnit": "Magasság mértékegység",
+ "settingsSpeedUnit": "Sebesség egysége",
+ "settingsVolumeUnit": "Térfogat egysége",
+ "settingsTwelveHourFormat": "12-órás formátum",
+ "settingsCoordinateFormat": "Koordináta formátuma",
+ "settingsServerVersion": "Server Version",
+ "settingsAppVersion": "App Version",
+ "settingsConnection": "Connection",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "Jelentések",
+ "reportScheduled": "Scheduled Reports",
+ "reportDevice": "Eszköz",
+ "reportGroup": "Csoport",
+ "reportFrom": "Kezdő dátum:",
+ "reportTo": "Végső dátum:",
+ "reportShow": "Mutat",
+ "reportClear": "Töröl",
+ "linkGoogleMaps": "Google Maps",
+ "linkAppleMaps": "Apple Maps",
+ "linkStreetView": "Street View",
+ "positionFixTime": "Idő beállítás",
+ "positionDeviceTime": "Eszköz idő",
+ "positionServerTime": "Szerver idő",
+ "positionValid": "Valós",
+ "positionAccuracy": "Pontosság",
+ "positionLatitude": "Szélességi fok",
+ "positionLongitude": "Hosszúsági fok",
+ "positionAltitude": "Magasság",
+ "positionSpeed": "Sebesség",
+ "positionCourse": "Irány",
+ "positionAddress": "Cím",
+ "positionProtocol": "Protokoll",
+ "positionDistance": "Távolság",
+ "positionRpm": "Fordulat",
+ "positionFuel": "Üzemanyag",
+ "positionPower": "Áramforrás",
+ "positionBattery": "Akkumulátor",
+ "positionRaw": "Nyers",
+ "positionIndex": "Index",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "Műholdak",
+ "positionSatVisible": "Látható műholdak",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "Roaming",
+ "positionEvent": "Esemény",
+ "positionAlarm": "Riasztás",
+ "positionStatus": "Státusz",
+ "positionOdometer": "Megtett út",
+ "positionServiceOdometer": "Szerviz megtett út",
+ "positionTripOdometer": "Utazás megtett út",
+ "positionHours": "Órák",
+ "positionSteps": "Lépések",
+ "positionInput": "Bemenet",
+ "positionHeartRate": "Heart Rate",
+ "positionOutput": "Kimenet",
+ "positionBatteryLevel": "Akkumulátor szint",
+ "positionFuelConsumption": "Fogyasztás",
+ "positionRfid": "RFID",
+ "positionVersionFw": "Firmware verzió",
+ "positionVersionHw": "Hardver verzió",
+ "positionIgnition": "Gyújtás",
+ "positionFlags": "Zászlók",
+ "positionCharge": "Töltés",
+ "positionIp": "IP",
+ "positionArchive": "Arhív",
+ "positionVin": "Alvázszám",
+ "positionApproximate": "Megközelít",
+ "positionThrottle": "Gázkar",
+ "positionMotion": "Mozgás",
+ "positionArmed": "Élesített",
+ "positionAcceleration": "Gyorsulás",
+ "positionTemp": "Hőmérséklet",
+ "positionDeviceTemp": "Eszköz hőmérséklet",
+ "positionCoolantTemp": "Coolant Temperature",
+ "positionOperator": "Operátor",
+ "positionCommand": "Parancs",
+ "positionBlocked": "Blokkolt",
+ "positionDtcs": "DTCk",
+ "positionObdSpeed": "ODB sebesség",
+ "positionObdOdometer": "ODB megtett út",
+ "positionDrivingTime": "Driving Time",
+ "positionDriverUniqueId": "Sofőr egyedi azonosítója",
+ "positionCard": "Card",
+ "positionImage": "Kép",
+ "positionVideo": "Video",
+ "positionAudio": "Hang",
+ "serverTitle": "Szerver beállítások",
+ "serverZoom": "Nagyítás",
+ "serverRegistration": "Regisztráció",
+ "serverReadonly": "Csak olvasható",
+ "serverForceSettings": "Erő beállítások",
+ "serverAnnouncement": "Bejelentés",
+ "serverName": "Server Name",
+ "serverDescription": "Server Description",
+ "serverColorPrimary": "Color Primary",
+ "serverColorSecondary": "Color Secondary",
+ "serverLogo": "Logo Image",
+ "serverLogoInverted": "Inverted Logo Image",
+ "serverChangeDisable": "Disable Server Change",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "Térkép",
+ "mapActive": "Aktív térképek",
+ "mapOverlay": "Térkép réteg",
+ "mapOverlayCustom": "Egyedi réteg",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API Key",
+ "mapOpenWeatherClouds": "OpenWeather Felhőtérkép",
+ "mapOpenWeatherPrecipitation": "OpenWeather Csapadéktérkép",
+ "mapOpenWeatherPressure": "OpenWeather Nyomás",
+ "mapOpenWeatherWind": "OpenWeather Széltérkép",
+ "mapOpenWeatherTemperature": "OpenWeather Hőtérkép",
+ "mapLayer": "Térkép réteg",
+ "mapCustom": "Egyedi (XYZ)",
+ "mapCustomArcgis": "Egyedi (ArcGIS)",
+ "mapCustomLabel": "Egyedi térkép",
+ "mapCarto": "Carto Basemaps",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Bing Maps kulcs",
+ "mapBingRoad": "Bing Maps Road",
+ "mapBingAerial": "Bing Maps Aerial",
+ "mapBingHybrid": "Bing Maps Hybrid",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Yandex térkép",
+ "mapYandexSat": "Yandex Műhold",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Rendelkezésre állási felmérés",
+ "mapMapboxStreets": "Mapbox utca",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox kültéri",
+ "mapMapboxSatellite": "Mapbox műhold",
+ "mapMapboxKey": "Mapbox Access Token",
+ "mapMapTilerBasic": "MapTiler Alap",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "MapTiler API Key",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ Access Token",
+ "mapTomTomBasic": "TomTom Alap",
+ "mapTomTomFlow": "TomTom Forgalom",
+ "mapTomTomIncidents": "TomTom Balesetek",
+ "mapTomTomKey": "TomTom API Key",
+ "mapHereBasic": "Here Alap",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Műhold",
+ "mapHereFlow": "Here Forgalom",
+ "mapHereKey": "Here API Key",
+ "mapShapePolygon": "Poligon",
+ "mapShapeCircle": "Kör",
+ "mapShapePolyline": "Vonallánc",
+ "mapLiveRoutes": "Élő útvonalak",
+ "mapDirection": "Show Direction",
+ "mapCurrentLocation": "Jelenlegi pozíció",
+ "mapPoiLayer": "POI réteg",
+ "mapClustering": "Jelölő csoportosítás",
+ "mapOnSelect": "Térkép mutatása a kijelölésen",
+ "mapDefault": "Default Map",
+ "stateTitle": "Helyzet",
+ "stateName": "Paraméter",
+ "stateValue": "Érték",
+ "commandTitle": "Parancs",
+ "commandSend": "Küld",
+ "commandSent": "Parancs elküldve",
+ "commandQueued": "Parancs várakozik",
+ "commandUnit": "Egység",
+ "commandCustom": "Egyedi parancs",
+ "commandDeviceIdentification": "Eszköz azonosító",
+ "commandPositionSingle": "Egyszeri jelentés",
+ "commandPositionPeriodic": "Pozició küldés",
+ "commandPositionStop": "Pozició küldés vége",
+ "commandEngineStop": "Motor letiltás",
+ "commandEngineResume": "Motor engedélyezés",
+ "commandAlarmArm": "Riasztó élesítés",
+ "commandAlarmDisarm": "Riasztó kikapcsolás",
+ "commandAlarmDismiss": "Riasztás némítása",
+ "commandSetTimezone": "Időzóna beállítás",
+ "commandRequestPhoto": "Kép lekérés",
+ "commandPowerOff": "Készülék kikapcsolás",
+ "commandRebootDevice": "Eszköz újraindítása",
+ "commandFactoryReset": "Gyári visszaállítás",
+ "commandSendSms": "SMS küldés",
+ "commandSendUssd": "USSD küldés",
+ "commandSosNumber": "SOS szám beállítás",
+ "commandSilenceTime": "Csendes idő beállítás",
+ "commandSetPhonebook": "Telefonkönyv beállítás",
+ "commandVoiceMessage": "Hangüzenet",
+ "commandOutputControl": "Kimenet Ellenőrzés",
+ "commandVoiceMonitoring": "Hangfelügyelet",
+ "commandSetAgps": "AGPS beállítása",
+ "commandSetIndicator": "Jelölő beállítása",
+ "commandConfiguration": "Konfiguráció",
+ "commandGetVersion": "Verzió beszerzése",
+ "commandFirmwareUpdate": "Firmware frissítése",
+ "commandSetConnection": "Kapcsolat beállítása",
+ "commandSetOdometer": "Távolságmérés beállítása",
+ "commandGetModemStatus": "Modem állapotának lekérdezése",
+ "commandGetDeviceStatus": "Eszköz állapotának lekérdezése",
+ "commandSetSpeedLimit": "Sebességhatár beállítása",
+ "commandModePowerSaving": "Energiatakarékos mód",
+ "commandModeDeepSleep": "Mélyalvás mód",
+ "commandAlarmGeofence": "Zóna riasztás beállítás",
+ "commandAlarmBattery": "Akkumulátor riasztás beállítás",
+ "commandAlarmSos": "SOS riasztás beállítása",
+ "commandAlarmRemove": "Eltávolítás riasztás beállítása",
+ "commandAlarmClock": "Óra riasztás beállítása",
+ "commandAlarmSpeed": "Sebesség riasztás beállítása",
+ "commandAlarmFall": "Leesés riasztás beállítása",
+ "commandAlarmVibration": "Rezgés riasztás beállítása",
+ "commandFrequency": "Frekvencia",
+ "commandTimezone": "Időzóna eltoása",
+ "commandMessage": "Üzenet",
+ "commandRadius": "Sugár",
+ "commandEnable": "Engedélyez",
+ "commandData": "Adat",
+ "commandIndex": "Index",
+ "commandPhone": "Telefonszám",
+ "commandServer": "Szerver",
+ "commandPort": "Port",
+ "eventAll": "Minden esemény",
+ "eventDeviceOnline": "Státusz online",
+ "eventDeviceUnknown": "Státusz ismeretlen",
+ "eventDeviceOffline": "Státusz offline",
+ "eventDeviceInactive": "Eszköz inaktív",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "Eszköz mozgásban",
+ "eventDeviceStopped": "Eszköz megállt",
+ "eventDeviceOverspeed": "Sebességhatár túllépés",
+ "eventDeviceFuelDrop": "Üzemanyag vesztés",
+ "eventDeviceFuelIncrease": "Fuel increase",
+ "eventCommandResult": "Parancs eredmény",
+ "eventGeofenceEnter": "Beléptett a területbe",
+ "eventGeofenceExit": "Kilépett a területből",
+ "eventAlarm": "Riasztás",
+ "eventIgnitionOn": "Gyújtás be",
+ "eventIgnitionOff": "Gyújtás ki",
+ "eventMaintenance": "Karbantartás szükséges",
+ "eventTextMessage": "Szöveges üzenet érkezett",
+ "eventDriverChanged": "Vezető váltás",
+ "eventMedia": "Media",
+ "eventsScrollToLast": "Utolsóhoz ugrás",
+ "eventsSoundEvents": "Sound Events",
+ "eventsSoundAlarms": "Sound Alarms",
+ "alarmGeneral": "Általános",
+ "alarmSos": "SOS",
+ "alarmVibration": "Rezgés",
+ "alarmMovement": "Mozgás",
+ "alarmLowspeed": "Alacsony sebesség",
+ "alarmOverspeed": "Gyorshajtás",
+ "alarmFallDown": "Leesett",
+ "alarmLowPower": "Alacsony feszültség",
+ "alarmLowBattery": "Alacsony töltöttség",
+ "alarmFault": "Meghibásodás",
+ "alarmPowerOff": "Kikapcsolva",
+ "alarmPowerOn": "Bekapcsolva",
+ "alarmDoor": "Ajtó",
+ "alarmLock": "Zárás",
+ "alarmUnlock": "Nyitás",
+ "alarmGeofence": "Terület",
+ "alarmGeofenceEnter": "Belépés a területre",
+ "alarmGeofenceExit": "Kilépés a területről",
+ "alarmGpsAntennaCut": "GPS jelvesztés",
+ "alarmAccident": "Baleset",
+ "alarmTow": "Vontatás",
+ "alarmIdle": "Tétlen",
+ "alarmHighRpm": "Magas fordulatszám",
+ "alarmHardAcceleration": "Erős gyorsítás",
+ "alarmHardBraking": "Erős fékezés",
+ "alarmHardCornering": "Hirtelen kanyarodás",
+ "alarmLaneChange": "Sávváltás",
+ "alarmFatigueDriving": "Veszélyes vezetés",
+ "alarmPowerCut": "Áramvesztés",
+ "alarmPowerRestored": "Áram helyreállítva",
+ "alarmJamming": "Zavarás",
+ "alarmTemperature": "Hőmérséklet",
+ "alarmParking": "Parkolás",
+ "alarmBonnet": "Motorház",
+ "alarmFootBrake": "Fék",
+ "alarmFuelLeak": "Üzemanyag szivárgás",
+ "alarmTampering": "Manipulálás",
+ "alarmRemoving": "Eltávolítás",
+ "notificationType": "Értesítés Típusa",
+ "notificationAlways": "Minden eszköz",
+ "notificationNotificators": "Csatornák",
+ "notificatorCommand": "Command",
+ "notificatorWeb": "Web",
+ "notificatorMail": "Levelezés",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Tűzoltóság",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Visszajátszás",
+ "reportCombined": "Combined",
+ "reportRoute": "Útvonal",
+ "reportEvents": "Események",
+ "reportTrips": "Utazások",
+ "reportStops": "Megállók",
+ "reportSummary": "Összegzés",
+ "reportDaily": "Napi összesítés",
+ "reportChart": "Diagram",
+ "reportConfigure": "Konfiguráció",
+ "reportEventTypes": "Esemény típusok",
+ "reportChartType": "Diagram típusa",
+ "reportShowMarkers": "Jelölők megjelenítése",
+ "reportExport": "Exportálás",
+ "reportEmail": "Email Riport",
+ "reportSchedule": "Schedule",
+ "reportPeriod": "Periódus",
+ "reportCustom": "Egyéni",
+ "reportToday": "Ma",
+ "reportYesterday": "Tegnap",
+ "reportThisWeek": "Ez a hét",
+ "reportPreviousWeek": "Előző hét",
+ "reportThisMonth": "Ez a hónap",
+ "reportPreviousMonth": "Előző hónap",
+ "reportDeviceName": "Eszköz neve",
+ "reportAverageSpeed": "Átlagos sebesség",
+ "reportMaximumSpeed": "Maximális sebesség",
+ "reportEngineHours": "Motorjárat órák",
+ "reportDuration": "Időtartam",
+ "reportStartDate": "Kezdés dátuma",
+ "reportStartTime": "Indulás ideje",
+ "reportStartAddress": "Induló címe",
+ "reportEndTime": "Megérkezés ideje",
+ "reportEndAddress": "Megérkezés címe",
+ "reportSpentFuel": "Elhasznált üzemanyag",
+ "reportStartOdometer": "Kilométer start",
+ "reportEndOdometer": "Kilométer Vég",
+ "statisticsTitle": "Statisztika",
+ "statisticsCaptureTime": "Rögzítés ideje",
+ "statisticsActiveUsers": "Aktív felhasználók",
+ "statisticsActiveDevices": "Aktív eszközök",
+ "statisticsRequests": "Kérések",
+ "statisticsMessagesReceived": "Fogadott üzenetek",
+ "statisticsMessagesStored": "Tárolt üzenetek",
+ "statisticsGeocoder": "Geocoder kérések",
+ "statisticsGeolocation": "Geopozíció kérések",
+ "categoryArrow": "Nyíl",
+ "categoryDefault": "Alapértelmezett",
+ "categoryAnimal": "Állat",
+ "categoryBicycle": "Bicikli",
+ "categoryBoat": "Csónak",
+ "categoryBus": "Busz",
+ "categoryCar": "Autó",
+ "categoryCamper": "Camper",
+ "categoryCrane": "Daru",
+ "categoryHelicopter": "Helikopter",
+ "categoryMotorcycle": "Motorbicikli",
+ "categoryOffroad": "Terep",
+ "categoryPerson": "Személy",
+ "categoryPickup": "Platós",
+ "categoryPlane": "Repülő",
+ "categoryShip": "Hajó",
+ "categoryTractor": "Traktor",
+ "categoryTrain": "Vonat",
+ "categoryTram": "Villamos",
+ "categoryTrolleybus": "Trolibusz",
+ "categoryTruck": "Kamion",
+ "categoryVan": "Teherkocsi",
+ "categoryScooter": "Roller",
+ "maintenanceStart": "Start",
+ "maintenancePeriod": "Periódus"
+} \ No newline at end of file
diff --git a/src/resources/l10n/id.json b/src/resources/l10n/id.json
new file mode 100644
index 00000000..c7883dae
--- /dev/null
+++ b/src/resources/l10n/id.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "Memuat...",
+ "sharedHide": "Sembunyikan",
+ "sharedSave": "Simpan",
+ "sharedUpload": "Upload",
+ "sharedSet": "Setel",
+ "sharedCancel": "Batal",
+ "sharedCopy": "Copy",
+ "sharedAdd": "Tambah",
+ "sharedEdit": "Ubah",
+ "sharedRemove": "Hapus",
+ "sharedRemoveConfirm": "Hapus Item?",
+ "sharedNoData": "No data",
+ "sharedSubject": "Subject",
+ "sharedYes": "Yes",
+ "sharedNo": "No",
+ "sharedKm": "km",
+ "sharedMi": "mi",
+ "sharedNmi": "nmi",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "kn",
+ "sharedKmh": "km/h",
+ "sharedMph": "mp/h",
+ "sharedHour": "Jam",
+ "sharedMinute": "Menit",
+ "sharedSecond": "Detik",
+ "sharedDays": "Hari",
+ "sharedHours": "Jam",
+ "sharedMinutes": "Menit",
+ "sharedDecimalDegrees": "Derajat Desimal",
+ "sharedDegreesDecimalMinutes": "Menit Derajat Desimal",
+ "sharedDegreesMinutesSeconds": "Menit Detik Derajat",
+ "sharedName": "Nama",
+ "sharedDescription": "Deskripsi",
+ "sharedSearch": "Cari",
+ "sharedIconScale": "Icon Scale",
+ "sharedGeofence": "Batas Wilayah",
+ "sharedGeofences": "Semua Batas Wilayah",
+ "sharedCreateGeofence": "Create Geofence",
+ "sharedNotifications": "Notifikasi",
+ "sharedNotification": "Notification",
+ "sharedAttributes": "Semua Atribut",
+ "sharedAttribute": "Atribut",
+ "sharedDrivers": "Antarmuka",
+ "sharedDriver": "Antarmuka",
+ "sharedArea": "Area",
+ "sharedSound": "Notifikasi Suara",
+ "sharedType": "Tipe",
+ "sharedDistance": "Jarak",
+ "sharedHourAbbreviation": "h",
+ "sharedMinuteAbbreviation": "m",
+ "sharedSecondAbbreviation": "s",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "l",
+ "sharedGallonAbbreviation": "gal",
+ "sharedLiter": "Liter",
+ "sharedImpGallon": "Imp. Gallon",
+ "sharedUsGallon": "U.S. Gallon",
+ "sharedLiterPerHourAbbreviation": "l/h",
+ "sharedGetMapState": "Dapatkan Status Peta",
+ "sharedComputedAttribute": "Atribut Terkomputerisasi",
+ "sharedComputedAttributes": "Semua Atribut Terkomputerisasi",
+ "sharedCheckComputedAttribute": "Pilih Atribut Terkomputerisasi",
+ "sharedExpression": "Ekspresi",
+ "sharedDevice": "Perangkat",
+ "sharedTest": "Test",
+ "sharedTestNotification": "Kirim Test Notifikasi",
+ "sharedTestNotificators": "Test Channels",
+ "sharedTestExpression": "Test Expression",
+ "sharedCalendar": "Kalender",
+ "sharedCalendars": "Semua Kalender",
+ "sharedFile": "Berkas",
+ "sharedSearchDevices": "Search Devices",
+ "sharedSortBy": "Sort By",
+ "sharedFilterMap": "Filter on Map",
+ "sharedSelectFile": "Pilih Berkas",
+ "sharedPhone": "Telepon",
+ "sharedRequired": "Diperlukan",
+ "sharedPreferences": "Preferensi",
+ "sharedPermissions": "Kewenangan",
+ "sharedConnections": "Connections",
+ "sharedExtra": "Ekstra",
+ "sharedPrimary": "Primary",
+ "sharedSecondary": "Secondary",
+ "sharedTypeString": "String",
+ "sharedTypeNumber": "Nomor",
+ "sharedTypeBoolean": "Pilihan",
+ "sharedTimezone": "Zona Waktu",
+ "sharedInfoTitle": "Info",
+ "sharedSavedCommand": "Perintah Tersimpan",
+ "sharedSavedCommands": "Saved Commands",
+ "sharedNew": "Baru...",
+ "sharedShowAddress": "Show Address",
+ "sharedShowDetails": "More Details",
+ "sharedDisabled": "Disabled",
+ "sharedMaintenance": "Maintenance",
+ "sharedDeviceAccumulators": "Accumulators",
+ "sharedAlarms": "Alarms",
+ "sharedLocation": "Location",
+ "sharedImport": "Import",
+ "sharedColumns": "Columns",
+ "sharedDropzoneText": "Drag and drop a file here or click",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "Simple",
+ "calendarRecurrence": "Recurrence",
+ "calendarOnce": "Once",
+ "calendarDaily": "Daily",
+ "calendarWeekly": "Weekly",
+ "calendarMonthly": "Monthly",
+ "calendarDays": "Days",
+ "calendarSunday": "Sunday",
+ "calendarMonday": "Monday",
+ "calendarTuesday": "Tuesday",
+ "calendarWednesday": "Wednesday",
+ "calendarThursday": "Thursday",
+ "calendarFriday": "Friday",
+ "calendarSaturday": "Saturday",
+ "attributeShowGeofences": "Show Geofences",
+ "attributeSpeedLimit": "Batas Kecepatan",
+ "attributeFuelDropThreshold": "Fuel Drop Threshold",
+ "attributeFuelIncreaseThreshold": "Fuel Increase Threshold",
+ "attributePolylineDistance": "Polyline Distance",
+ "attributeReportIgnoreOdometer": "Laporan: Biarkan Odometer",
+ "attributeWebReportColor": "Web: Warna Laporan",
+ "attributeDevicePassword": "Sandi Perangkat",
+ "attributeDeviceImage": "Device Image",
+ "attributeDeviceInactivityStart": "Device Inactivity Start",
+ "attributeDeviceInactivityPeriod": "Device Inactivity Period",
+ "attributeProcessingCopyAttributes": "Memproses: Salin Semua Atribut",
+ "attributeColor": "Warna",
+ "attributeWebLiveRouteLength": "Web: Panjang Rute Aktif",
+ "attributeWebSelectZoom": "Web: Perbesar Saat Pilih",
+ "attributeWebMaxZoom": "Web: Maximum Zoom",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Pushover User Key",
+ "attributePushoverDeviceNames": "Pushover Device Names",
+ "attributeMailSmtpHost": "Mail: SMTP Host",
+ "attributeMailSmtpPort": "Mail: SMTP Port",
+ "attributeMailSmtpStarttlsEnable": "Mail: SMTP STARTTLS Aktif",
+ "attributeMailSmtpStarttlsRequired": "Mail: SMTP STARTTLS Diperlukan",
+ "attributeMailSmtpSslEnable": "Mail: SMTP SSL Aktif",
+ "attributeMailSmtpSslTrust": "Mail: SMTP SSL Terpercaya",
+ "attributeMailSmtpSslProtocols": "Mail: SMTP SSL Protokol",
+ "attributeMailSmtpFrom": "Mail: SMTP Pengirim",
+ "attributeMailSmtpAuth": "Mail: SMTP Otorisasi Aktif",
+ "attributeMailSmtpUsername": "Mail: SMTP Pengguna",
+ "attributeMailSmtpPassword": "Mail: SMTP Sandi",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: Disable Attributes",
+ "attributeUiDisableGroups": "UI: Disable Groups",
+ "attributeUiDisableEvents": "UI: Disable Events",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "UI : Nonaktifkan Antarmuka",
+ "attributeUiDisableComputedAttributes": "UI : Nonaktifkan Computed Attributes",
+ "attributeUiDisableCalendars": "UI : Nonaktifkan Kalender",
+ "attributeUiDisableMaintenance": "UI: Disable Maintenance",
+ "attributeUiHidePositionAttributes": "UI: Hide Position Attributes",
+ "attributeUiDisableLoginLanguage": "UI: Disable Login Language",
+ "attributeNotificationTokens": "Notification Tokens",
+ "attributePopupInfo": "Popup Info",
+ "errorTitle": "Bermasalah",
+ "errorGeneral": "Semua Parameter Salah Atau Salah Aturan",
+ "errorConnection": "Koneksi Bermasalah",
+ "errorSocket": "Koneksi Web Socket Bermasalah",
+ "errorZero": "Can't be zero",
+ "userEmail": "Email",
+ "userPassword": "Sandi",
+ "userAdmin": "Admin",
+ "userRemember": "Diingat",
+ "userExpirationTime": "Kadaluarsa",
+ "userDeviceLimit": "Batas Perangkat",
+ "userUserLimit": "Batas Pengguna",
+ "userDeviceReadonly": "Perangkat Hanya Dilihat",
+ "userLimitCommands": "Limit Commands",
+ "userDisableReports": "Disable Reports",
+ "userFixedEmail": "No Email Change",
+ "userToken": "Token",
+ "userDeleteAccount": "Delete Account",
+ "userTemporary": "Temporary",
+ "loginTitle": "Masuk",
+ "loginLanguage": "Bahasa",
+ "loginReset": "Reset Password",
+ "loginRegister": "Daftar",
+ "loginLogin": "Masuk",
+ "loginOpenId": "Login with OpenID",
+ "loginFailed": "Email Atau Password Salah",
+ "loginCreated": "Pengguna Baru Telah Terdaftar",
+ "loginResetSuccess": "Check your email",
+ "loginUpdateSuccess": "New password is set",
+ "loginLogout": "Keluar",
+ "loginLogo": "Lambang",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "Perangkat Dan Status",
+ "deviceSelected": "Selected Device",
+ "deviceTitle": "Semua Perangkat",
+ "devicePrimaryInfo": "Device Title",
+ "deviceSecondaryInfo": "Device Detail",
+ "deviceIdentifier": "Pengindetifikasi",
+ "deviceModel": "Model",
+ "deviceContact": "Kontak",
+ "deviceCategory": "Kategori",
+ "deviceLastUpdate": "Terbaru",
+ "deviceCommand": "Perintah",
+ "deviceFollow": "Ikuti",
+ "deviceTotalDistance": "Total Jarak",
+ "deviceStatus": "Status",
+ "deviceStatusOnline": "Aktif",
+ "deviceStatusOffline": "Tidak Aktif",
+ "deviceStatusUnknown": "Tidak Diketahui",
+ "deviceRegisterFirst": "Register your first device",
+ "deviceIdentifierHelp": "IMEI, serial number or other id. It has to match the identifier device reports to the server.",
+ "deviceShare": "Share Device",
+ "groupDialog": "Grup",
+ "groupParent": "Grup",
+ "groupNoGroup": "No Grup",
+ "settingsTitle": "Pengaturan",
+ "settingsUser": "Akun",
+ "settingsGroups": "Semua Grup",
+ "settingsServer": "Server",
+ "settingsUsers": "Pengguna",
+ "settingsDistanceUnit": "Distance Unit",
+ "settingsAltitudeUnit": "Altitude Unit",
+ "settingsSpeedUnit": "Speed Unit",
+ "settingsVolumeUnit": "Volume Unit",
+ "settingsTwelveHourFormat": "Format 12 Jam",
+ "settingsCoordinateFormat": "Format Koordinat",
+ "settingsServerVersion": "Server Version",
+ "settingsAppVersion": "App Version",
+ "settingsConnection": "Connection",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "Semua Laporan",
+ "reportScheduled": "Scheduled Reports",
+ "reportDevice": "Perangkat",
+ "reportGroup": "Grup",
+ "reportFrom": "Dari",
+ "reportTo": "Ke",
+ "reportShow": "Tampilkan",
+ "reportClear": "Bersihkan",
+ "linkGoogleMaps": "Google Maps",
+ "linkAppleMaps": "Apple Maps",
+ "linkStreetView": "Street View",
+ "positionFixTime": "Fix Time",
+ "positionDeviceTime": "Device Time",
+ "positionServerTime": "Server Time",
+ "positionValid": "Validitas",
+ "positionAccuracy": "Akurasi",
+ "positionLatitude": "Lintang",
+ "positionLongitude": "Bujur",
+ "positionAltitude": "Ketinggian",
+ "positionSpeed": "Kecepatan",
+ "positionCourse": "Arah",
+ "positionAddress": "Alamat",
+ "positionProtocol": "Protokol",
+ "positionDistance": "Jarak",
+ "positionRpm": "RPM",
+ "positionFuel": "BBM",
+ "positionPower": "Tenaga",
+ "positionBattery": "Baterai",
+ "positionRaw": "Mentah",
+ "positionIndex": "Indek",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "Satellites",
+ "positionSatVisible": "Visible Satellites",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "Roaming",
+ "positionEvent": "Peristiwa",
+ "positionAlarm": "Alarm",
+ "positionStatus": "Status",
+ "positionOdometer": "Odometer",
+ "positionServiceOdometer": "Odometer Perbaikan",
+ "positionTripOdometer": "Odometer Perjalanan",
+ "positionHours": "Semua Jam",
+ "positionSteps": "Steps",
+ "positionInput": "Masukan",
+ "positionHeartRate": "Heart Rate",
+ "positionOutput": "Keluaran",
+ "positionBatteryLevel": "Level Battery",
+ "positionFuelConsumption": "Konsumsi BBM",
+ "positionRfid": "RFID",
+ "positionVersionFw": "Versi Firmware",
+ "positionVersionHw": "Versi Hardware",
+ "positionIgnition": "Mesin",
+ "positionFlags": "Semua Bendera",
+ "positionCharge": "Pengisian",
+ "positionIp": "IP",
+ "positionArchive": "Arsip",
+ "positionVin": "VIN",
+ "positionApproximate": "Perkiraan",
+ "positionThrottle": "Throttle",
+ "positionMotion": "Gerakan",
+ "positionArmed": "Aktif",
+ "positionAcceleration": "Akselerasi",
+ "positionTemp": "Temperature",
+ "positionDeviceTemp": "Suhu Perangkat",
+ "positionCoolantTemp": "Coolant Temperature",
+ "positionOperator": "Operator",
+ "positionCommand": "Perintah",
+ "positionBlocked": "Diblok",
+ "positionDtcs": "DTCs",
+ "positionObdSpeed": "Kecepatan OBD",
+ "positionObdOdometer": "Odometer OBD",
+ "positionDrivingTime": "Driving Time",
+ "positionDriverUniqueId": "ID Unik Antarmuka",
+ "positionCard": "Card",
+ "positionImage": "Image",
+ "positionVideo": "Video",
+ "positionAudio": "Audio",
+ "serverTitle": "Semua Pengaturan Server",
+ "serverZoom": "Perbesar",
+ "serverRegistration": "Pendaftaran",
+ "serverReadonly": "Hanya Dilihat",
+ "serverForceSettings": "Semua Pengaturan Paksa",
+ "serverAnnouncement": "Announcement",
+ "serverName": "Server Name",
+ "serverDescription": "Server Description",
+ "serverColorPrimary": "Color Primary",
+ "serverColorSecondary": "Color Secondary",
+ "serverLogo": "Logo Image",
+ "serverLogoInverted": "Inverted Logo Image",
+ "serverChangeDisable": "Disable Server Change",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "Peta",
+ "mapActive": "Active Maps",
+ "mapOverlay": "Map Overlay",
+ "mapOverlayCustom": "Custom Overlay",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API Key",
+ "mapOpenWeatherClouds": "OpenWeather Clouds",
+ "mapOpenWeatherPrecipitation": "OpenWeather Precipitation",
+ "mapOpenWeatherPressure": "OpenWeather Pressure",
+ "mapOpenWeatherWind": "OpenWeather Wind",
+ "mapOpenWeatherTemperature": "OpenWeather Temperature",
+ "mapLayer": "Layer Peta",
+ "mapCustom": "Custom (XYZ)",
+ "mapCustomArcgis": "Custom (ArcGIS)",
+ "mapCustomLabel": "Custom map",
+ "mapCarto": "Peta Carto",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Key Peta Bing",
+ "mapBingRoad": "Peta Jalan Bing",
+ "mapBingAerial": "Peta Udara Bing",
+ "mapBingHybrid": "Peta Bing Hybrid",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Peta Yandex",
+ "mapYandexSat": "Satelit Yandex",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox Streets",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox Outdoors",
+ "mapMapboxSatellite": "Mapbox Satellite",
+ "mapMapboxKey": "Mapbox Access Token",
+ "mapMapTilerBasic": "MapTiler Basic",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "MapTiler API Key",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ Access Token",
+ "mapTomTomBasic": "TomTom Basic",
+ "mapTomTomFlow": "TomTom Traffic Flow",
+ "mapTomTomIncidents": "TomTom Traffic Incidents",
+ "mapTomTomKey": "TomTom API Key",
+ "mapHereBasic": "Here Basic",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satellite",
+ "mapHereFlow": "Here Traffic Flow",
+ "mapHereKey": "Here API Key",
+ "mapShapePolygon": "Poligon",
+ "mapShapeCircle": "Lingkaran",
+ "mapShapePolyline": "Garis Poli",
+ "mapLiveRoutes": "Rute Aktif",
+ "mapDirection": "Show Direction",
+ "mapCurrentLocation": "Current Location",
+ "mapPoiLayer": "POI Layer",
+ "mapClustering": "Markers Clustering",
+ "mapOnSelect": "Show Map on Selection",
+ "mapDefault": "Default Map",
+ "stateTitle": "Status",
+ "stateName": "Atribut",
+ "stateValue": "Nilai",
+ "commandTitle": "Perintah",
+ "commandSend": "Kirim",
+ "commandSent": "Command sent",
+ "commandQueued": "Command queued",
+ "commandUnit": "Unit",
+ "commandCustom": "Perintah Buatan",
+ "commandDeviceIdentification": "Identifikasi Perangkat",
+ "commandPositionSingle": "Laporan Tunggal",
+ "commandPositionPeriodic": "Laporan Berkala",
+ "commandPositionStop": "Stop Laporan",
+ "commandEngineStop": "Matikan Mesin",
+ "commandEngineResume": "HIdupkan Mesin",
+ "commandAlarmArm": "Alarm Aktif",
+ "commandAlarmDisarm": "Alarm Tidak Aktif",
+ "commandAlarmDismiss": "Dismiss Alarm",
+ "commandSetTimezone": "Setel Zona Waktu",
+ "commandRequestPhoto": "Permintaan Foto",
+ "commandPowerOff": "Power Off Device",
+ "commandRebootDevice": "Hidupkan Ulang Perangkat",
+ "commandFactoryReset": "Factory Reset",
+ "commandSendSms": "Kirim SMS",
+ "commandSendUssd": "Kirim USSD",
+ "commandSosNumber": "Kirim Nomor Darurat",
+ "commandSilenceTime": "Setel Waktu DIam",
+ "commandSetPhonebook": "Setel Buku Telepon",
+ "commandVoiceMessage": "Pesan Suara",
+ "commandOutputControl": "Kontrol Output",
+ "commandVoiceMonitoring": "Voice Monitoring",
+ "commandSetAgps": "Set AGPS",
+ "commandSetIndicator": "Setel Indikasi",
+ "commandConfiguration": "Configuration",
+ "commandGetVersion": "Get Version",
+ "commandFirmwareUpdate": "Update Firmware",
+ "commandSetConnection": "Set Connection",
+ "commandSetOdometer": "Set Odometer",
+ "commandGetModemStatus": "Get Modem Status",
+ "commandGetDeviceStatus": "Get Device Status",
+ "commandSetSpeedLimit": "Set Speed Limit",
+ "commandModePowerSaving": "Power Saving Mode",
+ "commandModeDeepSleep": "Deep Sleep Mode",
+ "commandAlarmGeofence": "Set Geofence Alarm",
+ "commandAlarmBattery": "Set Battery Alarm",
+ "commandAlarmSos": "Set SOS Alarm",
+ "commandAlarmRemove": "Set Remove Alarm",
+ "commandAlarmClock": "Set Clock Alarm",
+ "commandAlarmSpeed": "Set Speed Alarm",
+ "commandAlarmFall": "Set Fall Alarm",
+ "commandAlarmVibration": "Set Vibration Alarm",
+ "commandFrequency": "Frekuensi",
+ "commandTimezone": "Zona Waktu Tidak Tepat",
+ "commandMessage": "Pesan",
+ "commandRadius": "Radius",
+ "commandEnable": "Enable",
+ "commandData": "Data",
+ "commandIndex": "Indeks",
+ "commandPhone": "Nomor Telepon",
+ "commandServer": "Server",
+ "commandPort": "Port",
+ "eventAll": "Semua Peristiwa",
+ "eventDeviceOnline": "Status online",
+ "eventDeviceUnknown": "Status unknown",
+ "eventDeviceOffline": "Status offline",
+ "eventDeviceInactive": "Device inactive",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "Device moving",
+ "eventDeviceStopped": "Device stopped",
+ "eventDeviceOverspeed": "Speed limit exceeded",
+ "eventDeviceFuelDrop": "Fuel drop",
+ "eventDeviceFuelIncrease": "Fuel increase",
+ "eventCommandResult": "Hasil Perintah",
+ "eventGeofenceEnter": "Geofence entered",
+ "eventGeofenceExit": "Geofence exited",
+ "eventAlarm": "Alarm",
+ "eventIgnitionOn": "Ignition on",
+ "eventIgnitionOff": "Ignition off",
+ "eventMaintenance": "Perlu Perbaikan",
+ "eventTextMessage": "Pesan Teks Diterima",
+ "eventDriverChanged": "Driver changed",
+ "eventMedia": "Media",
+ "eventsScrollToLast": "Bergulir Ke Terakhir",
+ "eventsSoundEvents": "Sound Events",
+ "eventsSoundAlarms": "Sound Alarms",
+ "alarmGeneral": "General",
+ "alarmSos": "SOS",
+ "alarmVibration": "Vibration",
+ "alarmMovement": "Movement",
+ "alarmLowspeed": "Low Speed",
+ "alarmOverspeed": "Overspeed",
+ "alarmFallDown": "Fall Down",
+ "alarmLowPower": "Low Power",
+ "alarmLowBattery": "Low Battery",
+ "alarmFault": "Fault",
+ "alarmPowerOff": "Power Off",
+ "alarmPowerOn": "Power On",
+ "alarmDoor": "Door",
+ "alarmLock": "Lock",
+ "alarmUnlock": "Unlock",
+ "alarmGeofence": "Geofence",
+ "alarmGeofenceEnter": "Geofence Enter",
+ "alarmGeofenceExit": "Geofence Exit",
+ "alarmGpsAntennaCut": "GPS Antenna Cut",
+ "alarmAccident": "Accident",
+ "alarmTow": "Tow",
+ "alarmIdle": "Idle",
+ "alarmHighRpm": "High RPM",
+ "alarmHardAcceleration": "Hard Acceleration",
+ "alarmHardBraking": "Hard Braking",
+ "alarmHardCornering": "Hard Cornering",
+ "alarmLaneChange": "Lane Change",
+ "alarmFatigueDriving": "Fatigue Driving",
+ "alarmPowerCut": "Power Cut",
+ "alarmPowerRestored": "Power Restored",
+ "alarmJamming": "Jamming",
+ "alarmTemperature": "Temperature",
+ "alarmParking": "Parking",
+ "alarmBonnet": "Bonnet",
+ "alarmFootBrake": "Foot Brake",
+ "alarmFuelLeak": "Fuel Leak",
+ "alarmTampering": "Tampering",
+ "alarmRemoving": "Removing",
+ "notificationType": "Tipe Notifikasi",
+ "notificationAlways": "All Devices",
+ "notificationNotificators": "Channels",
+ "notificatorCommand": "Command",
+ "notificatorWeb": "Web",
+ "notificatorMail": "Mail",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Replay",
+ "reportCombined": "Combined",
+ "reportRoute": "Rute",
+ "reportEvents": "Semua Peristiwa",
+ "reportTrips": "Perjalanan",
+ "reportStops": "Berhenti",
+ "reportSummary": "Ringkasan",
+ "reportDaily": "Daily Summary",
+ "reportChart": "Grafik",
+ "reportConfigure": "Pengaturan",
+ "reportEventTypes": "Tipe Peristiwa",
+ "reportChartType": "Tipe Grafik",
+ "reportShowMarkers": "Tampilkan Tanda",
+ "reportExport": "Ekspor",
+ "reportEmail": "Email Report",
+ "reportSchedule": "Schedule",
+ "reportPeriod": "Period",
+ "reportCustom": "Custom",
+ "reportToday": "Today",
+ "reportYesterday": "Yesterday",
+ "reportThisWeek": "This Week",
+ "reportPreviousWeek": "Previous Week",
+ "reportThisMonth": "This Month",
+ "reportPreviousMonth": "Previous Month",
+ "reportDeviceName": "Nama Perangkat",
+ "reportAverageSpeed": "Kecepatan Rata-rata",
+ "reportMaximumSpeed": "Kecepatan Tertinggi",
+ "reportEngineHours": "Durasi Mesin",
+ "reportDuration": "Durasi",
+ "reportStartDate": "Start Date",
+ "reportStartTime": "Waktu Awal",
+ "reportStartAddress": "Alamat Awal",
+ "reportEndTime": "Waktu Akhir",
+ "reportEndAddress": "Alamat Akhir",
+ "reportSpentFuel": "BBM Terpakai",
+ "reportStartOdometer": "Odometer Start",
+ "reportEndOdometer": "Odometer End",
+ "statisticsTitle": "Statistik",
+ "statisticsCaptureTime": "Waktu Didapat",
+ "statisticsActiveUsers": "Pengguna Aktif",
+ "statisticsActiveDevices": "Perangkat Aktif",
+ "statisticsRequests": "Permintaan",
+ "statisticsMessagesReceived": "Pesan Diterima",
+ "statisticsMessagesStored": "Pesan Disimpan",
+ "statisticsGeocoder": "Permintaan Geocoder",
+ "statisticsGeolocation": "Permintaan Geolokasi",
+ "categoryArrow": "Panah",
+ "categoryDefault": "Pengaturan Awal",
+ "categoryAnimal": "Hewan",
+ "categoryBicycle": "Sepeda",
+ "categoryBoat": "Perahu",
+ "categoryBus": "Bis",
+ "categoryCar": "Mobil",
+ "categoryCamper": "Camper",
+ "categoryCrane": "Crane",
+ "categoryHelicopter": "Helikopter",
+ "categoryMotorcycle": "Sepeda Motor",
+ "categoryOffroad": "Offroad",
+ "categoryPerson": "Orang",
+ "categoryPickup": "Jemput",
+ "categoryPlane": "Pesawat",
+ "categoryShip": "Kapal Laut",
+ "categoryTractor": "Traktor",
+ "categoryTrain": "Train",
+ "categoryTram": "Tram",
+ "categoryTrolleybus": "Trolleybus",
+ "categoryTruck": "Truk",
+ "categoryVan": "Van",
+ "categoryScooter": "Scooter",
+ "maintenanceStart": "Start",
+ "maintenancePeriod": "Period"
+} \ No newline at end of file
diff --git a/src/resources/l10n/it.json b/src/resources/l10n/it.json
new file mode 100644
index 00000000..03fcbb16
--- /dev/null
+++ b/src/resources/l10n/it.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "Caricamento...",
+ "sharedHide": "Nascondi",
+ "sharedSave": "Salva",
+ "sharedUpload": "Carica",
+ "sharedSet": "Imposta",
+ "sharedCancel": "Annulla",
+ "sharedCopy": "Copia",
+ "sharedAdd": "Aggiungi",
+ "sharedEdit": "Modifica",
+ "sharedRemove": "Rimuovi",
+ "sharedRemoveConfirm": "Rimuovere elemento?",
+ "sharedNoData": "Nessun dato",
+ "sharedSubject": "Soggetto",
+ "sharedYes": "Si",
+ "sharedNo": "No",
+ "sharedKm": "km",
+ "sharedMi": "mi",
+ "sharedNmi": "nmi",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "kn",
+ "sharedKmh": "km/h",
+ "sharedMph": "mph",
+ "sharedHour": "Ora",
+ "sharedMinute": "Minuto",
+ "sharedSecond": "Secondo",
+ "sharedDays": "giorni",
+ "sharedHours": "ore",
+ "sharedMinutes": "minuti",
+ "sharedDecimalDegrees": "Gradi",
+ "sharedDegreesDecimalMinutes": "Gradi Minuti",
+ "sharedDegreesMinutesSeconds": "Gradi Minuti Secondi",
+ "sharedName": "Nome",
+ "sharedDescription": "Descrizione",
+ "sharedSearch": "Cerca",
+ "sharedIconScale": "Grandezza Icona",
+ "sharedGeofence": "Geo Area",
+ "sharedGeofences": "Geo Aree",
+ "sharedCreateGeofence": "Crea Geo Area",
+ "sharedNotifications": "Notifiche",
+ "sharedNotification": "Notifica",
+ "sharedAttributes": "Attributi",
+ "sharedAttribute": "Attributo",
+ "sharedDrivers": "Autisti",
+ "sharedDriver": "Autista",
+ "sharedArea": "Area",
+ "sharedSound": "Suono Notifica",
+ "sharedType": "Tipo",
+ "sharedDistance": "Distanza",
+ "sharedHourAbbreviation": "h",
+ "sharedMinuteAbbreviation": "m",
+ "sharedSecondAbbreviation": "s",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "l",
+ "sharedGallonAbbreviation": "gal",
+ "sharedLiter": "Litri",
+ "sharedImpGallon": "Galloni Imperiali",
+ "sharedUsGallon": "Galloni USA",
+ "sharedLiterPerHourAbbreviation": "l/h",
+ "sharedGetMapState": "Ottieni Stato Mappa",
+ "sharedComputedAttribute": "Attributo Calcolato",
+ "sharedComputedAttributes": "Attributi Calcolati",
+ "sharedCheckComputedAttribute": "Controlla Attributo Calcolato",
+ "sharedExpression": "Espressione",
+ "sharedDevice": "Dispositivo",
+ "sharedTest": "Prova",
+ "sharedTestNotification": "Invia Notifica di Prova",
+ "sharedTestNotificators": "Prova Canali",
+ "sharedTestExpression": "Prova Espressione",
+ "sharedCalendar": "Calendario",
+ "sharedCalendars": "Calendari",
+ "sharedFile": "File",
+ "sharedSearchDevices": "Cerca Dispositivi",
+ "sharedSortBy": "Ordina per",
+ "sharedFilterMap": "Filtra su Mappa",
+ "sharedSelectFile": "Seleziona file",
+ "sharedPhone": "Telefono",
+ "sharedRequired": "Richiesto",
+ "sharedPreferences": "Preferenze",
+ "sharedPermissions": "Permessi",
+ "sharedConnections": "Connessioni",
+ "sharedExtra": "Informazioni Aggiuntive",
+ "sharedPrimary": "Primario",
+ "sharedSecondary": "Secondario",
+ "sharedTypeString": "Stringa",
+ "sharedTypeNumber": "Numero",
+ "sharedTypeBoolean": "Booleano",
+ "sharedTimezone": "Fuso Orario",
+ "sharedInfoTitle": "Informazioni",
+ "sharedSavedCommand": "Comando Salvato",
+ "sharedSavedCommands": "Comandi Salvati",
+ "sharedNew": "Nuovo…",
+ "sharedShowAddress": "Mostra Indirizzo",
+ "sharedShowDetails": "Dettagli Aggiuntivi",
+ "sharedDisabled": "Disattivato",
+ "sharedMaintenance": "Manutenzione",
+ "sharedDeviceAccumulators": "Accumulatori",
+ "sharedAlarms": "Allarmi",
+ "sharedLocation": "Posizione",
+ "sharedImport": "Importa",
+ "sharedColumns": "Colonne",
+ "sharedDropzoneText": "Trascina un file qui o clicca",
+ "sharedLogs": "Registri",
+ "sharedLink": "Collegamento",
+ "calendarSimple": "Semplice",
+ "calendarRecurrence": "Ricorrente",
+ "calendarOnce": "Una volta",
+ "calendarDaily": "Giornaliero",
+ "calendarWeekly": "Settimanale",
+ "calendarMonthly": "Mensile",
+ "calendarDays": "Giorni",
+ "calendarSunday": "Domenica",
+ "calendarMonday": "Lunedì",
+ "calendarTuesday": "Martedì",
+ "calendarWednesday": "Mercoledì",
+ "calendarThursday": "Giovedì",
+ "calendarFriday": "Venerdì",
+ "calendarSaturday": "Sabato",
+ "attributeShowGeofences": "Mostra Geo Aree",
+ "attributeSpeedLimit": "Limite Di Velocità",
+ "attributeFuelDropThreshold": "Soglia di calo carburante",
+ "attributeFuelIncreaseThreshold": "Soglia di aumento carburante",
+ "attributePolylineDistance": "Distanza Polilinea",
+ "attributeReportIgnoreOdometer": "Rapporto: Ignora Odometro",
+ "attributeWebReportColor": "Web: Colore Rapporto",
+ "attributeDevicePassword": "Password Dispositivo",
+ "attributeDeviceImage": "Immagine dispositivo",
+ "attributeDeviceInactivityStart": "Inizio Inattività Dispositivo",
+ "attributeDeviceInactivityPeriod": "Periodo Inattività Dispositivo",
+ "attributeProcessingCopyAttributes": "Elaborazione: Copia Attributi",
+ "attributeColor": "Colore",
+ "attributeWebLiveRouteLength": "Web: Lunghezza Percorso in Diretta",
+ "attributeWebSelectZoom": "Web: Ingrandimento su Selezione",
+ "attributeWebMaxZoom": "Web: Ingrandimento Massimo",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Pushover Chiave Utente",
+ "attributePushoverDeviceNames": "Pushover Nome Dispositivo",
+ "attributeMailSmtpHost": "Mail: Host SMTP",
+ "attributeMailSmtpPort": "Mail: Porta SMTP",
+ "attributeMailSmtpStarttlsEnable": "Mail: Attiva STARTTLS SMTP",
+ "attributeMailSmtpStarttlsRequired": "Mail: Richiesto STARTTLS SMTP",
+ "attributeMailSmtpSslEnable": "Mail: Attiva SSL SMTP",
+ "attributeMailSmtpSslTrust": "Mail: Fiducia SSL SMTP",
+ "attributeMailSmtpSslProtocols": "Mail: Protocolli SSL SMTP",
+ "attributeMailSmtpFrom": "Mail: SMTP Da",
+ "attributeMailSmtpAuth": "Mail: Attiva Autenticazione SMTP",
+ "attributeMailSmtpUsername": "Mail: Nome Utente SMTP",
+ "attributeMailSmtpPassword": "Mail: Password SMTP",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: Disattiva Attributi",
+ "attributeUiDisableGroups": "UI: Disattiva Gruppi",
+ "attributeUiDisableEvents": "UI: Disattiva Eventi",
+ "attributeUiDisableVehicleFeatures": "UI: disabilita le funzionalità del veicolo",
+ "attributeUiDisableDrivers": "UI: Disattiva Autisti",
+ "attributeUiDisableComputedAttributes": "UI: Disattiva Attributi Calcolati",
+ "attributeUiDisableCalendars": "UI: Disattiva Calendari",
+ "attributeUiDisableMaintenance": "UI: Disattiva Manutenzione",
+ "attributeUiHidePositionAttributes": "UI: Nascondi Attributi Posizione",
+ "attributeUiDisableLoginLanguage": "UI: Disabilita lingua di accesso",
+ "attributeNotificationTokens": "Token di Notifica",
+ "attributePopupInfo": "Informazioni Popup",
+ "errorTitle": "Errore",
+ "errorGeneral": "Parametri non validi o violazione dei vincoli",
+ "errorConnection": "Errore di connessione",
+ "errorSocket": "Errore connessione Web socket",
+ "errorZero": "Non può essere zero",
+ "userEmail": "Email",
+ "userPassword": "Password",
+ "userAdmin": "Amministratore",
+ "userRemember": "Ricorda",
+ "userExpirationTime": "Scadenza",
+ "userDeviceLimit": "Limite Dispositivo",
+ "userUserLimit": "Limite Utente",
+ "userDeviceReadonly": "Dispositivo in Sola Lettura",
+ "userLimitCommands": "Limita Comandi",
+ "userDisableReports": "Disattiva Rapporti",
+ "userFixedEmail": "Nessuna modifica Email",
+ "userToken": "Token",
+ "userDeleteAccount": "Elimina Account",
+ "userTemporary": "Temporaneo",
+ "loginTitle": "Accesso",
+ "loginLanguage": "Lingua",
+ "loginReset": "Resetta Password",
+ "loginRegister": "Registrazione",
+ "loginLogin": "Accesso",
+ "loginOpenId": "Accesso con OpenID",
+ "loginFailed": "Indirizzo email o password errati",
+ "loginCreated": "Un nuovo utente si è registrato",
+ "loginResetSuccess": "Controlla la tua email",
+ "loginUpdateSuccess": "Nuova password impostata",
+ "loginLogout": "Esci",
+ "loginLogo": "Logo",
+ "loginTotpCode": "Codice Password Monouso",
+ "loginTotpKey": "Chiave Password Monouso",
+ "devicesAndState": "Dispositivi e Stato",
+ "deviceSelected": "Dispositivo selezionato",
+ "deviceTitle": "Dispositivi",
+ "devicePrimaryInfo": "Titolo dispositivo",
+ "deviceSecondaryInfo": "Dettagli dispositivo",
+ "deviceIdentifier": "Identificativo",
+ "deviceModel": "Modello",
+ "deviceContact": "Contatto",
+ "deviceCategory": "Categoria",
+ "deviceLastUpdate": "Ultimo Aggiornamento",
+ "deviceCommand": "Comando",
+ "deviceFollow": "Segui",
+ "deviceTotalDistance": "Distanza Totale",
+ "deviceStatus": "Stato",
+ "deviceStatusOnline": "Connesso",
+ "deviceStatusOffline": "Disconnesso",
+ "deviceStatusUnknown": "Sconosciuto",
+ "deviceRegisterFirst": "Registra il tuo primo dispositivo",
+ "deviceIdentifierHelp": "IMEI, numero di serie o altro ID. Deve corrispondere ai rapporti del dispositivo identificatore sul server.",
+ "deviceShare": "Condividi Dispositivo",
+ "groupDialog": "Gruppo",
+ "groupParent": "Gruppo",
+ "groupNoGroup": "Nessun Gruppo",
+ "settingsTitle": "Impostazioni",
+ "settingsUser": "Profilo",
+ "settingsGroups": "Gruppi",
+ "settingsServer": "Server",
+ "settingsUsers": "Utenti",
+ "settingsDistanceUnit": "Unità distanza",
+ "settingsAltitudeUnit": "Unità altitudine",
+ "settingsSpeedUnit": "Unità velocità",
+ "settingsVolumeUnit": "Unità volume",
+ "settingsTwelveHourFormat": "Formato 12 ore",
+ "settingsCoordinateFormat": "Formato Coordinate",
+ "settingsServerVersion": "Versione server",
+ "settingsAppVersion": "Versione App",
+ "settingsConnection": "Connessione",
+ "settingsDarkMode": "Modalità Notturna",
+ "settingsTotpEnable": "Abilita Password Monouso",
+ "settingsTotpForce": "Forza Password Monouso",
+ "settingsServiceWorkerUpdateInterval": "Intervallo di Aggiornamento di ServiceWorker",
+ "settingsUpdateAvailable": "È disponibile un aggiornamento.",
+ "settingsSupport": "Supporto",
+ "reportTitle": "Rapporti",
+ "reportScheduled": "Rapporto programmato",
+ "reportDevice": "Dispositivo",
+ "reportGroup": "Gruppo",
+ "reportFrom": "Da",
+ "reportTo": "A",
+ "reportShow": "Visualizza",
+ "reportClear": "Pulisci",
+ "linkGoogleMaps": "Mappa Google",
+ "linkAppleMaps": "Mappa Apple",
+ "linkStreetView": "Vista Stradale",
+ "positionFixTime": "Tempo Corretto",
+ "positionDeviceTime": "Orario Dispositivo",
+ "positionServerTime": "Orario Server",
+ "positionValid": "Valido",
+ "positionAccuracy": "Precisione",
+ "positionLatitude": "Latitudine",
+ "positionLongitude": "Longitudine",
+ "positionAltitude": "Altitudine",
+ "positionSpeed": "Velocità",
+ "positionCourse": "Direzione",
+ "positionAddress": "Indirizzo",
+ "positionProtocol": "Protocollo",
+ "positionDistance": "Distanza",
+ "positionRpm": "Giri Motore",
+ "positionFuel": "Carburante",
+ "positionPower": "Alimentazione",
+ "positionBattery": "Batteria",
+ "positionRaw": "Dato grezzo",
+ "positionIndex": "Indice",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "Satelliti",
+ "positionSatVisible": "Satelliti Visibili",
+ "positionRssi": "Segnale GSM",
+ "positionGps": "GPS",
+ "positionRoaming": "Roaming",
+ "positionEvent": "Evento",
+ "positionAlarm": "Allarme",
+ "positionStatus": "Stato",
+ "positionOdometer": "Odometro",
+ "positionServiceOdometer": "Servizio Odometrico",
+ "positionTripOdometer": "Parziale Odometro",
+ "positionHours": "Ore",
+ "positionSteps": "Passi",
+ "positionInput": "Ingresso",
+ "positionHeartRate": "Frequenza Cardiaca",
+ "positionOutput": "Uscita",
+ "positionBatteryLevel": "Livello Batteria",
+ "positionFuelConsumption": "Consumo Carburante",
+ "positionRfid": "RFID",
+ "positionVersionFw": "Versione Firmware",
+ "positionVersionHw": "Versione Hardware",
+ "positionIgnition": "Accensione",
+ "positionFlags": "Bandiera",
+ "positionCharge": "Carica",
+ "positionIp": "IP",
+ "positionArchive": "Archivio",
+ "positionVin": "VIN",
+ "positionApproximate": "Approssimato",
+ "positionThrottle": "Acceleratore",
+ "positionMotion": "Movimento",
+ "positionArmed": "Armato/Attivo",
+ "positionAcceleration": "Accelerazione",
+ "positionTemp": "Temperatura",
+ "positionDeviceTemp": "Temperatura Dispositivo",
+ "positionCoolantTemp": "Temperatura Motore",
+ "positionOperator": "Operatore",
+ "positionCommand": "Comando",
+ "positionBlocked": "Bloccato",
+ "positionDtcs": "DTCs",
+ "positionObdSpeed": "Velocità OBD",
+ "positionObdOdometer": "Odometro OBD",
+ "positionDrivingTime": "Ore di Guida",
+ "positionDriverUniqueId": "ID Univoco Autista",
+ "positionCard": "Carta",
+ "positionImage": "Immagine",
+ "positionVideo": "Video",
+ "positionAudio": "Audio",
+ "serverTitle": "Impostazioni Server",
+ "serverZoom": "Zoom",
+ "serverRegistration": "Registrazione",
+ "serverReadonly": "Sola lettura",
+ "serverForceSettings": "Forza Impostazioni",
+ "serverAnnouncement": "Annuncio",
+ "serverName": "Nome del Server",
+ "serverDescription": "Descrizione del Server",
+ "serverColorPrimary": "Colore Primario",
+ "serverColorSecondary": "Colore Secondario",
+ "serverLogo": "Immagine Logo",
+ "serverLogoInverted": "Immagine Logo Invertita",
+ "serverChangeDisable": "Disattiva cambiamento Server",
+ "serverDisableShare": "Disattiva la Condivisione del Dispositivo",
+ "mapTitle": "Mappa",
+ "mapActive": "Mappe Attive",
+ "mapOverlay": "Soprapposizione Mappa",
+ "mapOverlayCustom": "Soprapposizione Personalizzata",
+ "mapOpenSeaMap": "Mappa OpenSea",
+ "mapOpenRailwayMap": "Mappa OpenRailway",
+ "mapOpenWeatherKey": "OpenWeather Chiave API",
+ "mapOpenWeatherClouds": "OpenWeather Nuvole",
+ "mapOpenWeatherPrecipitation": "OpenWeather Precipitazioni",
+ "mapOpenWeatherPressure": "OpenWeather Pressione",
+ "mapOpenWeatherWind": "OpenWeather Vento",
+ "mapOpenWeatherTemperature": "OpenWeather Temperatura",
+ "mapLayer": "Livello Mappa",
+ "mapCustom": "Personalizzata (XYZ)",
+ "mapCustomArcgis": "Personalizzata (ArcGIS)",
+ "mapCustomLabel": "Mappa Personalizzata",
+ "mapCarto": "Mappe base di Carto",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Stradale",
+ "mapGoogleHybrid": "Google Ibrida",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Bing Chiave Mappa",
+ "mapBingRoad": "Bing Mappa Stradale",
+ "mapBingAerial": "Bing Mappa Aerea",
+ "mapBingHybrid": "Bing Mappa ibrida",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Yandex Mappa",
+ "mapYandexSat": "Yandex Satellite",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox Stradale",
+ "mapMapboxStreetsDark": "Mapbox Stradale Notturno",
+ "mapMapboxOutdoors": "Mapbox all'Aperto",
+ "mapMapboxSatellite": "Mapbox Satellite",
+ "mapMapboxKey": "Mapbox Token di Accesso",
+ "mapMapTilerBasic": "MapTiler Base",
+ "mapMapTilerHybrid": "MapTiler Ibrida",
+ "mapMapTilerKey": "MapTiler Chiave API",
+ "mapLocationIqStreets": "LocationIQ Stradale",
+ "mapLocationIqDark": "LocationIQ Notturna",
+ "mapLocationIqKey": "LocationIQ Token di Accesso",
+ "mapTomTomBasic": "TomTom Base",
+ "mapTomTomFlow": "TomTom Traffico Stradale",
+ "mapTomTomIncidents": "TomTom Incidenti Stradali",
+ "mapTomTomKey": "TomTom Chiave API",
+ "mapHereBasic": "Here Base",
+ "mapHereHybrid": "Here Ibrida",
+ "mapHereSatellite": "Here Satellite",
+ "mapHereFlow": "Here Traffico Stradale",
+ "mapHereKey": "Here Chiave API",
+ "mapShapePolygon": "Poligono",
+ "mapShapeCircle": "Cerchio",
+ "mapShapePolyline": "Polilinea",
+ "mapLiveRoutes": "Percorso in Diretta",
+ "mapDirection": "Mostra direzione",
+ "mapCurrentLocation": "Posizione Corrente",
+ "mapPoiLayer": "Layer POI",
+ "mapClustering": "Gruppi di marcatori",
+ "mapOnSelect": "Mostra mappa su selezione",
+ "mapDefault": "Mappa predefinita",
+ "stateTitle": "Stato",
+ "stateName": "Attributo",
+ "stateValue": "Valore",
+ "commandTitle": "Comando",
+ "commandSend": "Invia",
+ "commandSent": "Comando inviato",
+ "commandQueued": "Comando accodato",
+ "commandUnit": "Unità",
+ "commandCustom": "Comando Personalizzato",
+ "commandDeviceIdentification": "Identificativo Dispositivo",
+ "commandPositionSingle": "Rapporto Singolo",
+ "commandPositionPeriodic": "Rapporti Periodici",
+ "commandPositionStop": "Ferma Rapporti",
+ "commandEngineStop": "Ferma Motore",
+ "commandEngineResume": "Riavvio Motore",
+ "commandAlarmArm": "Attiva Allarme",
+ "commandAlarmDisarm": "Disattiva Allarme",
+ "commandAlarmDismiss": "Ignora Allarme",
+ "commandSetTimezone": "Imposta Fuso Orario",
+ "commandRequestPhoto": "Richiedi Foto",
+ "commandPowerOff": "Spegni Dispositivo",
+ "commandRebootDevice": "Riavvia Dispositivo",
+ "commandFactoryReset": "Reset di fabbrica",
+ "commandSendSms": "Invia SMS",
+ "commandSendUssd": "Invia USSD",
+ "commandSosNumber": "Imposta Numero SOS",
+ "commandSilenceTime": "Imposta Orario Silenzio",
+ "commandSetPhonebook": "Imposta Rubrica",
+ "commandVoiceMessage": "Messaggio Vocale",
+ "commandOutputControl": "Controllo Uscita",
+ "commandVoiceMonitoring": "Monitoraggio Vocale",
+ "commandSetAgps": "Imposta AGPS",
+ "commandSetIndicator": "Imposta Indicatore",
+ "commandConfiguration": "Configurazione",
+ "commandGetVersion": "Rileva Versione",
+ "commandFirmwareUpdate": "Aggiorna Firmware",
+ "commandSetConnection": "Imposta Connessione",
+ "commandSetOdometer": "Imposta Odometro",
+ "commandGetModemStatus": "Rileva Stato Modem",
+ "commandGetDeviceStatus": "Rileva Stato Dispositivo",
+ "commandSetSpeedLimit": "Imposta Limite Velocità",
+ "commandModePowerSaving": "Modalità di Risparmio Energetico",
+ "commandModeDeepSleep": "Modalità Sonno Profondo",
+ "commandAlarmGeofence": "Imposta Allarme Geo Area",
+ "commandAlarmBattery": "Imposta Allarme Batteria",
+ "commandAlarmSos": "Imposta Allarme SOS",
+ "commandAlarmRemove": "Imposta Rimozione Allarme",
+ "commandAlarmClock": "Imposta Allarme Sveglia",
+ "commandAlarmSpeed": "Imposta Allarme Velocità",
+ "commandAlarmFall": "Imposta Allarme Caduta",
+ "commandAlarmVibration": "Imposta Allarme Vibrazione",
+ "commandFrequency": "Frequenza",
+ "commandTimezone": "Differenza Fuso Orario",
+ "commandMessage": "Messaggio",
+ "commandRadius": "Raggio",
+ "commandEnable": "Attiva",
+ "commandData": "Dati",
+ "commandIndex": "Indice",
+ "commandPhone": "Numero Telefonico",
+ "commandServer": "Server",
+ "commandPort": "Porta",
+ "eventAll": "Tutti gli Eventi",
+ "eventDeviceOnline": "Connesso al server",
+ "eventDeviceUnknown": "Stato sconosciuto",
+ "eventDeviceOffline": "Disconnesso dal Server",
+ "eventDeviceInactive": "Dispositivo non attivo",
+ "eventQueuedCommandSent": "Comando in coda inviato",
+ "eventDeviceMoving": "Dispositivo in movimento",
+ "eventDeviceStopped": "Dispositivo Fermo",
+ "eventDeviceOverspeed": "Limite di velocità superato",
+ "eventDeviceFuelDrop": "Perdita di carburante",
+ "eventDeviceFuelIncrease": "Incremento carburante",
+ "eventCommandResult": "Risultato comando",
+ "eventGeofenceEnter": "Entrato nella Geo Area",
+ "eventGeofenceExit": "Uscito dalla Geo Area",
+ "eventAlarm": "Allarme",
+ "eventIgnitionOn": "Accensione inserita",
+ "eventIgnitionOff": "Accensione disinserita",
+ "eventMaintenance": "Richiesta manutenzione",
+ "eventTextMessage": "Messaggio di testo ricevuto",
+ "eventDriverChanged": "Autista cambiato",
+ "eventMedia": "Media",
+ "eventsScrollToLast": "Scorri fino all'ultimo",
+ "eventsSoundEvents": "Suono eventi",
+ "eventsSoundAlarms": "Suono allarmi",
+ "alarmGeneral": "Generale",
+ "alarmSos": "SOS",
+ "alarmVibration": "Vibrazione",
+ "alarmMovement": "Movimento",
+ "alarmLowspeed": "Bassa Velocità",
+ "alarmOverspeed": "Velocità Eccessiva",
+ "alarmFallDown": "Caduta",
+ "alarmLowPower": "Alimentazione Bassa",
+ "alarmLowBattery": "Batteria Scarica",
+ "alarmFault": "Guasto",
+ "alarmPowerOff": "Spegni",
+ "alarmPowerOn": "Accendi",
+ "alarmDoor": "Porta",
+ "alarmLock": "Blocca",
+ "alarmUnlock": "Sblocca",
+ "alarmGeofence": "Geo Area",
+ "alarmGeofenceEnter": "Ingresso Geo Area",
+ "alarmGeofenceExit": "Uscita Geo Area",
+ "alarmGpsAntennaCut": "Antenna GPS Scollegata",
+ "alarmAccident": "Incidente",
+ "alarmTow": "Inclinazione",
+ "alarmIdle": "Inattivo",
+ "alarmHighRpm": "Giri Motore Elevati",
+ "alarmHardAcceleration": "Accelerazione Brusca",
+ "alarmHardBraking": "Frenata Brusca",
+ "alarmHardCornering": "Curva Stretta",
+ "alarmLaneChange": "Cambio Corsia",
+ "alarmFatigueDriving": "Affaticamento del Guidatore",
+ "alarmPowerCut": "Alimentazione Interrotta",
+ "alarmPowerRestored": "Alimentazione Ripristinata",
+ "alarmJamming": "Disturbatore Frequenze",
+ "alarmTemperature": "Temperatura",
+ "alarmParking": "Parcheggio",
+ "alarmBonnet": "Cofano",
+ "alarmFootBrake": "Freno a pedale",
+ "alarmFuelLeak": "Perdita carburante",
+ "alarmTampering": "Manomissione",
+ "alarmRemoving": "Rimozione",
+ "notificationType": "Tipo Notifica",
+ "notificationAlways": "Tutti i Dispositivi",
+ "notificationNotificators": "Canali",
+ "notificatorCommand": "Comandi",
+ "notificatorWeb": "Web",
+ "notificatorMail": "Email",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Riguarda il Percorso",
+ "reportCombined": "Combinato",
+ "reportRoute": "Percorso",
+ "reportEvents": "Eventi",
+ "reportTrips": "Viaggi",
+ "reportStops": "Fermate",
+ "reportSummary": "Sommario",
+ "reportDaily": "Riassunto Giornaliero",
+ "reportChart": "Grafico",
+ "reportConfigure": "Configura",
+ "reportEventTypes": "Tipi Evento",
+ "reportChartType": "Tipo Grafico",
+ "reportShowMarkers": "Mostra i marcatori",
+ "reportExport": "Esporta",
+ "reportEmail": "Rapporto Email",
+ "reportSchedule": "Cronoprogramma",
+ "reportPeriod": "Periodo",
+ "reportCustom": "Personalizzato",
+ "reportToday": "Oggi",
+ "reportYesterday": "Ieri",
+ "reportThisWeek": "Settimana Corrente",
+ "reportPreviousWeek": "Settimana Precedente",
+ "reportThisMonth": "Mese Corrente",
+ "reportPreviousMonth": "Mese Precedente",
+ "reportDeviceName": "Nome Dispositivo",
+ "reportAverageSpeed": "Velocità Media",
+ "reportMaximumSpeed": "Velocità Massima",
+ "reportEngineHours": "Ore di Guida",
+ "reportDuration": "Durata",
+ "reportStartDate": "Data di partenza",
+ "reportStartTime": "Ora di Partenza",
+ "reportStartAddress": "Indirizzo di Partenza",
+ "reportEndTime": "Ora di Arrivo",
+ "reportEndAddress": "Indirizzo di Arrivo",
+ "reportSpentFuel": "Carburante Consumato",
+ "reportStartOdometer": "Odometro Inizio",
+ "reportEndOdometer": "Odometro Fine",
+ "statisticsTitle": "Statistiche",
+ "statisticsCaptureTime": "Tempo di Cattura",
+ "statisticsActiveUsers": "Utenti Attivi",
+ "statisticsActiveDevices": "Dispositivi Attivi",
+ "statisticsRequests": "Richieste",
+ "statisticsMessagesReceived": "Messaggi Ricevuti",
+ "statisticsMessagesStored": "Messaggi Salvati",
+ "statisticsGeocoder": "Richieste Geocoder",
+ "statisticsGeolocation": "Richieste Geolocalizzazione",
+ "categoryArrow": "Freccia",
+ "categoryDefault": "Predefinita",
+ "categoryAnimal": "Animale",
+ "categoryBicycle": "Bicicletta",
+ "categoryBoat": "Barca",
+ "categoryBus": "Autobus",
+ "categoryCar": "Automobile",
+ "categoryCamper": "Camper",
+ "categoryCrane": "Gru",
+ "categoryHelicopter": "Elicottero",
+ "categoryMotorcycle": "Moto",
+ "categoryOffroad": "Fuoristrada",
+ "categoryPerson": "Persona",
+ "categoryPickup": "Pickup",
+ "categoryPlane": "Aeroplano",
+ "categoryShip": "Nave",
+ "categoryTractor": "Trattore",
+ "categoryTrain": "Treno",
+ "categoryTram": "Tram",
+ "categoryTrolleybus": "Filobus",
+ "categoryTruck": "Camion",
+ "categoryVan": "Furgone",
+ "categoryScooter": "Scooter",
+ "maintenanceStart": "Inizio",
+ "maintenancePeriod": "Periodo"
+} \ No newline at end of file
diff --git a/src/resources/l10n/ja.json b/src/resources/l10n/ja.json
new file mode 100644
index 00000000..3c99b0c0
--- /dev/null
+++ b/src/resources/l10n/ja.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "読み込み中...",
+ "sharedHide": "非表示",
+ "sharedSave": "保存",
+ "sharedUpload": "Upload",
+ "sharedSet": "設定",
+ "sharedCancel": "キャンセル",
+ "sharedCopy": "Copy",
+ "sharedAdd": "追加",
+ "sharedEdit": "編集",
+ "sharedRemove": "削除",
+ "sharedRemoveConfirm": "アイテムを削除しますか?",
+ "sharedNoData": "データがありません",
+ "sharedSubject": "Subject",
+ "sharedYes": "はい",
+ "sharedNo": "いいえ",
+ "sharedKm": "km",
+ "sharedMi": "mi",
+ "sharedNmi": "nmi",
+ "sharedMeters": "メートル",
+ "sharedFeet": "フィート",
+ "sharedKn": "kn",
+ "sharedKmh": "km/h",
+ "sharedMph": "mph",
+ "sharedHour": "時間",
+ "sharedMinute": "分",
+ "sharedSecond": "秒",
+ "sharedDays": "日",
+ "sharedHours": "時間",
+ "sharedMinutes": "分",
+ "sharedDecimalDegrees": "DEG形式(度)",
+ "sharedDegreesDecimalMinutes": "DMM形式(度分)",
+ "sharedDegreesMinutesSeconds": "DMS形式(度分秒)",
+ "sharedName": "名前",
+ "sharedDescription": "説明",
+ "sharedSearch": "検索",
+ "sharedIconScale": "アイコンの大きさ",
+ "sharedGeofence": "ジオフェンス",
+ "sharedGeofences": "ジオフェンス",
+ "sharedCreateGeofence": "ジオフェンスを作成",
+ "sharedNotifications": "通知",
+ "sharedNotification": "通知",
+ "sharedAttributes": "属性",
+ "sharedAttribute": "属性",
+ "sharedDrivers": "ドライバー",
+ "sharedDriver": "ドライバー",
+ "sharedArea": "エリア",
+ "sharedSound": "通知サウンド",
+ "sharedType": "タイプ",
+ "sharedDistance": "距離",
+ "sharedHourAbbreviation": "h",
+ "sharedMinuteAbbreviation": "m",
+ "sharedSecondAbbreviation": "s",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "l",
+ "sharedGallonAbbreviation": "gal",
+ "sharedLiter": "リットル",
+ "sharedImpGallon": "英ガロン",
+ "sharedUsGallon": "米ガロン",
+ "sharedLiterPerHourAbbreviation": "l/h",
+ "sharedGetMapState": "地図から情報を取得",
+ "sharedComputedAttribute": "算出属性",
+ "sharedComputedAttributes": "算出属性",
+ "sharedCheckComputedAttribute": "算出属性を確認",
+ "sharedExpression": "式",
+ "sharedDevice": "デバイス",
+ "sharedTest": "テスト",
+ "sharedTestNotification": "テスト通知を送信",
+ "sharedTestNotificators": "チャンネルをテスト",
+ "sharedTestExpression": "式をテスト",
+ "sharedCalendar": "カレンダー",
+ "sharedCalendars": "カレンダー",
+ "sharedFile": "ファイル",
+ "sharedSearchDevices": "デバイスを探す",
+ "sharedSortBy": "並び替え",
+ "sharedFilterMap": "マップのフィルター",
+ "sharedSelectFile": "ファイルを選択",
+ "sharedPhone": "電話",
+ "sharedRequired": "必須項目",
+ "sharedPreferences": "環境設定",
+ "sharedPermissions": "アクセス許可",
+ "sharedConnections": "接続",
+ "sharedExtra": "拡張",
+ "sharedPrimary": "第一",
+ "sharedSecondary": "第二",
+ "sharedTypeString": "文字列",
+ "sharedTypeNumber": "数値",
+ "sharedTypeBoolean": "論理値",
+ "sharedTimezone": "タイムゾーン",
+ "sharedInfoTitle": "情報",
+ "sharedSavedCommand": "保存したコマンド",
+ "sharedSavedCommands": "保存したコマンド",
+ "sharedNew": "新規…",
+ "sharedShowAddress": "住所を表示",
+ "sharedShowDetails": "詳細を見る",
+ "sharedDisabled": "無効",
+ "sharedMaintenance": "メンテナンス",
+ "sharedDeviceAccumulators": "蓄電池",
+ "sharedAlarms": "警報",
+ "sharedLocation": "場所",
+ "sharedImport": "インポート",
+ "sharedColumns": "列",
+ "sharedDropzoneText": "ファイルをドラッグ&ドロップするかクリック",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "シンプル",
+ "calendarRecurrence": "再発",
+ "calendarOnce": "一度",
+ "calendarDaily": "日次",
+ "calendarWeekly": "週次",
+ "calendarMonthly": "月次",
+ "calendarDays": "日",
+ "calendarSunday": "日曜日",
+ "calendarMonday": "月曜日",
+ "calendarTuesday": "火曜日",
+ "calendarWednesday": "水曜日",
+ "calendarThursday": "木曜日",
+ "calendarFriday": "金曜日",
+ "calendarSaturday": "土曜日",
+ "attributeShowGeofences": "ジオフェンスを表示",
+ "attributeSpeedLimit": "速度制限",
+ "attributeFuelDropThreshold": "燃料低下の閾値",
+ "attributeFuelIncreaseThreshold": "燃料増加の閾値",
+ "attributePolylineDistance": "経路距離",
+ "attributeReportIgnoreOdometer": "レポート: 走行距離計を無視する",
+ "attributeWebReportColor": "Web: レポートの色",
+ "attributeDevicePassword": "デバイスパスワード",
+ "attributeDeviceImage": "デバイスのイメージ",
+ "attributeDeviceInactivityStart": "デバイス非アクティブ開始",
+ "attributeDeviceInactivityPeriod": "デバイス非アクティブ期間",
+ "attributeProcessingCopyAttributes": "処理中: 属性のコピー",
+ "attributeColor": "色",
+ "attributeWebLiveRouteLength": "Web: リアルタイム経路長",
+ "attributeWebSelectZoom": "Web: 選択時にズーム",
+ "attributeWebMaxZoom": "Web: 最大化",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Pushover User Key",
+ "attributePushoverDeviceNames": "Pushover デバイス名",
+ "attributeMailSmtpHost": "メール: SMTP ホスト",
+ "attributeMailSmtpPort": "メール: SMTP ポート",
+ "attributeMailSmtpStarttlsEnable": "メール: SMTP STARTTLS 有効",
+ "attributeMailSmtpStarttlsRequired": "メール: SMTP STARTTLS 要求",
+ "attributeMailSmtpSslEnable": "メール: SMTP SSL 有効",
+ "attributeMailSmtpSslTrust": "メール: SMTP SSL 信頼",
+ "attributeMailSmtpSslProtocols": "メール: SMTP SSL プロトコル",
+ "attributeMailSmtpFrom": "メール: SMTP 元",
+ "attributeMailSmtpAuth": "メール: SMTP 認証有効",
+ "attributeMailSmtpUsername": "メール: SMTP ユーザー名",
+ "attributeMailSmtpPassword": "メール: SMTP パスワード",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI:属性を無効にする",
+ "attributeUiDisableGroups": "UI:グループを無効にする",
+ "attributeUiDisableEvents": "UI: イベントを無効にする",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "UI: ドライバーを無効にする",
+ "attributeUiDisableComputedAttributes": "UI: 計算された属性を無効にする",
+ "attributeUiDisableCalendars": "UI: カレンダーを無効にする",
+ "attributeUiDisableMaintenance": "UI: メンテナンスを無効にする",
+ "attributeUiHidePositionAttributes": "UI: 位置属性を非表示にする",
+ "attributeUiDisableLoginLanguage": "UI:ログイン言語を無効にする",
+ "attributeNotificationTokens": "通知トークン",
+ "attributePopupInfo": "ポップアップ情報",
+ "errorTitle": "エラー",
+ "errorGeneral": "無効なパラメータまたは制約違反",
+ "errorConnection": "接続エラー",
+ "errorSocket": "Web ソケット接続エラー",
+ "errorZero": "0にはできません。",
+ "userEmail": "メール",
+ "userPassword": "パスワード",
+ "userAdmin": "管理者",
+ "userRemember": "記憶する",
+ "userExpirationTime": "有効期限",
+ "userDeviceLimit": "デバイス制限",
+ "userUserLimit": "ユーザー制限",
+ "userDeviceReadonly": "デバイス読み取り専用",
+ "userLimitCommands": "リミットコマンド",
+ "userDisableReports": "レポートを無効にする",
+ "userFixedEmail": "E-Mail変更は無し",
+ "userToken": "トークン",
+ "userDeleteAccount": "アカウントを削除",
+ "userTemporary": "Temporary",
+ "loginTitle": "ログイン",
+ "loginLanguage": "言語",
+ "loginReset": "パスワードをリセット",
+ "loginRegister": "登録",
+ "loginLogin": "ログイン",
+ "loginOpenId": "Login with OpenID",
+ "loginFailed": "メールアドレスまたはパスワードが間違っています",
+ "loginCreated": "新しいユーザーが登録されました",
+ "loginResetSuccess": "メールを確認してください",
+ "loginUpdateSuccess": "新しいパスワードが設定されました",
+ "loginLogout": "ログアウト",
+ "loginLogo": "ロゴ",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "デバイスと状態",
+ "deviceSelected": "選択したデバイス",
+ "deviceTitle": "デバイス",
+ "devicePrimaryInfo": "デバイスのタイトル",
+ "deviceSecondaryInfo": "デバイスの詳細",
+ "deviceIdentifier": "ID",
+ "deviceModel": "モデル",
+ "deviceContact": "連絡先",
+ "deviceCategory": "カテゴリー",
+ "deviceLastUpdate": "最終更新",
+ "deviceCommand": "コマンド",
+ "deviceFollow": "デバイスを追跡",
+ "deviceTotalDistance": "総距離",
+ "deviceStatus": "ステータス",
+ "deviceStatusOnline": "オンライン",
+ "deviceStatusOffline": "オフライン",
+ "deviceStatusUnknown": "不明",
+ "deviceRegisterFirst": "最初のデバイスを登録",
+ "deviceIdentifierHelp": "IMEI、シリアル番号あるいはその他のID。サーバーに接続するデバイスのIDに一致している必要があります。",
+ "deviceShare": "Share Device",
+ "groupDialog": "グループ",
+ "groupParent": "グループ",
+ "groupNoGroup": "グループなし",
+ "settingsTitle": "設定",
+ "settingsUser": "アカウント",
+ "settingsGroups": "グループ",
+ "settingsServer": "サーバー",
+ "settingsUsers": "ユーザー",
+ "settingsDistanceUnit": "距離の単位",
+ "settingsAltitudeUnit": "高度の単位",
+ "settingsSpeedUnit": "速度の単位",
+ "settingsVolumeUnit": "容量の単位",
+ "settingsTwelveHourFormat": "12 時間形式",
+ "settingsCoordinateFormat": "座標形式",
+ "settingsServerVersion": "サーバーのバージョン",
+ "settingsAppVersion": "アプリのバージョン",
+ "settingsConnection": "接続",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "レポート",
+ "reportScheduled": "Scheduled Reports",
+ "reportDevice": "デバイス",
+ "reportGroup": "グループ",
+ "reportFrom": "開始日時",
+ "reportTo": "終了日時",
+ "reportShow": "詳細",
+ "reportClear": "クリア",
+ "linkGoogleMaps": "Googleマップ",
+ "linkAppleMaps": "Appleマップ",
+ "linkStreetView": "Street View",
+ "positionFixTime": "固定日時",
+ "positionDeviceTime": "デバイスの時刻",
+ "positionServerTime": "サーバーの時刻",
+ "positionValid": "測位",
+ "positionAccuracy": "正確な位置",
+ "positionLatitude": "緯度",
+ "positionLongitude": "経度",
+ "positionAltitude": "高度",
+ "positionSpeed": "速度",
+ "positionCourse": "方角",
+ "positionAddress": "場所",
+ "positionProtocol": "プロトコル",
+ "positionDistance": "距離",
+ "positionRpm": "RPM",
+ "positionFuel": "燃料",
+ "positionPower": "電源",
+ "positionBattery": "バッテリー",
+ "positionRaw": "Raw",
+ "positionIndex": "インデックス",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "衛星の位置",
+ "positionSatVisible": "衛星の表示",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "ローミング",
+ "positionEvent": "イベント",
+ "positionAlarm": "警報",
+ "positionStatus": "ステータス",
+ "positionOdometer": "走行距離計",
+ "positionServiceOdometer": "サービス走行距離計",
+ "positionTripOdometer": "トリップ走行距離計",
+ "positionHours": "時間",
+ "positionSteps": "ステップ",
+ "positionInput": "入力",
+ "positionHeartRate": "Heart Rate",
+ "positionOutput": "出力",
+ "positionBatteryLevel": "バッテリーレベル",
+ "positionFuelConsumption": "燃料消費",
+ "positionRfid": "RFID",
+ "positionVersionFw": "ファームウェアバージョン",
+ "positionVersionHw": "ハードウェアバージョン",
+ "positionIgnition": "イグニッション",
+ "positionFlags": "フラグ",
+ "positionCharge": "充電",
+ "positionIp": "IP",
+ "positionArchive": "アーカイブ",
+ "positionVin": "VIN",
+ "positionApproximate": "近似",
+ "positionThrottle": "スロットル",
+ "positionMotion": "運動",
+ "positionArmed": "強化",
+ "positionAcceleration": "加速",
+ "positionTemp": "温度",
+ "positionDeviceTemp": "デバイス温度",
+ "positionCoolantTemp": "Coolant Temperature",
+ "positionOperator": "運用",
+ "positionCommand": "コマンド",
+ "positionBlocked": "ブロック",
+ "positionDtcs": "DTC",
+ "positionObdSpeed": "OBD 速度",
+ "positionObdOdometer": "OBD 走行距離計",
+ "positionDrivingTime": "Driving Time",
+ "positionDriverUniqueId": "ドライバー固有ID",
+ "positionCard": "Card",
+ "positionImage": "画像",
+ "positionVideo": "ビデオ",
+ "positionAudio": "音声",
+ "serverTitle": "サーバー設定",
+ "serverZoom": "拡大縮小",
+ "serverRegistration": "新規ユーザー登録を許可",
+ "serverReadonly": "読み取り専用",
+ "serverForceSettings": "強制的に設定",
+ "serverAnnouncement": "お知らせ",
+ "serverName": "Server Name",
+ "serverDescription": "Server Description",
+ "serverColorPrimary": "Color Primary",
+ "serverColorSecondary": "Color Secondary",
+ "serverLogo": "Logo Image",
+ "serverLogoInverted": "Inverted Logo Image",
+ "serverChangeDisable": "Disable Server Change",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "地図",
+ "mapActive": "アクティブなマップ",
+ "mapOverlay": "マップのオーバーレイ",
+ "mapOverlayCustom": "カスタムオーバーレイ",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather APIキー",
+ "mapOpenWeatherClouds": "OpenWeather 雲",
+ "mapOpenWeatherPrecipitation": "OpenWeather 降水量",
+ "mapOpenWeatherPressure": "OpenWeather 気圧",
+ "mapOpenWeatherWind": "OpenWeather 風",
+ "mapOpenWeatherTemperature": "OpenWeather 気温",
+ "mapLayer": "使用する地図",
+ "mapCustom": "カスタム (XYZ)",
+ "mapCustomArcgis": "カスタム (ArcGIS)",
+ "mapCustomLabel": "カスタム地図",
+ "mapCarto": "Cartoベースマップ",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Map 道路",
+ "mapGoogleHybrid": "Google Map ハイブリッド",
+ "mapGoogleSatellite": "Google Map 人工衛星",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Bingマップキー",
+ "mapBingRoad": "Bingマップ",
+ "mapBingAerial": "Bing航空写真マップ",
+ "mapBingHybrid": "Bingハイブリッドマップ",
+ "mapBaidu": "百度地図",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Yandexマップ",
+ "mapYandexSat": "Yandex衛星写真マップ",
+ "mapWikimedia": "ウィキメディア",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox Streets",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox Outdoors",
+ "mapMapboxSatellite": "Mapbox Satellite",
+ "mapMapboxKey": "Mapbox Access Token",
+ "mapMapTilerBasic": "MapTiler Basic",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "MapTiler API Key",
+ "mapLocationIqStreets": "LocatioIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ Access Token",
+ "mapTomTomBasic": "TomTom Basic",
+ "mapTomTomFlow": "TomTom Traffic Flow",
+ "mapTomTomIncidents": "TomTom Traffic Incidents",
+ "mapTomTomKey": "TomTom API Key",
+ "mapHereBasic": "Here Basic",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satellite",
+ "mapHereFlow": "Here Traffic Flow",
+ "mapHereKey": "Here API Key",
+ "mapShapePolygon": "多角形",
+ "mapShapeCircle": "円形",
+ "mapShapePolyline": "折れ線形",
+ "mapLiveRoutes": "リアルタイム経路",
+ "mapDirection": "方向を表示",
+ "mapCurrentLocation": "現在の位置",
+ "mapPoiLayer": "POI レイヤー",
+ "mapClustering": "マーカー クラスタリング",
+ "mapOnSelect": "選択をマップで表示",
+ "mapDefault": "デフォルトのマップ",
+ "stateTitle": "詳細",
+ "stateName": "属性",
+ "stateValue": "値",
+ "commandTitle": "コマンド",
+ "commandSend": "送信",
+ "commandSent": "コマンドを送信した。",
+ "commandQueued": "コマンド実行待ち",
+ "commandUnit": "単位",
+ "commandCustom": "カスタムコマンド",
+ "commandDeviceIdentification": "デバイス ID",
+ "commandPositionSingle": "シングルレポート",
+ "commandPositionPeriodic": "定期レポート",
+ "commandPositionStop": "レポートを停止",
+ "commandEngineStop": "エンジン停止",
+ "commandEngineResume": "エンジン再始動",
+ "commandAlarmArm": "警報開始",
+ "commandAlarmDisarm": "警報解除",
+ "commandAlarmDismiss": "警報を無視する",
+ "commandSetTimezone": "タイムゾーンを設定",
+ "commandRequestPhoto": "写真をリクエスト",
+ "commandPowerOff": "デバイス電源 OFF",
+ "commandRebootDevice": "デバイスを再起動",
+ "commandFactoryReset": "工場出荷状態に戻す",
+ "commandSendSms": "SMSを送信",
+ "commandSendUssd": "USSDを送信",
+ "commandSosNumber": "SOS送信先電話番号",
+ "commandSilenceTime": "通知OFF時間帯",
+ "commandSetPhonebook": "電話帳設定",
+ "commandVoiceMessage": "音声メッセージ",
+ "commandOutputControl": "コマンド出力制御",
+ "commandVoiceMonitoring": "音声モニタ",
+ "commandSetAgps": "AGPS を設定",
+ "commandSetIndicator": "通知設定",
+ "commandConfiguration": "設定",
+ "commandGetVersion": "バージョンを取得",
+ "commandFirmwareUpdate": "ファームウェアを更新",
+ "commandSetConnection": "接続を設定",
+ "commandSetOdometer": "走行距離計を設定",
+ "commandGetModemStatus": "モデムのステータスを取得",
+ "commandGetDeviceStatus": "デバイスのステータスを取得",
+ "commandSetSpeedLimit": "速度制限を設定",
+ "commandModePowerSaving": "省電力モード",
+ "commandModeDeepSleep": "ディープスリープモード",
+ "commandAlarmGeofence": "ジオフェンス警報を設定",
+ "commandAlarmBattery": "バッテリー警報を設定",
+ "commandAlarmSos": "SOS警報を設定",
+ "commandAlarmRemove": "取り外し警報を設定",
+ "commandAlarmClock": "時計警報を設定",
+ "commandAlarmSpeed": "速度警報を設定",
+ "commandAlarmFall": "落下警報を設定",
+ "commandAlarmVibration": "振動警報を設定",
+ "commandFrequency": "周期",
+ "commandTimezone": "世界標準時との時差",
+ "commandMessage": "メッセージ",
+ "commandRadius": "Radius",
+ "commandEnable": "有効",
+ "commandData": "データー",
+ "commandIndex": "コマンド一覧",
+ "commandPhone": "電話番号",
+ "commandServer": "サーバー",
+ "commandPort": "ポート",
+ "eventAll": "全てのイベント",
+ "eventDeviceOnline": "オンライン状態",
+ "eventDeviceUnknown": "状態不明",
+ "eventDeviceOffline": "オフライン状態",
+ "eventDeviceInactive": "デバイス非アクティブ",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "デバイス移動中",
+ "eventDeviceStopped": "デバイス停止中",
+ "eventDeviceOverspeed": "速度制限を超過",
+ "eventDeviceFuelDrop": "燃料残量低下",
+ "eventDeviceFuelIncrease": "燃料増加",
+ "eventCommandResult": "コマンドの実行結果",
+ "eventGeofenceEnter": "ジオフェンスに進入しました。",
+ "eventGeofenceExit": "ジオフェンスから退出しました。",
+ "eventAlarm": "警報",
+ "eventIgnitionOn": "イグニッション ON",
+ "eventIgnitionOff": "イグニッション OFF",
+ "eventMaintenance": "メンテナンスが必要です。",
+ "eventTextMessage": "テキストメッセージを受信しました。",
+ "eventDriverChanged": "ドライバーが交代しました。",
+ "eventMedia": "メディア",
+ "eventsScrollToLast": "最後にスクロール",
+ "eventsSoundEvents": "警報イベント",
+ "eventsSoundAlarms": "警報音",
+ "alarmGeneral": "全般",
+ "alarmSos": "SOS",
+ "alarmVibration": "振動",
+ "alarmMovement": "移動",
+ "alarmLowspeed": "低速",
+ "alarmOverspeed": "速度超過",
+ "alarmFallDown": "転倒",
+ "alarmLowPower": "電力低下",
+ "alarmLowBattery": "バッテリー残量低下",
+ "alarmFault": "測位失敗",
+ "alarmPowerOff": "電源 OFF",
+ "alarmPowerOn": "電源 ON",
+ "alarmDoor": "ドア",
+ "alarmLock": "ロック",
+ "alarmUnlock": "ロック解除",
+ "alarmGeofence": "ジオフェンス",
+ "alarmGeofenceEnter": "ジオフェンスに進入",
+ "alarmGeofenceExit": "ジオフェンスから退出",
+ "alarmGpsAntennaCut": "GPSアンテナ切断",
+ "alarmAccident": "事故",
+ "alarmTow": "牽引",
+ "alarmIdle": "アイドリング",
+ "alarmHighRpm": "高 RPM",
+ "alarmHardAcceleration": "急加速",
+ "alarmHardBraking": "急ブレーキ",
+ "alarmHardCornering": "急転回",
+ "alarmLaneChange": "車線変更",
+ "alarmFatigueDriving": "過労運転",
+ "alarmPowerCut": "電源カット",
+ "alarmPowerRestored": "電源が復旧した。",
+ "alarmJamming": "ジャミング",
+ "alarmTemperature": "温度",
+ "alarmParking": "駐車",
+ "alarmBonnet": "ボンネット",
+ "alarmFootBrake": "フットブレーキ",
+ "alarmFuelLeak": "燃料漏れ",
+ "alarmTampering": "窃盗",
+ "alarmRemoving": "取り外し",
+ "notificationType": "通知の種別",
+ "notificationAlways": "すべてのデバイス",
+ "notificationNotificators": "チャンネル",
+ "notificatorCommand": "Command",
+ "notificatorWeb": "Web",
+ "notificatorMail": "メール",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "再生",
+ "reportCombined": "Combined",
+ "reportRoute": "移動経路",
+ "reportEvents": "イベント",
+ "reportTrips": "走行距離",
+ "reportStops": "停止",
+ "reportSummary": "概要",
+ "reportDaily": "日次サマリー",
+ "reportChart": "グラフ",
+ "reportConfigure": "設定",
+ "reportEventTypes": "イベント種別",
+ "reportChartType": "表示するグラフ",
+ "reportShowMarkers": "マーカーを表示",
+ "reportExport": "エクスポート",
+ "reportEmail": "Eメールレポート",
+ "reportSchedule": "Schedule",
+ "reportPeriod": "期間",
+ "reportCustom": "カスタム",
+ "reportToday": "今日",
+ "reportYesterday": "昨日",
+ "reportThisWeek": "今週",
+ "reportPreviousWeek": "先週",
+ "reportThisMonth": "今月",
+ "reportPreviousMonth": "先月",
+ "reportDeviceName": "デバイス名",
+ "reportAverageSpeed": "平均速度",
+ "reportMaximumSpeed": "最高速度",
+ "reportEngineHours": "エンジン稼働時間",
+ "reportDuration": "期間",
+ "reportStartDate": "開始日",
+ "reportStartTime": "開始日時",
+ "reportStartAddress": "出発地",
+ "reportEndTime": "終了日時",
+ "reportEndAddress": "到着地",
+ "reportSpentFuel": "燃料消費量",
+ "reportStartOdometer": "走行距離計開始",
+ "reportEndOdometer": "走行距離計終了",
+ "statisticsTitle": "統計",
+ "statisticsCaptureTime": "位置補足時間",
+ "statisticsActiveUsers": "稼働中ユーザー",
+ "statisticsActiveDevices": "稼働中デバイス",
+ "statisticsRequests": "リクエスト",
+ "statisticsMessagesReceived": "受信したメッセージ",
+ "statisticsMessagesStored": "保存したメッセージ",
+ "statisticsGeocoder": "ジオコーダーリクエスト",
+ "statisticsGeolocation": "ジオロケーションリクエスト",
+ "categoryArrow": "矢印",
+ "categoryDefault": "未指定",
+ "categoryAnimal": "動物",
+ "categoryBicycle": "自転車",
+ "categoryBoat": "ボート",
+ "categoryBus": "バス",
+ "categoryCar": "乗用車",
+ "categoryCamper": "Camper",
+ "categoryCrane": "クレーン",
+ "categoryHelicopter": "ヘリコプター",
+ "categoryMotorcycle": "オートバイ",
+ "categoryOffroad": "オフロード",
+ "categoryPerson": "個人",
+ "categoryPickup": "ピックアップ",
+ "categoryPlane": "航空機",
+ "categoryShip": "船舶",
+ "categoryTractor": "トラクター",
+ "categoryTrain": "鉄道",
+ "categoryTram": "トラム",
+ "categoryTrolleybus": "トロリーバス",
+ "categoryTruck": "トラック",
+ "categoryVan": "バン",
+ "categoryScooter": "スクーター",
+ "maintenanceStart": "メンテナンス開始",
+ "maintenancePeriod": "メンテナンス終了"
+} \ No newline at end of file
diff --git a/src/resources/l10n/ka.json b/src/resources/l10n/ka.json
new file mode 100644
index 00000000..5a6b4cb2
--- /dev/null
+++ b/src/resources/l10n/ka.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "იტვირთება...",
+ "sharedHide": "დამალვა",
+ "sharedSave": "შენახვა",
+ "sharedUpload": "Upload",
+ "sharedSet": "დაყენება",
+ "sharedCancel": "უარყოფა",
+ "sharedCopy": "Copy",
+ "sharedAdd": "დამატება",
+ "sharedEdit": "შეცვლა",
+ "sharedRemove": "წაშლა",
+ "sharedRemoveConfirm": "გსურთ წაშლა ?",
+ "sharedNoData": "Მონაცემები არ არის",
+ "sharedSubject": "Subject",
+ "sharedYes": "დიახ",
+ "sharedNo": "არა",
+ "sharedKm": "კმ",
+ "sharedMi": "მლ",
+ "sharedNmi": "nmi",
+ "sharedMeters": "მ",
+ "sharedFeet": "ფუტი",
+ "sharedKn": "kn",
+ "sharedKmh": "კმ/სთ",
+ "sharedMph": "მლ/სთ",
+ "sharedHour": "საათი",
+ "sharedMinute": "წუთი",
+ "sharedSecond": "წამი",
+ "sharedDays": "დღეები",
+ "sharedHours": "საათები",
+ "sharedMinutes": "წუთები",
+ "sharedDecimalDegrees": "ათობითი გრადუსი",
+ "sharedDegreesDecimalMinutes": "გრადუსი ათეული წუთები",
+ "sharedDegreesMinutesSeconds": "გრადუსი ათეული წამები",
+ "sharedName": "დასახელება",
+ "sharedDescription": "აღწერილობა",
+ "sharedSearch": "ძებნა",
+ "sharedIconScale": "იკონკის მასშტაბი",
+ "sharedGeofence": "გეოზონა",
+ "sharedGeofences": "გეოზონები",
+ "sharedCreateGeofence": "შექმენით Geofence",
+ "sharedNotifications": "შეტყობინებები",
+ "sharedNotification": "შეტყობინება",
+ "sharedAttributes": "ატრიბუტები",
+ "sharedAttribute": "ატრიბუტი",
+ "sharedDrivers": "მძღოლები",
+ "sharedDriver": "მძროლი",
+ "sharedArea": "ფართობი",
+ "sharedSound": "შეტყობინების Sound",
+ "sharedType": "ტიპი",
+ "sharedDistance": "დისტანცია",
+ "sharedHourAbbreviation": "ს",
+ "sharedMinuteAbbreviation": "ჭ",
+ "sharedSecondAbbreviation": "წ",
+ "sharedVoltAbbreviation": "ვ",
+ "sharedLiterAbbreviation": "ლ",
+ "sharedGallonAbbreviation": "გალონი",
+ "sharedLiter": "ლიტრი",
+ "sharedImpGallon": "ინგლისური გალონი",
+ "sharedUsGallon": "ამერიკული გალონი",
+ "sharedLiterPerHourAbbreviation": "ლ/სთ",
+ "sharedGetMapState": "რუკის მდგომარეობა",
+ "sharedComputedAttribute": "გამოთვილითი ატრიბუტი",
+ "sharedComputedAttributes": "გამოთვილითი ატრიბუტები",
+ "sharedCheckComputedAttribute": "შეამოწმეთ გამოთვილითი ატრიბუტი",
+ "sharedExpression": "გამოსახულება",
+ "sharedDevice": "ოწყობილობა",
+ "sharedTest": "ტესტი",
+ "sharedTestNotification": "სატესტო შეტყობინების გაგზავნა",
+ "sharedTestNotificators": "სატესტო არხი",
+ "sharedTestExpression": "სატესტო გამოხატულება",
+ "sharedCalendar": "კალენდარი",
+ "sharedCalendars": "კალენდრები",
+ "sharedFile": "ფაილი",
+ "sharedSearchDevices": "მოწყობილობების ძებნა",
+ "sharedSortBy": "დალაგება",
+ "sharedFilterMap": "რუკაზე გაფილტვრა ",
+ "sharedSelectFile": "აირჩიე ფაილი",
+ "sharedPhone": "ტელეფონი",
+ "sharedRequired": "სავალდებულოა",
+ "sharedPreferences": "პრეფერენციები",
+ "sharedPermissions": "უფლებები",
+ "sharedConnections": "კავშირები",
+ "sharedExtra": "დამატებითი",
+ "sharedPrimary": "პირველადი",
+ "sharedSecondary": "მეორადი",
+ "sharedTypeString": "სტრიქონი",
+ "sharedTypeNumber": "ნომერი",
+ "sharedTypeBoolean": "ლოგიკური",
+ "sharedTimezone": "დროის სარტყელი",
+ "sharedInfoTitle": "ინფორმაცია",
+ "sharedSavedCommand": "შენახული ბრძანებება",
+ "sharedSavedCommands": "შენახული ბრძანებები",
+ "sharedNew": "ახალი…",
+ "sharedShowAddress": "მისამართის ჩვენება",
+ "sharedShowDetails": "უფრო ვრცლად",
+ "sharedDisabled": "გამორთულია",
+ "sharedMaintenance": "მომსახურება",
+ "sharedDeviceAccumulators": "აკუმულატორები",
+ "sharedAlarms": "სიგნალიზაცია",
+ "sharedLocation": "ლოკაცია",
+ "sharedImport": "იმპორტი",
+ "sharedColumns": "სვეტი",
+ "sharedDropzoneText": "გადმოათრიეთ ფაილი აქ ან დააწკაპუნეთ",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "მარტივი",
+ "calendarRecurrence": "რეციდივი",
+ "calendarOnce": "ერთხელ",
+ "calendarDaily": "ყოველდღიური",
+ "calendarWeekly": "ყოველკვირეული",
+ "calendarMonthly": "ყოველთვიური",
+ "calendarDays": "Days",
+ "calendarSunday": "კვირა",
+ "calendarMonday": "ორშაბათი",
+ "calendarTuesday": "სამშაბათი",
+ "calendarWednesday": "თხშაბათი",
+ "calendarThursday": "ხუთშაბათი",
+ "calendarFriday": "პარასკევი",
+ "calendarSaturday": "შაბათი",
+ "attributeShowGeofences": "Geofences-ს ჩვენება",
+ "attributeSpeedLimit": "სიჩქარის ლიმიტი",
+ "attributeFuelDropThreshold": "საწვავის ვარდნის ბარიერი",
+ "attributeFuelIncreaseThreshold": "საწვავის გაზრდის ბარიერი",
+ "attributePolylineDistance": "ტეხილის მანძილი",
+ "attributeReportIgnoreOdometer": "Report: ოდომეტრობის იგნორირება",
+ "attributeWebReportColor": "Web: რეპორტის ფერი",
+ "attributeDevicePassword": "მოწყობილობის პაროლი",
+ "attributeDeviceImage": "მოწყობილობის სურათი",
+ "attributeDeviceInactivityStart": "მოწყობილობის უმოქმედობის დაწყება",
+ "attributeDeviceInactivityPeriod": "მოწყობილობის უმოქმედობის პერიოდი",
+ "attributeProcessingCopyAttributes": "დამუშავება: ატრიბუტების კოპირება",
+ "attributeColor": "ფერი",
+ "attributeWebLiveRouteLength": "Web: ცოცხალი მარშრუტის სიგრძე",
+ "attributeWebSelectZoom": "Web: მასშტაბის არჩევა",
+ "attributeWebMaxZoom": "Web: მაქსიმალური ზუმი",
+ "attributeTelegramChatId": "Telegram ჩატის ID",
+ "attributePushoverUserKey": "Pushover-ის მომხმარებლის გასაღები",
+ "attributePushoverDeviceNames": "Pushover-ის მოწყობილობის სახელები",
+ "attributeMailSmtpHost": "Mail: SMTP ჰოსტი",
+ "attributeMailSmtpPort": "Mail: SMTP პორტი",
+ "attributeMailSmtpStarttlsEnable": "Mail: SMTP STARTTLS ჩართვა",
+ "attributeMailSmtpStarttlsRequired": "Mail: SMTP STARTTLS სავალდებულოა",
+ "attributeMailSmtpSslEnable": "Mail: SMTP SSL ჩართვა",
+ "attributeMailSmtpSslTrust": "Mail: SMTP SSL ნდობა",
+ "attributeMailSmtpSslProtocols": "Mail: SMTP SSL პროტოკოლი",
+ "attributeMailSmtpFrom": "Mail: SMTP საიდან",
+ "attributeMailSmtpAuth": "Mail: SMTP აუტენტიფიკაციის ჩართვა",
+ "attributeMailSmtpUsername": "Mail: SMTP სახელი",
+ "attributeMailSmtpPassword": "Mail: SMTP პაროლი",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "მომსხმარებლის ინტერფეისი: გამორთეთ ატრიბუტები",
+ "attributeUiDisableGroups": "მომსხმარებლის ინტერფეისი: ჯგუფების გამორთვა",
+ "attributeUiDisableEvents": "UI: მოვლენების გამორთვა",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "UI: მძღოლების გამორთვა",
+ "attributeUiDisableComputedAttributes": "UI: გამოთვლითი ატრიბუტების გამორთვა",
+ "attributeUiDisableCalendars": "UI: კალენდრის გამორთვა",
+ "attributeUiDisableMaintenance": "UI: ტექნიკური მომსახურება გამორთვა",
+ "attributeUiHidePositionAttributes": "UI: პოზიციის ატრიბუტების დამალვა",
+ "attributeUiDisableLoginLanguage": "მომსხმარებლის ინტერფეისი: გამორთეთ შესვლის ენა",
+ "attributeNotificationTokens": "შეტყობინების ნიშნები",
+ "attributePopupInfo": "ამომხტარი ინფორმაცია",
+ "errorTitle": "შეცდომა",
+ "errorGeneral": "პარამეტრების ან შეზღუდვების არასწორი დარღვევა",
+ "errorConnection": "კავშირის შეცდომა",
+ "errorSocket": "ვებგვერდის კავშირის შეცდომა",
+ "errorZero": "არ შეიძლება იყოს ნული",
+ "userEmail": "ელ-ფოსტა",
+ "userPassword": "პაროლი",
+ "userAdmin": "ადმინი",
+ "userRemember": "დამიმახსოვრე",
+ "userExpirationTime": "ვადის გასვლა",
+ "userDeviceLimit": "მოწყობილობის ლიმიტი",
+ "userUserLimit": "მომხმარებლის ლიმიტი",
+ "userDeviceReadonly": "მოწყობილობა მხოლოდ წაკითხვით",
+ "userLimitCommands": "შეზღუდული ბრძანებები",
+ "userDisableReports": "რეპორტების გამორთვა",
+ "userFixedEmail": "ელფოსტა არ იცვლება",
+ "userToken": "ტოკენი",
+ "userDeleteAccount": "Ანგარიშის წაშლა",
+ "userTemporary": "Temporary",
+ "loginTitle": "ავტორიზაცია",
+ "loginLanguage": "ენა",
+ "loginReset": "პაროლის განულება",
+ "loginRegister": "რეგისტრაცია",
+ "loginLogin": "შესვლა",
+ "loginOpenId": "შედით OpenID-ით",
+ "loginFailed": "არასწორი ელ-ფოსტა ან პაროლი",
+ "loginCreated": "ახალი მომხარებელი დარეგისტრირდა",
+ "loginResetSuccess": "შეამოწმეთ თქვენი ელექტორნული ფოსტა",
+ "loginUpdateSuccess": "ახალი პაროლი დაყენებულია",
+ "loginLogout": "გამოსვლა",
+ "loginLogo": "ლოგო",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "მოწყობილობები და სტატუსი",
+ "deviceSelected": "არჩეული მოწყობილობები",
+ "deviceTitle": "მოწყობილობები",
+ "devicePrimaryInfo": "მოწყობილობის დასახელება",
+ "deviceSecondaryInfo": "მოწყობილობის დეტალები",
+ "deviceIdentifier": "იდენტიფიკატორი",
+ "deviceModel": "მოდელი",
+ "deviceContact": "კონტაქტი",
+ "deviceCategory": "კატეგორია",
+ "deviceLastUpdate": "ბოლო განახლება",
+ "deviceCommand": "ბრძანება",
+ "deviceFollow": "გაყოლა",
+ "deviceTotalDistance": "მთლიანი მანძილი",
+ "deviceStatus": "სტატუსი",
+ "deviceStatusOnline": "ონლაინ რეჟიმი",
+ "deviceStatusOffline": "ოფლაინ რეჟიმი",
+ "deviceStatusUnknown": "უცნობი",
+ "deviceRegisterFirst": "დაარეგისტრირეთ თქვენი პირველი მოწყობილობა",
+ "deviceIdentifierHelp": "IMEI, ან სხვა იდენტიფიკატორი. ის უნდა ემთხვეოდეს იდენტიფიკატორს რომელსაც მოწყობილბა უგზავნის სერვერს",
+ "deviceShare": "Share Device",
+ "groupDialog": "ჯგუფი",
+ "groupParent": "ჯგუფი",
+ "groupNoGroup": "ჯგუფის გარეშე",
+ "settingsTitle": "პარამეტრები",
+ "settingsUser": "პროფილი",
+ "settingsGroups": "ჯგუფები",
+ "settingsServer": "სერვერი",
+ "settingsUsers": "მომხამრებლები",
+ "settingsDistanceUnit": "მანძილის ერთეული",
+ "settingsAltitudeUnit": "სიმაღლის ერთეული",
+ "settingsSpeedUnit": "სიჩქარის ერთეული",
+ "settingsVolumeUnit": "მოცულობის ერთეული",
+ "settingsTwelveHourFormat": "12-საათიანი ფორმატი",
+ "settingsCoordinateFormat": "კოორდინატების ფორმატი",
+ "settingsServerVersion": "სერვერის ვერსია",
+ "settingsAppVersion": "აპლიკაციის ვერსია",
+ "settingsConnection": "კავშირი",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "რეპორტები",
+ "reportScheduled": "დაგეგმილი რეპორტები",
+ "reportDevice": "მოწყობილობა",
+ "reportGroup": "ჯგუფი",
+ "reportFrom": "დან",
+ "reportTo": "მდე",
+ "reportShow": "ჩვენება",
+ "reportClear": "გასუფთავება",
+ "linkGoogleMaps": "Google რუკა",
+ "linkAppleMaps": "Apple რუკა",
+ "linkStreetView": "ქუჩების დათვალიერება",
+ "positionFixTime": "დროის გასწორება",
+ "positionDeviceTime": "მოწყობილობის დრო",
+ "positionServerTime": "სერვერის დრო",
+ "positionValid": "ვარგისი",
+ "positionAccuracy": "სიზუსტე",
+ "positionLatitude": "განედი",
+ "positionLongitude": "გრძედი",
+ "positionAltitude": "სიმაღლე",
+ "positionSpeed": "სიჩქარე",
+ "positionCourse": "კურსი",
+ "positionAddress": "მისამართი",
+ "positionProtocol": "პროტოკოლი",
+ "positionDistance": "დისტანცია",
+ "positionRpm": "ბრუნი წუთში",
+ "positionFuel": "საწვავი",
+ "positionPower": "სიმძლავრე",
+ "positionBattery": "აკუმულატორი",
+ "positionRaw": "Raw",
+ "positionIndex": "ინდექსი",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "თანამგზავრები",
+ "positionSatVisible": "ხილული თანამგზავრები",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "როუმინგი",
+ "positionEvent": "მოვლენა",
+ "positionAlarm": "სიგნალიზაცია",
+ "positionStatus": "სტატუსი",
+ "positionOdometer": "ოდომეტრი",
+ "positionServiceOdometer": "ოდომეტრის სერვისი",
+ "positionTripOdometer": "მოგზაურობის ოდომეტრი",
+ "positionHours": "საათები",
+ "positionSteps": "ნაბიჯები",
+ "positionInput": "შემავალი",
+ "positionHeartRate": "Პულსი",
+ "positionOutput": "გამავალი",
+ "positionBatteryLevel": "აკუმულატორის დონე",
+ "positionFuelConsumption": "საწვავის მოხმარება",
+ "positionRfid": "RFID",
+ "positionVersionFw": "მიკროპროგრამის ვერსია",
+ "positionVersionHw": "მოწყობილობის ვერსია",
+ "positionIgnition": "ავტ. ანთების კლიტე",
+ "positionFlags": "მონიშვნა",
+ "positionCharge": "დამუხტვა",
+ "positionIp": "IP",
+ "positionArchive": "არქივი",
+ "positionVin": "VIN",
+ "positionApproximate": "მიახლოებითი",
+ "positionThrottle": "დროსელი",
+ "positionMotion": "მოძრაობა",
+ "positionArmed": "დაცული",
+ "positionAcceleration": "აჩქარება",
+ "positionTemp": "ტემპერატურა",
+ "positionDeviceTemp": "მოწყობილობის ტემპერატურა",
+ "positionCoolantTemp": "გამაგრილებლის ტემპერატურა",
+ "positionOperator": "ორიენტაცია",
+ "positionCommand": "ბრძანება",
+ "positionBlocked": "დაბლოკილია",
+ "positionDtcs": "DTCs",
+ "positionObdSpeed": "OBD სიჩქარე",
+ "positionObdOdometer": "OBD ოდომეტრი",
+ "positionDrivingTime": "მართვის დრო",
+ "positionDriverUniqueId": "მძღოლის უნიკალური N",
+ "positionCard": "ბარათი",
+ "positionImage": "სურათი",
+ "positionVideo": "ვიდეო",
+ "positionAudio": "აუდიო",
+ "serverTitle": "სერვერის პარამეტრები",
+ "serverZoom": "ზუმი",
+ "serverRegistration": "რეგისტრაცია",
+ "serverReadonly": "მხოლოდ ნახვის",
+ "serverForceSettings": "იძულებითი პარამეტრები",
+ "serverAnnouncement": "განცხადება",
+ "serverName": "სერვერის სახელი",
+ "serverDescription": "სერვერის აღწერილობა",
+ "serverColorPrimary": "პიველადი ფერი",
+ "serverColorSecondary": "მეორადი ფერი",
+ "serverLogo": "ლოგოს სურათი",
+ "serverLogoInverted": "Inverted Logo Image",
+ "serverChangeDisable": "Disable Server Change",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "რუკა",
+ "mapActive": "აქტიური რუკა",
+ "mapOverlay": "რუკის დაფარვა",
+ "mapOverlayCustom": "მორგებული გადაფარვა",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API გასაღები",
+ "mapOpenWeatherClouds": "OpenWeather ღრუბლები",
+ "mapOpenWeatherPrecipitation": "OpenWeather ნალექები",
+ "mapOpenWeatherPressure": "OpenWeather წნევა",
+ "mapOpenWeatherWind": "OpenWeather ქარი",
+ "mapOpenWeatherTemperature": "OpenWeather ტემპერატურა",
+ "mapLayer": "რუკის ფენა",
+ "mapCustom": "მორგებული (XYZ)",
+ "mapCustomArcgis": "მორგებული (ArcGIS)",
+ "mapCustomLabel": "მორგებული რუკა",
+ "mapCarto": "Carto ძირითადი რუკა",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google გზები",
+ "mapGoogleHybrid": "Google ჰიბრიდული",
+ "mapGoogleSatellite": "Google სატელიტი",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Bing Maps გასაღები",
+ "mapBingRoad": "Bing Maps გზები",
+ "mapBingAerial": "Bing Maps საჰაერო",
+ "mapBingHybrid": "Bing Maps ჰიბრიდული",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Yandex რუკა",
+ "mapYandexSat": "Yandex თანამგზავრული",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "კარტოგრაფიული განყოფილება",
+ "mapMapboxStreets": "Mapbox ქუჩები",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox ღია ცის ქვეშ",
+ "mapMapboxSatellite": "Mapbox სატელიტი",
+ "mapMapboxKey": "Mapbox-ის წვდომის ჟეტონი",
+ "mapMapTilerBasic": "MapTiler ძირითადი",
+ "mapMapTilerHybrid": "MapTiler ჰიბრიდული",
+ "mapMapTilerKey": "MapTiler API გასაღები",
+ "mapLocationIqStreets": "LocationIQ ქუჩები",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ წვდომის ჟეტონი",
+ "mapTomTomBasic": "TomTom ძირითადი",
+ "mapTomTomFlow": "TomTom Სატრანსპორტო ნაკადი",
+ "mapTomTomIncidents": "TomTom საგზაო ინციდენტები",
+ "mapTomTomKey": "TomTom API გასაღები",
+ "mapHereBasic": "Here Basic ძირითადი",
+ "mapHereHybrid": "Here ჰიბრიდი",
+ "mapHereSatellite": "Here სატელიტი",
+ "mapHereFlow": "Here Სატრანსპორტო ნაკადი",
+ "mapHereKey": "Here API გასაღები",
+ "mapShapePolygon": "მრავალკუთხედი",
+ "mapShapeCircle": "წრე",
+ "mapShapePolyline": "ტეხილი ხაზები",
+ "mapLiveRoutes": "ცოცხალი მარშრუტები",
+ "mapDirection": "მიმართულების ჩვენება",
+ "mapCurrentLocation": "ახლანდელი ადგილსამყოფელი",
+ "mapPoiLayer": "POI ფენა",
+ "mapClustering": "მარკერების კლასტერირება",
+ "mapOnSelect": "რუკის ჩვენება არჩევის დროს",
+ "mapDefault": "ნაგულისხმევი რუკა",
+ "stateTitle": "სტატუსი",
+ "stateName": "ატრიბუტი",
+ "stateValue": "მნიშვნელობა",
+ "commandTitle": "ბრძანება",
+ "commandSend": "გაგზავნა",
+ "commandSent": "ბრძანება გაიგზავნილია",
+ "commandQueued": "ბრძანება რიგშია",
+ "commandUnit": "ერთეული",
+ "commandCustom": "მორგებული ბრძანება",
+ "commandDeviceIdentification": "მოწყობილობის იდენტიფიკაცია",
+ "commandPositionSingle": "ერთჯერადი რეპორტი",
+ "commandPositionPeriodic": "პერიოდული რეპორტი",
+ "commandPositionStop": "რეპორტის შეჩერება",
+ "commandEngineStop": "ძრავის გამორთვა",
+ "commandEngineResume": "ძრავის ჩართვა",
+ "commandAlarmArm": "Arm სიგნალიზაცია",
+ "commandAlarmDisarm": "სიგნალიზაციის გამორთვა",
+ "commandAlarmDismiss": "სიგნალიზაციის გათავისუფლება",
+ "commandSetTimezone": "დროის სარტყელის დაყენება",
+ "commandRequestPhoto": "ფოტოს მოთხოვნა",
+ "commandPowerOff": "მოწყობილობის გამორთვა",
+ "commandRebootDevice": "მოწყობილობის გადატვირთვა",
+ "commandFactoryReset": "ქარხნულ პარამეტრებზე დაბრუნება",
+ "commandSendSms": "SMS გაგზავნა",
+ "commandSendUssd": "USSD გაგზავნა",
+ "commandSosNumber": "SOS ნომრის დაყენება",
+ "commandSilenceTime": "დუმილის დროის დაყენება",
+ "commandSetPhonebook": "სატელეფონო წიგნაკის დაყენება",
+ "commandVoiceMessage": "ხმოვანი შეტყობინება",
+ "commandOutputControl": "გამსლელის კონტროლი",
+ "commandVoiceMonitoring": "ხმოვანი მონიტორინგი",
+ "commandSetAgps": "AGPS-ის დაყენება",
+ "commandSetIndicator": "ინდიკატორის დაყენება",
+ "commandConfiguration": "კონფიგურაცია",
+ "commandGetVersion": "ვერსიის მიღება",
+ "commandFirmwareUpdate": "მიკროპროგრამის განახლება",
+ "commandSetConnection": "კავშირის დაყენება",
+ "commandSetOdometer": "ოდომეტრის დაყენება",
+ "commandGetModemStatus": "მოდემის სტატუსი",
+ "commandGetDeviceStatus": "მოწყობილობის სტატუსი",
+ "commandSetSpeedLimit": "სიჩქარის ლიმიტის დაყენება",
+ "commandModePowerSaving": "ენერგიის დაზოგვის რეჟიმი",
+ "commandModeDeepSleep": "ღრმა ძილის რეჟიმი",
+ "commandAlarmGeofence": "დააყენეთ Geofence სიგნალიზაცია",
+ "commandAlarmBattery": "დააყენეთ ბატარეის სიგნალიზაცია",
+ "commandAlarmSos": "დააყენეთ SOS სიგნალიზაცია",
+ "commandAlarmRemove": "დააყენეთ მოხსნის სიგნალიზაცია",
+ "commandAlarmClock": "საათის მაღვიძარას დაყენება",
+ "commandAlarmSpeed": "დააყენეთ სიჩქარის სიგნალიზაცია",
+ "commandAlarmFall": "დააყენეთ დაცემის სიგნალიზაცია",
+ "commandAlarmVibration": "დააყენეთ ვიბრაციის სიგნალიზაცია",
+ "commandFrequency": "სიხშირე",
+ "commandTimezone": "დროის სარტყელი ბიჯი",
+ "commandMessage": "შეტყობინება",
+ "commandRadius": "რადიუსი",
+ "commandEnable": "ჩართვა",
+ "commandData": "თარიღი",
+ "commandIndex": "ინდექსი",
+ "commandPhone": "ტელეფონის N",
+ "commandServer": "სერვერი",
+ "commandPort": "პორტი",
+ "eventAll": "ყველა მოვლენა",
+ "eventDeviceOnline": "ონლაინ სტატუსი",
+ "eventDeviceUnknown": "უცნობი სტატუსი",
+ "eventDeviceOffline": "ოფლაინ სტატუსი",
+ "eventDeviceInactive": "მოწყობილობა არააქტიურია",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "მოწყობილობა მოძრაობს",
+ "eventDeviceStopped": "მოწყობილობა გაჩერებულია",
+ "eventDeviceOverspeed": "სიჩქარის ლიმიტი გადაჭარბებულია",
+ "eventDeviceFuelDrop": "საწვავის დაქცევა",
+ "eventDeviceFuelIncrease": "საწვავის მატება",
+ "eventCommandResult": "ბრძანების შედეგი",
+ "eventGeofenceEnter": "გეოზონა დაყენებულია",
+ "eventGeofenceExit": "გეოზონა გამორთულია",
+ "eventAlarm": "სიგნალიზაცია",
+ "eventIgnitionOn": "ავტ. ანთაება ჩართულია",
+ "eventIgnitionOff": "ავტ. ანთაება გამორთულია",
+ "eventMaintenance": "საჭიროა ტექნიკური სამუშაოების ჩატარება",
+ "eventTextMessage": "მიღებულია ტექსტური შეტყობინება",
+ "eventDriverChanged": "მძღოლის ცვლილება",
+ "eventMedia": "მედია",
+ "eventsScrollToLast": "გადაახვიეთ ბოლომდე",
+ "eventsSoundEvents": "ხმოვანი მოვლენები",
+ "eventsSoundAlarms": "ხმოვანი სიგნალიზაცია",
+ "alarmGeneral": "საერთო",
+ "alarmSos": "SOS",
+ "alarmVibration": "ვიბრაცია",
+ "alarmMovement": "მოძრაობა",
+ "alarmLowspeed": "დაბალი სიჩქარე",
+ "alarmOverspeed": "სიჩქარის გადაჭარბება",
+ "alarmFallDown": "დაცემა",
+ "alarmLowPower": "დაბალი სიმძლავრე",
+ "alarmLowBattery": "Დამჯდარი ელემენტი",
+ "alarmFault": "გაუმართავობა",
+ "alarmPowerOff": "Გამორთულია",
+ "alarmPowerOn": "ჩართულია",
+ "alarmDoor": "კარები",
+ "alarmLock": "დაკეტილია",
+ "alarmUnlock": "გაღებულია",
+ "alarmGeofence": "გეოზონა",
+ "alarmGeofenceEnter": "გეოზონში შესვლა",
+ "alarmGeofenceExit": "გეოზონიდან გამოსვლა",
+ "alarmGpsAntennaCut": "GPS ანტენის კონფიგურაცია",
+ "alarmAccident": "ინციდენტი",
+ "alarmTow": "ბუქსირი",
+ "alarmIdle": "უქმი სვლის რეჟიმი",
+ "alarmHighRpm": "მაღალი RPM",
+ "alarmHardAcceleration": "მძიმე აჩქარება",
+ "alarmHardBraking": "მყარი დამუხრუჭება",
+ "alarmHardCornering": "მყარი მოხვევა",
+ "alarmLaneChange": "მოძრაობის ზოლის ცვლილება",
+ "alarmFatigueDriving": "დაღლილობის მართვა",
+ "alarmPowerCut": "კვების გამორთვა",
+ "alarmPowerRestored": "კვება აღდგენილია",
+ "alarmJamming": "ჭექითი ცვეთა",
+ "alarmTemperature": "ტემპერატურა",
+ "alarmParking": "პარკინგი",
+ "alarmBonnet": "კაპოტი",
+ "alarmFootBrake": "ფეხის მუხრუჭი",
+ "alarmFuelLeak": "საწვავის გაჟონვა",
+ "alarmTampering": "ჩარევა",
+ "alarmRemoving": "ამოღება",
+ "notificationType": "შეტყობინების ტიპი",
+ "notificationAlways": "ყველა მოწყობილობა",
+ "notificationNotificators": "არხები",
+ "notificatorCommand": "ბრძანება",
+ "notificatorWeb": "Web",
+ "notificatorMail": "Mail",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "გამეორება",
+ "reportCombined": "კომბინირებული",
+ "reportRoute": "მარშრუტი",
+ "reportEvents": "მოვლენები",
+ "reportTrips": "მოგზაურობები",
+ "reportStops": "გაჩერებები",
+ "reportSummary": "შემაჯამებელი",
+ "reportDaily": "ყოველდღიური ჯამი",
+ "reportChart": "დიაგრამა",
+ "reportConfigure": "კონფიგურაცია",
+ "reportEventTypes": "მოვლენის ტიპი",
+ "reportChartType": "დიაგრამის ტიპი",
+ "reportShowMarkers": "მარკერების ჩვენება",
+ "reportExport": "ექსპორტი",
+ "reportEmail": "რეპორტი მეილზე",
+ "reportSchedule": "განრიგი",
+ "reportPeriod": "პერიოდი",
+ "reportCustom": "ხელოვნური",
+ "reportToday": "დღეს",
+ "reportYesterday": "გუშინ",
+ "reportThisWeek": "ეს კვირა",
+ "reportPreviousWeek": "წინა კვირა",
+ "reportThisMonth": "ეს თვე",
+ "reportPreviousMonth": "წინა თვე",
+ "reportDeviceName": "მოწყობილობსი სახელი",
+ "reportAverageSpeed": "საშუალო სიჩქარე",
+ "reportMaximumSpeed": "მაქსიმალური სიჩქარე",
+ "reportEngineHours": "მოტოსაათი",
+ "reportDuration": "ხანგრძლივობა",
+ "reportStartDate": "დაწყების თარიღი",
+ "reportStartTime": "დაწყების დრო",
+ "reportStartAddress": "დაწყების მისამართი",
+ "reportEndTime": "დამთავრების დრო",
+ "reportEndAddress": "დამთავრების მისამართი",
+ "reportSpentFuel": "დახარჯული საწვავი",
+ "reportStartOdometer": "ოდომეტრის სტარტი",
+ "reportEndOdometer": "ოდომეტრის გაჩერება",
+ "statisticsTitle": "სტატისტიკა",
+ "statisticsCaptureTime": "დროის დაწყება",
+ "statisticsActiveUsers": "აქტიური მომხმარებლები",
+ "statisticsActiveDevices": "აქტიური მოწყობილობები",
+ "statisticsRequests": "მოთხოვნები",
+ "statisticsMessagesReceived": "შეტყობინება მიღებულია",
+ "statisticsMessagesStored": "შეტყობინებები შენახულია",
+ "statisticsGeocoder": "გეოკოდერის მოთხოვნა",
+ "statisticsGeolocation": "გეოლოკაციის მოთხოვნა",
+ "categoryArrow": "ისარი",
+ "categoryDefault": "ნაგულისხმევი",
+ "categoryAnimal": "ცხოველი",
+ "categoryBicycle": "მოტოციკლი",
+ "categoryBoat": "ნავი",
+ "categoryBus": "ავტობუსი",
+ "categoryCar": "ავტომანქანა",
+ "categoryCamper": "Camper",
+ "categoryCrane": "ამწე",
+ "categoryHelicopter": "შვეულფრენი",
+ "categoryMotorcycle": "მოტოციკლი",
+ "categoryOffroad": "ოფროუდი",
+ "categoryPerson": "ადამინაი",
+ "categoryPickup": "პიკაპი",
+ "categoryPlane": "პლანერი",
+ "categoryShip": "გემი",
+ "categoryTractor": "ტრაქტორი",
+ "categoryTrain": "მატარებელი",
+ "categoryTram": "ტრამვაი",
+ "categoryTrolleybus": "ტროლეიბუსი",
+ "categoryTruck": "სატვირთო",
+ "categoryVan": "ფურგონი",
+ "categoryScooter": "სკუტერი",
+ "maintenanceStart": "დაწყება",
+ "maintenancePeriod": "პერიოდი"
+} \ No newline at end of file
diff --git a/src/resources/l10n/kk.json b/src/resources/l10n/kk.json
new file mode 100644
index 00000000..706f2b78
--- /dev/null
+++ b/src/resources/l10n/kk.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "Жүктеу...",
+ "sharedHide": "Жасыру",
+ "sharedSave": "Сақтау",
+ "sharedUpload": "Upload",
+ "sharedSet": "Орнату",
+ "sharedCancel": "Болдырмау",
+ "sharedCopy": "Copy",
+ "sharedAdd": "Енгізу",
+ "sharedEdit": "Түзету",
+ "sharedRemove": "Жою",
+ "sharedRemoveConfirm": "Элементті жою?",
+ "sharedNoData": "No data",
+ "sharedSubject": "Subject",
+ "sharedYes": "Yes",
+ "sharedNo": "No",
+ "sharedKm": "км",
+ "sharedMi": "мили",
+ "sharedNmi": "теңіз мили",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "тораптар",
+ "sharedKmh": "км/сағ",
+ "sharedMph": "миля/сағ",
+ "sharedHour": "Сағаттар",
+ "sharedMinute": "Минуттар",
+ "sharedSecond": "Секундтар",
+ "sharedDays": "күндер",
+ "sharedHours": "сағат",
+ "sharedMinutes": "минут",
+ "sharedDecimalDegrees": "Ондық Градустар",
+ "sharedDegreesDecimalMinutes": "Градустар Ондық Минуттар",
+ "sharedDegreesMinutesSeconds": "Градустар Минуттар Секундтар",
+ "sharedName": "Атауы",
+ "sharedDescription": "Сипаттамасы",
+ "sharedSearch": "Іздеу",
+ "sharedIconScale": "Icon Scale",
+ "sharedGeofence": "Геозона",
+ "sharedGeofences": "Геозоналар",
+ "sharedCreateGeofence": "Create Geofence",
+ "sharedNotifications": "Ескертулер",
+ "sharedNotification": "Notification",
+ "sharedAttributes": "Атрибуттар",
+ "sharedAttribute": "Атрибут",
+ "sharedDrivers": "Drivers",
+ "sharedDriver": "Driver",
+ "sharedArea": "Аймақ",
+ "sharedSound": "Дыбыстық Ескерту",
+ "sharedType": "Түр",
+ "sharedDistance": "Қашықтық",
+ "sharedHourAbbreviation": "с",
+ "sharedMinuteAbbreviation": "м",
+ "sharedSecondAbbreviation": "s",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "l",
+ "sharedGallonAbbreviation": "gal",
+ "sharedLiter": "Liter",
+ "sharedImpGallon": "Imp. Gallon",
+ "sharedUsGallon": "U.S. Gallon",
+ "sharedLiterPerHourAbbreviation": "l/h",
+ "sharedGetMapState": "Картаның Жағдайын Білу",
+ "sharedComputedAttribute": "Computed Attribute",
+ "sharedComputedAttributes": "Computed Attributes",
+ "sharedCheckComputedAttribute": "Check Computed Attribute",
+ "sharedExpression": "Expression",
+ "sharedDevice": "Құрылғы",
+ "sharedTest": "Test",
+ "sharedTestNotification": "Тестілік Хабарландыру Жіберу",
+ "sharedTestNotificators": "Test Channels",
+ "sharedTestExpression": "Test Expression",
+ "sharedCalendar": "Күнтізбе",
+ "sharedCalendars": "Күнтізбелер",
+ "sharedFile": "Файл",
+ "sharedSearchDevices": "Search Devices",
+ "sharedSortBy": "Sort By",
+ "sharedFilterMap": "Filter on Map",
+ "sharedSelectFile": "Файлды таңдау",
+ "sharedPhone": "Телефон",
+ "sharedRequired": "Міндетті",
+ "sharedPreferences": "Баптау",
+ "sharedPermissions": "Рұқсат",
+ "sharedConnections": "Connections",
+ "sharedExtra": "Экстра",
+ "sharedPrimary": "Primary",
+ "sharedSecondary": "Secondary",
+ "sharedTypeString": "String",
+ "sharedTypeNumber": "Number",
+ "sharedTypeBoolean": "Boolean",
+ "sharedTimezone": "Сағаттық белдеу",
+ "sharedInfoTitle": "Info",
+ "sharedSavedCommand": "Saved Command",
+ "sharedSavedCommands": "Saved Commands",
+ "sharedNew": "New…",
+ "sharedShowAddress": "Show Address",
+ "sharedShowDetails": "More Details",
+ "sharedDisabled": "Disabled",
+ "sharedMaintenance": "Maintenance",
+ "sharedDeviceAccumulators": "Accumulators",
+ "sharedAlarms": "Alarms",
+ "sharedLocation": "Location",
+ "sharedImport": "Import",
+ "sharedColumns": "Columns",
+ "sharedDropzoneText": "Drag and drop a file here or click",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "Simple",
+ "calendarRecurrence": "Recurrence",
+ "calendarOnce": "Once",
+ "calendarDaily": "Daily",
+ "calendarWeekly": "Weekly",
+ "calendarMonthly": "Monthly",
+ "calendarDays": "Days",
+ "calendarSunday": "Sunday",
+ "calendarMonday": "Monday",
+ "calendarTuesday": "Tuesday",
+ "calendarWednesday": "Wednesday",
+ "calendarThursday": "Thursday",
+ "calendarFriday": "Friday",
+ "calendarSaturday": "Saturday",
+ "attributeShowGeofences": "Show Geofences",
+ "attributeSpeedLimit": "Жылдамдықты Шектеу",
+ "attributeFuelDropThreshold": "Fuel Drop Threshold",
+ "attributeFuelIncreaseThreshold": "Fuel Increase Threshold",
+ "attributePolylineDistance": "Polyline Distance",
+ "attributeReportIgnoreOdometer": "Есеп: одометр елемеу",
+ "attributeWebReportColor": "Веб: Түсі Есеп",
+ "attributeDevicePassword": "Құрылғы Құпия Сөз",
+ "attributeDeviceImage": "Device Image",
+ "attributeDeviceInactivityStart": "Device Inactivity Start",
+ "attributeDeviceInactivityPeriod": "Device Inactivity Period",
+ "attributeProcessingCopyAttributes": "Өңдеу: атрибуттары көшіру",
+ "attributeColor": "Түс",
+ "attributeWebLiveRouteLength": "Веб: Белсенді Маршруттың Ұзындығы",
+ "attributeWebSelectZoom": "Web: Zoom On Select",
+ "attributeWebMaxZoom": "Web: Maximum Zoom",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Pushover User Key",
+ "attributePushoverDeviceNames": "Pushover Device Names",
+ "attributeMailSmtpHost": "Mail: SMTP Host",
+ "attributeMailSmtpPort": "Mail: SMTP Port",
+ "attributeMailSmtpStarttlsEnable": "Mail: SMTP STARTTLS Enable",
+ "attributeMailSmtpStarttlsRequired": "Пошта: SMTP STARTTLS Required",
+ "attributeMailSmtpSslEnable": "Пошта: SMTP SSL Включить",
+ "attributeMailSmtpSslTrust": "Пошта: SMTP SSL Trust",
+ "attributeMailSmtpSslProtocols": "Пошта: SMTP SSL Протокол ",
+ "attributeMailSmtpFrom": "Пошта: SMTP From",
+ "attributeMailSmtpAuth": "Пошта: SMTP Auth Enable",
+ "attributeMailSmtpUsername": "Пошта: SMTP Пайдаланушы",
+ "attributeMailSmtpPassword": "Пошта: SMTP Құпиясөз",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: Disable Attributes",
+ "attributeUiDisableGroups": "UI: Disable Groups",
+ "attributeUiDisableEvents": "UI: Disable Events",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "UI: Disable Drivers",
+ "attributeUiDisableComputedAttributes": "UI: Disable Computed Attributes",
+ "attributeUiDisableCalendars": "UI: Disable Calendars",
+ "attributeUiDisableMaintenance": "UI: Disable Maintenance",
+ "attributeUiHidePositionAttributes": "UI: Hide Position Attributes",
+ "attributeUiDisableLoginLanguage": "UI: Disable Login Language",
+ "attributeNotificationTokens": "Notification Tokens",
+ "attributePopupInfo": "Popup Info",
+ "errorTitle": "Қате",
+ "errorGeneral": "Қолайсыз бұзылуы параметрлері немесе шектеулер",
+ "errorConnection": "Қосылу қатесі",
+ "errorSocket": "Web socket біріктіру қатесі",
+ "errorZero": "Can't be zero",
+ "userEmail": "Email",
+ "userPassword": "Құпиясөз",
+ "userAdmin": "Әкімші",
+ "userRemember": "Еске сақтау",
+ "userExpirationTime": "Қолданылу мерзімі",
+ "userDeviceLimit": "Құрылғыларды Шектеу",
+ "userUserLimit": "Қолданушылар Лимиті",
+ "userDeviceReadonly": "Құрылғыларды қарау ғана",
+ "userLimitCommands": "Limit Commands",
+ "userDisableReports": "Disable Reports",
+ "userFixedEmail": "No Email Change",
+ "userToken": "Кілт",
+ "userDeleteAccount": "Delete Account",
+ "userTemporary": "Temporary",
+ "loginTitle": "Кіру",
+ "loginLanguage": "Тіл",
+ "loginReset": "Reset Password",
+ "loginRegister": "Тіркеу",
+ "loginLogin": "Кіру",
+ "loginOpenId": "Login with OpenID",
+ "loginFailed": "Қате email немесе пароль",
+ "loginCreated": "Жаңа қолданушы тіркелді",
+ "loginResetSuccess": "Check your email",
+ "loginUpdateSuccess": "New password is set",
+ "loginLogout": "Шығу",
+ "loginLogo": "Логотип",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "Құрылғылар және Жай - күйлер",
+ "deviceSelected": "Selected Device",
+ "deviceTitle": "Құрылғылар",
+ "devicePrimaryInfo": "Device Title",
+ "deviceSecondaryInfo": "Device Detail",
+ "deviceIdentifier": "Идентификатор",
+ "deviceModel": "Моделі",
+ "deviceContact": "Байланыс",
+ "deviceCategory": "Санат",
+ "deviceLastUpdate": "Соңғы жаңарту",
+ "deviceCommand": "Команда",
+ "deviceFollow": "Ілесу",
+ "deviceTotalDistance": "Жалпы Тармақ",
+ "deviceStatus": "Мәртебе",
+ "deviceStatusOnline": "Онлайн",
+ "deviceStatusOffline": "Оффлайн",
+ "deviceStatusUnknown": "Белгісіз",
+ "deviceRegisterFirst": "Register your first device",
+ "deviceIdentifierHelp": "IMEI, serial number or other id. It has to match the identifier device reports to the server.",
+ "deviceShare": "Share Device",
+ "groupDialog": "Топ",
+ "groupParent": "Топ",
+ "groupNoGroup": "Топсыз",
+ "settingsTitle": "Баптау",
+ "settingsUser": "Аккаунт",
+ "settingsGroups": "Топтар",
+ "settingsServer": "Сервер",
+ "settingsUsers": "Қолданушылар",
+ "settingsDistanceUnit": "Distance Unit",
+ "settingsAltitudeUnit": "Altitude Unit",
+ "settingsSpeedUnit": "Speed Unit",
+ "settingsVolumeUnit": "Volume Unit",
+ "settingsTwelveHourFormat": "12 сағаттық пішім",
+ "settingsCoordinateFormat": "Координат пішімі",
+ "settingsServerVersion": "Server Version",
+ "settingsAppVersion": "App Version",
+ "settingsConnection": "Connection",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "Есептер",
+ "reportScheduled": "Scheduled Reports",
+ "reportDevice": "Құрылғы",
+ "reportGroup": "Топ",
+ "reportFrom": "Бастап",
+ "reportTo": "Дейін",
+ "reportShow": "Көрсету",
+ "reportClear": "Тазарту",
+ "linkGoogleMaps": "Google Maps",
+ "linkAppleMaps": "Apple Maps",
+ "linkStreetView": "Street View",
+ "positionFixTime": "Fix Time",
+ "positionDeviceTime": "Device Time",
+ "positionServerTime": "Server Time",
+ "positionValid": "Туралық",
+ "positionAccuracy": "Нақтылық",
+ "positionLatitude": "Ендігі",
+ "positionLongitude": "Ұзақтығы",
+ "positionAltitude": "Биіктігі",
+ "positionSpeed": "Жылдамдық",
+ "positionCourse": "Бағыты",
+ "positionAddress": "Мекен-жай",
+ "positionProtocol": "Хаттама",
+ "positionDistance": "Қашықтық",
+ "positionRpm": "RPM",
+ "positionFuel": "Отын",
+ "positionPower": "Қуат көзі",
+ "positionBattery": "Аккумулятор",
+ "positionRaw": "Raw",
+ "positionIndex": "Index",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "Satellites",
+ "positionSatVisible": "Visible Satellites",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "Roaming",
+ "positionEvent": "Event",
+ "positionAlarm": "Alarm",
+ "positionStatus": "Status",
+ "positionOdometer": "Odometer",
+ "positionServiceOdometer": "Service Odometer",
+ "positionTripOdometer": "Trip Odometer",
+ "positionHours": "Hours",
+ "positionSteps": "Steps",
+ "positionInput": "Input",
+ "positionHeartRate": "Heart Rate",
+ "positionOutput": "Output",
+ "positionBatteryLevel": "Battery Level",
+ "positionFuelConsumption": "Fuel Consumption",
+ "positionRfid": "RFID",
+ "positionVersionFw": "Firmware Version",
+ "positionVersionHw": "Hardware Version",
+ "positionIgnition": "Ignition",
+ "positionFlags": "Flags",
+ "positionCharge": "Charge",
+ "positionIp": "IP",
+ "positionArchive": "Archive",
+ "positionVin": "VIN",
+ "positionApproximate": "Approximate",
+ "positionThrottle": "Throttle",
+ "positionMotion": "Motion",
+ "positionArmed": "Armed",
+ "positionAcceleration": "Acceleration",
+ "positionTemp": "Temperature",
+ "positionDeviceTemp": "Device Temperature",
+ "positionCoolantTemp": "Coolant Temperature",
+ "positionOperator": "Operator",
+ "positionCommand": "Command",
+ "positionBlocked": "Blocked",
+ "positionDtcs": "DTCs",
+ "positionObdSpeed": "OBD Speed",
+ "positionObdOdometer": "OBD Odometer",
+ "positionDrivingTime": "Driving Time",
+ "positionDriverUniqueId": "Driver Unique Id",
+ "positionCard": "Card",
+ "positionImage": "Image",
+ "positionVideo": "Video",
+ "positionAudio": "Audio",
+ "serverTitle": "Серверді баптау",
+ "serverZoom": "Жақындау",
+ "serverRegistration": "Тіркеу",
+ "serverReadonly": "Тек қарап шығу",
+ "serverForceSettings": "Баптауды жылдамдату",
+ "serverAnnouncement": "Announcement",
+ "serverName": "Server Name",
+ "serverDescription": "Server Description",
+ "serverColorPrimary": "Color Primary",
+ "serverColorSecondary": "Color Secondary",
+ "serverLogo": "Logo Image",
+ "serverLogoInverted": "Inverted Logo Image",
+ "serverChangeDisable": "Disable Server Change",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "Карта",
+ "mapActive": "Active Maps",
+ "mapOverlay": "Map Overlay",
+ "mapOverlayCustom": "Custom Overlay",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API Key",
+ "mapOpenWeatherClouds": "OpenWeather Clouds",
+ "mapOpenWeatherPrecipitation": "OpenWeather Precipitation",
+ "mapOpenWeatherPressure": "OpenWeather Pressure",
+ "mapOpenWeatherWind": "OpenWeather Wind",
+ "mapOpenWeatherTemperature": "OpenWeather Temperature",
+ "mapLayer": "Карта қабаты",
+ "mapCustom": "Custom (XYZ)",
+ "mapCustomArcgis": "Custom (ArcGIS)",
+ "mapCustomLabel": "Custom map",
+ "mapCarto": "Carto Basemaps",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Bing Maps кілті",
+ "mapBingRoad": "Bing Maps жолдары",
+ "mapBingAerial": "Bing Maps Жер серігі",
+ "mapBingHybrid": "Bing Maps Будан",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Яндекс Карталар",
+ "mapYandexSat": "Яндекс Жер серіктері",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox Streets",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox Outdoors",
+ "mapMapboxSatellite": "Mapbox Satellite",
+ "mapMapboxKey": "Mapbox Access Token",
+ "mapMapTilerBasic": "MapTiler Basic",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "MapTiler API Key",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ Access Token",
+ "mapTomTomBasic": "TomTom Basic",
+ "mapTomTomFlow": "TomTom Traffic Flow",
+ "mapTomTomIncidents": "TomTom Traffic Incidents",
+ "mapTomTomKey": "TomTom API Key",
+ "mapHereBasic": "Here Basic",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satellite",
+ "mapHereFlow": "Here Traffic Flow",
+ "mapHereKey": "Here API Key",
+ "mapShapePolygon": "Көпбұрыш",
+ "mapShapeCircle": "Шеңбер",
+ "mapShapePolyline": "Сызық",
+ "mapLiveRoutes": "Нақты бағыттар",
+ "mapDirection": "Show Direction",
+ "mapCurrentLocation": "Current Location",
+ "mapPoiLayer": "POI Layer",
+ "mapClustering": "Markers Clustering",
+ "mapOnSelect": "Show Map on Selection",
+ "mapDefault": "Default Map",
+ "stateTitle": "Жай күйі",
+ "stateName": "Параметрі",
+ "stateValue": "Мағынасы",
+ "commandTitle": "Команда",
+ "commandSend": "Жіберу",
+ "commandSent": "Command sent",
+ "commandQueued": "Command queued",
+ "commandUnit": "Бірліктер",
+ "commandCustom": "Қолданушылар командасы",
+ "commandDeviceIdentification": "Құрылғы идентификациясы",
+ "commandPositionSingle": "Біржолғы қадағалау",
+ "commandPositionPeriodic": "Қадағалауды бастау",
+ "commandPositionStop": "Қадағалауды болдырмау",
+ "commandEngineStop": "Қозғалтқышты бұғаттау",
+ "commandEngineResume": "Қозғалтқышты бұғаттан шығару",
+ "commandAlarmArm": "Сигнал жабдығын іске қосу",
+ "commandAlarmDisarm": "Сигнал жабдығын істен айыру",
+ "commandAlarmDismiss": "Dismiss Alarm",
+ "commandSetTimezone": "Сағаттық белдеуді баптау",
+ "commandRequestPhoto": "Фото сұрау",
+ "commandPowerOff": "Power Off Device",
+ "commandRebootDevice": "Құрылғыны қайта жүктеу",
+ "commandFactoryReset": "Factory Reset",
+ "commandSendSms": "СМС жіберу",
+ "commandSendUssd": "USSD жіберу",
+ "commandSosNumber": "Төтенше нөмірді баптау",
+ "commandSilenceTime": "Тыныштық уақытын баптау",
+ "commandSetPhonebook": "Телефон анықтамасын баптау",
+ "commandVoiceMessage": "Дыбыстық хабарлама",
+ "commandOutputControl": "Шығуды бақылау",
+ "commandVoiceMonitoring": "Voice Monitoring",
+ "commandSetAgps": "Set AGPS",
+ "commandSetIndicator": "Индикаторды орнату",
+ "commandConfiguration": "Configuration",
+ "commandGetVersion": "Get Version",
+ "commandFirmwareUpdate": "Update Firmware",
+ "commandSetConnection": "Set Connection",
+ "commandSetOdometer": "Set Odometer",
+ "commandGetModemStatus": "Get Modem Status",
+ "commandGetDeviceStatus": "Get Device Status",
+ "commandSetSpeedLimit": "Set Speed Limit",
+ "commandModePowerSaving": "Power Saving Mode",
+ "commandModeDeepSleep": "Deep Sleep Mode",
+ "commandAlarmGeofence": "Set Geofence Alarm",
+ "commandAlarmBattery": "Set Battery Alarm",
+ "commandAlarmSos": "Set SOS Alarm",
+ "commandAlarmRemove": "Set Remove Alarm",
+ "commandAlarmClock": "Set Clock Alarm",
+ "commandAlarmSpeed": "Set Speed Alarm",
+ "commandAlarmFall": "Set Fall Alarm",
+ "commandAlarmVibration": "Set Vibration Alarm",
+ "commandFrequency": "Жиілік",
+ "commandTimezone": "Уақытша аймақты жылжыту",
+ "commandMessage": "Хабарлама",
+ "commandRadius": "Radius",
+ "commandEnable": "Enable",
+ "commandData": "Мәліметтер",
+ "commandIndex": "Индексі",
+ "commandPhone": "Телефон нөмірі",
+ "commandServer": "Server",
+ "commandPort": "Port",
+ "eventAll": "Барлық",
+ "eventDeviceOnline": "Status online",
+ "eventDeviceUnknown": "Status unknown",
+ "eventDeviceOffline": "Status offline",
+ "eventDeviceInactive": "Device inactive",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "Device moving",
+ "eventDeviceStopped": "Device stopped",
+ "eventDeviceOverspeed": "Speed limit exceeded",
+ "eventDeviceFuelDrop": "Fuel drop",
+ "eventDeviceFuelIncrease": "Fuel increase",
+ "eventCommandResult": "Команда нәтижесі",
+ "eventGeofenceEnter": "Geofence entered",
+ "eventGeofenceExit": "Geofence exited",
+ "eventAlarm": "Alarm",
+ "eventIgnitionOn": "Ignition on",
+ "eventIgnitionOff": "Ignition off",
+ "eventMaintenance": "Қызмет көрсету геозону",
+ "eventTextMessage": "Мәтіндік хабарлама алынды",
+ "eventDriverChanged": "Driver changed",
+ "eventMedia": "Media",
+ "eventsScrollToLast": "Scroll To Last",
+ "eventsSoundEvents": "Sound Events",
+ "eventsSoundAlarms": "Sound Alarms",
+ "alarmGeneral": "General",
+ "alarmSos": "SOS",
+ "alarmVibration": "Vibration",
+ "alarmMovement": "Movement",
+ "alarmLowspeed": "Low Speed",
+ "alarmOverspeed": "Overspeed",
+ "alarmFallDown": "Fall Down",
+ "alarmLowPower": "Low Power",
+ "alarmLowBattery": "Low Battery",
+ "alarmFault": "Fault",
+ "alarmPowerOff": "Power Off",
+ "alarmPowerOn": "Power On",
+ "alarmDoor": "Door",
+ "alarmLock": "Lock",
+ "alarmUnlock": "Unlock",
+ "alarmGeofence": "Geofence",
+ "alarmGeofenceEnter": "Geofence Enter",
+ "alarmGeofenceExit": "Geofence Exit",
+ "alarmGpsAntennaCut": "GPS Antenna Cut",
+ "alarmAccident": "Accident",
+ "alarmTow": "Tow",
+ "alarmIdle": "Idle",
+ "alarmHighRpm": "High RPM",
+ "alarmHardAcceleration": "Hard Acceleration",
+ "alarmHardBraking": "Hard Braking",
+ "alarmHardCornering": "Hard Cornering",
+ "alarmLaneChange": "Lane Change",
+ "alarmFatigueDriving": "Fatigue Driving",
+ "alarmPowerCut": "Power Cut",
+ "alarmPowerRestored": "Power Restored",
+ "alarmJamming": "Jamming",
+ "alarmTemperature": "Temperature",
+ "alarmParking": "Parking",
+ "alarmBonnet": "Bonnet",
+ "alarmFootBrake": "Foot Brake",
+ "alarmFuelLeak": "Fuel Leak",
+ "alarmTampering": "Tampering",
+ "alarmRemoving": "Removing",
+ "notificationType": "Ескерту түрі",
+ "notificationAlways": "All Devices",
+ "notificationNotificators": "Channels",
+ "notificatorCommand": "Command",
+ "notificatorWeb": "Web",
+ "notificatorMail": "Mail",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Replay",
+ "reportCombined": "Combined",
+ "reportRoute": "Бағыт",
+ "reportEvents": "Оқиғалар",
+ "reportTrips": "Сапарлар",
+ "reportStops": "Stops",
+ "reportSummary": "Мәлімет",
+ "reportDaily": "Daily Summary",
+ "reportChart": "Диаграмма",
+ "reportConfigure": "Конфигурациялау",
+ "reportEventTypes": "Оқиға түрі",
+ "reportChartType": "Түр Диаграммалар",
+ "reportShowMarkers": "Маркерлер көрсету",
+ "reportExport": "Экспорт",
+ "reportEmail": "Email Report",
+ "reportSchedule": "Schedule",
+ "reportPeriod": "Period",
+ "reportCustom": "Custom",
+ "reportToday": "Today",
+ "reportYesterday": "Yesterday",
+ "reportThisWeek": "This Week",
+ "reportPreviousWeek": "Previous Week",
+ "reportThisMonth": "This Month",
+ "reportPreviousMonth": "Previous Month",
+ "reportDeviceName": "Құрылғы атауы",
+ "reportAverageSpeed": "Орташа жылдамдық",
+ "reportMaximumSpeed": "Максималды жылдамдық",
+ "reportEngineHours": "Мотосағат",
+ "reportDuration": "Ұзақтығы",
+ "reportStartDate": "Start Date",
+ "reportStartTime": "Бастапқы уақыт",
+ "reportStartAddress": "Бастапқы мекен-жай",
+ "reportEndTime": "Ақырғы уақыт",
+ "reportEndAddress": "Ақырғы мекен-жай",
+ "reportSpentFuel": "Отын пайдаланылды",
+ "reportStartOdometer": "Odometer Start",
+ "reportEndOdometer": "Odometer End",
+ "statisticsTitle": "Статистика",
+ "statisticsCaptureTime": "Жинау уақыты",
+ "statisticsActiveUsers": "Белсенді қолданушылар",
+ "statisticsActiveDevices": "Белсенді құрылғылар",
+ "statisticsRequests": "Сұраныстар",
+ "statisticsMessagesReceived": "Хабарлама алынды",
+ "statisticsMessagesStored": "Хабарлама сақталды",
+ "statisticsGeocoder": "Геокодер Сұраулар",
+ "statisticsGeolocation": "Геолокация Сұраулар",
+ "categoryArrow": "Тетік",
+ "categoryDefault": "Қалып бойынша",
+ "categoryAnimal": "Жануар",
+ "categoryBicycle": "Велосипед",
+ "categoryBoat": "Boat",
+ "categoryBus": "Автобус",
+ "categoryCar": "Автомобиль",
+ "categoryCamper": "Camper",
+ "categoryCrane": "Crane",
+ "categoryHelicopter": "Helicopter",
+ "categoryMotorcycle": "Мотоцикл",
+ "categoryOffroad": "Offroad",
+ "categoryPerson": "Адам",
+ "categoryPickup": "Pickup",
+ "categoryPlane": "Ұшақ",
+ "categoryShip": "Кеме",
+ "categoryTractor": "Tractor",
+ "categoryTrain": "Train",
+ "categoryTram": "Tram",
+ "categoryTrolleybus": "Trolleybus",
+ "categoryTruck": "Жүк автомобилі",
+ "categoryVan": "Van",
+ "categoryScooter": "Scooter",
+ "maintenanceStart": "Start",
+ "maintenancePeriod": "Period"
+} \ No newline at end of file
diff --git a/src/resources/l10n/km.json b/src/resources/l10n/km.json
new file mode 100644
index 00000000..d6d38ea8
--- /dev/null
+++ b/src/resources/l10n/km.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "កំពុងផ្ទុក...",
+ "sharedHide": "លាក់",
+ "sharedSave": "រក្សារទុក",
+ "sharedUpload": "Upload",
+ "sharedSet": "កំណត់",
+ "sharedCancel": "បោះបង់",
+ "sharedCopy": "Copy",
+ "sharedAdd": "បន្ថែម",
+ "sharedEdit": "កែសម្រួល",
+ "sharedRemove": "យកចេញ",
+ "sharedRemoveConfirm": "យកចំណុចចេញ",
+ "sharedNoData": "No data",
+ "sharedSubject": "Subject",
+ "sharedYes": "Yes",
+ "sharedNo": "No",
+ "sharedKm": "គ.ម",
+ "sharedMi": "ម៉ាយ",
+ "sharedNmi": "nmi",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "kn",
+ "sharedKmh": "គ.ម/ម៉ោង",
+ "sharedMph": "ម៉ាយក្នុងមួយម៉ោង",
+ "sharedHour": "ម៉ោង",
+ "sharedMinute": "នាទី",
+ "sharedSecond": "វិនាទី",
+ "sharedDays": "days",
+ "sharedHours": "hours",
+ "sharedMinutes": "minutes",
+ "sharedDecimalDegrees": "អង្សាទសភាគ",
+ "sharedDegreesDecimalMinutes": "អង្សាទសភាគនាទី",
+ "sharedDegreesMinutesSeconds": "អង្សានាទីវិនាទី",
+ "sharedName": "ឈ្មោះ",
+ "sharedDescription": "បរិយាយ",
+ "sharedSearch": "ស្វែងរក",
+ "sharedIconScale": "Icon Scale",
+ "sharedGeofence": "កំណត់ភូមិសាស្ត្រ",
+ "sharedGeofences": "កំណត់ភូមិសាស្ត្រ",
+ "sharedCreateGeofence": "Create Geofence",
+ "sharedNotifications": "ការជូនដំណឹង",
+ "sharedNotification": "Notification",
+ "sharedAttributes": "គុណលក្ខណៈ",
+ "sharedAttribute": "គុណលក្ខណៈ",
+ "sharedDrivers": "Drivers",
+ "sharedDriver": "Driver",
+ "sharedArea": "តំបន់",
+ "sharedSound": "Notification Sound",
+ "sharedType": "ប្រភេទ",
+ "sharedDistance": "ចម្ងាយ",
+ "sharedHourAbbreviation": "ម៉ោង",
+ "sharedMinuteAbbreviation": "នាទី",
+ "sharedSecondAbbreviation": "s",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "l",
+ "sharedGallonAbbreviation": "gal",
+ "sharedLiter": "Liter",
+ "sharedImpGallon": "Imp. Gallon",
+ "sharedUsGallon": "U.S. Gallon",
+ "sharedLiterPerHourAbbreviation": "l/h",
+ "sharedGetMapState": "ទទួលបានការកំណត់ផែនទី",
+ "sharedComputedAttribute": "Computed Attribute",
+ "sharedComputedAttributes": "Computed Attributes",
+ "sharedCheckComputedAttribute": "Check Computed Attribute",
+ "sharedExpression": "Expression",
+ "sharedDevice": "ឧបករណ៍",
+ "sharedTest": "Test",
+ "sharedTestNotification": "Send Test Notification",
+ "sharedTestNotificators": "Test Channels",
+ "sharedTestExpression": "Test Expression",
+ "sharedCalendar": "ប្រតិទិន",
+ "sharedCalendars": "ប្រតិទិន",
+ "sharedFile": "ឯកសារ",
+ "sharedSearchDevices": "Search Devices",
+ "sharedSortBy": "Sort By",
+ "sharedFilterMap": "Filter on Map",
+ "sharedSelectFile": "ជ្រើសឯកសារ",
+ "sharedPhone": "Phone",
+ "sharedRequired": "Required",
+ "sharedPreferences": "Preferences",
+ "sharedPermissions": "Permissions",
+ "sharedConnections": "Connections",
+ "sharedExtra": "Extra",
+ "sharedPrimary": "Primary",
+ "sharedSecondary": "Secondary",
+ "sharedTypeString": "String",
+ "sharedTypeNumber": "Number",
+ "sharedTypeBoolean": "Boolean",
+ "sharedTimezone": "Timezone",
+ "sharedInfoTitle": "Info",
+ "sharedSavedCommand": "Saved Command",
+ "sharedSavedCommands": "Saved Commands",
+ "sharedNew": "New…",
+ "sharedShowAddress": "Show Address",
+ "sharedShowDetails": "More Details",
+ "sharedDisabled": "Disabled",
+ "sharedMaintenance": "Maintenance",
+ "sharedDeviceAccumulators": "Accumulators",
+ "sharedAlarms": "Alarms",
+ "sharedLocation": "Location",
+ "sharedImport": "Import",
+ "sharedColumns": "Columns",
+ "sharedDropzoneText": "Drag and drop a file here or click",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "Simple",
+ "calendarRecurrence": "Recurrence",
+ "calendarOnce": "Once",
+ "calendarDaily": "Daily",
+ "calendarWeekly": "Weekly",
+ "calendarMonthly": "Monthly",
+ "calendarDays": "Days",
+ "calendarSunday": "Sunday",
+ "calendarMonday": "Monday",
+ "calendarTuesday": "Tuesday",
+ "calendarWednesday": "Wednesday",
+ "calendarThursday": "Thursday",
+ "calendarFriday": "Friday",
+ "calendarSaturday": "Saturday",
+ "attributeShowGeofences": "Show Geofences",
+ "attributeSpeedLimit": "Speed Limit",
+ "attributeFuelDropThreshold": "Fuel Drop Threshold",
+ "attributeFuelIncreaseThreshold": "Fuel Increase Threshold",
+ "attributePolylineDistance": "Polyline Distance",
+ "attributeReportIgnoreOdometer": "Report: Ignore Odometer",
+ "attributeWebReportColor": "Web: Report Color",
+ "attributeDevicePassword": "Device Password",
+ "attributeDeviceImage": "Device Image",
+ "attributeDeviceInactivityStart": "Device Inactivity Start",
+ "attributeDeviceInactivityPeriod": "Device Inactivity Period",
+ "attributeProcessingCopyAttributes": "Processing: Copy Attributes",
+ "attributeColor": "Color",
+ "attributeWebLiveRouteLength": "Web: Live Route Length",
+ "attributeWebSelectZoom": "Web: Zoom On Select",
+ "attributeWebMaxZoom": "Web: Maximum Zoom",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Pushover User Key",
+ "attributePushoverDeviceNames": "Pushover Device Names",
+ "attributeMailSmtpHost": "Mail: SMTP Host",
+ "attributeMailSmtpPort": "Mail: SMTP Port",
+ "attributeMailSmtpStarttlsEnable": "Mail: SMTP STARTTLS Enable",
+ "attributeMailSmtpStarttlsRequired": "Mail: SMTP STARTTLS Required",
+ "attributeMailSmtpSslEnable": "Mail: SMTP SSL Enable",
+ "attributeMailSmtpSslTrust": "Mail: SMTP SSL Trust",
+ "attributeMailSmtpSslProtocols": "Mail: SMTP SSL Protocols",
+ "attributeMailSmtpFrom": "Mail: SMTP From",
+ "attributeMailSmtpAuth": "Mail: SMTP Auth Enable",
+ "attributeMailSmtpUsername": "Mail: SMTP Username",
+ "attributeMailSmtpPassword": "Mail: SMTP Password",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: Disable Attributes",
+ "attributeUiDisableGroups": "UI: Disable Groups",
+ "attributeUiDisableEvents": "UI: Disable Events",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "UI: Disable Drivers",
+ "attributeUiDisableComputedAttributes": "UI: Disable Computed Attributes",
+ "attributeUiDisableCalendars": "UI: Disable Calendars",
+ "attributeUiDisableMaintenance": "UI: Disable Maintenance",
+ "attributeUiHidePositionAttributes": "UI: Hide Position Attributes",
+ "attributeUiDisableLoginLanguage": "UI: Disable Login Language",
+ "attributeNotificationTokens": "Notification Tokens",
+ "attributePopupInfo": "Popup Info",
+ "errorTitle": "កំហុស",
+ "errorGeneral": "Invalid parameters or constraints violation",
+ "errorConnection": "កំហុសក្នុងការតភ្ជាប់",
+ "errorSocket": "កំហុសក្នុងការតភ្ជាប់ Web socket",
+ "errorZero": "Can't be zero",
+ "userEmail": "អ៊ីម៉ែល",
+ "userPassword": "ពាក្យសម្ងាត់",
+ "userAdmin": "អ្នកគ្រប់គ្រង",
+ "userRemember": "សូមចងចាំ",
+ "userExpirationTime": "ការផុតកំណត់",
+ "userDeviceLimit": "កំណត់ចំនួនឧបករណ៍",
+ "userUserLimit": "កំណត់ចំនួនអ្នកប្រើ",
+ "userDeviceReadonly": "ឧបករណ៍បានតែអាន",
+ "userLimitCommands": "Limit Commands",
+ "userDisableReports": "Disable Reports",
+ "userFixedEmail": "No Email Change",
+ "userToken": "សញ្ញាសម្ងាត់",
+ "userDeleteAccount": "Delete Account",
+ "userTemporary": "Temporary",
+ "loginTitle": "ចូល",
+ "loginLanguage": "ភាសា",
+ "loginReset": "Reset Password",
+ "loginRegister": "ចុះឈ្មោះ",
+ "loginLogin": "ចូល",
+ "loginOpenId": "Login with OpenID",
+ "loginFailed": "អាសយដ្ឋានអ៊ីម៉ែលឬពាក្យសម្ងាត់មិនត្រឹមត្រូវ",
+ "loginCreated": "អ្នកប្រើថ្មីត្រូវបានចុះបញ្ជី",
+ "loginResetSuccess": "Check your email",
+ "loginUpdateSuccess": "New password is set",
+ "loginLogout": "ចាកចេញ",
+ "loginLogo": "រូបសញ្ញា",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "ឧបករណ៍និងសភាព",
+ "deviceSelected": "Selected Device",
+ "deviceTitle": "ឧបករណ៍",
+ "devicePrimaryInfo": "Device Title",
+ "deviceSecondaryInfo": "Device Detail",
+ "deviceIdentifier": "គ្រឿងសម្គាល់",
+ "deviceModel": "គំរូ",
+ "deviceContact": "ទំនក់ទំនង",
+ "deviceCategory": "ប្រភេទ",
+ "deviceLastUpdate": "ទិន្នន័យចុងក្រោយ",
+ "deviceCommand": "ពាក្យបញ្ជា",
+ "deviceFollow": "តាមដាន",
+ "deviceTotalDistance": "ចម្ងាយសរុប",
+ "deviceStatus": "Status",
+ "deviceStatusOnline": "Online",
+ "deviceStatusOffline": "Offline",
+ "deviceStatusUnknown": "Unknown",
+ "deviceRegisterFirst": "Register your first device",
+ "deviceIdentifierHelp": "IMEI, serial number or other id. It has to match the identifier device reports to the server.",
+ "deviceShare": "Share Device",
+ "groupDialog": "ក្រុម",
+ "groupParent": "ក្រុម",
+ "groupNoGroup": "គ្មានក្រុម",
+ "settingsTitle": "ការកំណត់",
+ "settingsUser": "គណនី",
+ "settingsGroups": "ក្រុម",
+ "settingsServer": "ម៉ាស៊ីនមេ",
+ "settingsUsers": "អ្នកប្រើ",
+ "settingsDistanceUnit": "Distance Unit",
+ "settingsAltitudeUnit": "Altitude Unit",
+ "settingsSpeedUnit": "Speed Unit",
+ "settingsVolumeUnit": "Volume Unit",
+ "settingsTwelveHourFormat": "ទំរង់ 12 ម៉ោង",
+ "settingsCoordinateFormat": "ទំរង់កូអរដោនេ",
+ "settingsServerVersion": "Server Version",
+ "settingsAppVersion": "App Version",
+ "settingsConnection": "Connection",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "របាយការណ៍",
+ "reportScheduled": "Scheduled Reports",
+ "reportDevice": "ឧបករណ៍",
+ "reportGroup": "ក្រុម",
+ "reportFrom": "ពី",
+ "reportTo": "ទៅកាន់",
+ "reportShow": "បង្ហាញ",
+ "reportClear": "ដើម្បីជម្រះ",
+ "linkGoogleMaps": "Google Maps",
+ "linkAppleMaps": "Apple Maps",
+ "linkStreetView": "Street View",
+ "positionFixTime": "Fix Time",
+ "positionDeviceTime": "Device Time",
+ "positionServerTime": "Server Time",
+ "positionValid": "សុពលភាព",
+ "positionAccuracy": "ភាពត្រឹមត្រូវ",
+ "positionLatitude": "រយៈទទឹង",
+ "positionLongitude": "រយៈបណ្ដោយ",
+ "positionAltitude": "កម្ពស់",
+ "positionSpeed": "ល្បឿន",
+ "positionCourse": "ធ្វើដំណើរ",
+ "positionAddress": "អាសយដ្ឋាន",
+ "positionProtocol": "ពិធីការ",
+ "positionDistance": "ចម្ងាយ",
+ "positionRpm": "RPM",
+ "positionFuel": "Fuel",
+ "positionPower": "Power",
+ "positionBattery": "Battery",
+ "positionRaw": "Raw",
+ "positionIndex": "Index",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "Satellites",
+ "positionSatVisible": "Visible Satellites",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "Roaming",
+ "positionEvent": "Event",
+ "positionAlarm": "Alarm",
+ "positionStatus": "Status",
+ "positionOdometer": "Odometer",
+ "positionServiceOdometer": "Service Odometer",
+ "positionTripOdometer": "Trip Odometer",
+ "positionHours": "Hours",
+ "positionSteps": "Steps",
+ "positionInput": "Input",
+ "positionHeartRate": "Heart Rate",
+ "positionOutput": "Output",
+ "positionBatteryLevel": "Battery Level",
+ "positionFuelConsumption": "Fuel Consumption",
+ "positionRfid": "RFID",
+ "positionVersionFw": "Firmware Version",
+ "positionVersionHw": "Hardware Version",
+ "positionIgnition": "Ignition",
+ "positionFlags": "Flags",
+ "positionCharge": "Charge",
+ "positionIp": "IP",
+ "positionArchive": "Archive",
+ "positionVin": "VIN",
+ "positionApproximate": "Approximate",
+ "positionThrottle": "Throttle",
+ "positionMotion": "Motion",
+ "positionArmed": "Armed",
+ "positionAcceleration": "Acceleration",
+ "positionTemp": "Temperature",
+ "positionDeviceTemp": "Device Temperature",
+ "positionCoolantTemp": "Coolant Temperature",
+ "positionOperator": "Operator",
+ "positionCommand": "Command",
+ "positionBlocked": "Blocked",
+ "positionDtcs": "DTCs",
+ "positionObdSpeed": "OBD Speed",
+ "positionObdOdometer": "OBD Odometer",
+ "positionDrivingTime": "Driving Time",
+ "positionDriverUniqueId": "Driver Unique Id",
+ "positionCard": "Card",
+ "positionImage": "Image",
+ "positionVideo": "Video",
+ "positionAudio": "Audio",
+ "serverTitle": "ការកំណត់ម៉ាស៊ីនមេ",
+ "serverZoom": "ពង្រីក",
+ "serverRegistration": "ការចុះឈ្មោះ",
+ "serverReadonly": "អាច​បាន​តែ​អាន",
+ "serverForceSettings": "ការកំណត់កម្លាំង",
+ "serverAnnouncement": "Announcement",
+ "serverName": "Server Name",
+ "serverDescription": "Server Description",
+ "serverColorPrimary": "Color Primary",
+ "serverColorSecondary": "Color Secondary",
+ "serverLogo": "Logo Image",
+ "serverLogoInverted": "Inverted Logo Image",
+ "serverChangeDisable": "Disable Server Change",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "ផែនទី",
+ "mapActive": "Active Maps",
+ "mapOverlay": "Map Overlay",
+ "mapOverlayCustom": "Custom Overlay",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API Key",
+ "mapOpenWeatherClouds": "OpenWeather Clouds",
+ "mapOpenWeatherPrecipitation": "OpenWeather Precipitation",
+ "mapOpenWeatherPressure": "OpenWeather Pressure",
+ "mapOpenWeatherWind": "OpenWeather Wind",
+ "mapOpenWeatherTemperature": "OpenWeather Temperature",
+ "mapLayer": "ស្រទាប់ផែនទី",
+ "mapCustom": "Custom (XYZ)",
+ "mapCustomArcgis": "Custom (ArcGIS)",
+ "mapCustomLabel": "Custom map",
+ "mapCarto": "ផែនទីគោល Carto",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "គន្លឹះផែនទី Bing",
+ "mapBingRoad": "ផ្លូវ​​​លើផែនទី​​​Bing",
+ "mapBingAerial": "ផែនទីពីលើអាកាស​​​​​​​​​​​​Bing",
+ "mapBingHybrid": "Bing Maps Hybrid",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Yandex Map",
+ "mapYandexSat": "Yandex Satellite",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox Streets",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox Outdoors",
+ "mapMapboxSatellite": "Mapbox Satellite",
+ "mapMapboxKey": "Mapbox Access Token",
+ "mapMapTilerBasic": "MapTiler Basic",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "MapTiler API Key",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ Access Token",
+ "mapTomTomBasic": "TomTom Basic",
+ "mapTomTomFlow": "TomTom Traffic Flow",
+ "mapTomTomIncidents": "TomTom Traffic Incidents",
+ "mapTomTomKey": "TomTom API Key",
+ "mapHereBasic": "Here Basic",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satellite",
+ "mapHereFlow": "Here Traffic Flow",
+ "mapHereKey": "Here API Key",
+ "mapShapePolygon": "ពហុកោណ",
+ "mapShapeCircle": "រង្វង់",
+ "mapShapePolyline": "ពហុបន្ទាត់",
+ "mapLiveRoutes": "ផ្លូវបន្តផ្ទាល់",
+ "mapDirection": "Show Direction",
+ "mapCurrentLocation": "Current Location",
+ "mapPoiLayer": "POI Layer",
+ "mapClustering": "Markers Clustering",
+ "mapOnSelect": "Show Map on Selection",
+ "mapDefault": "Default Map",
+ "stateTitle": "សភាព",
+ "stateName": "គុណលក្ខណៈ",
+ "stateValue": "តម្លៃ",
+ "commandTitle": "ពាក្យបញ្ជា",
+ "commandSend": "បញ្ជូន",
+ "commandSent": "Command sent",
+ "commandQueued": "Command queued",
+ "commandUnit": "ឯកតា",
+ "commandCustom": "ពាក្យបញ្ជាផ្ទាល់ខ្លួន",
+ "commandDeviceIdentification": "អត្តសញ្ញាណឧបករណ៍",
+ "commandPositionSingle": "របាយការណ៍រួមមួយ",
+ "commandPositionPeriodic": "របាយការណ៍តាមកាលកំណត់",
+ "commandPositionStop": "បញ្ឈប់ការបញ្ជូនទិន្នន័យ",
+ "commandEngineStop": "ម៉ាស៊ីនបានបញ្ឈប់",
+ "commandEngineResume": "ម៉ាស៊ីនបានបន្ត",
+ "commandAlarmArm": "ចាប់ផ្តើមសំឡេងរោទិ៍",
+ "commandAlarmDisarm": "បញ្ឈប់សំឡេងរោទិ៍",
+ "commandAlarmDismiss": "Dismiss Alarm",
+ "commandSetTimezone": "កំណត់តំបន់ពេលវេលា",
+ "commandRequestPhoto": "ស្នើសុំរូបថត",
+ "commandPowerOff": "Power Off Device",
+ "commandRebootDevice": "ឧបករណ៍ចាប់ផ្ដើមឡើងវិញ",
+ "commandFactoryReset": "Factory Reset",
+ "commandSendSms": "ផ្ញើ SMS",
+ "commandSendUssd": "ផ្ញើ USSD",
+ "commandSosNumber": "កំណត់លេខ SOS",
+ "commandSilenceTime": "កំណត់ម៉ោងភាពស្ងប់ស្ងាត់",
+ "commandSetPhonebook": "ដើម្បីកំណត់សៀវភៅទូរស័ព្ទ",
+ "commandVoiceMessage": "សារជាសំឡេង",
+ "commandOutputControl": "ការត្រួតពិនិត្យទិន្នផល",
+ "commandVoiceMonitoring": "Voice Monitoring",
+ "commandSetAgps": "Set AGPS",
+ "commandSetIndicator": "កំណត់ចំណាំ",
+ "commandConfiguration": "Configuration",
+ "commandGetVersion": "Get Version",
+ "commandFirmwareUpdate": "Update Firmware",
+ "commandSetConnection": "Set Connection",
+ "commandSetOdometer": "Set Odometer",
+ "commandGetModemStatus": "Get Modem Status",
+ "commandGetDeviceStatus": "Get Device Status",
+ "commandSetSpeedLimit": "Set Speed Limit",
+ "commandModePowerSaving": "Power Saving Mode",
+ "commandModeDeepSleep": "Deep Sleep Mode",
+ "commandAlarmGeofence": "Set Geofence Alarm",
+ "commandAlarmBattery": "Set Battery Alarm",
+ "commandAlarmSos": "Set SOS Alarm",
+ "commandAlarmRemove": "Set Remove Alarm",
+ "commandAlarmClock": "Set Clock Alarm",
+ "commandAlarmSpeed": "Set Speed Alarm",
+ "commandAlarmFall": "Set Fall Alarm",
+ "commandAlarmVibration": "Set Vibration Alarm",
+ "commandFrequency": "ជាញឹកញាប់",
+ "commandTimezone": "តំបន់ពេលវេលា",
+ "commandMessage": "សារ",
+ "commandRadius": "Radius",
+ "commandEnable": "Enable",
+ "commandData": "ទិន្នន័យ",
+ "commandIndex": "សន្ទស្សន៍",
+ "commandPhone": "លេខទូរសព្ទ",
+ "commandServer": "Server",
+ "commandPort": "Port",
+ "eventAll": "ព្រឹត្តិការណ៍ទាំងអស់",
+ "eventDeviceOnline": "Status online",
+ "eventDeviceUnknown": "Status unknown",
+ "eventDeviceOffline": "Status offline",
+ "eventDeviceInactive": "Device inactive",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "Device moving",
+ "eventDeviceStopped": "Device stopped",
+ "eventDeviceOverspeed": "Speed limit exceeded",
+ "eventDeviceFuelDrop": "Fuel drop",
+ "eventDeviceFuelIncrease": "Fuel increase",
+ "eventCommandResult": "លទ្ធផលពាក្យបញ្ជា",
+ "eventGeofenceEnter": "Geofence entered",
+ "eventGeofenceExit": "Geofence exited",
+ "eventAlarm": "Alarm",
+ "eventIgnitionOn": "Ignition on",
+ "eventIgnitionOff": "Ignition off",
+ "eventMaintenance": "ថែទាំត្រូវការជាចាំបាច់",
+ "eventTextMessage": "Text message received",
+ "eventDriverChanged": "Driver changed",
+ "eventMedia": "Media",
+ "eventsScrollToLast": "Scroll To Last",
+ "eventsSoundEvents": "Sound Events",
+ "eventsSoundAlarms": "Sound Alarms",
+ "alarmGeneral": "General",
+ "alarmSos": "SOS",
+ "alarmVibration": "Vibration",
+ "alarmMovement": "Movement",
+ "alarmLowspeed": "Low Speed",
+ "alarmOverspeed": "Overspeed",
+ "alarmFallDown": "Fall Down",
+ "alarmLowPower": "Low Power",
+ "alarmLowBattery": "Low Battery",
+ "alarmFault": "Fault",
+ "alarmPowerOff": "Power Off",
+ "alarmPowerOn": "Power On",
+ "alarmDoor": "Door",
+ "alarmLock": "Lock",
+ "alarmUnlock": "Unlock",
+ "alarmGeofence": "Geofence",
+ "alarmGeofenceEnter": "Geofence Enter",
+ "alarmGeofenceExit": "Geofence Exit",
+ "alarmGpsAntennaCut": "GPS Antenna Cut",
+ "alarmAccident": "Accident",
+ "alarmTow": "Tow",
+ "alarmIdle": "Idle",
+ "alarmHighRpm": "High RPM",
+ "alarmHardAcceleration": "Hard Acceleration",
+ "alarmHardBraking": "Hard Braking",
+ "alarmHardCornering": "Hard Cornering",
+ "alarmLaneChange": "Lane Change",
+ "alarmFatigueDriving": "Fatigue Driving",
+ "alarmPowerCut": "Power Cut",
+ "alarmPowerRestored": "Power Restored",
+ "alarmJamming": "Jamming",
+ "alarmTemperature": "Temperature",
+ "alarmParking": "Parking",
+ "alarmBonnet": "Bonnet",
+ "alarmFootBrake": "Foot Brake",
+ "alarmFuelLeak": "Fuel Leak",
+ "alarmTampering": "Tampering",
+ "alarmRemoving": "Removing",
+ "notificationType": "ប្រភេទការជូនដំណឹង",
+ "notificationAlways": "All Devices",
+ "notificationNotificators": "Channels",
+ "notificatorCommand": "Command",
+ "notificatorWeb": "Web",
+ "notificatorMail": "Mail",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Replay",
+ "reportCombined": "Combined",
+ "reportRoute": "ផ្លូវ",
+ "reportEvents": "ព្រឹត្តិការណ៍",
+ "reportTrips": "ការ​ធ្វើដំណើរ",
+ "reportStops": "Stops",
+ "reportSummary": "សេចក្តីសង្ខេប",
+ "reportDaily": "Daily Summary",
+ "reportChart": "គំនូសតាង",
+ "reportConfigure": "កំណត់រចនាសម្ព័ន្ធ",
+ "reportEventTypes": "ប្រភេទព្រឹត្តិការណ៍",
+ "reportChartType": "ប្រភេទគំនូសតាង",
+ "reportShowMarkers": "បង្ហាញសញ្ញាសម្គាល់",
+ "reportExport": "ការនាំចេញ",
+ "reportEmail": "Email Report",
+ "reportSchedule": "Schedule",
+ "reportPeriod": "Period",
+ "reportCustom": "Custom",
+ "reportToday": "Today",
+ "reportYesterday": "Yesterday",
+ "reportThisWeek": "This Week",
+ "reportPreviousWeek": "Previous Week",
+ "reportThisMonth": "This Month",
+ "reportPreviousMonth": "Previous Month",
+ "reportDeviceName": "ឈ្មោះឧបករណ៍",
+ "reportAverageSpeed": "ល្បឿន​មធ្យម",
+ "reportMaximumSpeed": "ល្បឿនអតិបរមា",
+ "reportEngineHours": "ម៉ោងម៉ាស៊ីន",
+ "reportDuration": "រយៈពេល",
+ "reportStartDate": "Start Date",
+ "reportStartTime": "ម៉ោងចាប់ផ្តើម",
+ "reportStartAddress": "អាសយដ្ឋានចាប់ផ្តើម",
+ "reportEndTime": "ម៉ោងបញ្ចប់",
+ "reportEndAddress": "អាសយដ្ឋានចុង",
+ "reportSpentFuel": "ការចំណាយប្រេងឥន្ធនៈ",
+ "reportStartOdometer": "Odometer Start",
+ "reportEndOdometer": "Odometer End",
+ "statisticsTitle": "ស្ថិតិ",
+ "statisticsCaptureTime": "ពេលការចាប់យក",
+ "statisticsActiveUsers": "អ្នកប្រើប្រាស់សកម្ម",
+ "statisticsActiveDevices": "ឧបករណ៍សកម្ម",
+ "statisticsRequests": "សំណើ",
+ "statisticsMessagesReceived": "សារដែលបានទទួល",
+ "statisticsMessagesStored": "សារដែលបានរក្សាទុក",
+ "statisticsGeocoder": "Geocoder Requests",
+ "statisticsGeolocation": "Geolocation Requests",
+ "categoryArrow": "ព្រួញ",
+ "categoryDefault": "លំនាំដើម",
+ "categoryAnimal": "សត្វ",
+ "categoryBicycle": "កង់",
+ "categoryBoat": "Boat",
+ "categoryBus": "រថយន្ដក្រុង",
+ "categoryCar": "រថយន្ត",
+ "categoryCamper": "Camper",
+ "categoryCrane": "Crane",
+ "categoryHelicopter": "Helicopter",
+ "categoryMotorcycle": "ម៉ូតូ",
+ "categoryOffroad": "Offroad",
+ "categoryPerson": "មនុស្ស",
+ "categoryPickup": "Pickup",
+ "categoryPlane": "យន្តហោះ",
+ "categoryShip": "នាវា",
+ "categoryTractor": "Tractor",
+ "categoryTrain": "Train",
+ "categoryTram": "Tram",
+ "categoryTrolleybus": "Trolleybus",
+ "categoryTruck": "រថយន្តដឹកទំនិញ",
+ "categoryVan": "Van",
+ "categoryScooter": "Scooter",
+ "maintenanceStart": "Start",
+ "maintenancePeriod": "Period"
+} \ No newline at end of file
diff --git a/src/resources/l10n/ko.json b/src/resources/l10n/ko.json
new file mode 100644
index 00000000..0ba191cf
--- /dev/null
+++ b/src/resources/l10n/ko.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "로딩중...",
+ "sharedHide": "숨기기",
+ "sharedSave": "저장",
+ "sharedUpload": "Upload",
+ "sharedSet": "확인",
+ "sharedCancel": "취소",
+ "sharedCopy": "Copy",
+ "sharedAdd": "추가",
+ "sharedEdit": "편집",
+ "sharedRemove": "제거",
+ "sharedRemoveConfirm": "제거 하시겠습니까?",
+ "sharedNoData": "비어있음",
+ "sharedSubject": "Subject",
+ "sharedYes": "예",
+ "sharedNo": "아니오",
+ "sharedKm": "km",
+ "sharedMi": "mi",
+ "sharedNmi": "nmi",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "kn",
+ "sharedKmh": "km/h",
+ "sharedMph": "mph",
+ "sharedHour": "1시간",
+ "sharedMinute": "1분",
+ "sharedSecond": "초",
+ "sharedDays": "일",
+ "sharedHours": "시간",
+ "sharedMinutes": "분",
+ "sharedDecimalDegrees": "DD 좌표계",
+ "sharedDegreesDecimalMinutes": "DDM 좌표계",
+ "sharedDegreesMinutesSeconds": "DMS 좌표계",
+ "sharedName": "이름",
+ "sharedDescription": "내용",
+ "sharedSearch": "검색",
+ "sharedIconScale": "아이콘 크기",
+ "sharedGeofence": "지오펜스",
+ "sharedGeofences": "지오펜스",
+ "sharedCreateGeofence": "지오펜스 생성",
+ "sharedNotifications": "알림",
+ "sharedNotification": "알림",
+ "sharedAttributes": "속성",
+ "sharedAttribute": "속성",
+ "sharedDrivers": "운전자",
+ "sharedDriver": "운전자",
+ "sharedArea": "공간",
+ "sharedSound": "알림 소리",
+ "sharedType": "유형",
+ "sharedDistance": "거리",
+ "sharedHourAbbreviation": "시",
+ "sharedMinuteAbbreviation": "분",
+ "sharedSecondAbbreviation": "초",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "L",
+ "sharedGallonAbbreviation": "gal",
+ "sharedLiter": "L",
+ "sharedImpGallon": "영국 갤런",
+ "sharedUsGallon": "미국 갤런",
+ "sharedLiterPerHourAbbreviation": "L/H",
+ "sharedGetMapState": "지도 상태 가져오기",
+ "sharedComputedAttribute": "자동화 속성",
+ "sharedComputedAttributes": "자동화 속성",
+ "sharedCheckComputedAttribute": "자동화 속성 테스트",
+ "sharedExpression": "표현",
+ "sharedDevice": "기기",
+ "sharedTest": "테스트",
+ "sharedTestNotification": "테스트 알림 보내기",
+ "sharedTestNotificators": "채널 테스트",
+ "sharedTestExpression": "표현 테스트",
+ "sharedCalendar": "달력",
+ "sharedCalendars": "달력",
+ "sharedFile": "파일",
+ "sharedSearchDevices": "기기 검색",
+ "sharedSortBy": "정렬",
+ "sharedFilterMap": "지도 필터",
+ "sharedSelectFile": "파일 선택",
+ "sharedPhone": "전화번호",
+ "sharedRequired": "필수",
+ "sharedPreferences": "환경 설정",
+ "sharedPermissions": "권한",
+ "sharedConnections": "연결",
+ "sharedExtra": "기타",
+ "sharedPrimary": "주요",
+ "sharedSecondary": "일반",
+ "sharedTypeString": "문자열",
+ "sharedTypeNumber": "숫자",
+ "sharedTypeBoolean": "불리언",
+ "sharedTimezone": "시간대",
+ "sharedInfoTitle": "정보",
+ "sharedSavedCommand": "저장된 커맨드",
+ "sharedSavedCommands": "저장된 커맨드",
+ "sharedNew": "생성...",
+ "sharedShowAddress": "주소 보기",
+ "sharedShowDetails": "상세 보기",
+ "sharedDisabled": "비활성화됨",
+ "sharedMaintenance": "관리",
+ "sharedDeviceAccumulators": "누적",
+ "sharedAlarms": "알림",
+ "sharedLocation": "위치",
+ "sharedImport": "불러오기",
+ "sharedColumns": "열",
+ "sharedDropzoneText": "파일을 가져오거나 여기를 누르세요.",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "심플",
+ "calendarRecurrence": "반복 주기",
+ "calendarOnce": "한번",
+ "calendarDaily": "매일",
+ "calendarWeekly": "매주",
+ "calendarMonthly": "매달",
+ "calendarDays": "일",
+ "calendarSunday": "일요일",
+ "calendarMonday": "월요일",
+ "calendarTuesday": "화요일",
+ "calendarWednesday": "수요일",
+ "calendarThursday": "목요일",
+ "calendarFriday": "금요일",
+ "calendarSaturday": "토요일",
+ "attributeShowGeofences": "지오펜스 표시하기",
+ "attributeSpeedLimit": "속도 제한",
+ "attributeFuelDropThreshold": "기름 감소 한계값",
+ "attributeFuelIncreaseThreshold": "기름 증가 한계값",
+ "attributePolylineDistance": "연속 거리",
+ "attributeReportIgnoreOdometer": "보고: 주행거리 무시",
+ "attributeWebReportColor": "웹: 보고서 색상",
+ "attributeDevicePassword": "기기 비밀번호",
+ "attributeDeviceImage": "기기 이미지",
+ "attributeDeviceInactivityStart": "기기 비활성화 시작",
+ "attributeDeviceInactivityPeriod": "기기 비활성화 주기",
+ "attributeProcessingCopyAttributes": "진행 중: 속성 복사",
+ "attributeColor": "색상",
+ "attributeWebLiveRouteLength": "웹: 라이브 주행거리",
+ "attributeWebSelectZoom": "웹: 확대값 선택",
+ "attributeWebMaxZoom": "웹: 최대 확대값",
+ "attributeTelegramChatId": "텔레그램 채팅 ID",
+ "attributePushoverUserKey": "Pushover 유저키",
+ "attributePushoverDeviceNames": "Pushover 기기이름",
+ "attributeMailSmtpHost": "메일: SMTP 호스트",
+ "attributeMailSmtpPort": "메일: SMTP 포트",
+ "attributeMailSmtpStarttlsEnable": " 메일: SMTP STARTTLS 활성화",
+ "attributeMailSmtpStarttlsRequired": "메일: SMTP STARTTLS 필요함",
+ "attributeMailSmtpSslEnable": "메일: SMTP SSL 활성화",
+ "attributeMailSmtpSslTrust": "메일: SMTP SSL 신뢰",
+ "attributeMailSmtpSslProtocols": "메일: SMTP SSL 프로토콜",
+ "attributeMailSmtpFrom": "메일: SMTP From",
+ "attributeMailSmtpAuth": "메일: SMTP Auth 활성화",
+ "attributeMailSmtpUsername": "메일: SMTP 유저이름",
+ "attributeMailSmtpPassword": "메일: SMTP 비밀번호",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: 속성 비활성화",
+ "attributeUiDisableGroups": "UI: 그룹 비활성화",
+ "attributeUiDisableEvents": "UI: 이벤트 비활성화",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "UI: 운전자 비활성화",
+ "attributeUiDisableComputedAttributes": "UI: 자동화 속성 비활성화",
+ "attributeUiDisableCalendars": "UI: 달력 비활성화",
+ "attributeUiDisableMaintenance": "UI: 관리 비활성화",
+ "attributeUiHidePositionAttributes": "UI: 위치 정보 숨기기",
+ "attributeUiDisableLoginLanguage": "UI: 로그인 언어 숨기기",
+ "attributeNotificationTokens": "알림 토큰",
+ "attributePopupInfo": "팝업 정보",
+ "errorTitle": "오류",
+ "errorGeneral": "부적절한 매개변수 또는 범위 초과",
+ "errorConnection": "연결 오류",
+ "errorSocket": "웹 소켓 연결 오류",
+ "errorZero": "0이 될 수 없음",
+ "userEmail": "이메일",
+ "userPassword": "비밀번호",
+ "userAdmin": "관리자",
+ "userRemember": "저장하기",
+ "userExpirationTime": "기간 제한",
+ "userDeviceLimit": "기기 제한",
+ "userUserLimit": "사용자 제한",
+ "userDeviceReadonly": "기기 읽기전용",
+ "userLimitCommands": "커맨드 제한",
+ "userDisableReports": "보고서 비활성화",
+ "userFixedEmail": "이메일 변경 제한",
+ "userToken": "토큰",
+ "userDeleteAccount": "계정 삭제",
+ "userTemporary": "Temporary",
+ "loginTitle": "로그인",
+ "loginLanguage": "언어",
+ "loginReset": "비밀번호 초기화",
+ "loginRegister": "회원가입",
+ "loginLogin": "로그인",
+ "loginOpenId": "오픈ID로 로그인",
+ "loginFailed": "이메일 또는 비밀번호가 틀렸습니다",
+ "loginCreated": "회원가입 되었습니다.",
+ "loginResetSuccess": "이메일을 확인해주세요",
+ "loginUpdateSuccess": "비밀번호가 변경되었습니다",
+ "loginLogout": "로그아웃",
+ "loginLogo": "로고",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "기기와 상태",
+ "deviceSelected": "선택된 기기",
+ "deviceTitle": "기기",
+ "devicePrimaryInfo": "기기 이름",
+ "deviceSecondaryInfo": "기기 설명",
+ "deviceIdentifier": "고유값",
+ "deviceModel": "모델명",
+ "deviceContact": "연락처",
+ "deviceCategory": "카테고리",
+ "deviceLastUpdate": "마지막 변경",
+ "deviceCommand": "커맨드",
+ "deviceFollow": "따라가기",
+ "deviceTotalDistance": "총 주행거리",
+ "deviceStatus": "상태",
+ "deviceStatusOnline": "온라인",
+ "deviceStatusOffline": "오프라인",
+ "deviceStatusUnknown": "알 수 없음",
+ "deviceRegisterFirst": "첫 기기를 등록하세요",
+ "deviceIdentifierHelp": "IMEI, 일련번호와 같은 고유한 ID로 기기를 식별합니다.",
+ "deviceShare": "Share Device",
+ "groupDialog": "그룹",
+ "groupParent": "그룹",
+ "groupNoGroup": "그룹 없음",
+ "settingsTitle": "설정",
+ "settingsUser": "계정",
+ "settingsGroups": "그룹",
+ "settingsServer": "서버",
+ "settingsUsers": "사용자",
+ "settingsDistanceUnit": "거리 단위",
+ "settingsAltitudeUnit": "고도 단위",
+ "settingsSpeedUnit": "속도 단위",
+ "settingsVolumeUnit": "부피 단위",
+ "settingsTwelveHourFormat": "12시 형식",
+ "settingsCoordinateFormat": "좌표 형식",
+ "settingsServerVersion": "서버 버전",
+ "settingsAppVersion": "앱 버전",
+ "settingsConnection": "연결 상태",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "보고서",
+ "reportScheduled": "예정된 보고서",
+ "reportDevice": "기기",
+ "reportGroup": "그룹",
+ "reportFrom": "발신자",
+ "reportTo": "수신자",
+ "reportShow": "보이기",
+ "reportClear": "초기화",
+ "linkGoogleMaps": "구글 지도",
+ "linkAppleMaps": "애플 지도",
+ "linkStreetView": "스트리트 뷰",
+ "positionFixTime": "Fix 시간",
+ "positionDeviceTime": "기기 시간",
+ "positionServerTime": "서버 시간",
+ "positionValid": "유효성",
+ "positionAccuracy": "정확성",
+ "positionLatitude": "위도",
+ "positionLongitude": "경도",
+ "positionAltitude": "고도",
+ "positionSpeed": "속도",
+ "positionCourse": "코스",
+ "positionAddress": "주소",
+ "positionProtocol": "프로토콜",
+ "positionDistance": "거리",
+ "positionRpm": "RPM",
+ "positionFuel": "기름",
+ "positionPower": "전압",
+ "positionBattery": "배터리",
+ "positionRaw": "Raw",
+ "positionIndex": "인덱스",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "위성 수",
+ "positionSatVisible": "이용 중인 위성 수",
+ "positionRssi": "신호강도",
+ "positionGps": "GPS",
+ "positionRoaming": "로밍",
+ "positionEvent": "이벤트",
+ "positionAlarm": "알림",
+ "positionStatus": "상태",
+ "positionOdometer": "주행 거리계",
+ "positionServiceOdometer": "서비스 주행 거리계",
+ "positionTripOdometer": "구간 거리계",
+ "positionHours": "시간",
+ "positionSteps": "걸음 수",
+ "positionInput": "입력",
+ "positionHeartRate": "동기화 주기",
+ "positionOutput": "출력",
+ "positionBatteryLevel": "배터리 레벨",
+ "positionFuelConsumption": "기름 소비량",
+ "positionRfid": "RFID",
+ "positionVersionFw": "펌웨어 버전",
+ "positionVersionHw": "하드웨어 버전",
+ "positionIgnition": "시동",
+ "positionFlags": "플래그",
+ "positionCharge": "충전",
+ "positionIp": "IP",
+ "positionArchive": "저장됨",
+ "positionVin": "VIN",
+ "positionApproximate": "추정",
+ "positionThrottle": "스로틀",
+ "positionMotion": "움직임",
+ "positionArmed": "장착됨",
+ "positionAcceleration": "가속도",
+ "positionTemp": "온도",
+ "positionDeviceTemp": "기기 온도",
+ "positionCoolantTemp": "냉각수 온도",
+ "positionOperator": "소유자",
+ "positionCommand": "커맨드",
+ "positionBlocked": "차단됨",
+ "positionDtcs": "속도계",
+ "positionObdSpeed": "OBD 속도",
+ "positionObdOdometer": "OBD 주행거리",
+ "positionDrivingTime": "주행 시간",
+ "positionDriverUniqueId": "운전자 ID",
+ "positionCard": "카드",
+ "positionImage": "이미지",
+ "positionVideo": "비디오",
+ "positionAudio": "오디오",
+ "serverTitle": "서버 설정",
+ "serverZoom": "배율",
+ "serverRegistration": "회원가입",
+ "serverReadonly": "읽기 전용",
+ "serverForceSettings": "강제 설정",
+ "serverAnnouncement": "공지사항",
+ "serverName": "서버 이름",
+ "serverDescription": "서버 설명",
+ "serverColorPrimary": "주요 색상",
+ "serverColorSecondary": "일반 색상",
+ "serverLogo": "로고 이미지",
+ "serverLogoInverted": "Inverted Logo Image",
+ "serverChangeDisable": "Disable Server Change",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "지도",
+ "mapActive": "활성화된 지도",
+ "mapOverlay": "지도 오버레이",
+ "mapOverlayCustom": "커스텀 오버레이",
+ "mapOpenSeaMap": "OpenSea 지도",
+ "mapOpenRailwayMap": "OpenRailway 지도",
+ "mapOpenWeatherKey": "OpenWeather API 키",
+ "mapOpenWeatherClouds": "OpenWeather 구름",
+ "mapOpenWeatherPrecipitation": "OpenWeather 강수량",
+ "mapOpenWeatherPressure": "OpenWeather 기압",
+ "mapOpenWeatherWind": "OpenWeather 바람",
+ "mapOpenWeatherTemperature": "OpenWeather 온도",
+ "mapLayer": "지도 레이어",
+ "mapCustom": "커스텀 (XYZ)",
+ "mapCustomArcgis": "커스텀 (ArcGIS)",
+ "mapCustomLabel": "커스텀 지도",
+ "mapCarto": "Carto 지도",
+ "mapOsm": "OpenStreet 지도",
+ "mapGoogleRoad": "구글 지도",
+ "mapGoogleHybrid": "구글 하이브리드 지도",
+ "mapGoogleSatellite": "구글 위성 지도",
+ "mapOpenTopoMap": "OpenTopo 지도",
+ "mapBingKey": "빙 지도 키",
+ "mapBingRoad": "빙 도로 지도",
+ "mapBingAerial": "빙 위성 지도",
+ "mapBingHybrid": "빙 하이브리드 지도",
+ "mapBaidu": "바이두",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "얀덱스 지도",
+ "mapYandexSat": "얀덱스 위성 지도",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox 도로 지도",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox 실외 지도",
+ "mapMapboxSatellite": "Mapbox 위성 지도",
+ "mapMapboxKey": "Mapbox 액세스 토큰",
+ "mapMapTilerBasic": "MapTiler 기본 지도",
+ "mapMapTilerHybrid": "MapTiler 하이브리드 지도",
+ "mapMapTilerKey": "MapTiler API 키",
+ "mapLocationIqStreets": "LocationIQ 도로 지도",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ 액세스 토큰",
+ "mapTomTomBasic": "톰톰 기본 지도",
+ "mapTomTomFlow": "톰톰 교통정보 지도",
+ "mapTomTomIncidents": "톰톰 교통사고 지도",
+ "mapTomTomKey": "톰톰 API 키",
+ "mapHereBasic": "Here 기본 지도",
+ "mapHereHybrid": "Here 하이브리드 지도",
+ "mapHereSatellite": "Here 위성 지도",
+ "mapHereFlow": "Here 교통정보 지도",
+ "mapHereKey": "Here API 키",
+ "mapShapePolygon": "다각형",
+ "mapShapeCircle": "원",
+ "mapShapePolyline": "연속선",
+ "mapLiveRoutes": "실시간 경로",
+ "mapDirection": "방향 표시하기",
+ "mapCurrentLocation": "현재 위치",
+ "mapPoiLayer": "POI 레이어",
+ "mapClustering": "마커 군집화",
+ "mapOnSelect": "선택 시 지도에 표시하기",
+ "mapDefault": "기본 지도",
+ "stateTitle": "상태",
+ "stateName": "속성",
+ "stateValue": "값",
+ "commandTitle": "커맨드",
+ "commandSend": "전송",
+ "commandSent": "커맨드 전송됨",
+ "commandQueued": "커맨드 대기중",
+ "commandUnit": "단위",
+ "commandCustom": "커스텀 커맨드",
+ "commandDeviceIdentification": "장치 식별자",
+ "commandPositionSingle": "일회성 보고",
+ "commandPositionPeriodic": "정기적 보고",
+ "commandPositionStop": "보고 중지",
+ "commandEngineStop": "엔진 정지",
+ "commandEngineResume": "엔진 활성화",
+ "commandAlarmArm": "경보",
+ "commandAlarmDisarm": "경보 해제",
+ "commandAlarmDismiss": "경보 확인",
+ "commandSetTimezone": "시간대 설정",
+ "commandRequestPhoto": "사진 요청",
+ "commandPowerOff": "기기 종료",
+ "commandRebootDevice": "기기 재부팅",
+ "commandFactoryReset": "공장초기화",
+ "commandSendSms": "문자 보내기",
+ "commandSendUssd": "USSD 보내기",
+ "commandSosNumber": "긴급번호 설정",
+ "commandSilenceTime": "알림 금지 시간대",
+ "commandSetPhonebook": "연락처 설정",
+ "commandVoiceMessage": "음성 메시지",
+ "commandOutputControl": "출력 제어",
+ "commandVoiceMonitoring": "음성 모니터링",
+ "commandSetAgps": "AGPS 설정",
+ "commandSetIndicator": "방향 설정",
+ "commandConfiguration": "설정",
+ "commandGetVersion": "버전 정보",
+ "commandFirmwareUpdate": "펌웨어 업데이트",
+ "commandSetConnection": "연결 설정",
+ "commandSetOdometer": "주행거리 설정",
+ "commandGetModemStatus": "모뎀 정보",
+ "commandGetDeviceStatus": "기기 정보",
+ "commandSetSpeedLimit": "속도 제한",
+ "commandModePowerSaving": "절전 모드",
+ "commandModeDeepSleep": "딥슬립 모드",
+ "commandAlarmGeofence": "지오펜스 알림",
+ "commandAlarmBattery": "배터리 알림",
+ "commandAlarmSos": "SOS 알림",
+ "commandAlarmRemove": "알림 제거",
+ "commandAlarmClock": "시간 알림",
+ "commandAlarmSpeed": "속도 알림",
+ "commandAlarmFall": "추락 알림",
+ "commandAlarmVibration": "진동 알림",
+ "commandFrequency": "주기",
+ "commandTimezone": "시간대 오프셋",
+ "commandMessage": "메시지",
+ "commandRadius": "반경",
+ "commandEnable": "활성화",
+ "commandData": "데이터",
+ "commandIndex": "인덱스",
+ "commandPhone": "연락처",
+ "commandServer": "서버",
+ "commandPort": "포트",
+ "eventAll": "모든 이벤트",
+ "eventDeviceOnline": "온라인 상태",
+ "eventDeviceUnknown": "상태 알 수 없음",
+ "eventDeviceOffline": "오프라인 상태",
+ "eventDeviceInactive": "기기 비활성화",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "기기 이동 중",
+ "eventDeviceStopped": "기기 정지",
+ "eventDeviceOverspeed": "속도 제한 초과",
+ "eventDeviceFuelDrop": "기름 부족",
+ "eventDeviceFuelIncrease": "기름 증가",
+ "eventCommandResult": "커맨드 결과",
+ "eventGeofenceEnter": "지오펜스 진입",
+ "eventGeofenceExit": "지오펜스 퇴장",
+ "eventAlarm": "알림",
+ "eventIgnitionOn": "시동 켜짐",
+ "eventIgnitionOff": "시동 꺼짐",
+ "eventMaintenance": "관리 필요",
+ "eventTextMessage": "메시지가 왔습니다",
+ "eventDriverChanged": "운전자 변경됨",
+ "eventMedia": "미디어",
+ "eventsScrollToLast": "마지막으로 이동",
+ "eventsSoundEvents": "소리 이벤트",
+ "eventsSoundAlarms": "소리 알림",
+ "alarmGeneral": "일반",
+ "alarmSos": "SOS",
+ "alarmVibration": "진동",
+ "alarmMovement": "움직임",
+ "alarmLowspeed": "저속",
+ "alarmOverspeed": "과속",
+ "alarmFallDown": "떨어짐",
+ "alarmLowPower": "파워 부족",
+ "alarmLowBattery": "배터리 부족",
+ "alarmFault": "오류",
+ "alarmPowerOff": "꺼짐",
+ "alarmPowerOn": "켜짐",
+ "alarmDoor": "문",
+ "alarmLock": "잠김",
+ "alarmUnlock": "잠금해제",
+ "alarmGeofence": "지오펜스",
+ "alarmGeofenceEnter": "지오펜스 진입",
+ "alarmGeofenceExit": "지오펜스 퇴장",
+ "alarmGpsAntennaCut": "GPS 안테나 잘림",
+ "alarmAccident": "사고",
+ "alarmTow": "견인",
+ "alarmIdle": "대기",
+ "alarmHighRpm": "RPM 높음",
+ "alarmHardAcceleration": "급가속",
+ "alarmHardBraking": "급브레이크",
+ "alarmHardCornering": "급코너링",
+ "alarmLaneChange": "차선 변경",
+ "alarmFatigueDriving": "운전자 피로",
+ "alarmPowerCut": "전원 끊김",
+ "alarmPowerRestored": "전원 복구됨",
+ "alarmJamming": "전파 방해",
+ "alarmTemperature": "온도",
+ "alarmParking": "주차",
+ "alarmBonnet": "후드 열림",
+ "alarmFootBrake": "풋 브레이크",
+ "alarmFuelLeak": "기름 누출",
+ "alarmTampering": "변조됨",
+ "alarmRemoving": "제거됨",
+ "notificationType": "알림 유형",
+ "notificationAlways": "전체 기기",
+ "notificationNotificators": "채널",
+ "notificatorCommand": "커맨드",
+ "notificatorWeb": "웹",
+ "notificatorMail": "메일",
+ "notificatorSms": "메시지",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "텔레그램",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "리플레이",
+ "reportCombined": "혼합",
+ "reportRoute": "경로",
+ "reportEvents": "이벤트",
+ "reportTrips": "구간",
+ "reportStops": "정지",
+ "reportSummary": "요약",
+ "reportDaily": "일일 요약",
+ "reportChart": "차트",
+ "reportConfigure": "설정",
+ "reportEventTypes": "이벤트 유형",
+ "reportChartType": "차트 유형",
+ "reportShowMarkers": "마커 표시",
+ "reportExport": "내보내기",
+ "reportEmail": "이메일 보고",
+ "reportSchedule": "스케줄",
+ "reportPeriod": "주기",
+ "reportCustom": "커스텀",
+ "reportToday": "오늘",
+ "reportYesterday": "어제",
+ "reportThisWeek": "이번주",
+ "reportPreviousWeek": "지난주",
+ "reportThisMonth": "이번달",
+ "reportPreviousMonth": "지난달",
+ "reportDeviceName": "기기 이름",
+ "reportAverageSpeed": "평균 속도",
+ "reportMaximumSpeed": "최대 속도",
+ "reportEngineHours": "엔진 시간",
+ "reportDuration": "기간",
+ "reportStartDate": "시작일",
+ "reportStartTime": "시작시간",
+ "reportStartAddress": "시작주소",
+ "reportEndTime": "종료시간",
+ "reportEndAddress": "종료주소",
+ "reportSpentFuel": "사용한 연료",
+ "reportStartOdometer": "주행거리계 시작",
+ "reportEndOdometer": "주행거리계 종료",
+ "statisticsTitle": "통계",
+ "statisticsCaptureTime": "캡처 시간",
+ "statisticsActiveUsers": "활성화된 유저",
+ "statisticsActiveDevices": "활성화된 기기",
+ "statisticsRequests": "요청",
+ "statisticsMessagesReceived": "수신된 메시지",
+ "statisticsMessagesStored": "저장한 메세지",
+ "statisticsGeocoder": "지오코더 요청",
+ "statisticsGeolocation": "지오로케이션 요청",
+ "categoryArrow": "화살표",
+ "categoryDefault": "기본값",
+ "categoryAnimal": "동물",
+ "categoryBicycle": "자전거",
+ "categoryBoat": "배",
+ "categoryBus": "버스",
+ "categoryCar": "차",
+ "categoryCamper": "Camper",
+ "categoryCrane": "크레인",
+ "categoryHelicopter": "헬리콥터",
+ "categoryMotorcycle": "오토바이",
+ "categoryOffroad": "오프로드",
+ "categoryPerson": "사람",
+ "categoryPickup": "픽업 트럭",
+ "categoryPlane": "비행기",
+ "categoryShip": "배",
+ "categoryTractor": "트랙터",
+ "categoryTrain": "기차",
+ "categoryTram": "트램",
+ "categoryTrolleybus": "트롤리 버스",
+ "categoryTruck": "트럭",
+ "categoryVan": "밴",
+ "categoryScooter": "오토바이",
+ "maintenanceStart": "시작",
+ "maintenancePeriod": "주기"
+} \ No newline at end of file
diff --git a/src/resources/l10n/lo.json b/src/resources/l10n/lo.json
new file mode 100644
index 00000000..d0a40085
--- /dev/null
+++ b/src/resources/l10n/lo.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "ກຳລັງໂຫລດ...",
+ "sharedHide": "Hide",
+ "sharedSave": "ບັນທຶກ",
+ "sharedUpload": "Upload",
+ "sharedSet": "Set",
+ "sharedCancel": "ຍົກເລີກ",
+ "sharedCopy": "Copy",
+ "sharedAdd": "ເພີ່ມ",
+ "sharedEdit": "ແກ້ໄຂ",
+ "sharedRemove": "ລົບອອກ",
+ "sharedRemoveConfirm": "ລົບລາຍການນີ້ບໍ່?",
+ "sharedNoData": "No data",
+ "sharedSubject": "Subject",
+ "sharedYes": "Yes",
+ "sharedNo": "No",
+ "sharedKm": "ກມ.",
+ "sharedMi": "ໄມລ໌",
+ "sharedNmi": "nmi",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "ນ໊ອດ",
+ "sharedKmh": "ກມ. /ຊມ.",
+ "sharedMph": "ໄມລ໌ຕໍ່ຊົ່ວໂມງ",
+ "sharedHour": "ຊົ່ວໂມງ",
+ "sharedMinute": "ນາທີ",
+ "sharedSecond": "ວິນາທີ",
+ "sharedDays": "days",
+ "sharedHours": "hours",
+ "sharedMinutes": "minutes",
+ "sharedDecimalDegrees": "Decimal Degrees",
+ "sharedDegreesDecimalMinutes": "Degrees Decimal Minutes",
+ "sharedDegreesMinutesSeconds": "Degrees Minutes Seconds",
+ "sharedName": "ຊື່",
+ "sharedDescription": "ລັກສະນະ",
+ "sharedSearch": "ຄົ້ນຫາ",
+ "sharedIconScale": "Icon Scale",
+ "sharedGeofence": "ເຂດພື້ນທີ່",
+ "sharedGeofences": "ເຂດພື້ນທີ່",
+ "sharedCreateGeofence": "Create Geofence",
+ "sharedNotifications": "ການແຈ້ງເຕືອນ",
+ "sharedNotification": "Notification",
+ "sharedAttributes": "ຄຸນລັກສະນະ",
+ "sharedAttribute": "ຄຸນລັກສະນະ",
+ "sharedDrivers": "Drivers",
+ "sharedDriver": "Driver",
+ "sharedArea": "ພື້ນທີ່",
+ "sharedSound": "Notification Sound",
+ "sharedType": "Type",
+ "sharedDistance": "Distance",
+ "sharedHourAbbreviation": "h",
+ "sharedMinuteAbbreviation": "m",
+ "sharedSecondAbbreviation": "s",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "l",
+ "sharedGallonAbbreviation": "gal",
+ "sharedLiter": "Liter",
+ "sharedImpGallon": "Imp. Gallon",
+ "sharedUsGallon": "U.S. Gallon",
+ "sharedLiterPerHourAbbreviation": "l/h",
+ "sharedGetMapState": "Get Map State",
+ "sharedComputedAttribute": "Computed Attribute",
+ "sharedComputedAttributes": "Computed Attributes",
+ "sharedCheckComputedAttribute": "Check Computed Attribute",
+ "sharedExpression": "Expression",
+ "sharedDevice": "Device",
+ "sharedTest": "Test",
+ "sharedTestNotification": "Send Test Notification",
+ "sharedTestNotificators": "Test Channels",
+ "sharedTestExpression": "Test Expression",
+ "sharedCalendar": "Calendar",
+ "sharedCalendars": "Calendars",
+ "sharedFile": "File",
+ "sharedSearchDevices": "Search Devices",
+ "sharedSortBy": "Sort By",
+ "sharedFilterMap": "Filter on Map",
+ "sharedSelectFile": "Select File",
+ "sharedPhone": "Phone",
+ "sharedRequired": "Required",
+ "sharedPreferences": "Preferences",
+ "sharedPermissions": "Permissions",
+ "sharedConnections": "Connections",
+ "sharedExtra": "Extra",
+ "sharedPrimary": "Primary",
+ "sharedSecondary": "Secondary",
+ "sharedTypeString": "String",
+ "sharedTypeNumber": "Number",
+ "sharedTypeBoolean": "Boolean",
+ "sharedTimezone": "Timezone",
+ "sharedInfoTitle": "Info",
+ "sharedSavedCommand": "Saved Command",
+ "sharedSavedCommands": "Saved Commands",
+ "sharedNew": "New…",
+ "sharedShowAddress": "Show Address",
+ "sharedShowDetails": "More Details",
+ "sharedDisabled": "Disabled",
+ "sharedMaintenance": "Maintenance",
+ "sharedDeviceAccumulators": "Accumulators",
+ "sharedAlarms": "Alarms",
+ "sharedLocation": "Location",
+ "sharedImport": "Import",
+ "sharedColumns": "Columns",
+ "sharedDropzoneText": "Drag and drop a file here or click",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "Simple",
+ "calendarRecurrence": "Recurrence",
+ "calendarOnce": "Once",
+ "calendarDaily": "Daily",
+ "calendarWeekly": "Weekly",
+ "calendarMonthly": "Monthly",
+ "calendarDays": "Days",
+ "calendarSunday": "Sunday",
+ "calendarMonday": "Monday",
+ "calendarTuesday": "Tuesday",
+ "calendarWednesday": "Wednesday",
+ "calendarThursday": "Thursday",
+ "calendarFriday": "Friday",
+ "calendarSaturday": "Saturday",
+ "attributeShowGeofences": "Show Geofences",
+ "attributeSpeedLimit": "Speed Limit",
+ "attributeFuelDropThreshold": "Fuel Drop Threshold",
+ "attributeFuelIncreaseThreshold": "Fuel Increase Threshold",
+ "attributePolylineDistance": "Polyline Distance",
+ "attributeReportIgnoreOdometer": "Report: Ignore Odometer",
+ "attributeWebReportColor": "Web: Report Color",
+ "attributeDevicePassword": "Device Password",
+ "attributeDeviceImage": "Device Image",
+ "attributeDeviceInactivityStart": "Device Inactivity Start",
+ "attributeDeviceInactivityPeriod": "Device Inactivity Period",
+ "attributeProcessingCopyAttributes": "Processing: Copy Attributes",
+ "attributeColor": "Color",
+ "attributeWebLiveRouteLength": "Web: Live Route Length",
+ "attributeWebSelectZoom": "Web: Zoom On Select",
+ "attributeWebMaxZoom": "Web: Maximum Zoom",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Pushover User Key",
+ "attributePushoverDeviceNames": "Pushover Device Names",
+ "attributeMailSmtpHost": "Mail: SMTP Host",
+ "attributeMailSmtpPort": "Mail: SMTP Port",
+ "attributeMailSmtpStarttlsEnable": "Mail: SMTP STARTTLS Enable",
+ "attributeMailSmtpStarttlsRequired": "Mail: SMTP STARTTLS Required",
+ "attributeMailSmtpSslEnable": "Mail: SMTP SSL Enable",
+ "attributeMailSmtpSslTrust": "Mail: SMTP SSL Trust",
+ "attributeMailSmtpSslProtocols": "Mail: SMTP SSL Protocols",
+ "attributeMailSmtpFrom": "Mail: SMTP From",
+ "attributeMailSmtpAuth": "Mail: SMTP Auth Enable",
+ "attributeMailSmtpUsername": "Mail: SMTP Username",
+ "attributeMailSmtpPassword": "Mail: SMTP Password",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: Disable Attributes",
+ "attributeUiDisableGroups": "UI: Disable Groups",
+ "attributeUiDisableEvents": "UI: Disable Events",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "UI: Disable Drivers",
+ "attributeUiDisableComputedAttributes": "UI: Disable Computed Attributes",
+ "attributeUiDisableCalendars": "UI: Disable Calendars",
+ "attributeUiDisableMaintenance": "UI: Disable Maintenance",
+ "attributeUiHidePositionAttributes": "UI: Hide Position Attributes",
+ "attributeUiDisableLoginLanguage": "UI: Disable Login Language",
+ "attributeNotificationTokens": "Notification Tokens",
+ "attributePopupInfo": "Popup Info",
+ "errorTitle": "ຜິດພາດ",
+ "errorGeneral": "Invalid parameters or constraints violation",
+ "errorConnection": "ການເຊື່ອມຕໍ່ຜິດພາດ",
+ "errorSocket": "Web socket connection error",
+ "errorZero": "Can't be zero",
+ "userEmail": "ອີເມວ",
+ "userPassword": "ລະຫັດຜ່ານ",
+ "userAdmin": "ຜູ້ເບິ່ງແຍງລະບົບ",
+ "userRemember": "ຈື່ໄວ້",
+ "userExpirationTime": "Expiration",
+ "userDeviceLimit": "Device Limit",
+ "userUserLimit": "User Limit",
+ "userDeviceReadonly": "Device Readonly",
+ "userLimitCommands": "Limit Commands",
+ "userDisableReports": "Disable Reports",
+ "userFixedEmail": "No Email Change",
+ "userToken": "Token",
+ "userDeleteAccount": "Delete Account",
+ "userTemporary": "Temporary",
+ "loginTitle": "ເຂົ້າສູ່ລະບົບ",
+ "loginLanguage": "ພາສາ",
+ "loginReset": "Reset Password",
+ "loginRegister": "ລົງທະບຽນ",
+ "loginLogin": "ເຂົ້າສູ່ລະບົບ",
+ "loginOpenId": "Login with OpenID",
+ "loginFailed": "ທີ່ຢູ່ອີເມວຫລືລະຫັດຜ່ານບໍ່ຖືກຕ້ອງ",
+ "loginCreated": "ຜູ້ໃຊ້ໃຫມ່ ໄດ້ຮັບການລົງທະບຽນ",
+ "loginResetSuccess": "Check your email",
+ "loginUpdateSuccess": "New password is set",
+ "loginLogout": "ອອກຈາກລະບົບ",
+ "loginLogo": "Logo",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "ອຸປະກອນແລະສະຖານະ",
+ "deviceSelected": "Selected Device",
+ "deviceTitle": "ເຄື່ອງ/ອຸປະກອນ",
+ "devicePrimaryInfo": "Device Title",
+ "deviceSecondaryInfo": "Device Detail",
+ "deviceIdentifier": "ລະບຸເລກອຸປະກອນ",
+ "deviceModel": "Model",
+ "deviceContact": "Contact",
+ "deviceCategory": "Category",
+ "deviceLastUpdate": "ແກ້ໄຂລ່າສຸດ",
+ "deviceCommand": "ຄຳສັ່ງ",
+ "deviceFollow": "ຕິດຕາມ",
+ "deviceTotalDistance": "Total Distance",
+ "deviceStatus": "Status",
+ "deviceStatusOnline": "Online",
+ "deviceStatusOffline": "Offline",
+ "deviceStatusUnknown": "Unknown",
+ "deviceRegisterFirst": "Register your first device",
+ "deviceIdentifierHelp": "IMEI, serial number or other id. It has to match the identifier device reports to the server.",
+ "deviceShare": "Share Device",
+ "groupDialog": "ກຸ່ມ",
+ "groupParent": "ກຸ່ມ",
+ "groupNoGroup": "ບໍ່ຈັດໃນກຸ່ມ",
+ "settingsTitle": "ການຕັ້ງຄ່າ",
+ "settingsUser": "ບັນຊີຜູ້ໃຊ້",
+ "settingsGroups": "ຕັ້ງຄ່າກຸ່ມ",
+ "settingsServer": "ຕັ້ງຄ່າລະບົບ",
+ "settingsUsers": "ຕັ້ງຄ່າຜູ້ໃຊ້ງານ",
+ "settingsDistanceUnit": "Distance Unit",
+ "settingsAltitudeUnit": "Altitude Unit",
+ "settingsSpeedUnit": "Speed Unit",
+ "settingsVolumeUnit": "Volume Unit",
+ "settingsTwelveHourFormat": "ຮູບແບບເວລາ 12 ຊົ່ວໂມງ",
+ "settingsCoordinateFormat": "Coordinates Format",
+ "settingsServerVersion": "Server Version",
+ "settingsAppVersion": "App Version",
+ "settingsConnection": "Connection",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "ລາຍງານ",
+ "reportScheduled": "Scheduled Reports",
+ "reportDevice": "ລາຍງານເຄື່ອງ/ອຸປະກອນ",
+ "reportGroup": "Group",
+ "reportFrom": "ຈາກ",
+ "reportTo": "ໄປເຖິງ",
+ "reportShow": "ສະແດງ",
+ "reportClear": "ລົບລ້າງລາຍງານ",
+ "linkGoogleMaps": "Google Maps",
+ "linkAppleMaps": "Apple Maps",
+ "linkStreetView": "Street View",
+ "positionFixTime": "Fix Time",
+ "positionDeviceTime": "Device Time",
+ "positionServerTime": "Server Time",
+ "positionValid": "ຖືກຕ້ອງ",
+ "positionAccuracy": "Accuracy",
+ "positionLatitude": "ລາຕິຈູດ",
+ "positionLongitude": "ລອງຈິຈູດ",
+ "positionAltitude": "ລະດັບຄວາມສູງ",
+ "positionSpeed": "ຄວາມໄວ",
+ "positionCourse": "ທິດທາງ",
+ "positionAddress": "ທີ່ຢູ່",
+ "positionProtocol": "ໂປຣໂຕຄໍລ໌",
+ "positionDistance": "Distance",
+ "positionRpm": "RPM",
+ "positionFuel": "Fuel",
+ "positionPower": "Power",
+ "positionBattery": "Battery",
+ "positionRaw": "Raw",
+ "positionIndex": "Index",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "Satellites",
+ "positionSatVisible": "Visible Satellites",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "Roaming",
+ "positionEvent": "Event",
+ "positionAlarm": "Alarm",
+ "positionStatus": "Status",
+ "positionOdometer": "Odometer",
+ "positionServiceOdometer": "Service Odometer",
+ "positionTripOdometer": "Trip Odometer",
+ "positionHours": "Hours",
+ "positionSteps": "Steps",
+ "positionInput": "Input",
+ "positionHeartRate": "Heart Rate",
+ "positionOutput": "Output",
+ "positionBatteryLevel": "Battery Level",
+ "positionFuelConsumption": "Fuel Consumption",
+ "positionRfid": "RFID",
+ "positionVersionFw": "Firmware Version",
+ "positionVersionHw": "Hardware Version",
+ "positionIgnition": "Ignition",
+ "positionFlags": "Flags",
+ "positionCharge": "Charge",
+ "positionIp": "IP",
+ "positionArchive": "Archive",
+ "positionVin": "VIN",
+ "positionApproximate": "Approximate",
+ "positionThrottle": "Throttle",
+ "positionMotion": "Motion",
+ "positionArmed": "Armed",
+ "positionAcceleration": "Acceleration",
+ "positionTemp": "Temperature",
+ "positionDeviceTemp": "Device Temperature",
+ "positionCoolantTemp": "Coolant Temperature",
+ "positionOperator": "Operator",
+ "positionCommand": "Command",
+ "positionBlocked": "Blocked",
+ "positionDtcs": "DTCs",
+ "positionObdSpeed": "OBD Speed",
+ "positionObdOdometer": "OBD Odometer",
+ "positionDrivingTime": "Driving Time",
+ "positionDriverUniqueId": "Driver Unique Id",
+ "positionCard": "Card",
+ "positionImage": "Image",
+ "positionVideo": "Video",
+ "positionAudio": "Audio",
+ "serverTitle": "ການຕັ້ງຄ່າເຊີເວີ້",
+ "serverZoom": "ຂະຫຍາຍ +/-",
+ "serverRegistration": "ລົງທະບຽນ",
+ "serverReadonly": "ອ່ານໄດ້ຢ່າງດຽວ",
+ "serverForceSettings": "Force Settings",
+ "serverAnnouncement": "Announcement",
+ "serverName": "Server Name",
+ "serverDescription": "Server Description",
+ "serverColorPrimary": "Color Primary",
+ "serverColorSecondary": "Color Secondary",
+ "serverLogo": "Logo Image",
+ "serverLogoInverted": "Inverted Logo Image",
+ "serverChangeDisable": "Disable Server Change",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "ແຜ່ນທີ",
+ "mapActive": "Active Maps",
+ "mapOverlay": "Map Overlay",
+ "mapOverlayCustom": "Custom Overlay",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API Key",
+ "mapOpenWeatherClouds": "OpenWeather Clouds",
+ "mapOpenWeatherPrecipitation": "OpenWeather Precipitation",
+ "mapOpenWeatherPressure": "OpenWeather Pressure",
+ "mapOpenWeatherWind": "OpenWeather Wind",
+ "mapOpenWeatherTemperature": "OpenWeather Temperature",
+ "mapLayer": "ຊັ້ນແຜ່ນທີ",
+ "mapCustom": "Custom (XYZ)",
+ "mapCustomArcgis": "Custom (ArcGIS)",
+ "mapCustomLabel": "Custom map",
+ "mapCarto": "Carto Basemaps",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Bing Maps ສຳຄັນ",
+ "mapBingRoad": "Bing Maps ຖະຫນົນ",
+ "mapBingAerial": "Bing Maps ທາງອາກາດ",
+ "mapBingHybrid": "Bing Maps Hybrid",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Yandex Map",
+ "mapYandexSat": "Yandex Satellite",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox Streets",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox Outdoors",
+ "mapMapboxSatellite": "Mapbox Satellite",
+ "mapMapboxKey": "Mapbox Access Token",
+ "mapMapTilerBasic": "MapTiler Basic",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "MapTiler API Key",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ Access Token",
+ "mapTomTomBasic": "TomTom Basic",
+ "mapTomTomFlow": "TomTom Traffic Flow",
+ "mapTomTomIncidents": "TomTom Traffic Incidents",
+ "mapTomTomKey": "TomTom API Key",
+ "mapHereBasic": "Here Basic",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satellite",
+ "mapHereFlow": "Here Traffic Flow",
+ "mapHereKey": "Here API Key",
+ "mapShapePolygon": "ໂພລີກອນ",
+ "mapShapeCircle": "ວົງກົມ",
+ "mapShapePolyline": "Polyline",
+ "mapLiveRoutes": "Live Routes",
+ "mapDirection": "Show Direction",
+ "mapCurrentLocation": "Current Location",
+ "mapPoiLayer": "POI Layer",
+ "mapClustering": "Markers Clustering",
+ "mapOnSelect": "Show Map on Selection",
+ "mapDefault": "Default Map",
+ "stateTitle": "ສະຖານະ",
+ "stateName": "ຄຸນລັກສະນະ",
+ "stateValue": "ມູນຄ່າ",
+ "commandTitle": "ຄຳສັ່ງ",
+ "commandSend": "ສົ່ງ",
+ "commandSent": "Command sent",
+ "commandQueued": "Command queued",
+ "commandUnit": "ຫນ່ວຍ",
+ "commandCustom": "ຄຳສັ່ງກຳຫນົດເອງ",
+ "commandDeviceIdentification": "ໝາຍເລກອຸປະກອນ",
+ "commandPositionSingle": "ລາຍງານຕ່ຳແຫນ່ງດຽວ",
+ "commandPositionPeriodic": "ແກ້ໄຂຕ່ຳແຫນ່ງ",
+ "commandPositionStop": "ຕ່ຳແຫນ່ງ ຢຸດ",
+ "commandEngineStop": "ດັບເຄື່ອງຈັກ",
+ "commandEngineResume": "ຕິດເຄື່ອງຈັກຄືນໃຫມ່",
+ "commandAlarmArm": "ແຈ້ງເຕືອນຕິດຕໍ່ສາຂາ",
+ "commandAlarmDisarm": "ແຈ້ງເຕືອນຍົກເລີກຕິດຕໍ່ສາຂາ",
+ "commandAlarmDismiss": "Dismiss Alarm",
+ "commandSetTimezone": "ຕັ້ງຄ່າເຂດເວລາ",
+ "commandRequestPhoto": "ສັ່ງຖ່າຍຮູບ",
+ "commandPowerOff": "Power Off Device",
+ "commandRebootDevice": "ຣີບູດ",
+ "commandFactoryReset": "Factory Reset",
+ "commandSendSms": "ສົ່ງ SMS",
+ "commandSendUssd": "Send USSD",
+ "commandSosNumber": "ຕັ້ງຄ່າເລກໝາຍໂທສຸກເສີນ SOS",
+ "commandSilenceTime": "ຕັ້ງຄ່າຊ່ວງເວລາຢຸດນິ່ງ",
+ "commandSetPhonebook": "ຕັ້ງຄ່າສະໝຸດໂທລະສັບ",
+ "commandVoiceMessage": "ຂໍ້ຄວາມສຽງ",
+ "commandOutputControl": "ຄວບຄຸມຂໍ້ມູນທີ່ສົ່ງອອກ",
+ "commandVoiceMonitoring": "Voice Monitoring",
+ "commandSetAgps": "Set AGPS",
+ "commandSetIndicator": "Set Indicator",
+ "commandConfiguration": "Configuration",
+ "commandGetVersion": "Get Version",
+ "commandFirmwareUpdate": "Update Firmware",
+ "commandSetConnection": "Set Connection",
+ "commandSetOdometer": "Set Odometer",
+ "commandGetModemStatus": "Get Modem Status",
+ "commandGetDeviceStatus": "Get Device Status",
+ "commandSetSpeedLimit": "Set Speed Limit",
+ "commandModePowerSaving": "Power Saving Mode",
+ "commandModeDeepSleep": "Deep Sleep Mode",
+ "commandAlarmGeofence": "Set Geofence Alarm",
+ "commandAlarmBattery": "Set Battery Alarm",
+ "commandAlarmSos": "Set SOS Alarm",
+ "commandAlarmRemove": "Set Remove Alarm",
+ "commandAlarmClock": "Set Clock Alarm",
+ "commandAlarmSpeed": "Set Speed Alarm",
+ "commandAlarmFall": "Set Fall Alarm",
+ "commandAlarmVibration": "Set Vibration Alarm",
+ "commandFrequency": "ຄວາມຖີ່",
+ "commandTimezone": "Timezone Offset",
+ "commandMessage": "Message",
+ "commandRadius": "Radius",
+ "commandEnable": "Enable",
+ "commandData": "Data",
+ "commandIndex": "Index",
+ "commandPhone": "Phone Number",
+ "commandServer": "Server",
+ "commandPort": "Port",
+ "eventAll": "All Events",
+ "eventDeviceOnline": "Status online",
+ "eventDeviceUnknown": "Status unknown",
+ "eventDeviceOffline": "Status offline",
+ "eventDeviceInactive": "Device inactive",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "Device moving",
+ "eventDeviceStopped": "Device stopped",
+ "eventDeviceOverspeed": "Speed limit exceeded",
+ "eventDeviceFuelDrop": "Fuel drop",
+ "eventDeviceFuelIncrease": "Fuel increase",
+ "eventCommandResult": "ຜົນຮັບຈາກຄຳສັ່ງ",
+ "eventGeofenceEnter": "Geofence entered",
+ "eventGeofenceExit": "Geofence exited",
+ "eventAlarm": "Alarm",
+ "eventIgnitionOn": "Ignition on",
+ "eventIgnitionOff": "Ignition off",
+ "eventMaintenance": "Maintenance required",
+ "eventTextMessage": "Text message received",
+ "eventDriverChanged": "Driver changed",
+ "eventMedia": "Media",
+ "eventsScrollToLast": "Scroll To Last",
+ "eventsSoundEvents": "Sound Events",
+ "eventsSoundAlarms": "Sound Alarms",
+ "alarmGeneral": "General",
+ "alarmSos": "SOS",
+ "alarmVibration": "Vibration",
+ "alarmMovement": "Movement",
+ "alarmLowspeed": "Low Speed",
+ "alarmOverspeed": "Overspeed",
+ "alarmFallDown": "Fall Down",
+ "alarmLowPower": "Low Power",
+ "alarmLowBattery": "Low Battery",
+ "alarmFault": "Fault",
+ "alarmPowerOff": "Power Off",
+ "alarmPowerOn": "Power On",
+ "alarmDoor": "Door",
+ "alarmLock": "Lock",
+ "alarmUnlock": "Unlock",
+ "alarmGeofence": "Geofence",
+ "alarmGeofenceEnter": "Geofence Enter",
+ "alarmGeofenceExit": "Geofence Exit",
+ "alarmGpsAntennaCut": "GPS Antenna Cut",
+ "alarmAccident": "Accident",
+ "alarmTow": "Tow",
+ "alarmIdle": "Idle",
+ "alarmHighRpm": "High RPM",
+ "alarmHardAcceleration": "Hard Acceleration",
+ "alarmHardBraking": "Hard Braking",
+ "alarmHardCornering": "Hard Cornering",
+ "alarmLaneChange": "Lane Change",
+ "alarmFatigueDriving": "Fatigue Driving",
+ "alarmPowerCut": "Power Cut",
+ "alarmPowerRestored": "Power Restored",
+ "alarmJamming": "Jamming",
+ "alarmTemperature": "Temperature",
+ "alarmParking": "Parking",
+ "alarmBonnet": "Bonnet",
+ "alarmFootBrake": "Foot Brake",
+ "alarmFuelLeak": "Fuel Leak",
+ "alarmTampering": "Tampering",
+ "alarmRemoving": "Removing",
+ "notificationType": "ຊະນິດການແຈ້ງເຕືອນ",
+ "notificationAlways": "All Devices",
+ "notificationNotificators": "Channels",
+ "notificatorCommand": "Command",
+ "notificatorWeb": "Web",
+ "notificatorMail": "Mail",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Replay",
+ "reportCombined": "Combined",
+ "reportRoute": "Route",
+ "reportEvents": "Events",
+ "reportTrips": "Trips",
+ "reportStops": "Stops",
+ "reportSummary": "Summary",
+ "reportDaily": "Daily Summary",
+ "reportChart": "Chart",
+ "reportConfigure": "Configure",
+ "reportEventTypes": "Event Types",
+ "reportChartType": "Chart Type",
+ "reportShowMarkers": "Show Markers",
+ "reportExport": "Export",
+ "reportEmail": "Email Report",
+ "reportSchedule": "Schedule",
+ "reportPeriod": "Period",
+ "reportCustom": "Custom",
+ "reportToday": "Today",
+ "reportYesterday": "Yesterday",
+ "reportThisWeek": "This Week",
+ "reportPreviousWeek": "Previous Week",
+ "reportThisMonth": "This Month",
+ "reportPreviousMonth": "Previous Month",
+ "reportDeviceName": "Device Name",
+ "reportAverageSpeed": "Average Speed",
+ "reportMaximumSpeed": "Maximum Speed",
+ "reportEngineHours": "Engine Hours",
+ "reportDuration": "Duration",
+ "reportStartDate": "Start Date",
+ "reportStartTime": "Start Time",
+ "reportStartAddress": "Start Address",
+ "reportEndTime": "End Time",
+ "reportEndAddress": "End Address",
+ "reportSpentFuel": "Spent Fuel",
+ "reportStartOdometer": "Odometer Start",
+ "reportEndOdometer": "Odometer End",
+ "statisticsTitle": "Statistics",
+ "statisticsCaptureTime": "Capture Time",
+ "statisticsActiveUsers": "Active Users",
+ "statisticsActiveDevices": "Active Devices",
+ "statisticsRequests": "Requests",
+ "statisticsMessagesReceived": "Messages Received",
+ "statisticsMessagesStored": "Messages Stored",
+ "statisticsGeocoder": "Geocoder Requests",
+ "statisticsGeolocation": "Geolocation Requests",
+ "categoryArrow": "Arrow",
+ "categoryDefault": "Default",
+ "categoryAnimal": "Animal",
+ "categoryBicycle": "Bicycle",
+ "categoryBoat": "Boat",
+ "categoryBus": "Bus",
+ "categoryCar": "Car",
+ "categoryCamper": "Camper",
+ "categoryCrane": "Crane",
+ "categoryHelicopter": "Helicopter",
+ "categoryMotorcycle": "Motorcycle",
+ "categoryOffroad": "Offroad",
+ "categoryPerson": "Person",
+ "categoryPickup": "Pickup",
+ "categoryPlane": "Plane",
+ "categoryShip": "Ship",
+ "categoryTractor": "Tractor",
+ "categoryTrain": "Train",
+ "categoryTram": "Tram",
+ "categoryTrolleybus": "Trolleybus",
+ "categoryTruck": "Truck",
+ "categoryVan": "Van",
+ "categoryScooter": "Scooter",
+ "maintenanceStart": "Start",
+ "maintenancePeriod": "Period"
+} \ No newline at end of file
diff --git a/src/resources/l10n/lt.json b/src/resources/l10n/lt.json
new file mode 100644
index 00000000..d4ae0530
--- /dev/null
+++ b/src/resources/l10n/lt.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "Kraunasi..",
+ "sharedHide": "Paslėpti",
+ "sharedSave": "Išsaugoti",
+ "sharedUpload": "Įkelti",
+ "sharedSet": "Nustatyti",
+ "sharedCancel": "Atšaukti",
+ "sharedCopy": "Kopijuoti",
+ "sharedAdd": "Pridėti",
+ "sharedEdit": "Redaguoti",
+ "sharedRemove": "Pašalinti",
+ "sharedRemoveConfirm": "Pašalinti objektą?",
+ "sharedNoData": "Nėra duomenų",
+ "sharedSubject": "Subject",
+ "sharedYes": "Taip",
+ "sharedNo": "Ne",
+ "sharedKm": "km",
+ "sharedMi": "mi",
+ "sharedNmi": "jurmylė",
+ "sharedMeters": "m",
+ "sharedFeet": "pėdos",
+ "sharedKn": "mazgai",
+ "sharedKmh": "km/h",
+ "sharedMph": "mph",
+ "sharedHour": "Valanda(-os)",
+ "sharedMinute": "Minutė(-es)",
+ "sharedSecond": "Sekundė(-es)",
+ "sharedDays": "dienos",
+ "sharedHours": "valandos",
+ "sharedMinutes": "minutės",
+ "sharedDecimalDegrees": "Laipsniai ir jų dalys",
+ "sharedDegreesDecimalMinutes": "Laipsniai minutės ir jų dalys",
+ "sharedDegreesMinutesSeconds": "Laipsniai minutės sekundės",
+ "sharedName": "Pavadinimas",
+ "sharedDescription": "Aprašymas",
+ "sharedSearch": "Paieška",
+ "sharedIconScale": "Ikonos mastelis",
+ "sharedGeofence": "Georiba",
+ "sharedGeofences": "Georibos",
+ "sharedCreateGeofence": "Sukurti Georibas",
+ "sharedNotifications": "Perspėjimai",
+ "sharedNotification": "Pranešimas",
+ "sharedAttributes": "Parametrai",
+ "sharedAttribute": "Parametras",
+ "sharedDrivers": "Vairuotojai",
+ "sharedDriver": "Vairuotojas",
+ "sharedArea": "Plotas",
+ "sharedSound": "Perspėjimo garsas",
+ "sharedType": "Tipas",
+ "sharedDistance": "Atstumas",
+ "sharedHourAbbreviation": "h",
+ "sharedMinuteAbbreviation": "m",
+ "sharedSecondAbbreviation": "s",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "I",
+ "sharedGallonAbbreviation": "galonas",
+ "sharedLiter": "Litras",
+ "sharedImpGallon": "Standartinis galonas",
+ "sharedUsGallon": "JAV galonas",
+ "sharedLiterPerHourAbbreviation": "l/h",
+ "sharedGetMapState": "Gauti žemėlapio buseną",
+ "sharedComputedAttribute": "Apskaičiuotas parametras",
+ "sharedComputedAttributes": "Apskaičiuoti parametrai",
+ "sharedCheckComputedAttribute": "Patikrinti apskaičiuotą parametrą",
+ "sharedExpression": "Išraiška",
+ "sharedDevice": "Prietaisas",
+ "sharedTest": "Testas",
+ "sharedTestNotification": "Siųsti bandomąjį perspėjimą",
+ "sharedTestNotificators": "Bandyti kanalus",
+ "sharedTestExpression": "Bandyti reikšmę",
+ "sharedCalendar": "Kalendorius",
+ "sharedCalendars": "Kalendoriai",
+ "sharedFile": "Failas",
+ "sharedSearchDevices": "Ieškoti įrenginių",
+ "sharedSortBy": "Rūšiuoti pagal",
+ "sharedFilterMap": "Filtruoti žemėlapyje",
+ "sharedSelectFile": "Parinkite failą",
+ "sharedPhone": "Telefonas",
+ "sharedRequired": "Privalomas",
+ "sharedPreferences": "Pasirinkimai",
+ "sharedPermissions": "Leidimai",
+ "sharedConnections": "Susijungimai",
+ "sharedExtra": "Extra",
+ "sharedPrimary": "Pagrindinis",
+ "sharedSecondary": "Antrinis",
+ "sharedTypeString": "Tekstas",
+ "sharedTypeNumber": "Skaičius",
+ "sharedTypeBoolean": "Loginis",
+ "sharedTimezone": "Laiko zona",
+ "sharedInfoTitle": "Info",
+ "sharedSavedCommand": "Išsaugota komanda",
+ "sharedSavedCommands": "Išsaugotos komandos",
+ "sharedNew": "Naujas...",
+ "sharedShowAddress": "Rodyti adresą",
+ "sharedShowDetails": "Daugiau informacijos",
+ "sharedDisabled": "Išjungtas",
+ "sharedMaintenance": "Aptarnavimas",
+ "sharedDeviceAccumulators": "Akumuliatorius",
+ "sharedAlarms": "Perspėjimai",
+ "sharedLocation": "Lokacija",
+ "sharedImport": "Importuoti",
+ "sharedColumns": "Stulpeliai",
+ "sharedDropzoneText": "Nutempkite failą čia arba paspauskite",
+ "sharedLogs": "Žurnalai",
+ "sharedLink": "Link",
+ "calendarSimple": "Paprastas",
+ "calendarRecurrence": "Pasikartojimas",
+ "calendarOnce": "Vienkartinis",
+ "calendarDaily": "Kasdien",
+ "calendarWeekly": "Savaitinis",
+ "calendarMonthly": "Mėnesinis",
+ "calendarDays": "Dienos",
+ "calendarSunday": "Sekmadienis",
+ "calendarMonday": "Pirmadienis",
+ "calendarTuesday": "Antradienis",
+ "calendarWednesday": "Trečiadienis",
+ "calendarThursday": "Ketvirtadienis",
+ "calendarFriday": "Penktadienis",
+ "calendarSaturday": "Šeštadienis",
+ "attributeShowGeofences": "Rodyti Georibas",
+ "attributeSpeedLimit": "Greičio limitas",
+ "attributeFuelDropThreshold": "Kuro mažėjimo riba",
+ "attributeFuelIncreaseThreshold": "Kuro didėjimo riba",
+ "attributePolylineDistance": "Linijos atstumas",
+ "attributeReportIgnoreOdometer": "Ataskaita: Ignoruoti odometrą",
+ "attributeWebReportColor": "Web: Ataskaitos spalva",
+ "attributeDevicePassword": "Įrenginio slaptažodis",
+ "attributeDeviceImage": "Įrenginio vaizdas",
+ "attributeDeviceInactivityStart": "Įrenginio neaktyvumo pradžia",
+ "attributeDeviceInactivityPeriod": "Įrenginio neaktyvumo trukmė",
+ "attributeProcessingCopyAttributes": "Apdorojimas: Kopijuoti atributus",
+ "attributeColor": "Spalva",
+ "attributeWebLiveRouteLength": "Web: Tiesioginio maršruto ilgis",
+ "attributeWebSelectZoom": "Web: Išdidinti pažymėjus",
+ "attributeWebMaxZoom": "Web: Maximalus išdidinimas",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Pushover User Key",
+ "attributePushoverDeviceNames": "Pushover Device Names",
+ "attributeMailSmtpHost": "Mail: SMTP adresas",
+ "attributeMailSmtpPort": "Mail: SMTP prievadas",
+ "attributeMailSmtpStarttlsEnable": "Mail: SMTP STARTTLS įjungtas",
+ "attributeMailSmtpStarttlsRequired": "Mail: SMTP STARTLS privalomas",
+ "attributeMailSmtpSslEnable": "Mail: SMTP SSL įjungtas",
+ "attributeMailSmtpSslTrust": "Mail: SMTP SSL Trust",
+ "attributeMailSmtpSslProtocols": "Mail: SMTP SSL protokolai",
+ "attributeMailSmtpFrom": "Mail: SMTP Siuntėjas",
+ "attributeMailSmtpAuth": "Mail: SMTP autentifikacija įjungta",
+ "attributeMailSmtpUsername": "Mail: SMTP prisijungimas",
+ "attributeMailSmtpPassword": "Mail: SMTP slaptažodis",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: išjungti atributus",
+ "attributeUiDisableGroups": "UI: išjungti grupes",
+ "attributeUiDisableEvents": "UI: Išjungti įvykius",
+ "attributeUiDisableVehicleFeatures": "UI: Išjungti automobilio funkcijas",
+ "attributeUiDisableDrivers": "UI: Išjungti vairuotojus",
+ "attributeUiDisableComputedAttributes": "UI: Išjungti apskaičiuotus parametrus",
+ "attributeUiDisableCalendars": "UI: Išjungti kalendorius",
+ "attributeUiDisableMaintenance": "UI: Išjungti aptarnavimus",
+ "attributeUiHidePositionAttributes": "UI: Slėpti pozicijos adresą",
+ "attributeUiDisableLoginLanguage": "UI: Išjungti prisijungimo kalbą",
+ "attributeNotificationTokens": "Notification Tokens",
+ "attributePopupInfo": "Popup Info",
+ "errorTitle": "Klaida",
+ "errorGeneral": "Netinkami parametrai ar apribojimų pažeidimas",
+ "errorConnection": "Ryšio klaida",
+ "errorSocket": "Web socket prisijungimo klaida",
+ "errorZero": "Negali būti nulis",
+ "userEmail": "Vartotojo vardas",
+ "userPassword": "Slaptažodis",
+ "userAdmin": "Administratorius",
+ "userRemember": "Prisiminti",
+ "userExpirationTime": "Galiojimas",
+ "userDeviceLimit": "Prietaiso limitas",
+ "userUserLimit": "Vartotojo limitas",
+ "userDeviceReadonly": "Prietaisai skaitymo rėžimu",
+ "userLimitCommands": "Apriboti komandas",
+ "userDisableReports": "Išjungti ataskaitas",
+ "userFixedEmail": "Neleisti keisti Email",
+ "userToken": "Token",
+ "userDeleteAccount": "Pašalinti vartotoją",
+ "userTemporary": "Temporary",
+ "loginTitle": "Prisijungimas",
+ "loginLanguage": "Kalba",
+ "loginReset": "Atstatyti slaptažodį",
+ "loginRegister": "Registruotis",
+ "loginLogin": "Prisijungti",
+ "loginOpenId": "Jungtis naudojant Google",
+ "loginFailed": "Neteisingas el.paštas ir/ar slaptažodis",
+ "loginCreated": "Registracija sėkminga",
+ "loginResetSuccess": "Patikrinkite savo el. paštą",
+ "loginUpdateSuccess": "Naujas slaptažodis sukurtas",
+ "loginLogout": "Atsijungti",
+ "loginLogo": "Logotipas",
+ "loginTotpCode": "Vienkartinio slaptažodžio kodas",
+ "loginTotpKey": "Vienkartinio slaptažodžio raktas",
+ "devicesAndState": "Prietaisai ir Statusas",
+ "deviceSelected": "Pasirinktas įrenginys",
+ "deviceTitle": "Prietaisai",
+ "devicePrimaryInfo": "Įrenginio antraštė",
+ "deviceSecondaryInfo": "Įrenginio aprašymas",
+ "deviceIdentifier": "Identifikacinis kodas",
+ "deviceModel": "Modelis",
+ "deviceContact": "Kontaktas",
+ "deviceCategory": "Kategorija",
+ "deviceLastUpdate": "Naujausias atnaujinimas",
+ "deviceCommand": "Komanda",
+ "deviceFollow": "Sekti",
+ "deviceTotalDistance": "Viso distancija",
+ "deviceStatus": "Būsena",
+ "deviceStatusOnline": "Prisijungęs",
+ "deviceStatusOffline": "Neprisijungęs",
+ "deviceStatusUnknown": "Nežinoma",
+ "deviceRegisterFirst": "Registruokite savo pirmą įrenginį",
+ "deviceIdentifierHelp": "IMEI, serijinis numeris arba identifikacinis ID. Turi atitikti įrenginio ID siunčiamą į serverį.",
+ "deviceShare": "Dalintis įrenginiu",
+ "groupDialog": "Grupė",
+ "groupParent": "Grupė",
+ "groupNoGroup": "Nenurodyta grupė",
+ "settingsTitle": "Nustatymai",
+ "settingsUser": "Paskyra",
+ "settingsGroups": "Grupės",
+ "settingsServer": "Serveris",
+ "settingsUsers": "Vartotojai",
+ "settingsDistanceUnit": "Atstumo vienetai",
+ "settingsAltitudeUnit": "Aukščio vienetai",
+ "settingsSpeedUnit": "Greičio vienetai",
+ "settingsVolumeUnit": "Tūrio vienetai",
+ "settingsTwelveHourFormat": "12-val formatas",
+ "settingsCoordinateFormat": "Koordinačių formatas",
+ "settingsServerVersion": "Serverio versija",
+ "settingsAppVersion": "App versija",
+ "settingsConnection": "Susijungimas",
+ "settingsDarkMode": "Tamsi tema",
+ "settingsTotpEnable": "Įjungti vienkartinį slaptažodį",
+ "settingsTotpForce": "Priverstinis vienkartinis slaptažodis",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker atnaujinimo intervalas",
+ "settingsUpdateAvailable": "Yra atnaujinimas.",
+ "settingsSupport": "Pagalba",
+ "reportTitle": "Ataskaitos",
+ "reportScheduled": "Suplanuotos ataskaitos",
+ "reportDevice": "Prietaisas",
+ "reportGroup": "Grupė",
+ "reportFrom": "Nuo",
+ "reportTo": "Iki",
+ "reportShow": "Rodyti",
+ "reportClear": "Valyti",
+ "linkGoogleMaps": "Google žemėlapiai",
+ "linkAppleMaps": "Apple žemėlapiai",
+ "linkStreetView": "Gatvės vaizdas",
+ "positionFixTime": "Fiksuoti laiką",
+ "positionDeviceTime": "Įrenginio laikas",
+ "positionServerTime": "Serverio laikas",
+ "positionValid": "Galiojantis",
+ "positionAccuracy": "Tikslumas",
+ "positionLatitude": "Platuma",
+ "positionLongitude": "Ilguma",
+ "positionAltitude": "Aukštis",
+ "positionSpeed": "Greitis",
+ "positionCourse": "Kryptis",
+ "positionAddress": "Adresas",
+ "positionProtocol": "Protokolas",
+ "positionDistance": "Atstumas",
+ "positionRpm": "APM",
+ "positionFuel": "Kuras",
+ "positionPower": "Energija",
+ "positionBattery": "Baterija",
+ "positionRaw": "Neapdoroti duomenys",
+ "positionIndex": "Indeksas",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "Palydovai",
+ "positionSatVisible": "Matomi palydovai",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "Tarptinklinis ryšys",
+ "positionEvent": "Įvykis",
+ "positionAlarm": "Signalas",
+ "positionStatus": "Būsena",
+ "positionOdometer": "Odometras",
+ "positionServiceOdometer": "Serviso odometras",
+ "positionTripOdometer": "Kelionės odometras",
+ "positionHours": "Valandos",
+ "positionSteps": "Žingsniai",
+ "positionInput": "Įvestis",
+ "positionHeartRate": "Pulsas",
+ "positionOutput": "Išvestis",
+ "positionBatteryLevel": "Baterijos lygis",
+ "positionFuelConsumption": "Kuro suvartojimas",
+ "positionRfid": "RFID",
+ "positionVersionFw": "Programinės įrangos versija",
+ "positionVersionHw": "Techninės įrangos versija",
+ "positionIgnition": "Degimas",
+ "positionFlags": "Žymeklis",
+ "positionCharge": "Krovimas",
+ "positionIp": "IP",
+ "positionArchive": "Archyvas",
+ "positionVin": "VIN",
+ "positionApproximate": "Apytikslis",
+ "positionThrottle": "Throttle",
+ "positionMotion": "Judantis",
+ "positionArmed": "Uždėtas",
+ "positionAcceleration": "Akseleracija",
+ "positionTemp": "Temperatūra",
+ "positionDeviceTemp": "Įrenginio temperatūra",
+ "positionCoolantTemp": "Aušinimo temperatūra",
+ "positionOperator": "Operatorius",
+ "positionCommand": "Komanda",
+ "positionBlocked": "Užblokuotas",
+ "positionDtcs": "DTCs",
+ "positionObdSpeed": "OBD greitis",
+ "positionObdOdometer": "OBD odometras",
+ "positionDrivingTime": "Vairavimo laikas",
+ "positionDriverUniqueId": "Vairuotojo unikalus ID",
+ "positionCard": "Kortelė",
+ "positionImage": "Atvaizdas",
+ "positionVideo": "Vaizdo įrašas",
+ "positionAudio": "Garso įrašas",
+ "serverTitle": "Serverio nustatymai",
+ "serverZoom": "Priartinimas",
+ "serverRegistration": "Registracija",
+ "serverReadonly": "Skaitymo",
+ "serverForceSettings": "Priverstiniai nustatymai",
+ "serverAnnouncement": "Pranešimai",
+ "serverName": "Serverio pavadinimas",
+ "serverDescription": "Serverio aprašymas",
+ "serverColorPrimary": "Pirminė spalva",
+ "serverColorSecondary": "Papildoma spalva",
+ "serverLogo": "Logo vaizdas",
+ "serverLogoInverted": "Invertuotas logo vaizdas",
+ "serverChangeDisable": "Uždrausti keisti serverį",
+ "serverDisableShare": "Išjungti dalinimasi įrenginiais",
+ "mapTitle": "Žemėlapis",
+ "mapActive": "Aktyvūs žemėlapiai",
+ "mapOverlay": "Žemėlapių sluoksniai",
+ "mapOverlayCustom": "Pasirinkti sluoksnį",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API raktas",
+ "mapOpenWeatherClouds": "OpenWeather debesuotumas",
+ "mapOpenWeatherPrecipitation": "OpenWeather krituliai",
+ "mapOpenWeatherPressure": "OpenWeather slėgis",
+ "mapOpenWeatherWind": "OpenWeather vėjas",
+ "mapOpenWeatherTemperature": "OpenWeather temperatūra",
+ "mapLayer": "Žemėlapio sluoksnis",
+ "mapCustom": "Pasirinkti (XYZ)",
+ "mapCustomArcgis": "Pasirinkti (ArcGIS)",
+ "mapCustomLabel": "Pasirinkti žemėlapį",
+ "mapCarto": "Carto žemėlapiai",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google keliai",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Bing Maps raktas",
+ "mapBingRoad": "Bing Maps Keliai",
+ "mapBingAerial": "Bing Maps Palydovas",
+ "mapBingHybrid": "Bing Maps Mišrus",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Yandex žemėlapis",
+ "mapYandexSat": "Yandex Palydovinis",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox gatvės",
+ "mapMapboxStreetsDark": "Tamsus Mapbox Streets",
+ "mapMapboxOutdoors": "Mapbox Outdoors",
+ "mapMapboxSatellite": "Mapbox Palydovinis",
+ "mapMapboxKey": "Mapbox Access Token",
+ "mapMapTilerBasic": "MapTiler Basic",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "MapTiler API raktas",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ Access Token",
+ "mapTomTomBasic": "TomTom Basic",
+ "mapTomTomFlow": "TomTom Traffic Flow",
+ "mapTomTomIncidents": "TomTom eismo įvykiai",
+ "mapTomTomKey": "TomTom API raktas",
+ "mapHereBasic": "Here Basic",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satellite",
+ "mapHereFlow": "Here Traffic Flow",
+ "mapHereKey": "Here API raktas",
+ "mapShapePolygon": "Polygonas",
+ "mapShapeCircle": "Apskritimas",
+ "mapShapePolyline": "Polilinija",
+ "mapLiveRoutes": "Tiesioginiai maršrutai",
+ "mapDirection": "Rodyti kryptį",
+ "mapCurrentLocation": "Esama pozicija",
+ "mapPoiLayer": "Lankytinų vietų sluoksnis",
+ "mapClustering": "Žymų grupavimas",
+ "mapOnSelect": "Rodyti žemėlapį pažymėjus",
+ "mapDefault": "Pagrindinis žemėlapis",
+ "stateTitle": "Būklė",
+ "stateName": "Parametras",
+ "stateValue": "Reikšmė",
+ "commandTitle": "Komanda",
+ "commandSend": "Siųsti",
+ "commandSent": "Komanda nusiųsta",
+ "commandQueued": "Komanda eilėje",
+ "commandUnit": "Vienetas",
+ "commandCustom": "Vartotojo komanda",
+ "commandDeviceIdentification": "Įrenginio identifikacija",
+ "commandPositionSingle": "Vienkartinė ataskaita",
+ "commandPositionPeriodic": "Periodinės ataskaitos",
+ "commandPositionStop": "Stabdyti ataskaitas",
+ "commandEngineStop": "Stabdyti variklį",
+ "commandEngineResume": "Paleisti variklį",
+ "commandAlarmArm": "Uždėti signalą",
+ "commandAlarmDisarm": "Nuimti signalą",
+ "commandAlarmDismiss": "Nutraukti signalą",
+ "commandSetTimezone": "Nustatyti laiko zoną",
+ "commandRequestPhoto": "Gauti nuotrauką",
+ "commandPowerOff": "Išjungti įrenginį",
+ "commandRebootDevice": "Perkrauti įrenginį",
+ "commandFactoryReset": "Gamykliniai nustatymai",
+ "commandSendSms": "Siųsti SMS",
+ "commandSendUssd": "Siųsti USSD",
+ "commandSosNumber": "Nustatyti SOS numerį",
+ "commandSilenceTime": "Nustatyti tylos laiką",
+ "commandSetPhonebook": "Nustatyti adresatų knygą",
+ "commandVoiceMessage": "Balso žinutė",
+ "commandOutputControl": "Įšvesties kontrolė",
+ "commandVoiceMonitoring": "Balso stebėjimas",
+ "commandSetAgps": "Nustatyti A-GPS",
+ "commandSetIndicator": "Nustatyti Indikatorių",
+ "commandConfiguration": "Konfigūracija",
+ "commandGetVersion": "Gauti versiją",
+ "commandFirmwareUpdate": "Atnaujinti programinę įrangą",
+ "commandSetConnection": "Nustatyti prisijungimą",
+ "commandSetOdometer": "Nustatyti odometrą",
+ "commandGetModemStatus": "Gauti modemo būseną",
+ "commandGetDeviceStatus": "Gauti įrenginio būseną",
+ "commandSetSpeedLimit": "Nustatyti greičio limitą",
+ "commandModePowerSaving": "Energijos taupymo režimas",
+ "commandModeDeepSleep": "\"Užmigdyti\" įrenginį",
+ "commandAlarmGeofence": "Nustatyti georibos signalą",
+ "commandAlarmBattery": "Nustatyti baterijos signalą",
+ "commandAlarmSos": "Nustatyti SOS signalą",
+ "commandAlarmRemove": "Nustatyti pašalinimo signalą",
+ "commandAlarmClock": "Nustatyti laiko signalą",
+ "commandAlarmSpeed": "Nustatyti greičio signalą",
+ "commandAlarmFall": "Nustatyti kritimo signalą",
+ "commandAlarmVibration": "Nustatyti vibravimo signalą",
+ "commandFrequency": "Dažnis",
+ "commandTimezone": "Laiko zonos pataisa",
+ "commandMessage": "Žinutė",
+ "commandRadius": "Spindulys",
+ "commandEnable": "Įgalinti",
+ "commandData": "Duomenys",
+ "commandIndex": "Indeksas",
+ "commandPhone": "Telefono numeris",
+ "commandServer": "Serveris",
+ "commandPort": "Prievadas",
+ "eventAll": "Visi įvykiai",
+ "eventDeviceOnline": "Būsena pasiekiamas",
+ "eventDeviceUnknown": "Būsena nežinoma",
+ "eventDeviceOffline": "Būsena atsijungęs",
+ "eventDeviceInactive": "Įrenginys neaktyvus",
+ "eventQueuedCommandSent": "Išsiųsta komanda eilėje",
+ "eventDeviceMoving": "Įrenginys juda",
+ "eventDeviceStopped": "Įrenginys sustojo",
+ "eventDeviceOverspeed": "Greičio limitas viršytas",
+ "eventDeviceFuelDrop": "Kuras sumažėjo",
+ "eventDeviceFuelIncrease": "Kuras papildytas",
+ "eventCommandResult": "Komandos rezultatas",
+ "eventGeofenceEnter": "Georibų viduje",
+ "eventGeofenceExit": "Už georibų",
+ "eventAlarm": "Signalas",
+ "eventIgnitionOn": "Degimas įjungtas",
+ "eventIgnitionOff": "Degimas išjungtas",
+ "eventMaintenance": "Aptarnavimo poreikis",
+ "eventTextMessage": "Gauta tekstinė žinutė",
+ "eventDriverChanged": "Vairuotojas pasikeitė",
+ "eventMedia": "Medija",
+ "eventsScrollToLast": "Slinkti iki paskutinio",
+ "eventsSoundEvents": "Garso įvykiai",
+ "eventsSoundAlarms": "Garso signalai",
+ "alarmGeneral": "Pagrindinis",
+ "alarmSos": "SOS",
+ "alarmVibration": "Vibracija",
+ "alarmMovement": "Judėjimas",
+ "alarmLowspeed": "Mažas greitis",
+ "alarmOverspeed": "Greičio viršijimas",
+ "alarmFallDown": "Nukrito",
+ "alarmLowPower": "Žema įtampa",
+ "alarmLowBattery": "Mažas baterijos lygis",
+ "alarmFault": "Klaida",
+ "alarmPowerOff": "Išjungtas",
+ "alarmPowerOn": "Įjungtas",
+ "alarmDoor": "Durys",
+ "alarmLock": "Užrakinta",
+ "alarmUnlock": "Atrakinta",
+ "alarmGeofence": "Georiba",
+ "alarmGeofenceEnter": "Georiba įeiti",
+ "alarmGeofenceExit": "Georiba išeiti",
+ "alarmGpsAntennaCut": "GPS antena nutraukta",
+ "alarmAccident": "Įvykis",
+ "alarmTow": "Vilkimas",
+ "alarmIdle": "Tuščia eiga",
+ "alarmHighRpm": "Aukštos apsukos",
+ "alarmHardAcceleration": "Didelis pagreitis",
+ "alarmHardBraking": "Staigus stabdymas",
+ "alarmHardCornering": "Staigus posūkis",
+ "alarmLaneChange": "Juostų keitimas",
+ "alarmFatigueDriving": "Nuovargis",
+ "alarmPowerCut": "Atjungtas maitinimas",
+ "alarmPowerRestored": "Maitinimas atstatytas",
+ "alarmJamming": "Kamštis",
+ "alarmTemperature": "Temperatūra",
+ "alarmParking": "Parkavimas",
+ "alarmBonnet": "Variklio dangtis",
+ "alarmFootBrake": "Pertrauka maistui",
+ "alarmFuelLeak": "Kuro nuotekis",
+ "alarmTampering": "Gadinimas",
+ "alarmRemoving": "Pašalinimas",
+ "notificationType": "Įspėjimo tipas",
+ "notificationAlways": "Visi prietaisai",
+ "notificationNotificators": "Kanalai",
+ "notificatorCommand": "Komanda",
+ "notificatorWeb": "Web",
+ "notificatorMail": "El. paštas",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Pakartoti",
+ "reportCombined": "Sujungta",
+ "reportRoute": "Maršrutas",
+ "reportEvents": "Įvykiai",
+ "reportTrips": "Kelionės",
+ "reportStops": "Sustojimai",
+ "reportSummary": "Suvestinė",
+ "reportDaily": "Dienos suvestinė",
+ "reportChart": "Diagrama",
+ "reportConfigure": "Konfigūruoti",
+ "reportEventTypes": "Įvykių tipai",
+ "reportChartType": "Diagramų tipai",
+ "reportShowMarkers": "Rodyti žymeklius",
+ "reportExport": "Eksportuoti",
+ "reportEmail": "Ataskaitą siųsti el. paštu",
+ "reportSchedule": "Planuoti",
+ "reportPeriod": "Periodas",
+ "reportCustom": "Custom",
+ "reportToday": "Šiandien",
+ "reportYesterday": "Vakar",
+ "reportThisWeek": "Šią savaitę",
+ "reportPreviousWeek": "Praėjusią savaitę",
+ "reportThisMonth": "Šį mėnesį",
+ "reportPreviousMonth": "Praėjusį mėnesį",
+ "reportDeviceName": "Prietaiso vardas",
+ "reportAverageSpeed": "Vidutinis greitis",
+ "reportMaximumSpeed": "Maksimalus greitis",
+ "reportEngineHours": "Variklio valandos",
+ "reportDuration": "Trukmė",
+ "reportStartDate": "Pradžios data",
+ "reportStartTime": "Pradžios laikas",
+ "reportStartAddress": "Pradžios adresas",
+ "reportEndTime": "Pabaigos laikas",
+ "reportEndAddress": "Pabaigos adresas",
+ "reportSpentFuel": "Suvartoto kuro",
+ "reportStartOdometer": "Odometras startas",
+ "reportEndOdometer": "Odometras pabaiga",
+ "statisticsTitle": "Statistika",
+ "statisticsCaptureTime": "Užfiksuotas laikas",
+ "statisticsActiveUsers": "Aktyvus vartotojai",
+ "statisticsActiveDevices": "Aktyvūs prietaisai",
+ "statisticsRequests": "Prašymai",
+ "statisticsMessagesReceived": "Žinutė gauta",
+ "statisticsMessagesStored": "Žinutė išsaugota",
+ "statisticsGeocoder": "Geokoderio užklausos",
+ "statisticsGeolocation": "Geolokacijos užklausos",
+ "categoryArrow": "Rodyklė",
+ "categoryDefault": "Numatytas",
+ "categoryAnimal": "Gyvūnas",
+ "categoryBicycle": "Dviratis",
+ "categoryBoat": "Valtis",
+ "categoryBus": "Autobusas",
+ "categoryCar": "Automobilis",
+ "categoryCamper": "Kemperis",
+ "categoryCrane": "Kranas",
+ "categoryHelicopter": "Sraigtasparnis",
+ "categoryMotorcycle": "Motociklas",
+ "categoryOffroad": "Bekelės transportas",
+ "categoryPerson": "Asmuo",
+ "categoryPickup": "Pikapas",
+ "categoryPlane": "Lėktuvas",
+ "categoryShip": "Laivas",
+ "categoryTractor": "Traktorius",
+ "categoryTrain": "Traukinys",
+ "categoryTram": "Tramvajus",
+ "categoryTrolleybus": "Troleibusas",
+ "categoryTruck": "Vilkikas",
+ "categoryVan": "Autobusiuskas",
+ "categoryScooter": "Mopedas",
+ "maintenanceStart": "Pradėti",
+ "maintenancePeriod": "Periodas"
+} \ No newline at end of file
diff --git a/src/resources/l10n/lv.json b/src/resources/l10n/lv.json
new file mode 100644
index 00000000..5769fe30
--- /dev/null
+++ b/src/resources/l10n/lv.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "Ielādē...",
+ "sharedHide": "Paslēpt",
+ "sharedSave": "Saglabāt",
+ "sharedUpload": "Upload",
+ "sharedSet": "Iestatīt",
+ "sharedCancel": "Atcelt",
+ "sharedCopy": "Copy",
+ "sharedAdd": "Pievienot",
+ "sharedEdit": "Rediģēt",
+ "sharedRemove": "Dzēst",
+ "sharedRemoveConfirm": "Izdzēst?",
+ "sharedNoData": "Nav datu",
+ "sharedSubject": "Subject",
+ "sharedYes": "Jā",
+ "sharedNo": "Nē",
+ "sharedKm": "km",
+ "sharedMi": "jū",
+ "sharedNmi": "nmi",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "kn",
+ "sharedKmh": "km/h",
+ "sharedMph": "mph",
+ "sharedHour": "Stunda",
+ "sharedMinute": "Minūte",
+ "sharedSecond": "Sekunde",
+ "sharedDays": "dienas",
+ "sharedHours": "stundas",
+ "sharedMinutes": "minūtes",
+ "sharedDecimalDegrees": "Decimāldaļas",
+ "sharedDegreesDecimalMinutes": "Decimālgrādu minūtes",
+ "sharedDegreesMinutesSeconds": "Decimālgrādu sekundes",
+ "sharedName": "Vārds",
+ "sharedDescription": "Apraksts",
+ "sharedSearch": "Meklēt",
+ "sharedIconScale": "Icon Scale",
+ "sharedGeofence": "Geolaukums",
+ "sharedGeofences": "Geolaukumi",
+ "sharedCreateGeofence": "Create Geofence",
+ "sharedNotifications": "Paziņojumi",
+ "sharedNotification": "Paziņojums",
+ "sharedAttributes": "Vērtības",
+ "sharedAttribute": "Vērtība",
+ "sharedDrivers": "Vadītāji",
+ "sharedDriver": "Vadītājs",
+ "sharedArea": "Laukums",
+ "sharedSound": "Paziņojuma skaņa",
+ "sharedType": "Tips",
+ "sharedDistance": "Attālums",
+ "sharedHourAbbreviation": "h",
+ "sharedMinuteAbbreviation": "m",
+ "sharedSecondAbbreviation": "s",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "l",
+ "sharedGallonAbbreviation": "gal",
+ "sharedLiter": "Litri",
+ "sharedImpGallon": "Imperiālie galoni",
+ "sharedUsGallon": "ASV galoni",
+ "sharedLiterPerHourAbbreviation": "l/h",
+ "sharedGetMapState": "Saņemt kartes stāvokli",
+ "sharedComputedAttribute": "Aprēķināmās vērtības",
+ "sharedComputedAttributes": "Aprēķinātās vērtības",
+ "sharedCheckComputedAttribute": "Pārbaudīt aprēķinātās vērtības",
+ "sharedExpression": "Izteiksme",
+ "sharedDevice": "Ierīce",
+ "sharedTest": "Pārbaude",
+ "sharedTestNotification": "Nosūtīt testa paziņojumu",
+ "sharedTestNotificators": "Test Channels",
+ "sharedTestExpression": "Test Expression",
+ "sharedCalendar": "Kalendārs",
+ "sharedCalendars": "Kalendāri",
+ "sharedFile": "Fails",
+ "sharedSearchDevices": "Meklēt ierīces",
+ "sharedSortBy": "Kārtot pēc",
+ "sharedFilterMap": "Filter on Map",
+ "sharedSelectFile": "Izvēlieties failu",
+ "sharedPhone": "Tālrunis",
+ "sharedRequired": "Obligāts",
+ "sharedPreferences": "Izvēlnes",
+ "sharedPermissions": "Atļaujas",
+ "sharedConnections": "Savienojumi",
+ "sharedExtra": "Ekstra",
+ "sharedPrimary": "Primary",
+ "sharedSecondary": "Secondary",
+ "sharedTypeString": "Teksts",
+ "sharedTypeNumber": "Skaitlis",
+ "sharedTypeBoolean": "Boolean",
+ "sharedTimezone": "Laika zona",
+ "sharedInfoTitle": "Info",
+ "sharedSavedCommand": "Saglabatā komanda",
+ "sharedSavedCommands": "Saglabātās komandas",
+ "sharedNew": "Jauns...",
+ "sharedShowAddress": "Rādīt adresi",
+ "sharedShowDetails": "Vairāk detaļu",
+ "sharedDisabled": "Atspējots",
+ "sharedMaintenance": "Uzturēšana",
+ "sharedDeviceAccumulators": "Akumulatori",
+ "sharedAlarms": "Trauksmes",
+ "sharedLocation": "Atrašanās vieta",
+ "sharedImport": "Importēt",
+ "sharedColumns": "Kolonnas",
+ "sharedDropzoneText": "Drag and drop a file here or click",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "Simple",
+ "calendarRecurrence": "Recurrence",
+ "calendarOnce": "Once",
+ "calendarDaily": "Daily",
+ "calendarWeekly": "Weekly",
+ "calendarMonthly": "Monthly",
+ "calendarDays": "Days",
+ "calendarSunday": "Sunday",
+ "calendarMonday": "Monday",
+ "calendarTuesday": "Tuesday",
+ "calendarWednesday": "Wednesday",
+ "calendarThursday": "Thursday",
+ "calendarFriday": "Friday",
+ "calendarSaturday": "Saturday",
+ "attributeShowGeofences": "Show Geofences",
+ "attributeSpeedLimit": "Ātruma ierobežojums",
+ "attributeFuelDropThreshold": "Fuel Drop Threshold",
+ "attributeFuelIncreaseThreshold": "Fuel Increase Threshold",
+ "attributePolylineDistance": "Maršruta Distance",
+ "attributeReportIgnoreOdometer": "Ziņojums: Ignorēt Odometru",
+ "attributeWebReportColor": "Web: Atskaites krāsa",
+ "attributeDevicePassword": "Ierīces parole",
+ "attributeDeviceImage": "Ierīces attēls",
+ "attributeDeviceInactivityStart": "Ierīces Neaktivitātes Sākšana",
+ "attributeDeviceInactivityPeriod": "Ierīces Neaktivitātes Periods",
+ "attributeProcessingCopyAttributes": "Apstrāde: Kopē Atribūtus",
+ "attributeColor": "Krāsa",
+ "attributeWebLiveRouteLength": "Web: Reāllaika Ceļa Garums",
+ "attributeWebSelectZoom": "Web: Palielināt Iezīmēto",
+ "attributeWebMaxZoom": "Web: Maksimālais zooms",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Pushover User Key",
+ "attributePushoverDeviceNames": "Pushover Device Names",
+ "attributeMailSmtpHost": "E-pasts: SMTP adrese ",
+ "attributeMailSmtpPort": "E-pasts: SMTP Ports",
+ "attributeMailSmtpStarttlsEnable": "E-pasts: Ieslēgt SMTP STARTTLS",
+ "attributeMailSmtpStarttlsRequired": "E-pasts: Nepieciešams SMTP STARTTLS",
+ "attributeMailSmtpSslEnable": "E-pasts: Iespējot SMTP SSL",
+ "attributeMailSmtpSslTrust": "E-pasts: SMTP SSL Uzticams",
+ "attributeMailSmtpSslProtocols": "E-pasts: SMTP SSL Protokoli",
+ "attributeMailSmtpFrom": "E-pats: SMTP Forma",
+ "attributeMailSmtpAuth": "E-pats: SMTP Autentif. Ieslēgta",
+ "attributeMailSmtpUsername": "E-pats: SMTP Lietotājvārds",
+ "attributeMailSmtpPassword": "E-pats: SMTP Parole",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: Disable Attributes",
+ "attributeUiDisableGroups": "UI: Disable Groups",
+ "attributeUiDisableEvents": "UI: Atslēgt notikumus",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "UI: Atslēgt vadītājus",
+ "attributeUiDisableComputedAttributes": "UI: Atslēgt aprēķinātos atribūtus",
+ "attributeUiDisableCalendars": "UI: Atslēgt kalendārus",
+ "attributeUiDisableMaintenance": "UI: Atslegt Apkopi",
+ "attributeUiHidePositionAttributes": "UI: Slēpt pozīcijas atribūtus",
+ "attributeUiDisableLoginLanguage": "UI: Disable Login Language",
+ "attributeNotificationTokens": "Paziņojumu Žetoni",
+ "attributePopupInfo": "Popup Info",
+ "errorTitle": "Kļūda",
+ "errorGeneral": "Kļūdaini parametri vai satur kļūdas",
+ "errorConnection": "Savienojuma kļūda",
+ "errorSocket": "Web socket savienojuma kļūda",
+ "errorZero": "Nevar būt nulle",
+ "userEmail": "E-pasts",
+ "userPassword": "Parole",
+ "userAdmin": "Administrators",
+ "userRemember": "Atcerēties",
+ "userExpirationTime": "Derīguma termiņš",
+ "userDeviceLimit": "Ierīču Limits",
+ "userUserLimit": "Lietotāju Limits",
+ "userDeviceReadonly": "Tikai lasāma ierīce",
+ "userLimitCommands": "Ierobežot komandas",
+ "userDisableReports": "Atslēgt Atskaites",
+ "userFixedEmail": "No Email Change",
+ "userToken": "Zīme",
+ "userDeleteAccount": "Dzēst Kontu",
+ "userTemporary": "Temporary",
+ "loginTitle": "Lietotājvārds",
+ "loginLanguage": "Valoda",
+ "loginReset": "Atiestatīt paroli",
+ "loginRegister": "Reģistrēties",
+ "loginLogin": "Ieiet",
+ "loginOpenId": "Login with OpenID",
+ "loginFailed": "Kļūdains e-pasts vai parole",
+ "loginCreated": "Jauns lietotājs tika veiksmīgi reģistrēts ",
+ "loginResetSuccess": "Pārbaudiet savu e-pastu",
+ "loginUpdateSuccess": "Jaunā parole saglabāta",
+ "loginLogout": "Iziet",
+ "loginLogo": "Logo",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "Ierīces un to statusi",
+ "deviceSelected": "Selected Device",
+ "deviceTitle": "Ierīces",
+ "devicePrimaryInfo": "Device Title",
+ "deviceSecondaryInfo": "Device Detail",
+ "deviceIdentifier": "Identifikators",
+ "deviceModel": "Modelis",
+ "deviceContact": "Kontakti",
+ "deviceCategory": "Kategorija",
+ "deviceLastUpdate": "Pēdējā atjaunošana",
+ "deviceCommand": "Komanda",
+ "deviceFollow": "Sekojiet",
+ "deviceTotalDistance": "Kopējais attālums",
+ "deviceStatus": "Statuss",
+ "deviceStatusOnline": "Tiešsaistē",
+ "deviceStatusOffline": "Bezsaistē",
+ "deviceStatusUnknown": "Nezināms",
+ "deviceRegisterFirst": "Register your first device",
+ "deviceIdentifierHelp": "IMEI, serial number or other id. It has to match the identifier device reports to the server.",
+ "deviceShare": "Share Device",
+ "groupDialog": "Grupa",
+ "groupParent": "Grupa",
+ "groupNoGroup": "Neviena grupa",
+ "settingsTitle": "Iestatījumi",
+ "settingsUser": "Konts",
+ "settingsGroups": "Grupas",
+ "settingsServer": "Server",
+ "settingsUsers": "Lietotāji",
+ "settingsDistanceUnit": "Attāluma mērvienība",
+ "settingsAltitudeUnit": "Augstuma mērvienība",
+ "settingsSpeedUnit": "Ātruma mērvienība",
+ "settingsVolumeUnit": "Tilpuma mērvienība",
+ "settingsTwelveHourFormat": "12-h Formāts",
+ "settingsCoordinateFormat": "Koordinātu formāts",
+ "settingsServerVersion": "Server Version",
+ "settingsAppVersion": "App Version",
+ "settingsConnection": "Connection",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "Atskaites",
+ "reportScheduled": "Scheduled Reports",
+ "reportDevice": "Ierīce",
+ "reportGroup": "Grupa",
+ "reportFrom": "No",
+ "reportTo": "Līdz",
+ "reportShow": "Parādīt",
+ "reportClear": "Notīrīt",
+ "linkGoogleMaps": "Google kartes",
+ "linkAppleMaps": "Apple Maps",
+ "linkStreetView": "Ielas skats",
+ "positionFixTime": "Fix Time",
+ "positionDeviceTime": "Ierīces laiks",
+ "positionServerTime": "Servera Laiks",
+ "positionValid": "Derīgs",
+ "positionAccuracy": "Precizitāte",
+ "positionLatitude": "Platums",
+ "positionLongitude": "Garums",
+ "positionAltitude": "Augstums",
+ "positionSpeed": "Ātrums",
+ "positionCourse": "Kurss",
+ "positionAddress": "Adrese",
+ "positionProtocol": "Protokols",
+ "positionDistance": "Attālums",
+ "positionRpm": "RPM",
+ "positionFuel": "Degviela",
+ "positionPower": "Jauda",
+ "positionBattery": "Baterija",
+ "positionRaw": "Raw",
+ "positionIndex": "Index",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "Satelīti",
+ "positionSatVisible": "Redzamie satelīti",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "Viesabonēšana",
+ "positionEvent": "Notikums",
+ "positionAlarm": "Trauksme",
+ "positionStatus": "Statuss",
+ "positionOdometer": "Odometrs",
+ "positionServiceOdometer": "Servisa nobraukums",
+ "positionTripOdometer": "Nobraukums",
+ "positionHours": "Stundas",
+ "positionSteps": "Soļi",
+ "positionInput": "Ievade",
+ "positionHeartRate": "Heart Rate",
+ "positionOutput": "Izvade",
+ "positionBatteryLevel": "Baterijas līmenis",
+ "positionFuelConsumption": "Degvielas patēriņš",
+ "positionRfid": "RFID",
+ "positionVersionFw": "Programmatūras versija",
+ "positionVersionHw": "Aparatūras versija",
+ "positionIgnition": "Aizdedze",
+ "positionFlags": "Marķieri",
+ "positionCharge": "Lādējas",
+ "positionIp": "IP",
+ "positionArchive": "Arhīvs",
+ "positionVin": "VIN",
+ "positionApproximate": "Aptuvenais",
+ "positionThrottle": "Akselerators",
+ "positionMotion": "Kustība",
+ "positionArmed": "Aizsargāts",
+ "positionAcceleration": "Paātrinājums",
+ "positionTemp": "Temperatūra",
+ "positionDeviceTemp": "Ierīces Temperatūra",
+ "positionCoolantTemp": "Coolant Temperature",
+ "positionOperator": "Operators",
+ "positionCommand": "Komanda",
+ "positionBlocked": "Bloķēts",
+ "positionDtcs": "DTCs",
+ "positionObdSpeed": "OBD Ātrums",
+ "positionObdOdometer": "ODB Nobraukums",
+ "positionDrivingTime": "Driving Time",
+ "positionDriverUniqueId": "Vadītāja unikālais ID",
+ "positionCard": "Card",
+ "positionImage": "Attēls",
+ "positionVideo": "Video",
+ "positionAudio": "Skaņa",
+ "serverTitle": "Servera iestatījumi ",
+ "serverZoom": "Palielinājums",
+ "serverRegistration": "Reģistrācija",
+ "serverReadonly": "Tikai lasāms",
+ "serverForceSettings": "Pāriestatīt Iestatījumus",
+ "serverAnnouncement": "Paziņojums",
+ "serverName": "Server Name",
+ "serverDescription": "Server Description",
+ "serverColorPrimary": "Color Primary",
+ "serverColorSecondary": "Color Secondary",
+ "serverLogo": "Logo Image",
+ "serverLogoInverted": "Inverted Logo Image",
+ "serverChangeDisable": "Disable Server Change",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "Karte",
+ "mapActive": "Active Maps",
+ "mapOverlay": "Map Overlay",
+ "mapOverlayCustom": "Custom Overlay",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API Key",
+ "mapOpenWeatherClouds": "OpenWeather Clouds",
+ "mapOpenWeatherPrecipitation": "OpenWeather Precipitation",
+ "mapOpenWeatherPressure": "OpenWeather Pressure",
+ "mapOpenWeatherWind": "OpenWeather Wind",
+ "mapOpenWeatherTemperature": "OpenWeather Temperature",
+ "mapLayer": "Kartes slānis",
+ "mapCustom": "Personalizēts (XYZ)",
+ "mapCustomArcgis": "Personalizēts (ArcGIS)",
+ "mapCustomLabel": "Personalizēta karte",
+ "mapCarto": "Carto Basemaps",
+ "mapOsm": "OpenStreetKarte",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Bing Karšu Atslēga",
+ "mapBingRoad": "Bing Karšu Ceļi",
+ "mapBingAerial": "Bing Karšu Apgabals",
+ "mapBingHybrid": "Bing Hibrīdkarte",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Yandex Kartes",
+ "mapYandexSat": "Yandex Satelliti",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox Ielas",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox Outdoors",
+ "mapMapboxSatellite": "Mapbox Satelīts",
+ "mapMapboxKey": "Mapbox Access Token",
+ "mapMapTilerBasic": "MapTiler Basic",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "MapTiler API Key",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ Access Token",
+ "mapTomTomBasic": "TomTom Basic",
+ "mapTomTomFlow": "TomTom Traffic Flow",
+ "mapTomTomIncidents": "TomTom Traffic Incidents",
+ "mapTomTomKey": "TomTom API Key",
+ "mapHereBasic": "Here vienkāršā",
+ "mapHereHybrid": "Here hibrīda",
+ "mapHereSatellite": "Here satelītu",
+ "mapHereFlow": "Here Traffic Flow",
+ "mapHereKey": "Here API Key",
+ "mapShapePolygon": "Daudzstūris",
+ "mapShapeCircle": "Aplis",
+ "mapShapePolyline": "Lauzta līnija",
+ "mapLiveRoutes": "Reāllaika Maršruti",
+ "mapDirection": "Show Direction",
+ "mapCurrentLocation": "Pašreizējā atrašanās vieta",
+ "mapPoiLayer": "POI Slānis",
+ "mapClustering": "Markers Clustering",
+ "mapOnSelect": "Show Map on Selection",
+ "mapDefault": "Default Map",
+ "stateTitle": "Stāvoklis",
+ "stateName": "Īpašības",
+ "stateValue": "Vērtība",
+ "commandTitle": "Komanda",
+ "commandSend": "Sūtīt",
+ "commandSent": "Komanda nosūtīta",
+ "commandQueued": "Komanda ierindota",
+ "commandUnit": "Vienība",
+ "commandCustom": "Pielāgota komanda",
+ "commandDeviceIdentification": "Ierīces Identifikācija",
+ "commandPositionSingle": "Vienas Reizes Atskaite",
+ "commandPositionPeriodic": "Periodiska Atskaite",
+ "commandPositionStop": "Apturēt Atskaites",
+ "commandEngineStop": "Motora Slāpēšana",
+ "commandEngineResume": "Motora Startēšana",
+ "commandAlarmArm": "Aktivizēt Trauksmi",
+ "commandAlarmDisarm": "Izslēgt Trauksmi",
+ "commandAlarmDismiss": "Atslēgt Paziņojumu",
+ "commandSetTimezone": "Iestatīt Laika Zonu",
+ "commandRequestPhoto": "Pieprasīt Fotoattēlu",
+ "commandPowerOff": "Izslēgt Ierīci",
+ "commandRebootDevice": "Pārstartēt Ierīci",
+ "commandFactoryReset": "Sākotnējā režīma iestatīšana",
+ "commandSendSms": "Nosūtīt Īsziņu",
+ "commandSendUssd": "Nosūtīt USSD",
+ "commandSosNumber": "Iestatīt SOS numuru",
+ "commandSilenceTime": "Iestatīt Klusuma Laiku",
+ "commandSetPhonebook": "Iestatīt Tālruņa Grāmatu",
+ "commandVoiceMessage": "Balss Ziņojums",
+ "commandOutputControl": "Izejas Kontrole",
+ "commandVoiceMonitoring": "Balss uzraudzība",
+ "commandSetAgps": "Uzstādīt AGPS",
+ "commandSetIndicator": "Iestatīt Indikatoru",
+ "commandConfiguration": "Konfigurācija",
+ "commandGetVersion": "Saņemt versiju",
+ "commandFirmwareUpdate": "Atjaunot programmatūru",
+ "commandSetConnection": "Iestatīt savienojumu",
+ "commandSetOdometer": "Iestatīt odometru",
+ "commandGetModemStatus": "Saņemt modema statusu",
+ "commandGetDeviceStatus": "Saņemt ierīces statusu",
+ "commandSetSpeedLimit": "Iestatīt ātruma limitu",
+ "commandModePowerSaving": "Power Saving Mode",
+ "commandModeDeepSleep": "Deep Sleep Mode",
+ "commandAlarmGeofence": "Set Geofence Alarm",
+ "commandAlarmBattery": "Iestatīt akulumatora brīdinājumu",
+ "commandAlarmSos": "Set SOS Alarm",
+ "commandAlarmRemove": "Set Remove Alarm",
+ "commandAlarmClock": "Set Clock Alarm",
+ "commandAlarmSpeed": "Set Speed Alarm",
+ "commandAlarmFall": "Set Fall Alarm",
+ "commandAlarmVibration": "Set Vibration Alarm",
+ "commandFrequency": "Biežums",
+ "commandTimezone": "Laika Joslas Nobīde",
+ "commandMessage": "Ziņa",
+ "commandRadius": "Rādiuss",
+ "commandEnable": "Iespējot",
+ "commandData": "Datums",
+ "commandIndex": "Indekss",
+ "commandPhone": "Telefona Numurs",
+ "commandServer": "Serveris",
+ "commandPort": "Ports",
+ "eventAll": "Visi Notikumi",
+ "eventDeviceOnline": "Statuss aktīvs",
+ "eventDeviceUnknown": "Statuss nezināms",
+ "eventDeviceOffline": "Statuss bezsaistē",
+ "eventDeviceInactive": "Ierīce neaktīva",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "Ierīce pārvietojas",
+ "eventDeviceStopped": "Ierīce apturēta",
+ "eventDeviceOverspeed": "Ātruma limits pārsniegts",
+ "eventDeviceFuelDrop": "Degvielas kritums",
+ "eventDeviceFuelIncrease": "Fuel increase",
+ "eventCommandResult": "Komandas rezultāts",
+ "eventGeofenceEnter": "Ienāca apgabalā",
+ "eventGeofenceExit": "Pameta apgabalu",
+ "eventAlarm": "Trauksme",
+ "eventIgnitionOn": "Aizdedze Iesl.",
+ "eventIgnitionOff": "Aizdedze Izsl.",
+ "eventMaintenance": "Nepieciešama tehniskā apkope",
+ "eventTextMessage": "Saņemta īsziņa",
+ "eventDriverChanged": "Mainījies vadītājs",
+ "eventMedia": "Media",
+ "eventsScrollToLast": "Ritināt Uz Pēdējo",
+ "eventsSoundEvents": "Sound Events",
+ "eventsSoundAlarms": "Sound Alarms",
+ "alarmGeneral": "Vispārēji",
+ "alarmSos": "SOS",
+ "alarmVibration": "Vibrācija",
+ "alarmMovement": "Kustība",
+ "alarmLowspeed": "Zems Ātrums",
+ "alarmOverspeed": "Ātruma pārsniegšana",
+ "alarmFallDown": "Savienojuma Pārrāvums",
+ "alarmLowPower": "Zema Enerģija",
+ "alarmLowBattery": "Tukša Baterija",
+ "alarmFault": "Kļūda",
+ "alarmPowerOff": "Barošana Izsl.",
+ "alarmPowerOn": "Barošana Iesl.",
+ "alarmDoor": "Durvis",
+ "alarmLock": "Slēgt",
+ "alarmUnlock": "Atslēgt",
+ "alarmGeofence": "Apgabals",
+ "alarmGeofenceEnter": "Ienākšana Apgabalā",
+ "alarmGeofenceExit": "Apgabala pamešana",
+ "alarmGpsAntennaCut": "GPS Antena Atvienota",
+ "alarmAccident": "Negadījums",
+ "alarmTow": "Vilkšana",
+ "alarmIdle": "Tukšgaita",
+ "alarmHighRpm": "Augsti Apgriezieni",
+ "alarmHardAcceleration": "Straujšs paātrinājums",
+ "alarmHardBraking": "Strauja Bremzēšana",
+ "alarmHardCornering": "Strauja Manevrēšana",
+ "alarmLaneChange": "Joslas Maiņa",
+ "alarmFatigueDriving": "Neadekvāta Braukšana",
+ "alarmPowerCut": "Barošana Noslēgta",
+ "alarmPowerRestored": "Barošana Atjaunota",
+ "alarmJamming": "Sastrēgums",
+ "alarmTemperature": "Temperatūra",
+ "alarmParking": "Stāvvieta",
+ "alarmBonnet": "Motora pārsegs",
+ "alarmFootBrake": "Kājas bremze",
+ "alarmFuelLeak": "Degvielas noplūde",
+ "alarmTampering": "Iespaido",
+ "alarmRemoving": "Noņem",
+ "notificationType": "Ziņojuma Tips",
+ "notificationAlways": "Visas ierīces",
+ "notificationNotificators": "Kanāli",
+ "notificatorCommand": "Command",
+ "notificatorWeb": "Web",
+ "notificatorMail": "e-pasts",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Atkārtot",
+ "reportCombined": "Combined",
+ "reportRoute": "Maršruts",
+ "reportEvents": "Notikumi",
+ "reportTrips": "Braucieni",
+ "reportStops": "Pieturas",
+ "reportSummary": "Kopsavilkums",
+ "reportDaily": "Dienas Kopsavilkums",
+ "reportChart": "Grafiks",
+ "reportConfigure": "Konfigurēt",
+ "reportEventTypes": "Notikumu Veidi",
+ "reportChartType": "Grafika Veids",
+ "reportShowMarkers": "Rādīt Marķierus",
+ "reportExport": "Eksportēt",
+ "reportEmail": "e-pasta Ziņojums",
+ "reportSchedule": "Schedule",
+ "reportPeriod": "Periods",
+ "reportCustom": "Pielāgoti",
+ "reportToday": "Šodien",
+ "reportYesterday": "Vakar",
+ "reportThisWeek": "Šonedēļ",
+ "reportPreviousWeek": "Iepriekšējā nedēļa",
+ "reportThisMonth": "Šis mēnesis",
+ "reportPreviousMonth": "Iepriekšējais mēnesis",
+ "reportDeviceName": "Ierīces Nosaukums",
+ "reportAverageSpeed": "Vidējais Ātrums",
+ "reportMaximumSpeed": "Maksimālais Ātrums",
+ "reportEngineHours": "Motorstundas",
+ "reportDuration": "Ilgums",
+ "reportStartDate": "Sākuma Datums",
+ "reportStartTime": "Sākuma Laiks",
+ "reportStartAddress": "Sākuma Adrese",
+ "reportEndTime": "Beigu Laiks",
+ "reportEndAddress": "Beigu Adrese",
+ "reportSpentFuel": "Patērētā Degviela",
+ "reportStartOdometer": "Odometra sākums",
+ "reportEndOdometer": "Odometra beigas",
+ "statisticsTitle": "Statistika",
+ "statisticsCaptureTime": "Uzņemšanas Laiks",
+ "statisticsActiveUsers": "Aktīvie Lietotāji",
+ "statisticsActiveDevices": "Aktīvās Ierīces",
+ "statisticsRequests": "Pieprasījumi",
+ "statisticsMessagesReceived": "Saņemtie Ziņojumi",
+ "statisticsMessagesStored": "Saglabātie Ziņojumi",
+ "statisticsGeocoder": "Geocoder Pieprasījumi",
+ "statisticsGeolocation": "Atrašanās Vietu Pieprasījumi",
+ "categoryArrow": "Bultiņa",
+ "categoryDefault": "Noklusējums",
+ "categoryAnimal": "Dzīvnieks",
+ "categoryBicycle": "Velosipēds",
+ "categoryBoat": "Laiva",
+ "categoryBus": "Autobuss",
+ "categoryCar": "Automašīna",
+ "categoryCamper": "Camper",
+ "categoryCrane": "Celtnis",
+ "categoryHelicopter": "Helikopters",
+ "categoryMotorcycle": "Motocikls",
+ "categoryOffroad": "Bezceļnieks",
+ "categoryPerson": "Persona",
+ "categoryPickup": "Pikaps",
+ "categoryPlane": "Lidmašīna",
+ "categoryShip": "Kuģis",
+ "categoryTractor": "Traktors",
+ "categoryTrain": "Vilciens",
+ "categoryTram": "Tramvajs",
+ "categoryTrolleybus": "Trolejbuss",
+ "categoryTruck": "Smagā mašīna",
+ "categoryVan": "Busiņš",
+ "categoryScooter": "Skrejritenis",
+ "maintenanceStart": "Sākt",
+ "maintenancePeriod": "Periods"
+} \ No newline at end of file
diff --git a/src/resources/l10n/mk.json b/src/resources/l10n/mk.json
new file mode 100644
index 00000000..4aa2119e
--- /dev/null
+++ b/src/resources/l10n/mk.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "Вчитување...",
+ "sharedHide": "Сокриј",
+ "sharedSave": "Зачувај",
+ "sharedUpload": "Прикачи",
+ "sharedSet": "Постави",
+ "sharedCancel": "Откажи",
+ "sharedCopy": "Копирај",
+ "sharedAdd": "Додади",
+ "sharedEdit": "Уреди",
+ "sharedRemove": "Отстрани",
+ "sharedRemoveConfirm": "Дали да се отстрани предметот?",
+ "sharedNoData": "Нема податоци",
+ "sharedSubject": "Тема",
+ "sharedYes": "Да",
+ "sharedNo": "Не",
+ "sharedKm": "km",
+ "sharedMi": "mi",
+ "sharedNmi": "nmi",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "kn",
+ "sharedKmh": "km/h",
+ "sharedMph": "mph",
+ "sharedHour": "Час",
+ "sharedMinute": "Минута",
+ "sharedSecond": "Секунда",
+ "sharedDays": "денови",
+ "sharedHours": "часови",
+ "sharedMinutes": "минути",
+ "sharedDecimalDegrees": "Децимални Степени",
+ "sharedDegreesDecimalMinutes": "Степени Децимални Минути",
+ "sharedDegreesMinutesSeconds": "Степени Минути Секунди",
+ "sharedName": "Име",
+ "sharedDescription": "Опис",
+ "sharedSearch": "Пребарување",
+ "sharedIconScale": "Размер на икони",
+ "sharedGeofence": "Географска зона",
+ "sharedGeofences": "Географски зони",
+ "sharedCreateGeofence": "Креирај Географска зона",
+ "sharedNotifications": "Известувања",
+ "sharedNotification": "Известување",
+ "sharedAttributes": "Атрибути",
+ "sharedAttribute": "Атрибут",
+ "sharedDrivers": "Возачи",
+ "sharedDriver": "Возач",
+ "sharedArea": "Област",
+ "sharedSound": "Звук за известување",
+ "sharedType": "Тип",
+ "sharedDistance": "Растојание",
+ "sharedHourAbbreviation": "ч",
+ "sharedMinuteAbbreviation": "м",
+ "sharedSecondAbbreviation": "с",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "I",
+ "sharedGallonAbbreviation": "gal",
+ "sharedLiter": "Литар",
+ "sharedImpGallon": "Imp. Gallon",
+ "sharedUsGallon": "U.S. Gallon",
+ "sharedLiterPerHourAbbreviation": "л/ч",
+ "sharedGetMapState": "Состојба на Мапа",
+ "sharedComputedAttribute": "Пресметан Атрибут",
+ "sharedComputedAttributes": "Пресметани Атрибути",
+ "sharedCheckComputedAttribute": "Провери го пресметаниот атрибут",
+ "sharedExpression": "Израз",
+ "sharedDevice": "Уред",
+ "sharedTest": "Тест",
+ "sharedTestNotification": "Испрати тест известување",
+ "sharedTestNotificators": "Тест Канали",
+ "sharedTestExpression": "Тестирај Изразување",
+ "sharedCalendar": "Календар",
+ "sharedCalendars": "Календари",
+ "sharedFile": "Фајл",
+ "sharedSearchDevices": "Пребарај Уреди",
+ "sharedSortBy": "Подреди по",
+ "sharedFilterMap": "Филтер на Мапа",
+ "sharedSelectFile": "Избери фајл",
+ "sharedPhone": "Телефон",
+ "sharedRequired": "Задолжително",
+ "sharedPreferences": "Подесувања",
+ "sharedPermissions": "Дозволи",
+ "sharedConnections": "Конекции",
+ "sharedExtra": "Екстра",
+ "sharedPrimary": "Примарен",
+ "sharedSecondary": "Секундарен",
+ "sharedTypeString": "Стринг",
+ "sharedTypeNumber": "Број",
+ "sharedTypeBoolean": "Булеан",
+ "sharedTimezone": "Временска зона",
+ "sharedInfoTitle": "Инфо",
+ "sharedSavedCommand": "Зачувана Команда",
+ "sharedSavedCommands": "Зачувани Команди",
+ "sharedNew": "Нов...",
+ "sharedShowAddress": "Прикажи Адреса",
+ "sharedShowDetails": "Повеќе Детали",
+ "sharedDisabled": "Оневозможено",
+ "sharedMaintenance": "Одржување",
+ "sharedDeviceAccumulators": "Акумулатори",
+ "sharedAlarms": "Аларми",
+ "sharedLocation": "Локација",
+ "sharedImport": "Импортирај",
+ "sharedColumns": "Колони",
+ "sharedDropzoneText": "Повлечи фајл овде или кликни",
+ "sharedLogs": "Логови",
+ "sharedLink": "Линк",
+ "calendarSimple": "Едноставно",
+ "calendarRecurrence": "Повторување",
+ "calendarOnce": "Еднаш",
+ "calendarDaily": "Дневно",
+ "calendarWeekly": "Неделно",
+ "calendarMonthly": "Месечно",
+ "calendarDays": "Денови",
+ "calendarSunday": "Недела",
+ "calendarMonday": "Понеделник",
+ "calendarTuesday": "Вторник",
+ "calendarWednesday": "Среда",
+ "calendarThursday": "Четврток",
+ "calendarFriday": "Петок",
+ "calendarSaturday": "Сабота",
+ "attributeShowGeofences": "Прикажи Географски зони",
+ "attributeSpeedLimit": "Ограничување на брзината",
+ "attributeFuelDropThreshold": "Граница на прелиено гориво",
+ "attributeFuelIncreaseThreshold": "Граница на надополнето гориво",
+ "attributePolylineDistance": "Повеќелиниска оддалеченост",
+ "attributeReportIgnoreOdometer": "Извештај: Игнорирај го одометарот",
+ "attributeWebReportColor": "Веб: Боја на извештај",
+ "attributeDevicePassword": "Лозинка на уредот",
+ "attributeDeviceImage": "Слика на уредот",
+ "attributeDeviceInactivityStart": "Почеток на неактивност на уредот",
+ "attributeDeviceInactivityPeriod": "Период на неактивност на уредот",
+ "attributeProcessingCopyAttributes": "Процесирање: Копирање на атрибути",
+ "attributeColor": "Боја",
+ "attributeWebLiveRouteLength": "Веб: Должина на рута",
+ "attributeWebSelectZoom": "Веб: Зумирај го селектираното",
+ "attributeWebMaxZoom": "Веб: Максимално зумирање",
+ "attributeTelegramChatId": "Телеграм ID",
+ "attributePushoverUserKey": "Прoследи кориснички клуч",
+ "attributePushoverDeviceNames": "Проследи ги имињата на уредите",
+ "attributeMailSmtpHost": "Меил: SMTP хост",
+ "attributeMailSmtpPort": "Меил: SMTP порт",
+ "attributeMailSmtpStarttlsEnable": "Меил: Овозможи SMTP STARTTLS",
+ "attributeMailSmtpStarttlsRequired": "Меил: SMTP STARTTLS е потребен",
+ "attributeMailSmtpSslEnable": "Меил: Овозможи SMTP SSL",
+ "attributeMailSmtpSslTrust": "Меил: SMTP SSL Trust",
+ "attributeMailSmtpSslProtocols": "Меил: SMTP SSL протоколи",
+ "attributeMailSmtpFrom": "Меил: SMTP Испраќач",
+ "attributeMailSmtpAuth": "Меил: Овозможи SMTP автентикација",
+ "attributeMailSmtpUsername": "Меил: SMTP корисничко име",
+ "attributeMailSmtpPassword": "Меил: SMTP лозинка",
+ "attributeUiDisableSavedCommands": "Исклучи зачувани команди",
+ "attributeUiDisableAttributes": "UI: Оневозможи атрибути",
+ "attributeUiDisableGroups": "UI: Оневозможи групи",
+ "attributeUiDisableEvents": "UI: Оневозможи настани",
+ "attributeUiDisableVehicleFeatures": "UI: Оневозможи карактеристики на возила",
+ "attributeUiDisableDrivers": "UI: Оневозможи возачи",
+ "attributeUiDisableComputedAttributes": "UI: Оневозможи пресметани атрибути",
+ "attributeUiDisableCalendars": "UI: Оневозможи календари",
+ "attributeUiDisableMaintenance": "UI: Оневозможи одржување",
+ "attributeUiHidePositionAttributes": "UI: Сокриј атрибути на позиција",
+ "attributeUiDisableLoginLanguage": "UI: Оневозможи промена на јазик при најава",
+ "attributeNotificationTokens": "Токени за известување",
+ "attributePopupInfo": "Popup инфо",
+ "errorTitle": "Грешка",
+ "errorGeneral": "Невалидни параметри или кршење на ограничувањето",
+ "errorConnection": "Грешка во комуникација",
+ "errorSocket": "Web socket грешка во комуникација",
+ "errorZero": "Не може да биди нула",
+ "userEmail": "e-mail",
+ "userPassword": "Лозинка",
+ "userAdmin": "Администратор",
+ "userRemember": "Запамти",
+ "userExpirationTime": "Истекува на",
+ "userDeviceLimit": "Максимален број на уреди",
+ "userUserLimit": "Максимален број на корисници",
+ "userDeviceReadonly": "Уредот е Readonly",
+ "userLimitCommands": "Ограничи команди",
+ "userDisableReports": "Оневозможи извештаи",
+ "userFixedEmail": "Забрани промена на e-mail",
+ "userToken": "Токен",
+ "userDeleteAccount": "Избриши Акаунт",
+ "userTemporary": "Привремено",
+ "loginTitle": "Најава",
+ "loginLanguage": "Јазик",
+ "loginReset": "Ресетирај лозинка",
+ "loginRegister": "Регистрација",
+ "loginLogin": "Најава",
+ "loginOpenId": "Најава со OpenID",
+ "loginFailed": "невалидна e-mail адреса или лозинка",
+ "loginCreated": "Нов корисник е регистриран",
+ "loginResetSuccess": "Проверете ја вашата e-mail адреса",
+ "loginUpdateSuccess": "Новата лозинка е поставена",
+ "loginLogout": "Одјава",
+ "loginLogo": "Лого",
+ "loginTotpCode": "Еднократен код за лозинка",
+ "loginTotpKey": "Еднократен клуч за лозинка",
+ "devicesAndState": "Уреди и статус",
+ "deviceSelected": "Избран уред",
+ "deviceTitle": "Уреди",
+ "devicePrimaryInfo": "Назив на уред",
+ "deviceSecondaryInfo": "Детали за уред",
+ "deviceIdentifier": "Идентификатор",
+ "deviceModel": "Модел",
+ "deviceContact": "Контакт",
+ "deviceCategory": "Категорија",
+ "deviceLastUpdate": "Последна промена",
+ "deviceCommand": "Команда",
+ "deviceFollow": "Прати",
+ "deviceTotalDistance": "Вкупно растојание",
+ "deviceStatus": "Статус",
+ "deviceStatusOnline": "Достапен",
+ "deviceStatusOffline": "Недостапен",
+ "deviceStatusUnknown": "Непознато",
+ "deviceRegisterFirst": "Регистрирај го твојот прв уред",
+ "deviceIdentifierHelp": "IMEI, сериски број или друг ID. Треба да биде ист со уредот кој се поврзува на сервер",
+ "deviceShare": "Сподели уред",
+ "groupDialog": "Група",
+ "groupParent": "Група",
+ "groupNoGroup": "Нема група",
+ "settingsTitle": "Подесувања",
+ "settingsUser": "Акаунт",
+ "settingsGroups": "Групи",
+ "settingsServer": "Сервер",
+ "settingsUsers": "Корисници",
+ "settingsDistanceUnit": "Единица за растојание",
+ "settingsAltitudeUnit": "Единица за висина",
+ "settingsSpeedUnit": "Единица за брзина",
+ "settingsVolumeUnit": "Единица за волумен",
+ "settingsTwelveHourFormat": "12-часовен формат",
+ "settingsCoordinateFormat": "Формат за координати",
+ "settingsServerVersion": "Верзија на сервер",
+ "settingsAppVersion": "Верзија на апликација",
+ "settingsConnection": "Конекција",
+ "settingsDarkMode": "Dark мод",
+ "settingsTotpEnable": "Овозможи еднократна лозинка",
+ "settingsTotpForce": "Форсирај еднократна лозинка",
+ "settingsServiceWorkerUpdateInterval": "Интервал на ажурирање за ServiceWorker",
+ "settingsUpdateAvailable": "Достапна е нова верзија",
+ "settingsSupport": "Поддршка",
+ "reportTitle": "Извештаи",
+ "reportScheduled": "Редовни извештаи",
+ "reportDevice": "Уред",
+ "reportGroup": "Група",
+ "reportFrom": "Од",
+ "reportTo": "До",
+ "reportShow": "Прикажи",
+ "reportClear": "Избриши",
+ "linkGoogleMaps": "Google мапи",
+ "linkAppleMaps": "Apple мапи",
+ "linkStreetView": "Уличен поглед мод",
+ "positionFixTime": "Време за фиксирање",
+ "positionDeviceTime": "Време на уредот",
+ "positionServerTime": "Време на сервер",
+ "positionValid": "Валидно",
+ "positionAccuracy": "Прецизност",
+ "positionLatitude": "Географска ширина",
+ "positionLongitude": "Географска должина",
+ "positionAltitude": "Висина",
+ "positionSpeed": "Брзина",
+ "positionCourse": "Правец",
+ "positionAddress": "Адреса",
+ "positionProtocol": "Протокол",
+ "positionDistance": "Растојание",
+ "positionRpm": "Обртаи",
+ "positionFuel": "Гориво",
+ "positionPower": "Напојување",
+ "positionBattery": "Батерија",
+ "positionRaw": "Сирови податоци",
+ "positionIndex": "Index",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "Сателити",
+ "positionSatVisible": "Видливи сателити",
+ "positionRssi": "RSSI",
+ "positionGps": "ГПС",
+ "positionRoaming": "Роаминг",
+ "positionEvent": "Настан",
+ "positionAlarm": "Аларм",
+ "positionStatus": "Статус",
+ "positionOdometer": "Одометар",
+ "positionServiceOdometer": "Сервисен одометар",
+ "positionTripOdometer": "Патен одометар",
+ "positionHours": "Часови",
+ "positionSteps": "Чекори",
+ "positionInput": "Влез",
+ "positionHeartRate": "Пулс",
+ "positionOutput": "Излез",
+ "positionBatteryLevel": "Ниво на батерија",
+ "positionFuelConsumption": "Потрошувачка на гориво",
+ "positionRfid": "RFID",
+ "positionVersionFw": "Верзија на фирмвер",
+ "positionVersionHw": "Верзија на хардвер",
+ "positionIgnition": "Контакт-IGN",
+ "positionFlags": "Ознаки",
+ "positionCharge": "Полнење",
+ "positionIp": "IP",
+ "positionArchive": "Архива",
+ "positionVin": "Бр. шасија",
+ "positionApproximate": "Приближно",
+ "positionThrottle": "Гас",
+ "positionMotion": "Движење",
+ "positionArmed": "Активен",
+ "positionAcceleration": "Забрзување",
+ "positionTemp": "Температура",
+ "positionDeviceTemp": "Температура на уред",
+ "positionCoolantTemp": "Температура на разладен елемент",
+ "positionOperator": "Оператор",
+ "positionCommand": "Команда",
+ "positionBlocked": "Блокирано",
+ "positionDtcs": "DTCs",
+ "positionObdSpeed": "OBD Брзина",
+ "positionObdOdometer": "OBD одометар",
+ "positionDrivingTime": "Време на возење",
+ "positionDriverUniqueId": "ID на возач",
+ "positionCard": "Картичка",
+ "positionImage": "Слика",
+ "positionVideo": "Видео",
+ "positionAudio": "Аудио",
+ "serverTitle": "Подесувања за сервер",
+ "serverZoom": "Зумирање",
+ "serverRegistration": "Регистрација",
+ "serverReadonly": "Само преглед",
+ "serverForceSettings": "Форсирани поставки",
+ "serverAnnouncement": "Објава",
+ "serverName": "Име на сервер",
+ "serverDescription": "Опис на сервер",
+ "serverColorPrimary": "Примарна боја",
+ "serverColorSecondary": "Секундарна боја",
+ "serverLogo": "Слика за лого",
+ "serverLogoInverted": "Инверзна слика за лого",
+ "serverChangeDisable": "Оневозможи промена на сервер",
+ "serverDisableShare": "Оневозможи споделување на уреди",
+ "mapTitle": "Мапа",
+ "mapActive": "Активни мапи",
+ "mapOverlay": "Преклопени мапи",
+ "mapOverlayCustom": "Прилагодено преклопување",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API клуч",
+ "mapOpenWeatherClouds": "OpenWeather облаци",
+ "mapOpenWeatherPrecipitation": "OpenWeather врнежи",
+ "mapOpenWeatherPressure": "OpenWeather атм. притисок",
+ "mapOpenWeatherWind": "OpenWeather Ветер",
+ "mapOpenWeatherTemperature": "OpenWeather Температура",
+ "mapLayer": "Тип на мапа",
+ "mapCustom": "Прилагодено (XYZ)",
+ "mapCustomArcgis": "Прилагодено (ArcGIS)",
+ "mapCustomLabel": "Прилагодена мапа",
+ "mapCarto": "Carto Basemaps",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google патишта",
+ "mapGoogleHybrid": "Google хибрид",
+ "mapGoogleSatellite": "Google сателити",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Bing Maps клуч",
+ "mapBingRoad": "Bing Maps патишта",
+ "mapBingAerial": "Bing Maps воздушна",
+ "mapBingHybrid": "Bing Maps хибрид",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Yandex Map",
+ "mapYandexSat": "Yandex сателит",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox Streets",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox Outdoors",
+ "mapMapboxSatellite": "Mapbox сателит",
+ "mapMapboxKey": "Mapbox Токен за пристап",
+ "mapMapTilerBasic": "MapTiler основна",
+ "mapMapTilerHybrid": "MapTiler хибрид",
+ "mapMapTilerKey": "MapTiler API клуч",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ токен за пристап",
+ "mapTomTomBasic": "TomTom основна",
+ "mapTomTomFlow": "TomTom сообраќај",
+ "mapTomTomIncidents": "TomTom инциденти во сообраќај",
+ "mapTomTomKey": "TomTom API клуч",
+ "mapHereBasic": "Here основна",
+ "mapHereHybrid": "Here хибрид",
+ "mapHereSatellite": "Here сателит",
+ "mapHereFlow": "Here сообраќај",
+ "mapHereKey": "Here API клуч",
+ "mapShapePolygon": "Полигон",
+ "mapShapeCircle": "Круг",
+ "mapShapePolyline": "Полилинија",
+ "mapLiveRoutes": "Рути во живо",
+ "mapDirection": "Покажи насоки",
+ "mapCurrentLocation": "Моментална локација",
+ "mapPoiLayer": "Слој за точки од интерес",
+ "mapClustering": "Организација на маркери",
+ "mapOnSelect": "Прикажи мапа при селекција",
+ "mapDefault": "Предефинирана мапа",
+ "stateTitle": "Состојба",
+ "stateName": "Атрибут",
+ "stateValue": "Вредност",
+ "commandTitle": "Команда",
+ "commandSend": "Испрати",
+ "commandSent": "Командата е испратена",
+ "commandQueued": "Командата е на чекање",
+ "commandUnit": "Единица",
+ "commandCustom": "Прилагодена команда",
+ "commandDeviceIdentification": "Идентификација на уред",
+ "commandPositionSingle": "Единечно известување",
+ "commandPositionPeriodic": "Периодично известување",
+ "commandPositionStop": "Исклучи известување",
+ "commandEngineStop": "Изгаснат мотор",
+ "commandEngineResume": "Мотор во работа",
+ "commandAlarmArm": "Активирај аларм",
+ "commandAlarmDisarm": "Деактивирај аларм",
+ "commandAlarmDismiss": "Откажи аларм",
+ "commandSetTimezone": "Постави временска зона",
+ "commandRequestPhoto": "Побарај фотографија",
+ "commandPowerOff": "Исклучи уред",
+ "commandRebootDevice": "Рестартирај уред",
+ "commandFactoryReset": "Фабрични подесувања",
+ "commandSendSms": "Испрати СМС",
+ "commandSendUssd": "Испрати USSD",
+ "commandSosNumber": "Постави SOS број",
+ "commandSilenceTime": "Постави тивко време",
+ "commandSetPhonebook": "Постави Контакти",
+ "commandVoiceMessage": "Говорна порака",
+ "commandOutputControl": "Контрола на излез",
+ "commandVoiceMonitoring": "Гласовен мониторинг",
+ "commandSetAgps": "Постави АГПС",
+ "commandSetIndicator": "Постави индикатор",
+ "commandConfiguration": "Конфигурација",
+ "commandGetVersion": "Провери верзија",
+ "commandFirmwareUpdate": "Надградба на фирмвер",
+ "commandSetConnection": "Постави конекција",
+ "commandSetOdometer": "Постави одометар",
+ "commandGetModemStatus": "Провери статус на модем",
+ "commandGetDeviceStatus": "Провери статус на уред",
+ "commandSetSpeedLimit": "Постави ограничување на брзина",
+ "commandModePowerSaving": "Економичен мод",
+ "commandModeDeepSleep": "Deep Sleep мод",
+ "commandAlarmGeofence": "Постави аларм за географска зона",
+ "commandAlarmBattery": "Постави аларм за батерија",
+ "commandAlarmSos": "Постави СОС аларм",
+ "commandAlarmRemove": "Поставки за бришење на аларм",
+ "commandAlarmClock": "Постави временски аларм",
+ "commandAlarmSpeed": "Постави аларм за брзина",
+ "commandAlarmFall": "Постави аларм за пад",
+ "commandAlarmVibration": "Постави аларм за вибрации",
+ "commandFrequency": "Фреквенција",
+ "commandTimezone": "Отстапување од временска зона",
+ "commandMessage": "Порака",
+ "commandRadius": "Радиус",
+ "commandEnable": "Овозможи",
+ "commandData": "Податоци",
+ "commandIndex": "Индекс",
+ "commandPhone": "Телефонски број",
+ "commandServer": "Сервер",
+ "commandPort": "Порт",
+ "eventAll": "Сите настани",
+ "eventDeviceOnline": "Статус вклучен",
+ "eventDeviceUnknown": "Статус напознат",
+ "eventDeviceOffline": "Статус исклучен",
+ "eventDeviceInactive": "Уредот е неактивен",
+ "eventQueuedCommandSent": "Испратена команда на чекање",
+ "eventDeviceMoving": "Уредот е во движење",
+ "eventDeviceStopped": "Уредот е запрен",
+ "eventDeviceOverspeed": "Брзината е над дозволената",
+ "eventDeviceFuelDrop": "Пад на ниво на гориво",
+ "eventDeviceFuelIncrease": "Дотур на гориво",
+ "eventCommandResult": "Статус од команда",
+ "eventGeofenceEnter": "Влез во географска зона",
+ "eventGeofenceExit": "Излез од географска зона",
+ "eventAlarm": "Аларм",
+ "eventIgnitionOn": "Вклучен контакт-IGN",
+ "eventIgnitionOff": "Исклучен контакт-IGN",
+ "eventMaintenance": "Потребно е да се изврши одржување",
+ "eventTextMessage": "Примена е текст порака",
+ "eventDriverChanged": "Променет возач",
+ "eventMedia": "Media",
+ "eventsScrollToLast": "Прелистај до последна",
+ "eventsSoundEvents": "Звучни настани",
+ "eventsSoundAlarms": "звучни аларми",
+ "alarmGeneral": "Општо",
+ "alarmSos": "СОС",
+ "alarmVibration": "Вибрација",
+ "alarmMovement": "Движење",
+ "alarmLowspeed": "Мала брзина",
+ "alarmOverspeed": "Прекорачување на брзината",
+ "alarmFallDown": "Пад",
+ "alarmLowPower": "Слаб напон",
+ "alarmLowBattery": "Ниско ниво на батерија",
+ "alarmFault": "дефект, грешка",
+ "alarmPowerOff": "Исклучи",
+ "alarmPowerOn": "Уклучи",
+ "alarmDoor": "Врата",
+ "alarmLock": "Заклучи",
+ "alarmUnlock": "Отклучи",
+ "alarmGeofence": "Географска зона",
+ "alarmGeofenceEnter": "Влез во географска зона",
+ "alarmGeofenceExit": "Излез од географска зона",
+ "alarmGpsAntennaCut": "ГПС антената е исклучена",
+ "alarmAccident": "Несреќа",
+ "alarmTow": "Влечи",
+ "alarmIdle": "Неутрална брзина-лер",
+ "alarmHighRpm": "Високи обртаи",
+ "alarmHardAcceleration": "Нагло забрзување",
+ "alarmHardBraking": "Нагло кочење",
+ "alarmHardCornering": "Нагло свртување",
+ "alarmLaneChange": "Промена на траса",
+ "alarmFatigueDriving": "Возење со замор",
+ "alarmPowerCut": "Прекин на напојување",
+ "alarmPowerRestored": "Напојувањето е вратено во функција",
+ "alarmJamming": "Блокирање",
+ "alarmTemperature": "Температура",
+ "alarmParking": "Паркирање",
+ "alarmBonnet": "Хауба",
+ "alarmFootBrake": "Кочница",
+ "alarmFuelLeak": "Истекување на гориво",
+ "alarmTampering": "Манипулирање",
+ "alarmRemoving": "Отстранување",
+ "notificationType": "Тип на известување",
+ "notificationAlways": "Сите уреди",
+ "notificationNotificators": "Канали",
+ "notificatorCommand": "Команда",
+ "notificatorWeb": "Веб",
+ "notificatorMail": "е-mail",
+ "notificatorSms": "СМС",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Телеграм",
+ "notificatorPushover": "Проследи",
+ "reportReplay": "Репродукција",
+ "reportCombined": "Комбинирано",
+ "reportRoute": "Рута",
+ "reportEvents": "Настани",
+ "reportTrips": "Патувања",
+ "reportStops": "Застанувања",
+ "reportSummary": "Преглед",
+ "reportDaily": "Дневен преглед",
+ "reportChart": "Графикон",
+ "reportConfigure": "Конфигурирај",
+ "reportEventTypes": "Типови на настани",
+ "reportChartType": "Тип на графикон",
+ "reportShowMarkers": "Покажи маркери",
+ "reportExport": "Извези",
+ "reportEmail": "Извештај по е-mail",
+ "reportSchedule": "Распоред",
+ "reportPeriod": "Период",
+ "reportCustom": "Прилагодено",
+ "reportToday": "Денес",
+ "reportYesterday": "Вчера",
+ "reportThisWeek": "Тековна седмица",
+ "reportPreviousWeek": "Претходна седмица",
+ "reportThisMonth": "Тековен месец",
+ "reportPreviousMonth": "Претходен месец",
+ "reportDeviceName": "Име на уред",
+ "reportAverageSpeed": "Просечна брзина",
+ "reportMaximumSpeed": "Максимална брзина",
+ "reportEngineHours": "Работни саати",
+ "reportDuration": "Времетраење",
+ "reportStartDate": "Почетен датум",
+ "reportStartTime": "Почетно време",
+ "reportStartAddress": "Почетна адреса",
+ "reportEndTime": "Крајно време",
+ "reportEndAddress": "Крајна адреса",
+ "reportSpentFuel": "Потрошено гориво",
+ "reportStartOdometer": "Почетна километража",
+ "reportEndOdometer": "Крајна километража",
+ "statisticsTitle": "Статистики",
+ "statisticsCaptureTime": "Време на сликање",
+ "statisticsActiveUsers": "Активни корисници",
+ "statisticsActiveDevices": "Активни уреди",
+ "statisticsRequests": "Барања",
+ "statisticsMessagesReceived": "Примени пораки",
+ "statisticsMessagesStored": "Сочувани пораки",
+ "statisticsGeocoder": "барања од геокодер",
+ "statisticsGeolocation": "барања од геолокација",
+ "categoryArrow": "Стрелка",
+ "categoryDefault": "Основно",
+ "categoryAnimal": "Животно",
+ "categoryBicycle": "Велосипед",
+ "categoryBoat": "Чамец",
+ "categoryBus": "Автобус",
+ "categoryCar": "Автомобил",
+ "categoryCamper": "Кампер",
+ "categoryCrane": "Кран",
+ "categoryHelicopter": "Хеликоптер",
+ "categoryMotorcycle": "Моторцикл",
+ "categoryOffroad": "Теренец",
+ "categoryPerson": "Личност",
+ "categoryPickup": "Пикап",
+ "categoryPlane": "Авион",
+ "categoryShip": "Брод",
+ "categoryTractor": "Трактор",
+ "categoryTrain": "Воз",
+ "categoryTram": "Трамвај",
+ "categoryTrolleybus": "Тролејбус",
+ "categoryTruck": "Камион",
+ "categoryVan": "Комбе",
+ "categoryScooter": "Скутер",
+ "maintenanceStart": "Почеток",
+ "maintenancePeriod": "Период"
+} \ No newline at end of file
diff --git a/src/resources/l10n/ml.json b/src/resources/l10n/ml.json
new file mode 100644
index 00000000..40dde8f2
--- /dev/null
+++ b/src/resources/l10n/ml.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "ലോഡുചെയ്യുന്നു ..",
+ "sharedHide": "മറയ്ക്കുക",
+ "sharedSave": "സേവ്",
+ "sharedUpload": "Upload",
+ "sharedSet": "സെറ്റ്",
+ "sharedCancel": "റദ്ദാക്കുക",
+ "sharedCopy": "Copy",
+ "sharedAdd": "ചേര്‍ക്കുക",
+ "sharedEdit": "തിരുത്തുക",
+ "sharedRemove": "നീക്കം ചെയ്യുക",
+ "sharedRemoveConfirm": "ഐറ്റം നീക്കം ചെയ്യട്ടെ ?",
+ "sharedNoData": "No data",
+ "sharedSubject": "Subject",
+ "sharedYes": "Yes",
+ "sharedNo": "No",
+ "sharedKm": "കി.മീ.",
+ "sharedMi": "മൈൽ",
+ "sharedNmi": "നൗട്ടിക്കൽ മൈൽ",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "കനോട്സ്",
+ "sharedKmh": "കിലോമീറ്റർ /മണിക്കൂർ",
+ "sharedMph": "മൈൽസ് / മണിക്കൂർ",
+ "sharedHour": "മണിക്കൂർ",
+ "sharedMinute": "മിനിറ്റ്",
+ "sharedSecond": "സെക്കന്റ്",
+ "sharedDays": "ദിവസങ്ങൾ ",
+ "sharedHours": "മണിക്കൂറുകൾ",
+ "sharedMinutes": "മിനിറ്റ് ",
+ "sharedDecimalDegrees": "ദശാംശ ഡിഗ്രി",
+ "sharedDegreesDecimalMinutes": "ഡിഗ്രി ദശാംശ മിനിറ്റ് ",
+ "sharedDegreesMinutesSeconds": "ഡിഗ്രി മിനിറ്റ് സെക്കൻഡ് ",
+ "sharedName": "പേര്",
+ "sharedDescription": "വിവരണം",
+ "sharedSearch": "സെർച്ച് ",
+ "sharedIconScale": "Icon Scale",
+ "sharedGeofence": "ജിയോഫെൻസ് ",
+ "sharedGeofences": "ജിയോഫെൻസെസ് ",
+ "sharedCreateGeofence": "Create Geofence",
+ "sharedNotifications": "അറിയിപ്പുകൾ",
+ "sharedNotification": "Notification",
+ "sharedAttributes": "അട്രീബൂട്ട്സ് ",
+ "sharedAttribute": "അട്രീബൂട്ട്",
+ "sharedDrivers": "Drivers",
+ "sharedDriver": "Driver",
+ "sharedArea": "ഏരിയ",
+ "sharedSound": "അറിയിപ്പ് ശബ്ദം",
+ "sharedType": "ടൈപ്പ്",
+ "sharedDistance": "ദൂരം ",
+ "sharedHourAbbreviation": "മണിക്കൂർ ",
+ "sharedMinuteAbbreviation": "മിനിറ്റ് ",
+ "sharedSecondAbbreviation": "s",
+ "sharedVoltAbbreviation": "വോൾട്ട് ",
+ "sharedLiterAbbreviation": "ലിറ്റർ ",
+ "sharedGallonAbbreviation": "gal",
+ "sharedLiter": "Liter",
+ "sharedImpGallon": "Imp. Gallon",
+ "sharedUsGallon": "U.S. Gallon",
+ "sharedLiterPerHourAbbreviation": "ലിറ്റർ / മണിക്കൂർ ",
+ "sharedGetMapState": "മാപ് സ്റ്റേറ്റ് ലഭ്യമാക്കുക ",
+ "sharedComputedAttribute": "കമ്പ്യൂട്ടേഡ്‌ അട്രീബൂട്ട് ",
+ "sharedComputedAttributes": "കമ്പ്യൂട്ടേഡ്‌ അട്രിബൂട്ടുകൾ ",
+ "sharedCheckComputedAttribute": "കമ്പ്യൂട്ടേഡ്‌ അട്രീബൂട്ട് പരിശോധിക്കുക ",
+ "sharedExpression": "എക്സ്പ്രെഷൻ ",
+ "sharedDevice": "വാഹനം",
+ "sharedTest": "Test",
+ "sharedTestNotification": "ടെസ്റ്റ് നോട്ടിഫിക്കേഷൻ അയക്കുക ",
+ "sharedTestNotificators": "Test Channels",
+ "sharedTestExpression": "Test Expression",
+ "sharedCalendar": "കലണ്ടർ ",
+ "sharedCalendars": "കലണ്ടറുകൾ ",
+ "sharedFile": "ഫയൽ ",
+ "sharedSearchDevices": "Search Devices",
+ "sharedSortBy": "Sort By",
+ "sharedFilterMap": "Filter on Map",
+ "sharedSelectFile": "ഫയൽ തിരഞ്ഞെടുക്കുക",
+ "sharedPhone": "ഫോൺ",
+ "sharedRequired": "അനിവാര്യം",
+ "sharedPreferences": "ഇഷ്ടങ്ങൾ ",
+ "sharedPermissions": "പെർമിഷനുകൾ",
+ "sharedConnections": "Connections",
+ "sharedExtra": "എക്സ്ട്രാ ",
+ "sharedPrimary": "Primary",
+ "sharedSecondary": "Secondary",
+ "sharedTypeString": "സ്ട്രിംഗ്",
+ "sharedTypeNumber": "നമ്പർ ",
+ "sharedTypeBoolean": "ബൂളിയാൻ ",
+ "sharedTimezone": "ടൈം സോൺ ",
+ "sharedInfoTitle": "ഇൻഫോ ",
+ "sharedSavedCommand": "Saved Command",
+ "sharedSavedCommands": "Saved Commands",
+ "sharedNew": "New…",
+ "sharedShowAddress": "Show Address",
+ "sharedShowDetails": "More Details",
+ "sharedDisabled": "Disabled",
+ "sharedMaintenance": "Maintenance",
+ "sharedDeviceAccumulators": "Accumulators",
+ "sharedAlarms": "Alarms",
+ "sharedLocation": "Location",
+ "sharedImport": "Import",
+ "sharedColumns": "Columns",
+ "sharedDropzoneText": "Drag and drop a file here or click",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "Simple",
+ "calendarRecurrence": "Recurrence",
+ "calendarOnce": "Once",
+ "calendarDaily": "Daily",
+ "calendarWeekly": "Weekly",
+ "calendarMonthly": "Monthly",
+ "calendarDays": "Days",
+ "calendarSunday": "Sunday",
+ "calendarMonday": "Monday",
+ "calendarTuesday": "Tuesday",
+ "calendarWednesday": "Wednesday",
+ "calendarThursday": "Thursday",
+ "calendarFriday": "Friday",
+ "calendarSaturday": "Saturday",
+ "attributeShowGeofences": "Show Geofences",
+ "attributeSpeedLimit": "സ്പീഡ് ലിമിറ്റ്‌ ",
+ "attributeFuelDropThreshold": "Fuel Drop Threshold",
+ "attributeFuelIncreaseThreshold": "Fuel Increase Threshold",
+ "attributePolylineDistance": "Polyline Distance",
+ "attributeReportIgnoreOdometer": "Report: Ignore Odometer",
+ "attributeWebReportColor": "Web: Report Color",
+ "attributeDevicePassword": "Device Password",
+ "attributeDeviceImage": "Device Image",
+ "attributeDeviceInactivityStart": "Device Inactivity Start",
+ "attributeDeviceInactivityPeriod": "Device Inactivity Period",
+ "attributeProcessingCopyAttributes": "Processing: Copy Attributes",
+ "attributeColor": "Color",
+ "attributeWebLiveRouteLength": "Web: Live Route Length",
+ "attributeWebSelectZoom": "Web: Zoom On Select",
+ "attributeWebMaxZoom": "Web: Maximum Zoom",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Pushover User Key",
+ "attributePushoverDeviceNames": "Pushover Device Names",
+ "attributeMailSmtpHost": "Mail: SMTP Host",
+ "attributeMailSmtpPort": "Mail: SMTP Port",
+ "attributeMailSmtpStarttlsEnable": "Mail: SMTP STARTTLS Enable",
+ "attributeMailSmtpStarttlsRequired": "Mail: SMTP STARTTLS Required",
+ "attributeMailSmtpSslEnable": "Mail: SMTP SSL Enable",
+ "attributeMailSmtpSslTrust": "Mail: SMTP SSL Trust",
+ "attributeMailSmtpSslProtocols": "Mail: SMTP SSL Protocols",
+ "attributeMailSmtpFrom": "Mail: SMTP From",
+ "attributeMailSmtpAuth": "Mail: SMTP Auth Enable",
+ "attributeMailSmtpUsername": "Mail: SMTP Username",
+ "attributeMailSmtpPassword": "Mail: SMTP Password",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: Disable Attributes",
+ "attributeUiDisableGroups": "UI: Disable Groups",
+ "attributeUiDisableEvents": "UI: Disable Events",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "UI: Disable Drivers",
+ "attributeUiDisableComputedAttributes": "UI: Disable Computed Attributes",
+ "attributeUiDisableCalendars": "UI: Disable Calendars",
+ "attributeUiDisableMaintenance": "UI: Disable Maintenance",
+ "attributeUiHidePositionAttributes": "UI: Hide Position Attributes",
+ "attributeUiDisableLoginLanguage": "UI: Disable Login Language",
+ "attributeNotificationTokens": "Notification Tokens",
+ "attributePopupInfo": "Popup Info",
+ "errorTitle": "എറർ !",
+ "errorGeneral": "പാരാമീറ്റർ എറർ !",
+ "errorConnection": "കണക്ഷൻ എറർ !",
+ "errorSocket": "വെബ് സോക്കറ്റ് കണക്ഷൻ എറർ !",
+ "errorZero": "Can't be zero",
+ "userEmail": "ഇമെയിൽ",
+ "userPassword": "പാസ്സ്‌വേർഡ് ",
+ "userAdmin": "അഡ്മിൻ ",
+ "userRemember": "ഓർത്തിരിക്കുക ",
+ "userExpirationTime": "Expiration",
+ "userDeviceLimit": "Device Limit",
+ "userUserLimit": "User Limit",
+ "userDeviceReadonly": "Device Readonly",
+ "userLimitCommands": "Limit Commands",
+ "userDisableReports": "Disable Reports",
+ "userFixedEmail": "No Email Change",
+ "userToken": "Token",
+ "userDeleteAccount": "Delete Account",
+ "userTemporary": "Temporary",
+ "loginTitle": "ലോഗിൻ ചെയ്യുക ",
+ "loginLanguage": "ഭാഷ",
+ "loginReset": "Reset Password",
+ "loginRegister": "രെജിസ്റ്റർ ",
+ "loginLogin": "അകത്തു പ്രവേശിക്കുക",
+ "loginOpenId": "Login with OpenID",
+ "loginFailed": "തെറ്റായ ഇമെയിൽ വിലാസവും പാസ്വേഡും",
+ "loginCreated": "പുതിയ ഉപയോക്താവ് രജിസ്റ്റർ ചെയ്തു",
+ "loginResetSuccess": "Check your email",
+ "loginUpdateSuccess": "New password is set",
+ "loginLogout": "പുറത്തുകടക്കുക",
+ "loginLogo": "ലോഗോ",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "സാധനങ്ങളിന് നില ",
+ "deviceSelected": "Selected Device",
+ "deviceTitle": "സാധനങ്ങളിന് ",
+ "devicePrimaryInfo": "Device Title",
+ "deviceSecondaryInfo": "Device Detail",
+ "deviceIdentifier": "ഐഡന്റിഫയർ",
+ "deviceModel": "Model",
+ "deviceContact": "Contact",
+ "deviceCategory": "Category",
+ "deviceLastUpdate": "Last Update",
+ "deviceCommand": "Command",
+ "deviceFollow": "Follow",
+ "deviceTotalDistance": "Total Distance",
+ "deviceStatus": "Status",
+ "deviceStatusOnline": "Online",
+ "deviceStatusOffline": "Offline",
+ "deviceStatusUnknown": "Unknown",
+ "deviceRegisterFirst": "Register your first device",
+ "deviceIdentifierHelp": "IMEI, serial number or other id. It has to match the identifier device reports to the server.",
+ "deviceShare": "Share Device",
+ "groupDialog": "ഗ്രൂപ്പ്",
+ "groupParent": "Group",
+ "groupNoGroup": "No Group",
+ "settingsTitle": "Settings",
+ "settingsUser": "Account",
+ "settingsGroups": "Groups",
+ "settingsServer": "Server",
+ "settingsUsers": "Users",
+ "settingsDistanceUnit": "Distance Unit",
+ "settingsAltitudeUnit": "Altitude Unit",
+ "settingsSpeedUnit": "Speed Unit",
+ "settingsVolumeUnit": "Volume Unit",
+ "settingsTwelveHourFormat": "12-hour Format",
+ "settingsCoordinateFormat": "Coordinates Format",
+ "settingsServerVersion": "Server Version",
+ "settingsAppVersion": "App Version",
+ "settingsConnection": "Connection",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "Reports",
+ "reportScheduled": "Scheduled Reports",
+ "reportDevice": "ഉപകരണം",
+ "reportGroup": "Group",
+ "reportFrom": "From",
+ "reportTo": "To",
+ "reportShow": "Show",
+ "reportClear": "Clear",
+ "linkGoogleMaps": "Google Maps",
+ "linkAppleMaps": "Apple Maps",
+ "linkStreetView": "Street View",
+ "positionFixTime": "Fix Time",
+ "positionDeviceTime": "Device Time",
+ "positionServerTime": "Server Time",
+ "positionValid": "Valid",
+ "positionAccuracy": "Accuracy",
+ "positionLatitude": "അക്ഷാംശം",
+ "positionLongitude": "രേഖാംശം",
+ "positionAltitude": "Altitude",
+ "positionSpeed": "വേഗം",
+ "positionCourse": "Course",
+ "positionAddress": "Address",
+ "positionProtocol": "Protocol",
+ "positionDistance": "Distance",
+ "positionRpm": "RPM",
+ "positionFuel": "Fuel",
+ "positionPower": "Power",
+ "positionBattery": "Battery",
+ "positionRaw": "Raw",
+ "positionIndex": "Index",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "Satellites",
+ "positionSatVisible": "Visible Satellites",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "Roaming",
+ "positionEvent": "Event",
+ "positionAlarm": "Alarm",
+ "positionStatus": "Status",
+ "positionOdometer": "Odometer",
+ "positionServiceOdometer": "Service Odometer",
+ "positionTripOdometer": "Trip Odometer",
+ "positionHours": "Hours",
+ "positionSteps": "Steps",
+ "positionInput": "Input",
+ "positionHeartRate": "Heart Rate",
+ "positionOutput": "Output",
+ "positionBatteryLevel": "Battery Level",
+ "positionFuelConsumption": "Fuel Consumption",
+ "positionRfid": "RFID",
+ "positionVersionFw": "Firmware Version",
+ "positionVersionHw": "Hardware Version",
+ "positionIgnition": "Ignition",
+ "positionFlags": "Flags",
+ "positionCharge": "Charge",
+ "positionIp": "IP",
+ "positionArchive": "Archive",
+ "positionVin": "VIN",
+ "positionApproximate": "Approximate",
+ "positionThrottle": "Throttle",
+ "positionMotion": "Motion",
+ "positionArmed": "Armed",
+ "positionAcceleration": "Acceleration",
+ "positionTemp": "Temperature",
+ "positionDeviceTemp": "Device Temperature",
+ "positionCoolantTemp": "Coolant Temperature",
+ "positionOperator": "Operator",
+ "positionCommand": "Command",
+ "positionBlocked": "Blocked",
+ "positionDtcs": "DTCs",
+ "positionObdSpeed": "OBD Speed",
+ "positionObdOdometer": "OBD Odometer",
+ "positionDrivingTime": "Driving Time",
+ "positionDriverUniqueId": "Driver Unique Id",
+ "positionCard": "Card",
+ "positionImage": "Image",
+ "positionVideo": "Video",
+ "positionAudio": "Audio",
+ "serverTitle": "Server Settings",
+ "serverZoom": "വലുതാക്കിയോ ചെറുതാക്കിയോ കാണിക്കുക",
+ "serverRegistration": "രജിസ്ട്രേഷൻ",
+ "serverReadonly": "Readonly",
+ "serverForceSettings": "Force Settings",
+ "serverAnnouncement": "Announcement",
+ "serverName": "Server Name",
+ "serverDescription": "Server Description",
+ "serverColorPrimary": "Color Primary",
+ "serverColorSecondary": "Color Secondary",
+ "serverLogo": "Logo Image",
+ "serverLogoInverted": "Inverted Logo Image",
+ "serverChangeDisable": "Disable Server Change",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "ഭൂപടം",
+ "mapActive": "Active Maps",
+ "mapOverlay": "Map Overlay",
+ "mapOverlayCustom": "Custom Overlay",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API Key",
+ "mapOpenWeatherClouds": "OpenWeather Clouds",
+ "mapOpenWeatherPrecipitation": "OpenWeather Precipitation",
+ "mapOpenWeatherPressure": "OpenWeather Pressure",
+ "mapOpenWeatherWind": "OpenWeather Wind",
+ "mapOpenWeatherTemperature": "OpenWeather Temperature",
+ "mapLayer": "Map Layer",
+ "mapCustom": "Custom (XYZ)",
+ "mapCustomArcgis": "Custom (ArcGIS)",
+ "mapCustomLabel": "Custom map",
+ "mapCarto": "Carto Basemaps",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Bing Maps Key",
+ "mapBingRoad": "Bing Maps Road",
+ "mapBingAerial": "Bing Maps Aerial",
+ "mapBingHybrid": "Bing Maps Hybrid",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Yandex Map",
+ "mapYandexSat": "Yandex Satellite",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox Streets",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox Outdoors",
+ "mapMapboxSatellite": "Mapbox Satellite",
+ "mapMapboxKey": "Mapbox Access Token",
+ "mapMapTilerBasic": "MapTiler Basic",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "MapTiler API Key",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ Access Token",
+ "mapTomTomBasic": "TomTom Basic",
+ "mapTomTomFlow": "TomTom Traffic Flow",
+ "mapTomTomIncidents": "TomTom Traffic Incidents",
+ "mapTomTomKey": "TomTom API Key",
+ "mapHereBasic": "Here Basic",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satellite",
+ "mapHereFlow": "Here Traffic Flow",
+ "mapHereKey": "Here API Key",
+ "mapShapePolygon": "Polygon",
+ "mapShapeCircle": "Circle",
+ "mapShapePolyline": "Polyline",
+ "mapLiveRoutes": "Live Routes",
+ "mapDirection": "Show Direction",
+ "mapCurrentLocation": "Current Location",
+ "mapPoiLayer": "POI Layer",
+ "mapClustering": "Markers Clustering",
+ "mapOnSelect": "Show Map on Selection",
+ "mapDefault": "Default Map",
+ "stateTitle": "State",
+ "stateName": "Attribute",
+ "stateValue": "Value",
+ "commandTitle": "Command",
+ "commandSend": "Send",
+ "commandSent": "Command sent",
+ "commandQueued": "Command queued",
+ "commandUnit": "Unit",
+ "commandCustom": "Custom command",
+ "commandDeviceIdentification": "Device Identification",
+ "commandPositionSingle": "Single Reporting",
+ "commandPositionPeriodic": "Periodic Reporting",
+ "commandPositionStop": "Stop Reporting",
+ "commandEngineStop": "Engine Stop",
+ "commandEngineResume": "Engine Resume",
+ "commandAlarmArm": "Arm Alarm",
+ "commandAlarmDisarm": "Disarm Alarm",
+ "commandAlarmDismiss": "Dismiss Alarm",
+ "commandSetTimezone": "Set Timezone",
+ "commandRequestPhoto": "Request Photo",
+ "commandPowerOff": "Power Off Device",
+ "commandRebootDevice": "Reboot Device",
+ "commandFactoryReset": "Factory Reset",
+ "commandSendSms": "Send SMS",
+ "commandSendUssd": "Send USSD",
+ "commandSosNumber": "Set SOS Number",
+ "commandSilenceTime": "Set Silence Time",
+ "commandSetPhonebook": "Set Phonebook",
+ "commandVoiceMessage": "Voice Message",
+ "commandOutputControl": "Output Control",
+ "commandVoiceMonitoring": "Voice Monitoring",
+ "commandSetAgps": "Set AGPS",
+ "commandSetIndicator": "Set Indicator",
+ "commandConfiguration": "Configuration",
+ "commandGetVersion": "Get Version",
+ "commandFirmwareUpdate": "Update Firmware",
+ "commandSetConnection": "Set Connection",
+ "commandSetOdometer": "Set Odometer",
+ "commandGetModemStatus": "Get Modem Status",
+ "commandGetDeviceStatus": "Get Device Status",
+ "commandSetSpeedLimit": "Set Speed Limit",
+ "commandModePowerSaving": "Power Saving Mode",
+ "commandModeDeepSleep": "Deep Sleep Mode",
+ "commandAlarmGeofence": "Set Geofence Alarm",
+ "commandAlarmBattery": "Set Battery Alarm",
+ "commandAlarmSos": "Set SOS Alarm",
+ "commandAlarmRemove": "Set Remove Alarm",
+ "commandAlarmClock": "Set Clock Alarm",
+ "commandAlarmSpeed": "Set Speed Alarm",
+ "commandAlarmFall": "Set Fall Alarm",
+ "commandAlarmVibration": "Set Vibration Alarm",
+ "commandFrequency": "Frequency",
+ "commandTimezone": "Timezone Offset",
+ "commandMessage": "Message",
+ "commandRadius": "Radius",
+ "commandEnable": "Enable",
+ "commandData": "Data",
+ "commandIndex": "Index",
+ "commandPhone": "Phone Number",
+ "commandServer": "Server",
+ "commandPort": "Port",
+ "eventAll": "All Events",
+ "eventDeviceOnline": "Status online",
+ "eventDeviceUnknown": "Status unknown",
+ "eventDeviceOffline": "Status offline",
+ "eventDeviceInactive": "Device inactive",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "Device moving",
+ "eventDeviceStopped": "Device stopped",
+ "eventDeviceOverspeed": "Speed limit exceeded",
+ "eventDeviceFuelDrop": "Fuel drop",
+ "eventDeviceFuelIncrease": "Fuel increase",
+ "eventCommandResult": "Command result",
+ "eventGeofenceEnter": "Geofence entered",
+ "eventGeofenceExit": "Geofence exited",
+ "eventAlarm": "Alarm",
+ "eventIgnitionOn": "Ignition on",
+ "eventIgnitionOff": "Ignition off",
+ "eventMaintenance": "Maintenance required",
+ "eventTextMessage": "Text message received",
+ "eventDriverChanged": "Driver changed",
+ "eventMedia": "Media",
+ "eventsScrollToLast": "Scroll To Last",
+ "eventsSoundEvents": "Sound Events",
+ "eventsSoundAlarms": "Sound Alarms",
+ "alarmGeneral": "General",
+ "alarmSos": "SOS",
+ "alarmVibration": "Vibration",
+ "alarmMovement": "Movement",
+ "alarmLowspeed": "Low Speed",
+ "alarmOverspeed": "Overspeed",
+ "alarmFallDown": "Fall Down",
+ "alarmLowPower": "Low Power",
+ "alarmLowBattery": "Low Battery",
+ "alarmFault": "Fault",
+ "alarmPowerOff": "Power Off",
+ "alarmPowerOn": "Power On",
+ "alarmDoor": "Door",
+ "alarmLock": "Lock",
+ "alarmUnlock": "Unlock",
+ "alarmGeofence": "Geofence",
+ "alarmGeofenceEnter": "Geofence Enter",
+ "alarmGeofenceExit": "Geofence Exit",
+ "alarmGpsAntennaCut": "GPS Antenna Cut",
+ "alarmAccident": "Accident",
+ "alarmTow": "Tow",
+ "alarmIdle": "Idle",
+ "alarmHighRpm": "High RPM",
+ "alarmHardAcceleration": "Hard Acceleration",
+ "alarmHardBraking": "Hard Braking",
+ "alarmHardCornering": "Hard Cornering",
+ "alarmLaneChange": "Lane Change",
+ "alarmFatigueDriving": "Fatigue Driving",
+ "alarmPowerCut": "Power Cut",
+ "alarmPowerRestored": "Power Restored",
+ "alarmJamming": "Jamming",
+ "alarmTemperature": "Temperature",
+ "alarmParking": "Parking",
+ "alarmBonnet": "Bonnet",
+ "alarmFootBrake": "Foot Brake",
+ "alarmFuelLeak": "Fuel Leak",
+ "alarmTampering": "Tampering",
+ "alarmRemoving": "Removing",
+ "notificationType": "Type of Notification",
+ "notificationAlways": "All Devices",
+ "notificationNotificators": "Channels",
+ "notificatorCommand": "Command",
+ "notificatorWeb": "Web",
+ "notificatorMail": "Mail",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Replay",
+ "reportCombined": "Combined",
+ "reportRoute": "Route",
+ "reportEvents": "Events",
+ "reportTrips": "Trips",
+ "reportStops": "Stops",
+ "reportSummary": "Summary",
+ "reportDaily": "Daily Summary",
+ "reportChart": "Chart",
+ "reportConfigure": "Configure",
+ "reportEventTypes": "Event Types",
+ "reportChartType": "Chart Type",
+ "reportShowMarkers": "Show Markers",
+ "reportExport": "Export",
+ "reportEmail": "Email Report",
+ "reportSchedule": "Schedule",
+ "reportPeriod": "Period",
+ "reportCustom": "Custom",
+ "reportToday": "Today",
+ "reportYesterday": "Yesterday",
+ "reportThisWeek": "This Week",
+ "reportPreviousWeek": "Previous Week",
+ "reportThisMonth": "This Month",
+ "reportPreviousMonth": "Previous Month",
+ "reportDeviceName": "Device Name",
+ "reportAverageSpeed": "Average Speed",
+ "reportMaximumSpeed": "Maximum Speed",
+ "reportEngineHours": "Engine Hours",
+ "reportDuration": "Duration",
+ "reportStartDate": "Start Date",
+ "reportStartTime": "Start Time",
+ "reportStartAddress": "Start Address",
+ "reportEndTime": "End Time",
+ "reportEndAddress": "End Address",
+ "reportSpentFuel": "Spent Fuel",
+ "reportStartOdometer": "Odometer Start",
+ "reportEndOdometer": "Odometer End",
+ "statisticsTitle": "Statistics",
+ "statisticsCaptureTime": "Capture Time",
+ "statisticsActiveUsers": "Active Users",
+ "statisticsActiveDevices": "Active Devices",
+ "statisticsRequests": "Requests",
+ "statisticsMessagesReceived": "Messages Received",
+ "statisticsMessagesStored": "Messages Stored",
+ "statisticsGeocoder": "Geocoder Requests",
+ "statisticsGeolocation": "Geolocation Requests",
+ "categoryArrow": "Arrow",
+ "categoryDefault": "Default",
+ "categoryAnimal": "Animal",
+ "categoryBicycle": "Bicycle",
+ "categoryBoat": "Boat",
+ "categoryBus": "Bus",
+ "categoryCar": "Car",
+ "categoryCamper": "Camper",
+ "categoryCrane": "Crane",
+ "categoryHelicopter": "Helicopter",
+ "categoryMotorcycle": "Motorcycle",
+ "categoryOffroad": "Offroad",
+ "categoryPerson": "Person",
+ "categoryPickup": "Pickup",
+ "categoryPlane": "Plane",
+ "categoryShip": "Ship",
+ "categoryTractor": "Tractor",
+ "categoryTrain": "Train",
+ "categoryTram": "Tram",
+ "categoryTrolleybus": "Trolleybus",
+ "categoryTruck": "Truck",
+ "categoryVan": "Van",
+ "categoryScooter": "Scooter",
+ "maintenanceStart": "Start",
+ "maintenancePeriod": "Period"
+} \ No newline at end of file
diff --git a/src/resources/l10n/mn.json b/src/resources/l10n/mn.json
new file mode 100644
index 00000000..ea3ecedb
--- /dev/null
+++ b/src/resources/l10n/mn.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "Ачааллаж байна...",
+ "sharedHide": "Нуух",
+ "sharedSave": "Хадгалах",
+ "sharedUpload": "Upload",
+ "sharedSet": "Тааруулах",
+ "sharedCancel": "Цуцлах",
+ "sharedCopy": "Copy",
+ "sharedAdd": "Нэмэх",
+ "sharedEdit": "Засах",
+ "sharedRemove": "Хасах",
+ "sharedRemoveConfirm": "Үүнийг устгах уу?",
+ "sharedNoData": "No data",
+ "sharedSubject": "Subject",
+ "sharedYes": "Тийм",
+ "sharedNo": "Үгүй",
+ "sharedKm": "км",
+ "sharedMi": "миль",
+ "sharedNmi": "nmi",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "Зангилаа",
+ "sharedKmh": "км/ц",
+ "sharedMph": "миль/цаг",
+ "sharedHour": "Цаг",
+ "sharedMinute": "Минут",
+ "sharedSecond": "Секунд",
+ "sharedDays": "өдөр",
+ "sharedHours": "цаг",
+ "sharedMinutes": "минут",
+ "sharedDecimalDegrees": "Градус 10-тын бутархай",
+ "sharedDegreesDecimalMinutes": "Градус Минут нь 10-тын бутархай",
+ "sharedDegreesMinutesSeconds": "Градус Минут Секунд",
+ "sharedName": "Нэр",
+ "sharedDescription": "Тайлбар",
+ "sharedSearch": "Хайх",
+ "sharedIconScale": "Icon Scale",
+ "sharedGeofence": "Хязгаарлалт",
+ "sharedGeofences": "Хязгаарлалтууд",
+ "sharedCreateGeofence": "Create Geofence",
+ "sharedNotifications": "Мэдэгдэлүүд",
+ "sharedNotification": "Мэдэгдэл",
+ "sharedAttributes": "Утгууд",
+ "sharedAttribute": "Утга",
+ "sharedDrivers": "Жолооч нар",
+ "sharedDriver": "Жолооч",
+ "sharedArea": "Талбай",
+ "sharedSound": "Дуут дохио",
+ "sharedType": "Төрөл",
+ "sharedDistance": "Зай",
+ "sharedHourAbbreviation": "цаг",
+ "sharedMinuteAbbreviation": "мин",
+ "sharedSecondAbbreviation": "сек",
+ "sharedVoltAbbreviation": "Вольт",
+ "sharedLiterAbbreviation": "л",
+ "sharedGallonAbbreviation": "галлон",
+ "sharedLiter": "литр",
+ "sharedImpGallon": "Англи галлон",
+ "sharedUsGallon": "Америк галлон",
+ "sharedLiterPerHourAbbreviation": "литр/цаг",
+ "sharedGetMapState": "Зургийн нөхцөл хуулбарлах",
+ "sharedComputedAttribute": "Тооцолсон утга",
+ "sharedComputedAttributes": "Тооцолсон утганууд",
+ "sharedCheckComputedAttribute": "Тооцолсон утга шалгах",
+ "sharedExpression": "Илэрхийлэл",
+ "sharedDevice": "Төхөөрөмж",
+ "sharedTest": "Test",
+ "sharedTestNotification": "Туршилтын мэдэгдэл илгээх",
+ "sharedTestNotificators": "Test Channels",
+ "sharedTestExpression": "Test Expression",
+ "sharedCalendar": "Календар",
+ "sharedCalendars": "Календарууд",
+ "sharedFile": "Файл",
+ "sharedSearchDevices": "Search Devices",
+ "sharedSortBy": "Sort By",
+ "sharedFilterMap": "Filter on Map",
+ "sharedSelectFile": "Файл сонгох",
+ "sharedPhone": "Утас",
+ "sharedRequired": "Шаардлагатай",
+ "sharedPreferences": "Тохиргоо",
+ "sharedPermissions": "Зөвшөөрөл",
+ "sharedConnections": "Холболт",
+ "sharedExtra": "Нэмэлт",
+ "sharedPrimary": "Primary",
+ "sharedSecondary": "Secondary",
+ "sharedTypeString": "Бутархай утга",
+ "sharedTypeNumber": "Тоон утга",
+ "sharedTypeBoolean": "Логик утга",
+ "sharedTimezone": "Цагийн бүс",
+ "sharedInfoTitle": "Мэдээлэл",
+ "sharedSavedCommand": "Хадгалсан комманд",
+ "sharedSavedCommands": "Хадгалсан коммандууд",
+ "sharedNew": "Шинэ...",
+ "sharedShowAddress": "Хаягийг харуулах",
+ "sharedShowDetails": "Дэлгэрэнгүй мэдээлэл",
+ "sharedDisabled": "Хүчингүй",
+ "sharedMaintenance": "Засвар үйлчилгээ",
+ "sharedDeviceAccumulators": "Хуримтлагдах утга",
+ "sharedAlarms": "Дохиолол",
+ "sharedLocation": "Байрлал",
+ "sharedImport": "Оруулах",
+ "sharedColumns": "Columns",
+ "sharedDropzoneText": "Drag and drop a file here or click",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "Simple",
+ "calendarRecurrence": "Recurrence",
+ "calendarOnce": "Once",
+ "calendarDaily": "Daily",
+ "calendarWeekly": "Weekly",
+ "calendarMonthly": "Monthly",
+ "calendarDays": "Days",
+ "calendarSunday": "Sunday",
+ "calendarMonday": "Monday",
+ "calendarTuesday": "Tuesday",
+ "calendarWednesday": "Wednesday",
+ "calendarThursday": "Thursday",
+ "calendarFriday": "Friday",
+ "calendarSaturday": "Saturday",
+ "attributeShowGeofences": "Show Geofences",
+ "attributeSpeedLimit": "Хурдны хязгаар",
+ "attributeFuelDropThreshold": "Fuel Drop Threshold",
+ "attributeFuelIncreaseThreshold": "Fuel Increase Threshold",
+ "attributePolylineDistance": "Polyline Distance",
+ "attributeReportIgnoreOdometer": "Тайлан: Хянахын гүйлт тооцоохгүй",
+ "attributeWebReportColor": "Веб: Тайлангийн өнгө",
+ "attributeDevicePassword": "Төхөөрөмжийн нууц үг",
+ "attributeDeviceImage": "Device Image",
+ "attributeDeviceInactivityStart": "Төхөөрөмж Хэзээ унтсан",
+ "attributeDeviceInactivityPeriod": "Төхөөрөмж Унтсан хугацаа",
+ "attributeProcessingCopyAttributes": "Боловсруулж байна: Утга хуулж байна",
+ "attributeColor": "Өнгө",
+ "attributeWebLiveRouteLength": "Web: Live Route Length",
+ "attributeWebSelectZoom": "Web: Zoom On Select",
+ "attributeWebMaxZoom": "Web: Maximum Zoom",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Pushover User Key",
+ "attributePushoverDeviceNames": "Pushover Device Names",
+ "attributeMailSmtpHost": "Mail: SMTP Host",
+ "attributeMailSmtpPort": "Mail: SMTP Port",
+ "attributeMailSmtpStarttlsEnable": "Mail: SMTP STARTTLS Enable",
+ "attributeMailSmtpStarttlsRequired": "Mail: SMTP STARTTLS Required",
+ "attributeMailSmtpSslEnable": "Mail: SMTP SSL Enable",
+ "attributeMailSmtpSslTrust": "Mail: SMTP SSL Trust",
+ "attributeMailSmtpSslProtocols": "Mail: SMTP SSL Protocols",
+ "attributeMailSmtpFrom": "Mail: SMTP From",
+ "attributeMailSmtpAuth": "Mail: SMTP Auth Enable",
+ "attributeMailSmtpUsername": "Mail: SMTP Username",
+ "attributeMailSmtpPassword": "Mail: SMTP Password",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: Disable Attributes",
+ "attributeUiDisableGroups": "UI: Disable Groups",
+ "attributeUiDisableEvents": "UI: Disable Events",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "UI: Disable Drivers",
+ "attributeUiDisableComputedAttributes": "UI: Disable Computed Attributes",
+ "attributeUiDisableCalendars": "UI: Disable Calendars",
+ "attributeUiDisableMaintenance": "UI: Disable Maintenance",
+ "attributeUiHidePositionAttributes": "UI: Hide Position Attributes",
+ "attributeUiDisableLoginLanguage": "UI: Disable Login Language",
+ "attributeNotificationTokens": "Notification Tokens",
+ "attributePopupInfo": "Popup Info",
+ "errorTitle": "Алдаа",
+ "errorGeneral": "Invalid parameters or constraints violation",
+ "errorConnection": "Холболтын алдаа",
+ "errorSocket": "Web socket connection error",
+ "errorZero": "Нойл утга байж болохгүй",
+ "userEmail": "Имэйл",
+ "userPassword": "Нууц үг",
+ "userAdmin": "Админ",
+ "userRemember": "Санах",
+ "userExpirationTime": "Expiration",
+ "userDeviceLimit": "Device Limit",
+ "userUserLimit": "User Limit",
+ "userDeviceReadonly": "Device Readonly",
+ "userLimitCommands": "Limit Commands",
+ "userDisableReports": "Disable Reports",
+ "userFixedEmail": "No Email Change",
+ "userToken": "Token",
+ "userDeleteAccount": "Delete Account",
+ "userTemporary": "Temporary",
+ "loginTitle": "Нэвтрэх",
+ "loginLanguage": "Хэл",
+ "loginReset": "Нууц үг сэргээх",
+ "loginRegister": "Бүртгүүлэх",
+ "loginLogin": "Нэвтрэх",
+ "loginOpenId": "Login with OpenID",
+ "loginFailed": "Имэйл хаяг эсвэл нууц үг буруу байна",
+ "loginCreated": "Шинэ хэрэглэгч бүртгэгдлээ",
+ "loginResetSuccess": "Имэйлээ шалгана уу",
+ "loginUpdateSuccess": "New password is set",
+ "loginLogout": "Системээс гарах",
+ "loginLogo": "Лого",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "Devices and State",
+ "deviceSelected": "Selected Device",
+ "deviceTitle": "Төхөөрөмжүүд",
+ "devicePrimaryInfo": "Device Title",
+ "deviceSecondaryInfo": "Device Detail",
+ "deviceIdentifier": "Identifier",
+ "deviceModel": "Модел",
+ "deviceContact": "Contact",
+ "deviceCategory": "Category",
+ "deviceLastUpdate": "Last Update",
+ "deviceCommand": "Комманд",
+ "deviceFollow": "Follow",
+ "deviceTotalDistance": "Total Distance",
+ "deviceStatus": "Статус",
+ "deviceStatusOnline": "Online",
+ "deviceStatusOffline": "Offline",
+ "deviceStatusUnknown": "Unknown",
+ "deviceRegisterFirst": "Register your first device",
+ "deviceIdentifierHelp": "IMEI, serial number or other id. It has to match the identifier device reports to the server.",
+ "deviceShare": "Share Device",
+ "groupDialog": "Бүлэг",
+ "groupParent": "Бүлэг",
+ "groupNoGroup": "No Group",
+ "settingsTitle": "Тохиргоонууд",
+ "settingsUser": "Account",
+ "settingsGroups": "Groups",
+ "settingsServer": "Сервер",
+ "settingsUsers": "Хэрэглэгчид",
+ "settingsDistanceUnit": "Зайн нэгж",
+ "settingsAltitudeUnit": "Altitude Unit",
+ "settingsSpeedUnit": "Хурдны нэгж",
+ "settingsVolumeUnit": "Эзэлхүүний нэгж",
+ "settingsTwelveHourFormat": "12-цагийн формат",
+ "settingsCoordinateFormat": "Coordinates Format",
+ "settingsServerVersion": "Server Version",
+ "settingsAppVersion": "App Version",
+ "settingsConnection": "Connection",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "Тайлангууд",
+ "reportScheduled": "Scheduled Reports",
+ "reportDevice": "Төхөөрөмж",
+ "reportGroup": "Group",
+ "reportFrom": "From",
+ "reportTo": "To",
+ "reportShow": "Харуулах",
+ "reportClear": "Clear",
+ "linkGoogleMaps": "Google Maps",
+ "linkAppleMaps": "Apple Maps",
+ "linkStreetView": "Street View",
+ "positionFixTime": "Fix Time",
+ "positionDeviceTime": "Device Time",
+ "positionServerTime": "Server Time",
+ "positionValid": "Valid",
+ "positionAccuracy": "Accuracy",
+ "positionLatitude": "Өргөрөг",
+ "positionLongitude": "Уртраг",
+ "positionAltitude": "Өндөр",
+ "positionSpeed": "Хурд",
+ "positionCourse": "Course",
+ "positionAddress": "Хаяг",
+ "positionProtocol": "Протокол",
+ "positionDistance": "Distance",
+ "positionRpm": "RPM",
+ "positionFuel": "Түлш",
+ "positionPower": "Power",
+ "positionBattery": "Батарей",
+ "positionRaw": "Raw",
+ "positionIndex": "Индекс",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "Satellites",
+ "positionSatVisible": "Visible Satellites",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "Roaming",
+ "positionEvent": "Event",
+ "positionAlarm": "Alarm",
+ "positionStatus": "Статус",
+ "positionOdometer": "Гүйлт",
+ "positionServiceOdometer": "Service Odometer",
+ "positionTripOdometer": "Trip Odometer",
+ "positionHours": "Hours",
+ "positionSteps": "Steps",
+ "positionInput": "Input",
+ "positionHeartRate": "Heart Rate",
+ "positionOutput": "Output",
+ "positionBatteryLevel": "Battery Level",
+ "positionFuelConsumption": "Fuel Consumption",
+ "positionRfid": "RFID",
+ "positionVersionFw": "Firmware Version",
+ "positionVersionHw": "Hardware Version",
+ "positionIgnition": "Ignition",
+ "positionFlags": "Flags",
+ "positionCharge": "Charge",
+ "positionIp": "IP",
+ "positionArchive": "Archive",
+ "positionVin": "VIN",
+ "positionApproximate": "Approximate",
+ "positionThrottle": "Throttle",
+ "positionMotion": "Motion",
+ "positionArmed": "Armed",
+ "positionAcceleration": "Acceleration",
+ "positionTemp": "Temperature",
+ "positionDeviceTemp": "Device Temperature",
+ "positionCoolantTemp": "Coolant Temperature",
+ "positionOperator": "Operator",
+ "positionCommand": "Комманд",
+ "positionBlocked": "Blocked",
+ "positionDtcs": "DTCs",
+ "positionObdSpeed": "OBD Speed",
+ "positionObdOdometer": "OBD Odometer",
+ "positionDrivingTime": "Driving Time",
+ "positionDriverUniqueId": "Driver Unique Id",
+ "positionCard": "Card",
+ "positionImage": "Image",
+ "positionVideo": "Video",
+ "positionAudio": "Audio",
+ "serverTitle": "Серверийн тохиргоо",
+ "serverZoom": "Zoom",
+ "serverRegistration": "Registration",
+ "serverReadonly": "Readonly",
+ "serverForceSettings": "Force Settings",
+ "serverAnnouncement": "Announcement",
+ "serverName": "Server Name",
+ "serverDescription": "Server Description",
+ "serverColorPrimary": "Color Primary",
+ "serverColorSecondary": "Color Secondary",
+ "serverLogo": "Logo Image",
+ "serverLogoInverted": "Inverted Logo Image",
+ "serverChangeDisable": "Disable Server Change",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "Map",
+ "mapActive": "Active Maps",
+ "mapOverlay": "Map Overlay",
+ "mapOverlayCustom": "Custom Overlay",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API Key",
+ "mapOpenWeatherClouds": "OpenWeather Clouds",
+ "mapOpenWeatherPrecipitation": "OpenWeather Precipitation",
+ "mapOpenWeatherPressure": "OpenWeather Pressure",
+ "mapOpenWeatherWind": "OpenWeather Wind",
+ "mapOpenWeatherTemperature": "OpenWeather Temperature",
+ "mapLayer": "Map Layer",
+ "mapCustom": "Custom (XYZ)",
+ "mapCustomArcgis": "Custom (ArcGIS)",
+ "mapCustomLabel": "Custom map",
+ "mapCarto": "Carto Basemaps",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Bing Maps Key",
+ "mapBingRoad": "Bing Maps Road",
+ "mapBingAerial": "Bing Maps Aerial",
+ "mapBingHybrid": "Bing Maps Hybrid",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Yandex Map",
+ "mapYandexSat": "Yandex Satellite",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox Streets",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox Outdoors",
+ "mapMapboxSatellite": "Mapbox Satellite",
+ "mapMapboxKey": "Mapbox Access Token",
+ "mapMapTilerBasic": "MapTiler Basic",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "MapTiler API Key",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ Access Token",
+ "mapTomTomBasic": "TomTom Basic",
+ "mapTomTomFlow": "TomTom Traffic Flow",
+ "mapTomTomIncidents": "TomTom Traffic Incidents",
+ "mapTomTomKey": "TomTom API Key",
+ "mapHereBasic": "Here Basic",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satellite",
+ "mapHereFlow": "Here Traffic Flow",
+ "mapHereKey": "Here API Key",
+ "mapShapePolygon": "Polygon",
+ "mapShapeCircle": "Circle",
+ "mapShapePolyline": "Polyline",
+ "mapLiveRoutes": "Live Routes",
+ "mapDirection": "Show Direction",
+ "mapCurrentLocation": "Current Location",
+ "mapPoiLayer": "POI Layer",
+ "mapClustering": "Markers Clustering",
+ "mapOnSelect": "Show Map on Selection",
+ "mapDefault": "Default Map",
+ "stateTitle": "State",
+ "stateName": "Attribute",
+ "stateValue": "Value",
+ "commandTitle": "Command",
+ "commandSend": "Send",
+ "commandSent": "Command sent",
+ "commandQueued": "Command queued",
+ "commandUnit": "Нэгж",
+ "commandCustom": "Custom command",
+ "commandDeviceIdentification": "Device Identification",
+ "commandPositionSingle": "Single Reporting",
+ "commandPositionPeriodic": "Periodic Reporting",
+ "commandPositionStop": "Stop Reporting",
+ "commandEngineStop": "Engine Stop",
+ "commandEngineResume": "Engine Resume",
+ "commandAlarmArm": "Arm Alarm",
+ "commandAlarmDisarm": "Disarm Alarm",
+ "commandAlarmDismiss": "Dismiss Alarm",
+ "commandSetTimezone": "Set Timezone",
+ "commandRequestPhoto": "Request Photo",
+ "commandPowerOff": "Power Off Device",
+ "commandRebootDevice": "Reboot Device",
+ "commandFactoryReset": "Factory Reset",
+ "commandSendSms": "Send SMS",
+ "commandSendUssd": "Send USSD",
+ "commandSosNumber": "Set SOS Number",
+ "commandSilenceTime": "Set Silence Time",
+ "commandSetPhonebook": "Set Phonebook",
+ "commandVoiceMessage": "Voice Message",
+ "commandOutputControl": "Output Control",
+ "commandVoiceMonitoring": "Voice Monitoring",
+ "commandSetAgps": "Set AGPS",
+ "commandSetIndicator": "Set Indicator",
+ "commandConfiguration": "Configuration",
+ "commandGetVersion": "Get Version",
+ "commandFirmwareUpdate": "Update Firmware",
+ "commandSetConnection": "Set Connection",
+ "commandSetOdometer": "Set Odometer",
+ "commandGetModemStatus": "Get Modem Status",
+ "commandGetDeviceStatus": "Get Device Status",
+ "commandSetSpeedLimit": "Set Speed Limit",
+ "commandModePowerSaving": "Power Saving Mode",
+ "commandModeDeepSleep": "Deep Sleep Mode",
+ "commandAlarmGeofence": "Set Geofence Alarm",
+ "commandAlarmBattery": "Set Battery Alarm",
+ "commandAlarmSos": "Set SOS Alarm",
+ "commandAlarmRemove": "Set Remove Alarm",
+ "commandAlarmClock": "Set Clock Alarm",
+ "commandAlarmSpeed": "Set Speed Alarm",
+ "commandAlarmFall": "Set Fall Alarm",
+ "commandAlarmVibration": "Set Vibration Alarm",
+ "commandFrequency": "Frequency",
+ "commandTimezone": "Timezone Offset",
+ "commandMessage": "Message",
+ "commandRadius": "Radius",
+ "commandEnable": "Enable",
+ "commandData": "Data",
+ "commandIndex": "Index",
+ "commandPhone": "Phone Number",
+ "commandServer": "Server",
+ "commandPort": "Port",
+ "eventAll": "All Events",
+ "eventDeviceOnline": "Status online",
+ "eventDeviceUnknown": "Status unknown",
+ "eventDeviceOffline": "Status offline",
+ "eventDeviceInactive": "Device inactive",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "Device moving",
+ "eventDeviceStopped": "Device stopped",
+ "eventDeviceOverspeed": "Speed limit exceeded",
+ "eventDeviceFuelDrop": "Fuel drop",
+ "eventDeviceFuelIncrease": "Fuel increase",
+ "eventCommandResult": "Command result",
+ "eventGeofenceEnter": "Geofence entered",
+ "eventGeofenceExit": "Geofence exited",
+ "eventAlarm": "Alarm",
+ "eventIgnitionOn": "Ignition on",
+ "eventIgnitionOff": "Ignition off",
+ "eventMaintenance": "Maintenance required",
+ "eventTextMessage": "Text message received",
+ "eventDriverChanged": "Driver changed",
+ "eventMedia": "Media",
+ "eventsScrollToLast": "Scroll To Last",
+ "eventsSoundEvents": "Sound Events",
+ "eventsSoundAlarms": "Sound Alarms",
+ "alarmGeneral": "General",
+ "alarmSos": "SOS",
+ "alarmVibration": "Vibration",
+ "alarmMovement": "Movement",
+ "alarmLowspeed": "Low Speed",
+ "alarmOverspeed": "Overspeed",
+ "alarmFallDown": "Fall Down",
+ "alarmLowPower": "Low Power",
+ "alarmLowBattery": "Low Battery",
+ "alarmFault": "Fault",
+ "alarmPowerOff": "Power Off",
+ "alarmPowerOn": "Power On",
+ "alarmDoor": "Door",
+ "alarmLock": "Lock",
+ "alarmUnlock": "Unlock",
+ "alarmGeofence": "Geofence",
+ "alarmGeofenceEnter": "Geofence Enter",
+ "alarmGeofenceExit": "Geofence Exit",
+ "alarmGpsAntennaCut": "GPS Antenna Cut",
+ "alarmAccident": "Accident",
+ "alarmTow": "Tow",
+ "alarmIdle": "Idle",
+ "alarmHighRpm": "High RPM",
+ "alarmHardAcceleration": "Hard Acceleration",
+ "alarmHardBraking": "Hard Braking",
+ "alarmHardCornering": "Hard Cornering",
+ "alarmLaneChange": "Lane Change",
+ "alarmFatigueDriving": "Fatigue Driving",
+ "alarmPowerCut": "Power Cut",
+ "alarmPowerRestored": "Power Restored",
+ "alarmJamming": "Jamming",
+ "alarmTemperature": "Temperature",
+ "alarmParking": "Parking",
+ "alarmBonnet": "Bonnet",
+ "alarmFootBrake": "Foot Brake",
+ "alarmFuelLeak": "Fuel Leak",
+ "alarmTampering": "Tampering",
+ "alarmRemoving": "Removing",
+ "notificationType": "Type of Notification",
+ "notificationAlways": "All Devices",
+ "notificationNotificators": "Channels",
+ "notificatorCommand": "Command",
+ "notificatorWeb": "Web",
+ "notificatorMail": "Mail",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Replay",
+ "reportCombined": "Combined",
+ "reportRoute": "Route",
+ "reportEvents": "Events",
+ "reportTrips": "Trips",
+ "reportStops": "Stops",
+ "reportSummary": "Summary",
+ "reportDaily": "Daily Summary",
+ "reportChart": "Chart",
+ "reportConfigure": "Configure",
+ "reportEventTypes": "Event Types",
+ "reportChartType": "Chart Type",
+ "reportShowMarkers": "Show Markers",
+ "reportExport": "Export",
+ "reportEmail": "Имэйл тайлан",
+ "reportSchedule": "Schedule",
+ "reportPeriod": "Period",
+ "reportCustom": "Custom",
+ "reportToday": "Өнөөдөр",
+ "reportYesterday": "Өчигдөр",
+ "reportThisWeek": "Энэ долоо хоног",
+ "reportPreviousWeek": "Өмнөх долоо хоног",
+ "reportThisMonth": "Энэ сар",
+ "reportPreviousMonth": "Өмнөх сар",
+ "reportDeviceName": "Төхөөрөмжийн нэр",
+ "reportAverageSpeed": "Дундаж хурд",
+ "reportMaximumSpeed": "Дээд хурд",
+ "reportEngineHours": "Engine Hours",
+ "reportDuration": "Хугацаа",
+ "reportStartDate": "Эхлэх өдөр",
+ "reportStartTime": "Эхлэх цаг",
+ "reportStartAddress": "Start Address",
+ "reportEndTime": "End Time",
+ "reportEndAddress": "End Address",
+ "reportSpentFuel": "Spent Fuel",
+ "reportStartOdometer": "Odometer Start",
+ "reportEndOdometer": "Odometer End",
+ "statisticsTitle": "Статистик",
+ "statisticsCaptureTime": "Capture Time",
+ "statisticsActiveUsers": "Идэвхитэй хэрэглэгчид",
+ "statisticsActiveDevices": "Идэвхитэй төхөөрөмжүүд",
+ "statisticsRequests": "Requests",
+ "statisticsMessagesReceived": "Messages Received",
+ "statisticsMessagesStored": "Messages Stored",
+ "statisticsGeocoder": "Geocoder Requests",
+ "statisticsGeolocation": "Geolocation Requests",
+ "categoryArrow": "Arrow",
+ "categoryDefault": "Default",
+ "categoryAnimal": "Animal",
+ "categoryBicycle": "Bicycle",
+ "categoryBoat": "Boat",
+ "categoryBus": "Bus",
+ "categoryCar": "Car",
+ "categoryCamper": "Camper",
+ "categoryCrane": "Crane",
+ "categoryHelicopter": "Helicopter",
+ "categoryMotorcycle": "Motorcycle",
+ "categoryOffroad": "Offroad",
+ "categoryPerson": "Person",
+ "categoryPickup": "Pickup",
+ "categoryPlane": "Plane",
+ "categoryShip": "Ship",
+ "categoryTractor": "Tractor",
+ "categoryTrain": "Train",
+ "categoryTram": "Tram",
+ "categoryTrolleybus": "Trolleybus",
+ "categoryTruck": "Truck",
+ "categoryVan": "Van",
+ "categoryScooter": "Scooter",
+ "maintenanceStart": "Start",
+ "maintenancePeriod": "Period"
+} \ No newline at end of file
diff --git a/src/resources/l10n/ms.json b/src/resources/l10n/ms.json
new file mode 100644
index 00000000..50b4048a
--- /dev/null
+++ b/src/resources/l10n/ms.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "Memuatkan...",
+ "sharedHide": "Hide",
+ "sharedSave": "Simpan",
+ "sharedUpload": "Upload",
+ "sharedSet": "Set",
+ "sharedCancel": "Batal",
+ "sharedCopy": "Copy",
+ "sharedAdd": "Tambah",
+ "sharedEdit": "Ubah",
+ "sharedRemove": "Hapus",
+ "sharedRemoveConfirm": "Hapuskan item?",
+ "sharedNoData": "No data",
+ "sharedSubject": "Subject",
+ "sharedYes": "Yes",
+ "sharedNo": "No",
+ "sharedKm": "km",
+ "sharedMi": "mi",
+ "sharedNmi": "nmi",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "kn",
+ "sharedKmh": "km/h",
+ "sharedMph": "mph",
+ "sharedHour": "Jam",
+ "sharedMinute": "Minit",
+ "sharedSecond": "Saat",
+ "sharedDays": "days",
+ "sharedHours": "hours",
+ "sharedMinutes": "minutes",
+ "sharedDecimalDegrees": "Decimal Degrees",
+ "sharedDegreesDecimalMinutes": "Degrees Decimal Minutes",
+ "sharedDegreesMinutesSeconds": "Degrees Minutes Seconds",
+ "sharedName": "Name",
+ "sharedDescription": "Description",
+ "sharedSearch": "Search",
+ "sharedIconScale": "Icon Scale",
+ "sharedGeofence": "Geofence",
+ "sharedGeofences": "Geofences",
+ "sharedCreateGeofence": "Create Geofence",
+ "sharedNotifications": "Notifications",
+ "sharedNotification": "Notification",
+ "sharedAttributes": "Attributes",
+ "sharedAttribute": "Attribute",
+ "sharedDrivers": "Drivers",
+ "sharedDriver": "Driver",
+ "sharedArea": "Area",
+ "sharedSound": "Notification Sound",
+ "sharedType": "Type",
+ "sharedDistance": "Distance",
+ "sharedHourAbbreviation": "h",
+ "sharedMinuteAbbreviation": "m",
+ "sharedSecondAbbreviation": "s",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "l",
+ "sharedGallonAbbreviation": "gal",
+ "sharedLiter": "Liter",
+ "sharedImpGallon": "Imp. Gallon",
+ "sharedUsGallon": "U.S. Gallon",
+ "sharedLiterPerHourAbbreviation": "l/h",
+ "sharedGetMapState": "Get Map State",
+ "sharedComputedAttribute": "Computed Attribute",
+ "sharedComputedAttributes": "Computed Attributes",
+ "sharedCheckComputedAttribute": "Check Computed Attribute",
+ "sharedExpression": "Expression",
+ "sharedDevice": "Device",
+ "sharedTest": "Test",
+ "sharedTestNotification": "Send Test Notification",
+ "sharedTestNotificators": "Test Channels",
+ "sharedTestExpression": "Test Expression",
+ "sharedCalendar": "Calendar",
+ "sharedCalendars": "Calendars",
+ "sharedFile": "File",
+ "sharedSearchDevices": "Search Devices",
+ "sharedSortBy": "Sort By",
+ "sharedFilterMap": "Filter on Map",
+ "sharedSelectFile": "Select File",
+ "sharedPhone": "Phone",
+ "sharedRequired": "Required",
+ "sharedPreferences": "Preferences",
+ "sharedPermissions": "Permissions",
+ "sharedConnections": "Connections",
+ "sharedExtra": "Extra",
+ "sharedPrimary": "Primary",
+ "sharedSecondary": "Secondary",
+ "sharedTypeString": "String",
+ "sharedTypeNumber": "Number",
+ "sharedTypeBoolean": "Boolean",
+ "sharedTimezone": "Timezone",
+ "sharedInfoTitle": "Info",
+ "sharedSavedCommand": "Saved Command",
+ "sharedSavedCommands": "Saved Commands",
+ "sharedNew": "New…",
+ "sharedShowAddress": "Show Address",
+ "sharedShowDetails": "More Details",
+ "sharedDisabled": "Disabled",
+ "sharedMaintenance": "Maintenance",
+ "sharedDeviceAccumulators": "Accumulators",
+ "sharedAlarms": "Alarms",
+ "sharedLocation": "Location",
+ "sharedImport": "Import",
+ "sharedColumns": "Columns",
+ "sharedDropzoneText": "Drag and drop a file here or click",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "Simple",
+ "calendarRecurrence": "Recurrence",
+ "calendarOnce": "Once",
+ "calendarDaily": "Daily",
+ "calendarWeekly": "Weekly",
+ "calendarMonthly": "Monthly",
+ "calendarDays": "Days",
+ "calendarSunday": "Sunday",
+ "calendarMonday": "Monday",
+ "calendarTuesday": "Tuesday",
+ "calendarWednesday": "Wednesday",
+ "calendarThursday": "Thursday",
+ "calendarFriday": "Friday",
+ "calendarSaturday": "Saturday",
+ "attributeShowGeofences": "Show Geofences",
+ "attributeSpeedLimit": "Speed Limit",
+ "attributeFuelDropThreshold": "Fuel Drop Threshold",
+ "attributeFuelIncreaseThreshold": "Fuel Increase Threshold",
+ "attributePolylineDistance": "Polyline Distance",
+ "attributeReportIgnoreOdometer": "Report: Ignore Odometer",
+ "attributeWebReportColor": "Web: Report Color",
+ "attributeDevicePassword": "Device Password",
+ "attributeDeviceImage": "Device Image",
+ "attributeDeviceInactivityStart": "Device Inactivity Start",
+ "attributeDeviceInactivityPeriod": "Device Inactivity Period",
+ "attributeProcessingCopyAttributes": "Processing: Copy Attributes",
+ "attributeColor": "Color",
+ "attributeWebLiveRouteLength": "Web: Live Route Length",
+ "attributeWebSelectZoom": "Web: Zoom On Select",
+ "attributeWebMaxZoom": "Web: Maximum Zoom",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Pushover User Key",
+ "attributePushoverDeviceNames": "Pushover Device Names",
+ "attributeMailSmtpHost": "Mail: SMTP Host",
+ "attributeMailSmtpPort": "Mail: SMTP Port",
+ "attributeMailSmtpStarttlsEnable": "Mail: SMTP STARTTLS Enable",
+ "attributeMailSmtpStarttlsRequired": "Mail: SMTP STARTTLS Required",
+ "attributeMailSmtpSslEnable": "Mail: SMTP SSL Enable",
+ "attributeMailSmtpSslTrust": "Mail: SMTP SSL Trust",
+ "attributeMailSmtpSslProtocols": "Mail: SMTP SSL Protocols",
+ "attributeMailSmtpFrom": "Mail: SMTP From",
+ "attributeMailSmtpAuth": "Mail: SMTP Auth Enable",
+ "attributeMailSmtpUsername": "Mail: SMTP Username",
+ "attributeMailSmtpPassword": "Mail: SMTP Password",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: Disable Attributes",
+ "attributeUiDisableGroups": "UI: Disable Groups",
+ "attributeUiDisableEvents": "UI: Disable Events",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "UI: Disable Drivers",
+ "attributeUiDisableComputedAttributes": "UI: Disable Computed Attributes",
+ "attributeUiDisableCalendars": "UI: Disable Calendars",
+ "attributeUiDisableMaintenance": "UI: Disable Maintenance",
+ "attributeUiHidePositionAttributes": "UI: Hide Position Attributes",
+ "attributeUiDisableLoginLanguage": "UI: Disable Login Language",
+ "attributeNotificationTokens": "Notification Tokens",
+ "attributePopupInfo": "Popup Info",
+ "errorTitle": "Ralat",
+ "errorGeneral": "Invalid parameters or constraints violation",
+ "errorConnection": "Ralat penyambungan",
+ "errorSocket": "Web socket connection error",
+ "errorZero": "Can't be zero",
+ "userEmail": "Emel",
+ "userPassword": "Katalaluan",
+ "userAdmin": "Admin",
+ "userRemember": "Remember",
+ "userExpirationTime": "Expiration",
+ "userDeviceLimit": "Device Limit",
+ "userUserLimit": "User Limit",
+ "userDeviceReadonly": "Device Readonly",
+ "userLimitCommands": "Limit Commands",
+ "userDisableReports": "Disable Reports",
+ "userFixedEmail": "No Email Change",
+ "userToken": "Token",
+ "userDeleteAccount": "Delete Account",
+ "userTemporary": "Temporary",
+ "loginTitle": "Log masuk",
+ "loginLanguage": "Bahasa",
+ "loginReset": "Reset Password",
+ "loginRegister": "Daftar",
+ "loginLogin": "Log masuk",
+ "loginOpenId": "Login with OpenID",
+ "loginFailed": "Kesalahan emel atau katalaluan",
+ "loginCreated": "Pengguna baru telah didaftarkan",
+ "loginResetSuccess": "Check your email",
+ "loginUpdateSuccess": "New password is set",
+ "loginLogout": "Keluar",
+ "loginLogo": "Logo",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "Peranti dan State",
+ "deviceSelected": "Selected Device",
+ "deviceTitle": "Peranti",
+ "devicePrimaryInfo": "Device Title",
+ "deviceSecondaryInfo": "Device Detail",
+ "deviceIdentifier": "IMEI/ID",
+ "deviceModel": "Model",
+ "deviceContact": "Contact",
+ "deviceCategory": "Category",
+ "deviceLastUpdate": "Kemaskini Terakhir",
+ "deviceCommand": "Arahan",
+ "deviceFollow": "Ikut",
+ "deviceTotalDistance": "Total Distance",
+ "deviceStatus": "Status",
+ "deviceStatusOnline": "Online",
+ "deviceStatusOffline": "Offline",
+ "deviceStatusUnknown": "Unknown",
+ "deviceRegisterFirst": "Register your first device",
+ "deviceIdentifierHelp": "IMEI, serial number or other id. It has to match the identifier device reports to the server.",
+ "deviceShare": "Share Device",
+ "groupDialog": "Group",
+ "groupParent": "Group",
+ "groupNoGroup": "No Group",
+ "settingsTitle": "Tetapan",
+ "settingsUser": "Akaun",
+ "settingsGroups": "Groups",
+ "settingsServer": "Server",
+ "settingsUsers": "Pengguna",
+ "settingsDistanceUnit": "Distance Unit",
+ "settingsAltitudeUnit": "Altitude Unit",
+ "settingsSpeedUnit": "Speed Unit",
+ "settingsVolumeUnit": "Volume Unit",
+ "settingsTwelveHourFormat": "12-hour Format",
+ "settingsCoordinateFormat": "Coordinates Format",
+ "settingsServerVersion": "Server Version",
+ "settingsAppVersion": "App Version",
+ "settingsConnection": "Connection",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "Laporan",
+ "reportScheduled": "Scheduled Reports",
+ "reportDevice": "Peranti",
+ "reportGroup": "Group",
+ "reportFrom": "Daripada",
+ "reportTo": "Ke",
+ "reportShow": "Papar",
+ "reportClear": "Kosongkan",
+ "linkGoogleMaps": "Google Maps",
+ "linkAppleMaps": "Apple Maps",
+ "linkStreetView": "Street View",
+ "positionFixTime": "Fix Time",
+ "positionDeviceTime": "Device Time",
+ "positionServerTime": "Server Time",
+ "positionValid": "Sah",
+ "positionAccuracy": "Accuracy",
+ "positionLatitude": "Latitud",
+ "positionLongitude": "Longitud",
+ "positionAltitude": "Altitud",
+ "positionSpeed": "Kelajuan",
+ "positionCourse": "Course",
+ "positionAddress": "Alamat",
+ "positionProtocol": "Protokol",
+ "positionDistance": "Distance",
+ "positionRpm": "RPM",
+ "positionFuel": "Fuel",
+ "positionPower": "Power",
+ "positionBattery": "Battery",
+ "positionRaw": "Raw",
+ "positionIndex": "Index",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "Satellites",
+ "positionSatVisible": "Visible Satellites",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "Roaming",
+ "positionEvent": "Event",
+ "positionAlarm": "Alarm",
+ "positionStatus": "Status",
+ "positionOdometer": "Odometer",
+ "positionServiceOdometer": "Service Odometer",
+ "positionTripOdometer": "Trip Odometer",
+ "positionHours": "Hours",
+ "positionSteps": "Steps",
+ "positionInput": "Input",
+ "positionHeartRate": "Heart Rate",
+ "positionOutput": "Output",
+ "positionBatteryLevel": "Battery Level",
+ "positionFuelConsumption": "Fuel Consumption",
+ "positionRfid": "RFID",
+ "positionVersionFw": "Firmware Version",
+ "positionVersionHw": "Hardware Version",
+ "positionIgnition": "Ignition",
+ "positionFlags": "Flags",
+ "positionCharge": "Charge",
+ "positionIp": "IP",
+ "positionArchive": "Archive",
+ "positionVin": "VIN",
+ "positionApproximate": "Approximate",
+ "positionThrottle": "Throttle",
+ "positionMotion": "Motion",
+ "positionArmed": "Armed",
+ "positionAcceleration": "Acceleration",
+ "positionTemp": "Temperature",
+ "positionDeviceTemp": "Device Temperature",
+ "positionCoolantTemp": "Coolant Temperature",
+ "positionOperator": "Operator",
+ "positionCommand": "Command",
+ "positionBlocked": "Blocked",
+ "positionDtcs": "DTCs",
+ "positionObdSpeed": "OBD Speed",
+ "positionObdOdometer": "OBD Odometer",
+ "positionDrivingTime": "Driving Time",
+ "positionDriverUniqueId": "Driver Unique Id",
+ "positionCard": "Card",
+ "positionImage": "Image",
+ "positionVideo": "Video",
+ "positionAudio": "Audio",
+ "serverTitle": "Tetapan Server",
+ "serverZoom": "Besarkan",
+ "serverRegistration": "Pendaftaran",
+ "serverReadonly": "Baca Sahaja",
+ "serverForceSettings": "Force Settings",
+ "serverAnnouncement": "Announcement",
+ "serverName": "Server Name",
+ "serverDescription": "Server Description",
+ "serverColorPrimary": "Color Primary",
+ "serverColorSecondary": "Color Secondary",
+ "serverLogo": "Logo Image",
+ "serverLogoInverted": "Inverted Logo Image",
+ "serverChangeDisable": "Disable Server Change",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "Peta",
+ "mapActive": "Active Maps",
+ "mapOverlay": "Map Overlay",
+ "mapOverlayCustom": "Custom Overlay",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API Key",
+ "mapOpenWeatherClouds": "OpenWeather Clouds",
+ "mapOpenWeatherPrecipitation": "OpenWeather Precipitation",
+ "mapOpenWeatherPressure": "OpenWeather Pressure",
+ "mapOpenWeatherWind": "OpenWeather Wind",
+ "mapOpenWeatherTemperature": "OpenWeather Temperature",
+ "mapLayer": "Map Layer",
+ "mapCustom": "Custom (XYZ)",
+ "mapCustomArcgis": "Custom (ArcGIS)",
+ "mapCustomLabel": "Custom map",
+ "mapCarto": "Carto Basemaps",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Bing Maps Key",
+ "mapBingRoad": "Bing Maps Road",
+ "mapBingAerial": "Bing Maps Aerial",
+ "mapBingHybrid": "Bing Maps Hybrid",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Yandex Map",
+ "mapYandexSat": "Yandex Satellite",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox Streets",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox Outdoors",
+ "mapMapboxSatellite": "Mapbox Satellite",
+ "mapMapboxKey": "Mapbox Access Token",
+ "mapMapTilerBasic": "MapTiler Basic",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "MapTiler API Key",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ Access Token",
+ "mapTomTomBasic": "TomTom Basic",
+ "mapTomTomFlow": "TomTom Traffic Flow",
+ "mapTomTomIncidents": "TomTom Traffic Incidents",
+ "mapTomTomKey": "TomTom API Key",
+ "mapHereBasic": "Here Basic",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satellite",
+ "mapHereFlow": "Here Traffic Flow",
+ "mapHereKey": "Here API Key",
+ "mapShapePolygon": "Polygon",
+ "mapShapeCircle": "Circle",
+ "mapShapePolyline": "Polyline",
+ "mapLiveRoutes": "Live Routes",
+ "mapDirection": "Show Direction",
+ "mapCurrentLocation": "Current Location",
+ "mapPoiLayer": "POI Layer",
+ "mapClustering": "Markers Clustering",
+ "mapOnSelect": "Show Map on Selection",
+ "mapDefault": "Default Map",
+ "stateTitle": "Negeri",
+ "stateName": "Atribut",
+ "stateValue": "Nilai",
+ "commandTitle": "Arahan",
+ "commandSend": "Hantar",
+ "commandSent": "Command sent",
+ "commandQueued": "Command queued",
+ "commandUnit": "Unit",
+ "commandCustom": "Custom command",
+ "commandDeviceIdentification": "Device Identification",
+ "commandPositionSingle": "Single Reporting",
+ "commandPositionPeriodic": "Laporan Berkala",
+ "commandPositionStop": "Hentikan Laporan",
+ "commandEngineStop": "Matikan Enjin",
+ "commandEngineResume": "Hidupkan Enjin",
+ "commandAlarmArm": "Arm Alarm",
+ "commandAlarmDisarm": "Disarm Alarm",
+ "commandAlarmDismiss": "Dismiss Alarm",
+ "commandSetTimezone": "Set Timezone",
+ "commandRequestPhoto": "Request Photo",
+ "commandPowerOff": "Power Off Device",
+ "commandRebootDevice": "Reboot Device",
+ "commandFactoryReset": "Factory Reset",
+ "commandSendSms": "Send SMS",
+ "commandSendUssd": "Send USSD",
+ "commandSosNumber": "Set SOS Number",
+ "commandSilenceTime": "Set Silence Time",
+ "commandSetPhonebook": "Set Phonebook",
+ "commandVoiceMessage": "Voice Message",
+ "commandOutputControl": "Output Control",
+ "commandVoiceMonitoring": "Voice Monitoring",
+ "commandSetAgps": "Set AGPS",
+ "commandSetIndicator": "Set Indicator",
+ "commandConfiguration": "Configuration",
+ "commandGetVersion": "Get Version",
+ "commandFirmwareUpdate": "Update Firmware",
+ "commandSetConnection": "Set Connection",
+ "commandSetOdometer": "Set Odometer",
+ "commandGetModemStatus": "Get Modem Status",
+ "commandGetDeviceStatus": "Get Device Status",
+ "commandSetSpeedLimit": "Set Speed Limit",
+ "commandModePowerSaving": "Power Saving Mode",
+ "commandModeDeepSleep": "Deep Sleep Mode",
+ "commandAlarmGeofence": "Set Geofence Alarm",
+ "commandAlarmBattery": "Set Battery Alarm",
+ "commandAlarmSos": "Set SOS Alarm",
+ "commandAlarmRemove": "Set Remove Alarm",
+ "commandAlarmClock": "Set Clock Alarm",
+ "commandAlarmSpeed": "Set Speed Alarm",
+ "commandAlarmFall": "Set Fall Alarm",
+ "commandAlarmVibration": "Set Vibration Alarm",
+ "commandFrequency": "Frekuensi",
+ "commandTimezone": "Timezone Offset",
+ "commandMessage": "Message",
+ "commandRadius": "Radius",
+ "commandEnable": "Enable",
+ "commandData": "Data",
+ "commandIndex": "Index",
+ "commandPhone": "Phone Number",
+ "commandServer": "Server",
+ "commandPort": "Port",
+ "eventAll": "All Events",
+ "eventDeviceOnline": "Status online",
+ "eventDeviceUnknown": "Status unknown",
+ "eventDeviceOffline": "Status offline",
+ "eventDeviceInactive": "Device inactive",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "Device moving",
+ "eventDeviceStopped": "Device stopped",
+ "eventDeviceOverspeed": "Speed limit exceeded",
+ "eventDeviceFuelDrop": "Fuel drop",
+ "eventDeviceFuelIncrease": "Fuel increase",
+ "eventCommandResult": "Command result",
+ "eventGeofenceEnter": "Geofence entered",
+ "eventGeofenceExit": "Geofence exited",
+ "eventAlarm": "Alarm",
+ "eventIgnitionOn": "Ignition on",
+ "eventIgnitionOff": "Ignition off",
+ "eventMaintenance": "Maintenance required",
+ "eventTextMessage": "Text message received",
+ "eventDriverChanged": "Driver changed",
+ "eventMedia": "Media",
+ "eventsScrollToLast": "Scroll To Last",
+ "eventsSoundEvents": "Sound Events",
+ "eventsSoundAlarms": "Sound Alarms",
+ "alarmGeneral": "General",
+ "alarmSos": "SOS",
+ "alarmVibration": "Vibration",
+ "alarmMovement": "Movement",
+ "alarmLowspeed": "Low Speed",
+ "alarmOverspeed": "Overspeed",
+ "alarmFallDown": "Fall Down",
+ "alarmLowPower": "Low Power",
+ "alarmLowBattery": "Low Battery",
+ "alarmFault": "Fault",
+ "alarmPowerOff": "Power Off",
+ "alarmPowerOn": "Power On",
+ "alarmDoor": "Door",
+ "alarmLock": "Lock",
+ "alarmUnlock": "Unlock",
+ "alarmGeofence": "Geofence",
+ "alarmGeofenceEnter": "Geofence Enter",
+ "alarmGeofenceExit": "Geofence Exit",
+ "alarmGpsAntennaCut": "GPS Antenna Cut",
+ "alarmAccident": "Accident",
+ "alarmTow": "Tow",
+ "alarmIdle": "Idle",
+ "alarmHighRpm": "High RPM",
+ "alarmHardAcceleration": "Hard Acceleration",
+ "alarmHardBraking": "Hard Braking",
+ "alarmHardCornering": "Hard Cornering",
+ "alarmLaneChange": "Lane Change",
+ "alarmFatigueDriving": "Fatigue Driving",
+ "alarmPowerCut": "Power Cut",
+ "alarmPowerRestored": "Power Restored",
+ "alarmJamming": "Jamming",
+ "alarmTemperature": "Temperature",
+ "alarmParking": "Parking",
+ "alarmBonnet": "Bonnet",
+ "alarmFootBrake": "Foot Brake",
+ "alarmFuelLeak": "Fuel Leak",
+ "alarmTampering": "Tampering",
+ "alarmRemoving": "Removing",
+ "notificationType": "Type of Notification",
+ "notificationAlways": "All Devices",
+ "notificationNotificators": "Channels",
+ "notificatorCommand": "Command",
+ "notificatorWeb": "Web",
+ "notificatorMail": "Mail",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Replay",
+ "reportCombined": "Combined",
+ "reportRoute": "Route",
+ "reportEvents": "Events",
+ "reportTrips": "Trips",
+ "reportStops": "Stops",
+ "reportSummary": "Summary",
+ "reportDaily": "Daily Summary",
+ "reportChart": "Chart",
+ "reportConfigure": "Configure",
+ "reportEventTypes": "Event Types",
+ "reportChartType": "Chart Type",
+ "reportShowMarkers": "Show Markers",
+ "reportExport": "Export",
+ "reportEmail": "Email Report",
+ "reportSchedule": "Schedule",
+ "reportPeriod": "Period",
+ "reportCustom": "Custom",
+ "reportToday": "Today",
+ "reportYesterday": "Yesterday",
+ "reportThisWeek": "This Week",
+ "reportPreviousWeek": "Previous Week",
+ "reportThisMonth": "This Month",
+ "reportPreviousMonth": "Previous Month",
+ "reportDeviceName": "Device Name",
+ "reportAverageSpeed": "Average Speed",
+ "reportMaximumSpeed": "Maximum Speed",
+ "reportEngineHours": "Engine Hours",
+ "reportDuration": "Duration",
+ "reportStartDate": "Start Date",
+ "reportStartTime": "Start Time",
+ "reportStartAddress": "Start Address",
+ "reportEndTime": "End Time",
+ "reportEndAddress": "End Address",
+ "reportSpentFuel": "Spent Fuel",
+ "reportStartOdometer": "Odometer Start",
+ "reportEndOdometer": "Odometer End",
+ "statisticsTitle": "Statistics",
+ "statisticsCaptureTime": "Capture Time",
+ "statisticsActiveUsers": "Active Users",
+ "statisticsActiveDevices": "Active Devices",
+ "statisticsRequests": "Requests",
+ "statisticsMessagesReceived": "Messages Received",
+ "statisticsMessagesStored": "Messages Stored",
+ "statisticsGeocoder": "Geocoder Requests",
+ "statisticsGeolocation": "Geolocation Requests",
+ "categoryArrow": "Arrow",
+ "categoryDefault": "Default",
+ "categoryAnimal": "Animal",
+ "categoryBicycle": "Bicycle",
+ "categoryBoat": "Boat",
+ "categoryBus": "Bus",
+ "categoryCar": "Car",
+ "categoryCamper": "Camper",
+ "categoryCrane": "Crane",
+ "categoryHelicopter": "Helicopter",
+ "categoryMotorcycle": "Motorcycle",
+ "categoryOffroad": "Offroad",
+ "categoryPerson": "Person",
+ "categoryPickup": "Pickup",
+ "categoryPlane": "Plane",
+ "categoryShip": "Ship",
+ "categoryTractor": "Tractor",
+ "categoryTrain": "Train",
+ "categoryTram": "Tram",
+ "categoryTrolleybus": "Trolleybus",
+ "categoryTruck": "Truck",
+ "categoryVan": "Van",
+ "categoryScooter": "Scooter",
+ "maintenanceStart": "Start",
+ "maintenancePeriod": "Period"
+} \ No newline at end of file
diff --git a/src/resources/l10n/nb.json b/src/resources/l10n/nb.json
new file mode 100644
index 00000000..a3faa8c0
--- /dev/null
+++ b/src/resources/l10n/nb.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "Laster...",
+ "sharedHide": "Skjul",
+ "sharedSave": "Lagre",
+ "sharedUpload": "Upload",
+ "sharedSet": "Sett",
+ "sharedCancel": "Avbryt",
+ "sharedCopy": "Copy",
+ "sharedAdd": "Legg til",
+ "sharedEdit": "Endre",
+ "sharedRemove": "Fjern",
+ "sharedRemoveConfirm": "Fjern element?",
+ "sharedNoData": "No data",
+ "sharedSubject": "Subject",
+ "sharedYes": "Yes",
+ "sharedNo": "No",
+ "sharedKm": "km",
+ "sharedMi": "mi",
+ "sharedNmi": "nm",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "kn",
+ "sharedKmh": "km/t",
+ "sharedMph": "mph",
+ "sharedHour": "Time",
+ "sharedMinute": "Minutt",
+ "sharedSecond": "Sekund",
+ "sharedDays": "dager",
+ "sharedHours": "timer",
+ "sharedMinutes": "minutter",
+ "sharedDecimalDegrees": "Desimalgrader",
+ "sharedDegreesDecimalMinutes": "Grader desimalminutter",
+ "sharedDegreesMinutesSeconds": "Grader minutter sekund",
+ "sharedName": "Navn",
+ "sharedDescription": "Beskrivelse",
+ "sharedSearch": "Søk",
+ "sharedIconScale": "Icon Scale",
+ "sharedGeofence": "geo-gjerde",
+ "sharedGeofences": "Geo-gjerder",
+ "sharedCreateGeofence": "Create Geofence",
+ "sharedNotifications": "Varsler",
+ "sharedNotification": "Varsel",
+ "sharedAttributes": "Atributter",
+ "sharedAttribute": "Egenskap",
+ "sharedDrivers": "Sjåfører",
+ "sharedDriver": "Sjåfør",
+ "sharedArea": "Område",
+ "sharedSound": "Varslingslyd",
+ "sharedType": "Type",
+ "sharedDistance": "Avstand",
+ "sharedHourAbbreviation": "t",
+ "sharedMinuteAbbreviation": "m",
+ "sharedSecondAbbreviation": "s",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "l",
+ "sharedGallonAbbreviation": "gal",
+ "sharedLiter": "Liter",
+ "sharedImpGallon": "Britisk Gallon",
+ "sharedUsGallon": "Amerikansk Gallon",
+ "sharedLiterPerHourAbbreviation": "l/t",
+ "sharedGetMapState": "Få karttilstand",
+ "sharedComputedAttribute": "Utregnede atributter",
+ "sharedComputedAttributes": "Utregnede atributter",
+ "sharedCheckComputedAttribute": "Sjekk utregnede atributter",
+ "sharedExpression": "Uttrykk",
+ "sharedDevice": "Enhet",
+ "sharedTest": "Test",
+ "sharedTestNotification": "Send testvarsling",
+ "sharedTestNotificators": "Test Channels",
+ "sharedTestExpression": "Test Expression",
+ "sharedCalendar": "Kalender",
+ "sharedCalendars": "Kalendere",
+ "sharedFile": "Fil",
+ "sharedSearchDevices": "Search Devices",
+ "sharedSortBy": "Sort By",
+ "sharedFilterMap": "Filter on Map",
+ "sharedSelectFile": "Velg fil",
+ "sharedPhone": "Telefon",
+ "sharedRequired": "Nødvendig",
+ "sharedPreferences": "Innstillinger",
+ "sharedPermissions": "Tilgang",
+ "sharedConnections": "Connections",
+ "sharedExtra": "Ekstra",
+ "sharedPrimary": "Primary",
+ "sharedSecondary": "Secondary",
+ "sharedTypeString": "Streng",
+ "sharedTypeNumber": "Nummer",
+ "sharedTypeBoolean": "Boolsk",
+ "sharedTimezone": "Tidssone",
+ "sharedInfoTitle": "Info",
+ "sharedSavedCommand": "Lagret kommando",
+ "sharedSavedCommands": "Lagrede kommandoer",
+ "sharedNew": "Ny",
+ "sharedShowAddress": "Vis adresse",
+ "sharedShowDetails": "More Details",
+ "sharedDisabled": "Deaktivert",
+ "sharedMaintenance": "Vedlikehold",
+ "sharedDeviceAccumulators": "Målere",
+ "sharedAlarms": "Alarmer",
+ "sharedLocation": "Location",
+ "sharedImport": "Import",
+ "sharedColumns": "Columns",
+ "sharedDropzoneText": "Drag and drop a file here or click",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "Simple",
+ "calendarRecurrence": "Recurrence",
+ "calendarOnce": "Once",
+ "calendarDaily": "Daily",
+ "calendarWeekly": "Weekly",
+ "calendarMonthly": "Monthly",
+ "calendarDays": "Days",
+ "calendarSunday": "Sunday",
+ "calendarMonday": "Monday",
+ "calendarTuesday": "Tuesday",
+ "calendarWednesday": "Wednesday",
+ "calendarThursday": "Thursday",
+ "calendarFriday": "Friday",
+ "calendarSaturday": "Saturday",
+ "attributeShowGeofences": "Show Geofences",
+ "attributeSpeedLimit": "Fartsgrense",
+ "attributeFuelDropThreshold": "Fuel Drop Threshold",
+ "attributeFuelIncreaseThreshold": "Fuel Increase Threshold",
+ "attributePolylineDistance": "Polylinjedistanse",
+ "attributeReportIgnoreOdometer": "Rapport: Ignorer odometer",
+ "attributeWebReportColor": "Web: Rapport farge",
+ "attributeDevicePassword": "Enhetspassord",
+ "attributeDeviceImage": "Device Image",
+ "attributeDeviceInactivityStart": "Device Inactivity Start",
+ "attributeDeviceInactivityPeriod": "Device Inactivity Period",
+ "attributeProcessingCopyAttributes": "Behandling: Kopier egenskaper",
+ "attributeColor": "Farge",
+ "attributeWebLiveRouteLength": "Web: Live rute lengde",
+ "attributeWebSelectZoom": "Web: Skaler ved valg",
+ "attributeWebMaxZoom": "Web: Maksimal forstørring",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Pushover User Key",
+ "attributePushoverDeviceNames": "Pushover Device Names",
+ "attributeMailSmtpHost": "Mail: SMTP-vert",
+ "attributeMailSmtpPort": "Mail: SMTP-port",
+ "attributeMailSmtpStarttlsEnable": "Mail: Aktiver SMTP STARTTLS",
+ "attributeMailSmtpStarttlsRequired": "Mail: SMTP STARTTLS påkrevd",
+ "attributeMailSmtpSslEnable": "Mail: Aktiver SMTP SSL",
+ "attributeMailSmtpSslTrust": "Mail: SMTP SSL tillit",
+ "attributeMailSmtpSslProtocols": "Mail: SMTP SSL protokoller",
+ "attributeMailSmtpFrom": "Mail: SMTP Fra",
+ "attributeMailSmtpAuth": "E-post: Aktiver SMTP autentisering",
+ "attributeMailSmtpUsername": "Mail: SMTP brukernavn",
+ "attributeMailSmtpPassword": "Mail: SMTP passord",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: Disable Attributes",
+ "attributeUiDisableGroups": "UI: Disable Groups",
+ "attributeUiDisableEvents": "UI: Deaktiver hendelser",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "UI: Deaktiver sjåfører",
+ "attributeUiDisableComputedAttributes": "UI: Deaktiver utregnede egenskaper",
+ "attributeUiDisableCalendars": "UI: Deaktiver kalendere",
+ "attributeUiDisableMaintenance": "UI: Deaktiver Vedlikehold",
+ "attributeUiHidePositionAttributes": "UI: Skjul posisjonsegenskaper",
+ "attributeUiDisableLoginLanguage": "UI: Disable Login Language",
+ "attributeNotificationTokens": "Notification Tokens",
+ "attributePopupInfo": "Popup Info",
+ "errorTitle": "Feil",
+ "errorGeneral": "Ugyldige parametere eller begrensingsbrudd",
+ "errorConnection": "Forbindelse feilet",
+ "errorSocket": "Web socket tilkoblingsfeil",
+ "errorZero": "Kan ikke være null",
+ "userEmail": "E-post",
+ "userPassword": "Passord",
+ "userAdmin": "Admin",
+ "userRemember": "Husk",
+ "userExpirationTime": "Utløpstid",
+ "userDeviceLimit": "Enhetsgrense",
+ "userUserLimit": "Brukergrense",
+ "userDeviceReadonly": "Skrivebeskyttet enhet",
+ "userLimitCommands": "Begrens kommandoer",
+ "userDisableReports": "Disable Reports",
+ "userFixedEmail": "No Email Change",
+ "userToken": "Symbol",
+ "userDeleteAccount": "Delete Account",
+ "userTemporary": "Temporary",
+ "loginTitle": "Logg inn",
+ "loginLanguage": "Språk",
+ "loginReset": "Reset Password",
+ "loginRegister": "Registrer",
+ "loginLogin": "Logg inn",
+ "loginOpenId": "Login with OpenID",
+ "loginFailed": "Feil e-post eller passord",
+ "loginCreated": "Ny bruker har blitt registrert",
+ "loginResetSuccess": "Check your email",
+ "loginUpdateSuccess": "New password is set",
+ "loginLogout": "Logg ut",
+ "loginLogo": "Logo",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "Enheter og status",
+ "deviceSelected": "Selected Device",
+ "deviceTitle": "Enheter",
+ "devicePrimaryInfo": "Device Title",
+ "deviceSecondaryInfo": "Device Detail",
+ "deviceIdentifier": "Identifikator",
+ "deviceModel": "Modell",
+ "deviceContact": "Kontakt",
+ "deviceCategory": "Kategori",
+ "deviceLastUpdate": "Sist oppdatert",
+ "deviceCommand": "Kommando",
+ "deviceFollow": "Følg",
+ "deviceTotalDistance": "Total distanse",
+ "deviceStatus": "Status",
+ "deviceStatusOnline": "Tilkoblet",
+ "deviceStatusOffline": "Frakoblet",
+ "deviceStatusUnknown": "Ukjent",
+ "deviceRegisterFirst": "Register your first device",
+ "deviceIdentifierHelp": "IMEI, serial number or other id. It has to match the identifier device reports to the server.",
+ "deviceShare": "Share Device",
+ "groupDialog": "Gruppe",
+ "groupParent": "Gruppe",
+ "groupNoGroup": "Ingen gruppe",
+ "settingsTitle": "Innstillinger",
+ "settingsUser": "Konto",
+ "settingsGroups": "Grupper",
+ "settingsServer": "Server",
+ "settingsUsers": "Brukere",
+ "settingsDistanceUnit": "Avstandsenhet",
+ "settingsAltitudeUnit": "Altitude Unit",
+ "settingsSpeedUnit": "Hastighetsenhet",
+ "settingsVolumeUnit": "Volumenhet",
+ "settingsTwelveHourFormat": "Tolvtimersformat",
+ "settingsCoordinateFormat": "Koordinatformat",
+ "settingsServerVersion": "Server Version",
+ "settingsAppVersion": "App Version",
+ "settingsConnection": "Connection",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "Rapporter",
+ "reportScheduled": "Scheduled Reports",
+ "reportDevice": "Enhet",
+ "reportGroup": "Gruppe",
+ "reportFrom": "Fra",
+ "reportTo": "Til",
+ "reportShow": "Vis",
+ "reportClear": "Nullstill",
+ "linkGoogleMaps": "Google Maps",
+ "linkAppleMaps": "Apple Maps",
+ "linkStreetView": "Street View",
+ "positionFixTime": "Fix Time",
+ "positionDeviceTime": "Device Time",
+ "positionServerTime": "Server Time",
+ "positionValid": "Gyldig",
+ "positionAccuracy": "Nøyaktighet",
+ "positionLatitude": "Breddegrad",
+ "positionLongitude": "Lengdegrad",
+ "positionAltitude": "Høyde",
+ "positionSpeed": "Hastighet",
+ "positionCourse": "Retning",
+ "positionAddress": "Adresse",
+ "positionProtocol": "Protokoll",
+ "positionDistance": "Avstand",
+ "positionRpm": "r/min",
+ "positionFuel": "Drivstoff",
+ "positionPower": "Spenning",
+ "positionBattery": "Batteri",
+ "positionRaw": "Rå",
+ "positionIndex": "Register",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "Satelitter",
+ "positionSatVisible": "Synlige satelitter",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "Nettveksling (Roaming)",
+ "positionEvent": "Hendelse",
+ "positionAlarm": "Alarm",
+ "positionStatus": "Status",
+ "positionOdometer": "Kilometerteller",
+ "positionServiceOdometer": "Service Odometer",
+ "positionTripOdometer": "Turmåler",
+ "positionHours": "Timer",
+ "positionSteps": "Steg",
+ "positionInput": "Inngang",
+ "positionHeartRate": "Heart Rate",
+ "positionOutput": "Utgang",
+ "positionBatteryLevel": "Batterinivå",
+ "positionFuelConsumption": "Drivstofforbruk",
+ "positionRfid": "RFID",
+ "positionVersionFw": "Fastvareversjon",
+ "positionVersionHw": "Maskinvareversjon",
+ "positionIgnition": "Tenning",
+ "positionFlags": "Status indikatorer",
+ "positionCharge": "Lade",
+ "positionIp": "IP",
+ "positionArchive": "Arkiv",
+ "positionVin": "VIN",
+ "positionApproximate": "Tilnærmet",
+ "positionThrottle": "Gasspedal",
+ "positionMotion": "Bevegelse",
+ "positionArmed": "Slått På",
+ "positionAcceleration": "Akselerasjon",
+ "positionTemp": "Temperature",
+ "positionDeviceTemp": "Enhetstemperatur",
+ "positionCoolantTemp": "Coolant Temperature",
+ "positionOperator": "Operatør",
+ "positionCommand": "Kommando",
+ "positionBlocked": "Blokkert",
+ "positionDtcs": "Diagnosefeilkoder",
+ "positionObdSpeed": "OBD hastighet",
+ "positionObdOdometer": "OBD kilometerteller",
+ "positionDrivingTime": "Driving Time",
+ "positionDriverUniqueId": "Sjåførunik ID",
+ "positionCard": "Card",
+ "positionImage": "Bilde",
+ "positionVideo": "Video",
+ "positionAudio": "Lyd",
+ "serverTitle": "Serverinnstillinger",
+ "serverZoom": "Zoom",
+ "serverRegistration": "Registering",
+ "serverReadonly": "Skrivebeskyttet",
+ "serverForceSettings": "Tving innstillinger",
+ "serverAnnouncement": "Announcement",
+ "serverName": "Server Name",
+ "serverDescription": "Server Description",
+ "serverColorPrimary": "Color Primary",
+ "serverColorSecondary": "Color Secondary",
+ "serverLogo": "Logo Image",
+ "serverLogoInverted": "Inverted Logo Image",
+ "serverChangeDisable": "Disable Server Change",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "Kart",
+ "mapActive": "Active Maps",
+ "mapOverlay": "Map Overlay",
+ "mapOverlayCustom": "Custom Overlay",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API Key",
+ "mapOpenWeatherClouds": "OpenWeather Clouds",
+ "mapOpenWeatherPrecipitation": "OpenWeather Precipitation",
+ "mapOpenWeatherPressure": "OpenWeather Pressure",
+ "mapOpenWeatherWind": "OpenWeather Wind",
+ "mapOpenWeatherTemperature": "OpenWeather Temperature",
+ "mapLayer": "Kartlag",
+ "mapCustom": "Tilpasset (XYZ)",
+ "mapCustomArcgis": "Tilpasset (ArcGIS)",
+ "mapCustomLabel": "Tilpasset kart",
+ "mapCarto": "Carto basiskart",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Bing Maps-nøkkel",
+ "mapBingRoad": "Bing Maps-veg",
+ "mapBingAerial": "Bing Maps-flyfoto",
+ "mapBingHybrid": "Bing Kart Hybrid",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Yandex Map",
+ "mapYandexSat": "Yandex Satelitt",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox Streets",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox Outdoors",
+ "mapMapboxSatellite": "Mapbox Satellite",
+ "mapMapboxKey": "Mapbox Access Token",
+ "mapMapTilerBasic": "MapTiler Basic",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "MapTiler API Key",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ Access Token",
+ "mapTomTomBasic": "TomTom Basic",
+ "mapTomTomFlow": "TomTom Traffic Flow",
+ "mapTomTomIncidents": "TomTom Traffic Incidents",
+ "mapTomTomKey": "TomTom API Key",
+ "mapHereBasic": "Here Basic",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satellite",
+ "mapHereFlow": "Here Traffic Flow",
+ "mapHereKey": "Here API Key",
+ "mapShapePolygon": "Mangekant",
+ "mapShapeCircle": "Sirkel",
+ "mapShapePolyline": "Polylinje",
+ "mapLiveRoutes": "Live rute",
+ "mapDirection": "Show Direction",
+ "mapCurrentLocation": "Current Location",
+ "mapPoiLayer": "POI-lag",
+ "mapClustering": "Markers Clustering",
+ "mapOnSelect": "Show Map on Selection",
+ "mapDefault": "Default Map",
+ "stateTitle": "Status",
+ "stateName": "Egenskap",
+ "stateValue": "Verdi",
+ "commandTitle": "Kommando",
+ "commandSend": "Send",
+ "commandSent": "Kommando er sent",
+ "commandQueued": "Kommando er satt i kø",
+ "commandUnit": "Enhet",
+ "commandCustom": "Egendefinert kommando",
+ "commandDeviceIdentification": "Enhetsidentifikasjon",
+ "commandPositionSingle": "Enkel-rapportering",
+ "commandPositionPeriodic": "Periodisk rapportering",
+ "commandPositionStop": "Stopp rapportering",
+ "commandEngineStop": "Stopp motor",
+ "commandEngineResume": "Fortsett motor",
+ "commandAlarmArm": "Slå alarm på",
+ "commandAlarmDisarm": "Slå alarm av",
+ "commandAlarmDismiss": "Dismiss Alarm",
+ "commandSetTimezone": "Sett tidssone",
+ "commandRequestPhoto": "Be om foto",
+ "commandPowerOff": "Skru av enhet",
+ "commandRebootDevice": "Omstart enhet",
+ "commandFactoryReset": "Factory Reset",
+ "commandSendSms": "Send SMS",
+ "commandSendUssd": "Send USSD",
+ "commandSosNumber": "Sett SOS-nummer",
+ "commandSilenceTime": "Sett stilletid",
+ "commandSetPhonebook": "Sett telefonbok",
+ "commandVoiceMessage": "Talemelding",
+ "commandOutputControl": "Utgangkontroll",
+ "commandVoiceMonitoring": "Stemmeovervåking",
+ "commandSetAgps": "Sett AGPS",
+ "commandSetIndicator": "Sett Indikator",
+ "commandConfiguration": "Oppsett",
+ "commandGetVersion": "Vis versjon",
+ "commandFirmwareUpdate": "Oppdater fastvare",
+ "commandSetConnection": "Angi tilkobling",
+ "commandSetOdometer": "Angi kilometerteller",
+ "commandGetModemStatus": "Få modemtilstand",
+ "commandGetDeviceStatus": "Få enhetstilstand",
+ "commandSetSpeedLimit": "Set Speed Limit",
+ "commandModePowerSaving": "Power Saving Mode",
+ "commandModeDeepSleep": "Deep Sleep Mode",
+ "commandAlarmGeofence": "Set Geofence Alarm",
+ "commandAlarmBattery": "Set Battery Alarm",
+ "commandAlarmSos": "Set SOS Alarm",
+ "commandAlarmRemove": "Set Remove Alarm",
+ "commandAlarmClock": "Set Clock Alarm",
+ "commandAlarmSpeed": "Set Speed Alarm",
+ "commandAlarmFall": "Set Fall Alarm",
+ "commandAlarmVibration": "Set Vibration Alarm",
+ "commandFrequency": "Frekvens",
+ "commandTimezone": "Tidszoneforskyvning",
+ "commandMessage": "Melding",
+ "commandRadius": "Radius",
+ "commandEnable": "Aktiver",
+ "commandData": "Data",
+ "commandIndex": "Register",
+ "commandPhone": "Telefonnummer",
+ "commandServer": "Tjener",
+ "commandPort": "Port",
+ "eventAll": "Alle hendelser",
+ "eventDeviceOnline": "Status online",
+ "eventDeviceUnknown": "Enhetsstatus ukjent",
+ "eventDeviceOffline": "Status offline",
+ "eventDeviceInactive": "Device inactive",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "Enheten beveger seg",
+ "eventDeviceStopped": "Enhet stoppet",
+ "eventDeviceOverspeed": "Fartsgrense overskredet",
+ "eventDeviceFuelDrop": "Drivstoffall",
+ "eventDeviceFuelIncrease": "Fuel increase",
+ "eventCommandResult": "Kommandoresultat",
+ "eventGeofenceEnter": "Ankommet geogjerde",
+ "eventGeofenceExit": "Forlatt geogjerde",
+ "eventAlarm": "Alarm",
+ "eventIgnitionOn": "Tenning på",
+ "eventIgnitionOff": "Tenning av",
+ "eventMaintenance": "Vedlikehold Kreves",
+ "eventTextMessage": "Tekst melding mottatt",
+ "eventDriverChanged": "Sjåfør endret",
+ "eventMedia": "Media",
+ "eventsScrollToLast": "Vis siste",
+ "eventsSoundEvents": "Sound Events",
+ "eventsSoundAlarms": "Sound Alarms",
+ "alarmGeneral": "Generell",
+ "alarmSos": "SOS",
+ "alarmVibration": "Vibrasjon",
+ "alarmMovement": "Bevegelse",
+ "alarmLowspeed": "Lav hastighet",
+ "alarmOverspeed": "Høgt turtall",
+ "alarmFallDown": "Fall",
+ "alarmLowPower": "Lite strøm",
+ "alarmLowBattery": "Lavt batterinivå",
+ "alarmFault": "Feil",
+ "alarmPowerOff": "Spenning av",
+ "alarmPowerOn": "Spenning På",
+ "alarmDoor": "Dør",
+ "alarmLock": "Lås",
+ "alarmUnlock": "lås opp",
+ "alarmGeofence": "Geo-gjerde",
+ "alarmGeofenceEnter": "Ankommet Geogjerde",
+ "alarmGeofenceExit": "Forlatt Geogjerde",
+ "alarmGpsAntennaCut": "GPS-Antenne Fjernet",
+ "alarmAccident": "Ulykke",
+ "alarmTow": "Tauing",
+ "alarmIdle": "Tomgang",
+ "alarmHighRpm": "Høyt turtall",
+ "alarmHardAcceleration": "Hard Akselerasjon",
+ "alarmHardBraking": "Hard Oppbremsing",
+ "alarmHardCornering": "Hard svingning",
+ "alarmLaneChange": "Filskifte",
+ "alarmFatigueDriving": "Tretthetskjøring",
+ "alarmPowerCut": "Spenning Kuttet",
+ "alarmPowerRestored": "Spenning Gjenopprettet",
+ "alarmJamming": "Jamming",
+ "alarmTemperature": "Tempratur",
+ "alarmParking": "Parkering",
+ "alarmBonnet": "Panser",
+ "alarmFootBrake": "Bremsepedal",
+ "alarmFuelLeak": "Drivstofflekasje",
+ "alarmTampering": "Tukling",
+ "alarmRemoving": "Fjerner",
+ "notificationType": "Varseltype",
+ "notificationAlways": "Alle enheter",
+ "notificationNotificators": "Kanaler",
+ "notificatorCommand": "Command",
+ "notificatorWeb": "Web",
+ "notificatorMail": "Mail",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Replay",
+ "reportCombined": "Combined",
+ "reportRoute": "Rute",
+ "reportEvents": "Hendelser",
+ "reportTrips": "Turer",
+ "reportStops": "Stopp",
+ "reportSummary": "Oppsumering",
+ "reportDaily": "Daily Summary",
+ "reportChart": "Diagram",
+ "reportConfigure": "Sett opp",
+ "reportEventTypes": "Hendelsestyper",
+ "reportChartType": "Diagramtype",
+ "reportShowMarkers": "Vis markører",
+ "reportExport": "Eksporter",
+ "reportEmail": "E-post-rapport",
+ "reportSchedule": "Schedule",
+ "reportPeriod": "Periode",
+ "reportCustom": "Egendefinert",
+ "reportToday": "Idag",
+ "reportYesterday": "I går",
+ "reportThisWeek": "Denne uken",
+ "reportPreviousWeek": "Forrige uke",
+ "reportThisMonth": "Denne måneden",
+ "reportPreviousMonth": "Forrige måned",
+ "reportDeviceName": "Enhetsnavn",
+ "reportAverageSpeed": "Gjennomsnittshastighet ",
+ "reportMaximumSpeed": "Maksimumshastighet",
+ "reportEngineHours": "Motortimer",
+ "reportDuration": "Varighet",
+ "reportStartDate": "Start Date",
+ "reportStartTime": "Starttidspunkt",
+ "reportStartAddress": "Startadresse",
+ "reportEndTime": "Sluttidspunkt",
+ "reportEndAddress": "Sluttadresse",
+ "reportSpentFuel": "Brukt drivstoff",
+ "reportStartOdometer": "Kilometerteller start",
+ "reportEndOdometer": "Kilometerteller stopp",
+ "statisticsTitle": "Statistikk",
+ "statisticsCaptureTime": "Opptakstidspunkt",
+ "statisticsActiveUsers": "Aktive brukere",
+ "statisticsActiveDevices": "Aktive enheter",
+ "statisticsRequests": "Forespørsler",
+ "statisticsMessagesReceived": "Meldinger mottatt",
+ "statisticsMessagesStored": "Meldinger lagret",
+ "statisticsGeocoder": "Geocoder-forespørsler",
+ "statisticsGeolocation": "Geolocation-forespørsler",
+ "categoryArrow": "Pil",
+ "categoryDefault": "Standard",
+ "categoryAnimal": "Dyr",
+ "categoryBicycle": "Sykkel",
+ "categoryBoat": "Båt",
+ "categoryBus": "Buss",
+ "categoryCar": "Bil",
+ "categoryCamper": "Camper",
+ "categoryCrane": "Kran",
+ "categoryHelicopter": "Helikopter",
+ "categoryMotorcycle": "Motorsykkel",
+ "categoryOffroad": "Terrengkjøretøy",
+ "categoryPerson": "Person",
+ "categoryPickup": "Pickup",
+ "categoryPlane": "Fly",
+ "categoryShip": "Skip",
+ "categoryTractor": "Traktor",
+ "categoryTrain": "Tog",
+ "categoryTram": "Trikk",
+ "categoryTrolleybus": "Strømbuss",
+ "categoryTruck": "Lastebil",
+ "categoryVan": "Varebil",
+ "categoryScooter": "Scooter",
+ "maintenanceStart": "Start",
+ "maintenancePeriod": "Periode"
+} \ No newline at end of file
diff --git a/src/resources/l10n/ne.json b/src/resources/l10n/ne.json
new file mode 100644
index 00000000..0c89df5d
--- /dev/null
+++ b/src/resources/l10n/ne.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "लोड हुँदै ",
+ "sharedHide": "लुकाउने ",
+ "sharedSave": "सुरक्षित गर्ने ",
+ "sharedUpload": "Upload",
+ "sharedSet": "सेट गर्ने ",
+ "sharedCancel": "रद्ध गर्ने ",
+ "sharedCopy": "Copy",
+ "sharedAdd": "थप्ने",
+ "sharedEdit": "सच्याउने",
+ "sharedRemove": "हटाउने ",
+ "sharedRemoveConfirm": "हटाउने हो?",
+ "sharedNoData": "डाटा छैन",
+ "sharedSubject": "Subject",
+ "sharedYes": "हो",
+ "sharedNo": "छैन",
+ "sharedKm": "कि मि ",
+ "sharedMi": "माइल",
+ "sharedNmi": "नटिकल माइल",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "किलो नट ",
+ "sharedKmh": "कि मि /घण्टा ",
+ "sharedMph": "माइल /घण्टा ",
+ "sharedHour": "घण्टा ",
+ "sharedMinute": "मिनेट ",
+ "sharedSecond": "सेकेन्ड ",
+ "sharedDays": "दिन ",
+ "sharedHours": "घण्टा ",
+ "sharedMinutes": "मिनेट",
+ "sharedDecimalDegrees": "डेसिमल डिग्री ",
+ "sharedDegreesDecimalMinutes": "डिग्री डेसिमल मिनेट ",
+ "sharedDegreesMinutesSeconds": "डिग्री मिनेट सेकेन्ड ",
+ "sharedName": "नाम ",
+ "sharedDescription": "विवरण ",
+ "sharedSearch": "खोज्ने ",
+ "sharedIconScale": "आइकन स्केल",
+ "sharedGeofence": "भू परिधि ",
+ "sharedGeofences": "भू परिधिहरू ",
+ "sharedCreateGeofence": "geofence सिर्जना गर्नुहोस्",
+ "sharedNotifications": "सूचनाहरु ",
+ "sharedNotification": "सूचना ",
+ "sharedAttributes": "विशेषताहरू",
+ "sharedAttribute": "विशेषता",
+ "sharedDrivers": "चालकहरु ",
+ "sharedDriver": "चालक ",
+ "sharedArea": "क्षेत्र ",
+ "sharedSound": "सूचना ध्वनि ",
+ "sharedType": "प्रकार ",
+ "sharedDistance": "दूरी ",
+ "sharedHourAbbreviation": "घ ",
+ "sharedMinuteAbbreviation": "मि",
+ "sharedSecondAbbreviation": "से ",
+ "sharedVoltAbbreviation": "भोल्ट ",
+ "sharedLiterAbbreviation": "ई",
+ "sharedGallonAbbreviation": "ग्या",
+ "sharedLiter": "लिटर ",
+ "sharedImpGallon": "ग्यालन ",
+ "sharedUsGallon": "अमेरिकी ग्यालन ",
+ "sharedLiterPerHourAbbreviation": "ई/घ",
+ "sharedGetMapState": "नक्साको स्थिति पाउने ",
+ "sharedComputedAttribute": "गणना गरिएको विशेषता",
+ "sharedComputedAttributes": "गणना गरिएको विशेषताहरू",
+ "sharedCheckComputedAttribute": "गणना गरिएको विशेषता जाँच गर्नुहोस्",
+ "sharedExpression": "अभिव्यक्ति",
+ "sharedDevice": "यन्त्र ",
+ "sharedTest": "परीक्षण",
+ "sharedTestNotification": "परीक्षण सूचना पठाउनुहोस्",
+ "sharedTestNotificators": "परीक्षण च्यानलहरू",
+ "sharedTestExpression": "परीक्षण अभिव्यक्ति",
+ "sharedCalendar": "पात्रो",
+ "sharedCalendars": "पात्रोहरू",
+ "sharedFile": "फाइल",
+ "sharedSearchDevices": "खोज उपकरण",
+ "sharedSortBy": "द्वारा क्रमबद्ध",
+ "sharedFilterMap": "नक्सामा फिल्टर गर्नुहोस्",
+ "sharedSelectFile": "फाइल चयन गर्नुहोस्",
+ "sharedPhone": "फोन",
+ "sharedRequired": "आवश्यक छ",
+ "sharedPreferences": "प्राथमिकताहरू",
+ "sharedPermissions": "अनुमतिहरू",
+ "sharedConnections": "जडानहरू",
+ "sharedExtra": "अतिरिक्त",
+ "sharedPrimary": "प्राथमिक",
+ "sharedSecondary": "माध्यमिक",
+ "sharedTypeString": "स्ट्रिंग",
+ "sharedTypeNumber": "संख्या",
+ "sharedTypeBoolean": "बूलियन",
+ "sharedTimezone": "समय क्षेत्र",
+ "sharedInfoTitle": "जानकारी",
+ "sharedSavedCommand": "संचित आदेश",
+ "sharedSavedCommands": "संचित आदेशहरू",
+ "sharedNew": "नयाँ...",
+ "sharedShowAddress": "ठेगाना देखाउनुहोस्",
+ "sharedShowDetails": "थप विवरण",
+ "sharedDisabled": "अक्षम",
+ "sharedMaintenance": "मर्मत",
+ "sharedDeviceAccumulators": "सञ्चयकर्ताहरू",
+ "sharedAlarms": "अलार्म",
+ "sharedLocation": "स्थान",
+ "sharedImport": "आयात",
+ "sharedColumns": "स्तम्भहरू",
+ "sharedDropzoneText": "यहाँ एउटा फाइल तान्नुहोस् र छोड्नुहोस् वा क्लिक गर्नुहोस्",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "सरल",
+ "calendarRecurrence": "पुनरावृत्ति",
+ "calendarOnce": "एक पटक",
+ "calendarDaily": "दैनिक",
+ "calendarWeekly": "साप्ताहिक",
+ "calendarMonthly": "मासिक",
+ "calendarDays": "दिनहरू",
+ "calendarSunday": "आइतबार",
+ "calendarMonday": "सोमबार",
+ "calendarTuesday": "मंगलबार",
+ "calendarWednesday": "बुधबार",
+ "calendarThursday": "बिहीबार",
+ "calendarFriday": "शुक्रबार",
+ "calendarSaturday": "शनिबार",
+ "attributeShowGeofences": "Show Geofences",
+ "attributeSpeedLimit": "गति सिमित",
+ "attributeFuelDropThreshold": "इन्धन ड्रप थ्रेसहोल्ड",
+ "attributeFuelIncreaseThreshold": "इन्धन वृद्धि थ्रेसहोल्ड",
+ "attributePolylineDistance": "बहुरेखा दूरि",
+ "attributeReportIgnoreOdometer": "प्रतिबेदन: ओडोमिटर वेवास्ता",
+ "attributeWebReportColor": "वेब: प्रतिबेदन रङ्ग",
+ "attributeDevicePassword": "यन्त्र गोप्य शब्द",
+ "attributeDeviceImage": "उपकरण फोटो",
+ "attributeDeviceInactivityStart": "यन्त्र निष्क्रियता सुरु",
+ "attributeDeviceInactivityPeriod": "यन्त्र निष्क्रियता अवधि",
+ "attributeProcessingCopyAttributes": "प्रशोधन: प्रतिलिपि विशेषताहरू",
+ "attributeColor": "रङ्ग",
+ "attributeWebLiveRouteLength": "वेब: प्रत्यक्ष मार्ग लम्बाई",
+ "attributeWebSelectZoom": "वेब: जूम अन चयन गर्नुहोस्",
+ "attributeWebMaxZoom": "वेब: अधिकतम जुम",
+ "attributeTelegramChatId": "टेलिग्राम च्याट आईडी",
+ "attributePushoverUserKey": "Pushover प्रयोगकर्ता कुञ्जी",
+ "attributePushoverDeviceNames": "Pushover उपकरण नामहरू",
+ "attributeMailSmtpHost": "मेल: SMTP होस्ट",
+ "attributeMailSmtpPort": "मेल: SMTP पोर्ट",
+ "attributeMailSmtpStarttlsEnable": "मेल: SMTP STARTTLS सक्षम गर्नुहोस्",
+ "attributeMailSmtpStarttlsRequired": "मेल: SMTP STARTTLS आवश्यक छ",
+ "attributeMailSmtpSslEnable": "मेल: SMTP SSL सक्षम गर्नुहोस्",
+ "attributeMailSmtpSslTrust": "मेल: SMTP SSL ट्रस्ट",
+ "attributeMailSmtpSslProtocols": "मेल: SMTP SSL प्रोटोकलहरू",
+ "attributeMailSmtpFrom": "मेल: SMTP बाट",
+ "attributeMailSmtpAuth": "मेल: SMTP प्रमाणीकरण सक्षम गर्नुहोस्",
+ "attributeMailSmtpUsername": "मेल: SMTP प्रयोगकर्ता नाम",
+ "attributeMailSmtpPassword": "मेल: SMTP पासवर्ड",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: विशेषताहरू असक्षम गर्नुहोस्",
+ "attributeUiDisableGroups": "UI: समूहहरू असक्षम पार्नुहोस्",
+ "attributeUiDisableEvents": "UI: अक्षम घटनाहरू",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "UI: अक्षम ड्राईभरहरू",
+ "attributeUiDisableComputedAttributes": "UI: गणना गरिएका विशेषताहरू असक्षम पार्नुहोस्",
+ "attributeUiDisableCalendars": "UI: अक्षम पात्रो",
+ "attributeUiDisableMaintenance": "UI: अक्षम मर्मत",
+ "attributeUiHidePositionAttributes": "UI: स्थिति विशेषताहरू लुकाउनुहोस्",
+ "attributeUiDisableLoginLanguage": "UI: लगइन भाषा असक्षम गर्नुहोस्",
+ "attributeNotificationTokens": "सूचना टोकनहरू",
+ "attributePopupInfo": "Popup Info",
+ "errorTitle": "त्रुटी",
+ "errorGeneral": "अवैध प्यारामिटर वा बाधा उल्लङ्घन",
+ "errorConnection": "जडान मा त्रुटी भयो ",
+ "errorSocket": "वेब सकेट जडान त्रुटि",
+ "errorZero": "शून्य हुन सक्दैन",
+ "userEmail": "इ मेल ",
+ "userPassword": "गोप्य शब्द ",
+ "userAdmin": "ब्यबस्थापक",
+ "userRemember": "सम्झनु",
+ "userExpirationTime": "मिति समाप्ति",
+ "userDeviceLimit": "यन्त्र सिमित",
+ "userUserLimit": "प्रयोगकर्ता सिमित",
+ "userDeviceReadonly": "यन्त्र पढ्ने मात्र",
+ "userLimitCommands": "सिमित आदेशहरू",
+ "userDisableReports": "रिपोर्टहरू असक्षम गर्नुहोस्",
+ "userFixedEmail": "कुनै इमेल परिवर्तन छैन",
+ "userToken": "टोकन",
+ "userDeleteAccount": "खाता मेटाउनुहोस्",
+ "userTemporary": "Temporary",
+ "loginTitle": "लगिन गर्ने ",
+ "loginLanguage": "भाषा ",
+ "loginReset": "पासवर्ड रिसेट",
+ "loginRegister": "दर्ता गर्ने",
+ "loginLogin": "भित्रिने ",
+ "loginOpenId": "Login with OpenID",
+ "loginFailed": "इ मेल वा गोप्य शब्द गलत भयो ",
+ "loginCreated": "नया प्रयोगकर्ता दर्ता भयो ",
+ "loginResetSuccess": "आफ्नो इमेल जाँच गर्नुहोस्",
+ "loginUpdateSuccess": "नयाँ पासवर्ड सेट गरिएको छ",
+ "loginLogout": "बाहिरिने ",
+ "loginLogo": "लोगो",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "यन्त्रहरू तथा अवस्था ",
+ "deviceSelected": "Selected Device",
+ "deviceTitle": "यन्त्रहरू ",
+ "devicePrimaryInfo": "Device Title",
+ "deviceSecondaryInfo": "Device Detail",
+ "deviceIdentifier": "परिचायक ",
+ "deviceModel": "मोडेल",
+ "deviceContact": "सम्पर्क गर्नुहोस्",
+ "deviceCategory": "वर्ग",
+ "deviceLastUpdate": "अन्तिम अपडेट ",
+ "deviceCommand": "आदेश ",
+ "deviceFollow": "पिछा गर्ने ",
+ "deviceTotalDistance": "कुल दूरि",
+ "deviceStatus": "स्थिति",
+ "deviceStatusOnline": "अनलाईन",
+ "deviceStatusOffline": "अफलाईन",
+ "deviceStatusUnknown": "अज्ञात",
+ "deviceRegisterFirst": "Register your first device",
+ "deviceIdentifierHelp": "IMEI, serial number or other id. It has to match the identifier device reports to the server.",
+ "deviceShare": "Share Device",
+ "groupDialog": "समुह",
+ "groupParent": "समुह",
+ "groupNoGroup": "समुह नभएको",
+ "settingsTitle": "सेटिङ्ग ",
+ "settingsUser": "खाता ",
+ "settingsGroups": "समुहहरू",
+ "settingsServer": "सर्भर ",
+ "settingsUsers": "प्रयोगकर्ताहरु ",
+ "settingsDistanceUnit": "दूरि एकाई",
+ "settingsAltitudeUnit": "उचाई एकाइ",
+ "settingsSpeedUnit": "गति एकाई",
+ "settingsVolumeUnit": "आयातन एकाई",
+ "settingsTwelveHourFormat": "12-घण्टे ढाचाँ",
+ "settingsCoordinateFormat": "निर्देशांक ढाचाँ",
+ "settingsServerVersion": "Server Version",
+ "settingsAppVersion": "App Version",
+ "settingsConnection": "Connection",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "प्रतिबेदनहरु ",
+ "reportScheduled": "Scheduled Reports",
+ "reportDevice": "यन्त्र ",
+ "reportGroup": "समुह",
+ "reportFrom": "बाट ",
+ "reportTo": "सम्म ",
+ "reportShow": "देखाउने ",
+ "reportClear": "सफा गर्ने ",
+ "linkGoogleMaps": "गुगल नक्शा",
+ "linkAppleMaps": "एप्पल नक्शा",
+ "linkStreetView": "सडक दृश्य",
+ "positionFixTime": "समय तय गर्नुहोस्",
+ "positionDeviceTime": "उपकरण समय",
+ "positionServerTime": "सर्भर समय",
+ "positionValid": "ठिक",
+ "positionAccuracy": "शुद्धता",
+ "positionLatitude": "अक्षांश",
+ "positionLongitude": "देशान्तर ",
+ "positionAltitude": "उचाई ",
+ "positionSpeed": "गति ",
+ "positionCourse": "दिशा ",
+ "positionAddress": "ठेगाना ",
+ "positionProtocol": "प्रोटोकल ",
+ "positionDistance": "दूरि",
+ "positionRpm": "RPM",
+ "positionFuel": "ईन्धन",
+ "positionPower": "शक्ति",
+ "positionBattery": "ब्याट्री",
+ "positionRaw": "कच्चा",
+ "positionIndex": "अनुक्रमणिका",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "भू-उपग्रहहरू",
+ "positionSatVisible": "देखिने भू-उपग्रहहरू",
+ "positionRssi": "RSSI",
+ "positionGps": "जिपियस्",
+ "positionRoaming": "Roaming",
+ "positionEvent": "घटना",
+ "positionAlarm": "अलार्म",
+ "positionStatus": "स्थिति",
+ "positionOdometer": "ओडोमिटर",
+ "positionServiceOdometer": "Service ओडोमीटर",
+ "positionTripOdometer": "ट्रिप ओडोमीटर",
+ "positionHours": "घण्टा",
+ "positionSteps": "पाइलाहरू",
+ "positionInput": "निवेश",
+ "positionHeartRate": "Heart Rate",
+ "positionOutput": "आउटपुट",
+ "positionBatteryLevel": "ब्याट्री स्तर",
+ "positionFuelConsumption": "इन्धन खपत",
+ "positionRfid": "RFID",
+ "positionVersionFw": "फर्मवेयर संस्करण",
+ "positionVersionHw": "हार्डवेयर संस्करण",
+ "positionIgnition": "प्रज्वलन",
+ "positionFlags": "झण्डाहरू",
+ "positionCharge": "चार्ज",
+ "positionIp": "IP",
+ "positionArchive": "अभिलेख",
+ "positionVin": "VIN",
+ "positionApproximate": "अनुमान",
+ "positionThrottle": "थ्रोटल",
+ "positionMotion": "गति",
+ "positionArmed": "सशस्त्र",
+ "positionAcceleration": "प्रवेग",
+ "positionTemp": "तापक्रम",
+ "positionDeviceTemp": "यन्त्र तापक्रम",
+ "positionCoolantTemp": "Coolant Temperature",
+ "positionOperator": "सञ्चालक",
+ "positionCommand": "आदेश",
+ "positionBlocked": "अवरुद्ध",
+ "positionDtcs": "DTCs",
+ "positionObdSpeed": "OBD गति",
+ "positionObdOdometer": "OBD ओडोमिटर",
+ "positionDrivingTime": "Driving Time",
+ "positionDriverUniqueId": "चालक अद्वितीय आईडी",
+ "positionCard": "Card",
+ "positionImage": "छवि",
+ "positionVideo": "भिडियो",
+ "positionAudio": "ध्वनि",
+ "serverTitle": "सर्भर सेटिंग",
+ "serverZoom": "ठुलो बनाउने ",
+ "serverRegistration": "दर्ता ",
+ "serverReadonly": "पढ्ने मात्रै ",
+ "serverForceSettings": "बल सेटिङहरू",
+ "serverAnnouncement": "घोषणा",
+ "serverName": "Server Name",
+ "serverDescription": "Server Description",
+ "serverColorPrimary": "Color Primary",
+ "serverColorSecondary": "Color Secondary",
+ "serverLogo": "Logo Image",
+ "serverLogoInverted": "Inverted Logo Image",
+ "serverChangeDisable": "Disable Server Change",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "नक्शा ",
+ "mapActive": "सक्रिय नक्शा",
+ "mapOverlay": "नक्सा ओभरले",
+ "mapOverlayCustom": "अनुकूलन ओभरले",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API कुञ्जी",
+ "mapOpenWeatherClouds": "OpenWeather बादलहरू",
+ "mapOpenWeatherPrecipitation": "OpenWeather वर्षा",
+ "mapOpenWeatherPressure": "OpenWeather दबाब",
+ "mapOpenWeatherWind": "OpenWeather पवन",
+ "mapOpenWeatherTemperature": "OpenWeather तापमान",
+ "mapLayer": "नक्शा को तह ",
+ "mapCustom": "अनुकूलन (XYZ)",
+ "mapCustomArcgis": "अनुकूलन (ArcGIS)",
+ "mapCustomLabel": "अनुकूल नक्शा",
+ "mapCarto": "कार्टो आधारनक्सा",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "बिंग नक्शाको चाबी (कि) ",
+ "mapBingRoad": "बिंग नक्शा (सडक)",
+ "mapBingAerial": "बिंग नक्शा (एरियल)",
+ "mapBingHybrid": "Bing Maps Hybrid",
+ "mapBaidu": "बाईडु",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "यान्डेक्स नक्सा",
+ "mapYandexSat": "यान्डेक्स भू-उपग्रह",
+ "mapWikimedia": "विकिमिडिया",
+ "mapOrdnanceSurvey": "आयुध सर्वेक्षण",
+ "mapMapboxStreets": "म्यापबक्स सडकहरू",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "आउटडोर आउटडोर",
+ "mapMapboxSatellite": "म्यापबक्स उपग्रह",
+ "mapMapboxKey": "म्यापबक्स पहुँच टोकन",
+ "mapMapTilerBasic": "MapTiler आधारभूत",
+ "mapMapTilerHybrid": "MapTiler हाइब्रिड",
+ "mapMapTilerKey": "MapTiler API कुञ्जी",
+ "mapLocationIqStreets": "स्थानIQ सडकहरू",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ पहुँच टोकन",
+ "mapTomTomBasic": "टमटम बेसिक",
+ "mapTomTomFlow": "TomTom यातायात प्रवाह",
+ "mapTomTomIncidents": "टमटम ट्राफिक घटनाहरू",
+ "mapTomTomKey": "TomTom API Key",
+ "mapHereBasic": "Here Basic",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satellite",
+ "mapHereFlow": "यहाँ ट्राफिक प्रवाह",
+ "mapHereKey": "Here API Key",
+ "mapShapePolygon": "बहुभुज",
+ "mapShapeCircle": "वृत्त",
+ "mapShapePolyline": "बहुरेखा",
+ "mapLiveRoutes": "प्रत्यक्ष मार्गहरू",
+ "mapDirection": "Show Direction",
+ "mapCurrentLocation": "वर्तमान स्थान",
+ "mapPoiLayer": "POI तह",
+ "mapClustering": "मार्कर क्लस्टरिङ",
+ "mapOnSelect": "चयनमा नक्सा देखाउनुहोस्",
+ "mapDefault": "Default Map",
+ "stateTitle": "अवस्था ",
+ "stateName": "बिषेशता",
+ "stateValue": "मूल्य ",
+ "commandTitle": "आदेश ",
+ "commandSend": "पठाउने ",
+ "commandSent": "आदेश पठाइयो",
+ "commandQueued": "कमाण्ड पङ्क्तिबद्ध",
+ "commandUnit": "इकाई ",
+ "commandCustom": "अनुकुल आदेश",
+ "commandDeviceIdentification": "यन्त्र पहिचान",
+ "commandPositionSingle": "एकल प्रतिबेदन",
+ "commandPositionPeriodic": "आवधिक प्रतिबेदन ",
+ "commandPositionStop": "प्रतिबेदन बन्द गर्ने ",
+ "commandEngineStop": "इन्जिन बन्द गर्ने ",
+ "commandEngineResume": "इन्जिन खोल्ने ",
+ "commandAlarmArm": "हात चेतावनी",
+ "commandAlarmDisarm": "निशस्त्र अलार्म",
+ "commandAlarmDismiss": "अलार्म खारेज गर्नुहोस्",
+ "commandSetTimezone": "समय क्षेत्र सेट गर्नुहोस्",
+ "commandRequestPhoto": "फोटो अनुरोध",
+ "commandPowerOff": "पावर अफ यन्त्र",
+ "commandRebootDevice": " यन्त्र रिबुट गर्नुहोस्",
+ "commandFactoryReset": "Factory रिसेट",
+ "commandSendSms": "एसएमएस पठाउनुहोस्",
+ "commandSendUssd": "USSD पठाउनुहोस्",
+ "commandSosNumber": "SOS नम्बर सेट गर्नुहोस्",
+ "commandSilenceTime": "मौन समय सेट गर्नुहोस्",
+ "commandSetPhonebook": "फोनबुक सेट गर्नुहोस्",
+ "commandVoiceMessage": "आवाज सन्देश",
+ "commandOutputControl": "आउटपुट नियन्त्रण",
+ "commandVoiceMonitoring": "आवाज निगरानी",
+ "commandSetAgps": "जिपियस् सेट गर्नुहोस्",
+ "commandSetIndicator": "सूचक सेट गर्नुहोस्",
+ "commandConfiguration": "कन्फिगरेसन",
+ "commandGetVersion": "संस्करण प्राप्त गर्नुहोस्",
+ "commandFirmwareUpdate": "फर्मवेयर अपडेट गर्नुहोस्",
+ "commandSetConnection": "जडान सेट गर्नुहोस्",
+ "commandSetOdometer": "ओडोमिटर सेट गर्नुहोस्",
+ "commandGetModemStatus": "मोदेमको स्थिति जान्नुहोस्",
+ "commandGetDeviceStatus": "यन्त्रको स्थिति जान्नुहोस्",
+ "commandSetSpeedLimit": "गति सीमा सेट गर्नुहोस्",
+ "commandModePowerSaving": "पावर बचत मोड",
+ "commandModeDeepSleep": "गहिरो निद्रा मोड",
+ "commandAlarmGeofence": "Geofence अलार्म सेट गर्नुहोस्",
+ "commandAlarmBattery": "ब्याट्री अलार्म सेट गर्नुहोस्",
+ "commandAlarmSos": "SOS अलार्म सेट गर्नुहोस्",
+ "commandAlarmRemove": "अलार्म हटाउनुहोस् सेट गर्नुहोस्",
+ "commandAlarmClock": "घडी अलार्म सेट गर्नुहोस्",
+ "commandAlarmSpeed": "गति अलार्म सेट गर्नुहोस्",
+ "commandAlarmFall": "पतन अलार्म सेट गर्नुहोस्",
+ "commandAlarmVibration": "कम्पन अलार्म सेट गर्नुहोस्",
+ "commandFrequency": "आव्रती ",
+ "commandTimezone": "समय क्षेत्र अफसेट",
+ "commandMessage": "सन्देश",
+ "commandRadius": "त्रिज्या",
+ "commandEnable": "सक्षम गर्नुहोस्",
+ "commandData": "डाटा",
+ "commandIndex": "अनुक्रमणिका",
+ "commandPhone": "फोन नम्बर",
+ "commandServer": "सर्भर",
+ "commandPort": "पोर्ट",
+ "eventAll": "सबै घटना",
+ "eventDeviceOnline": "अनलाईन स्थिति",
+ "eventDeviceUnknown": "अज्ञात स्थिति",
+ "eventDeviceOffline": "अफलाईन स्थिति",
+ "eventDeviceInactive": "यन्त्र निष्क्रिय छ",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "यन्त्र चलिरहेको छ",
+ "eventDeviceStopped": "यन्त्र रोकियो",
+ "eventDeviceOverspeed": "गति सिमा नाघ्यो",
+ "eventDeviceFuelDrop": "इन्धन खस्यो",
+ "eventDeviceFuelIncrease": "इन्धन बढ्यो",
+ "eventCommandResult": "आदेश परिणाम",
+ "eventGeofenceEnter": "Geofence प्रवेश गर्नुभयो",
+ "eventGeofenceExit": "Geofence बाहिर निस्कियो",
+ "eventAlarm": "अलार्म",
+ "eventIgnitionOn": "इग्निशन अन",
+ "eventIgnitionOff": "इग्निशन बन्द",
+ "eventMaintenance": "मर्मत आवश्यक",
+ "eventTextMessage": "पाठ सन्देश प्राप्त भयो",
+ "eventDriverChanged": "चालक परिवर्तन भयो",
+ "eventMedia": "मिडिया",
+ "eventsScrollToLast": "अन्तिममा स्क्रोल गर्नुहोस्",
+ "eventsSoundEvents": "Sound Events",
+ "eventsSoundAlarms": "Sound Alarms",
+ "alarmGeneral": "सामान्य",
+ "alarmSos": "SOS",
+ "alarmVibration": "कम्पन",
+ "alarmMovement": "आन्दोलन",
+ "alarmLowspeed": "कम गति",
+ "alarmOverspeed": "अधिक गति",
+ "alarmFallDown": "तल झर्नु",
+ "alarmLowPower": "कम शक्ति",
+ "alarmLowBattery": "कम ब्याट्री",
+ "alarmFault": "गल्ती",
+ "alarmPowerOff": "पावर अफ",
+ "alarmPowerOn": "चालु गर",
+ "alarmDoor": "ढोका",
+ "alarmLock": "ताला",
+ "alarmUnlock": "अनलक गर्नुहोस्",
+ "alarmGeofence": "जियोफेन्स",
+ "alarmGeofenceEnter": "Geofence प्रविष्ट गर्नुहोस्",
+ "alarmGeofenceExit": "Geofence निकास",
+ "alarmGpsAntennaCut": "काटिएको जिपियस् आन्टेना",
+ "alarmAccident": "दुर्घटना",
+ "alarmTow": "टो",
+ "alarmIdle": "निष्क्रिय",
+ "alarmHighRpm": "उच्च RPM",
+ "alarmHardAcceleration": "हार्ड एक्सेलेरेशन",
+ "alarmHardBraking": "कडा ब्रेकिङ",
+ "alarmHardCornering": "कडा कुना",
+ "alarmLaneChange": "लेन परिवर्तन",
+ "alarmFatigueDriving": "थकान ड्राइभिङ",
+ "alarmPowerCut": "पावर कट",
+ "alarmPowerRestored": "शक्ति पुनर्स्थापित",
+ "alarmJamming": "जामिङ",
+ "alarmTemperature": "तापक्रम",
+ "alarmParking": "पार्किङ",
+ "alarmBonnet": "बोनेट",
+ "alarmFootBrake": "फुट ब्रेक",
+ "alarmFuelLeak": "इन्धन चुहावट",
+ "alarmTampering": "छेडछाड",
+ "alarmRemoving": "हटाउँदै",
+ "notificationType": "सुचना प्रकार",
+ "notificationAlways": "सबै उपकरणहरू",
+ "notificationNotificators": "च्यानलहरू",
+ "notificatorCommand": "Command",
+ "notificatorWeb": "वेब",
+ "notificatorMail": "पत्र",
+ "notificatorSms": "यसएमयस",
+ "notificatorFirebase": "फायरबेस",
+ "notificatorTraccar": "ट्रयाकर",
+ "notificatorTelegram": "टेलिग्राम",
+ "notificatorPushover": "पुशओभर",
+ "reportReplay": "रिप्ले",
+ "reportCombined": "Combined",
+ "reportRoute": "रुट",
+ "reportEvents": "घटनाहरू",
+ "reportTrips": "यात्राहरू",
+ "reportStops": "रोकिन्छ",
+ "reportSummary": "सारांश",
+ "reportDaily": "दैनिक सारांश",
+ "reportChart": "मानचित्र",
+ "reportConfigure": "कन्फिगर ",
+ "reportEventTypes": "घटना प्रकार",
+ "reportChartType": "मानचित्र प्रकार",
+ "reportShowMarkers": "मार्करहरू देखाउनुहोस्",
+ "reportExport": "निकास",
+ "reportEmail": "प्रतिबेदन ई-मेल गर्ने",
+ "reportSchedule": "Schedule",
+ "reportPeriod": "अवधि",
+ "reportCustom": "अनुकुल",
+ "reportToday": "आज",
+ "reportYesterday": "हिजो",
+ "reportThisWeek": "हालको हप्ता",
+ "reportPreviousWeek": "अघिल्लो हप्ता",
+ "reportThisMonth": "हालको महिना",
+ "reportPreviousMonth": "अघिल्लो महिना",
+ "reportDeviceName": "यन्त्र नाम",
+ "reportAverageSpeed": "औसत गति",
+ "reportMaximumSpeed": "अधिकतम गति",
+ "reportEngineHours": "इन्जिन घण्टा",
+ "reportDuration": "अवधि",
+ "reportStartDate": "सुरू मिति",
+ "reportStartTime": "शुरु समय",
+ "reportStartAddress": "शुरु ठेगाना",
+ "reportEndTime": "अन्त्य समय",
+ "reportEndAddress": "अन्त्य ठेगाना",
+ "reportSpentFuel": "खर्चित ईन्धन",
+ "reportStartOdometer": "ओडोमिटर सुरु",
+ "reportEndOdometer": "ओडोमिटर अन्त्य",
+ "statisticsTitle": "तथ्याङ्क ",
+ "statisticsCaptureTime": "समय कब्जा",
+ "statisticsActiveUsers": "सक्रिय प्रयोगकर्ता",
+ "statisticsActiveDevices": "सक्रिय यन्त्रहरू",
+ "statisticsRequests": "अनुरोधहरू",
+ "statisticsMessagesReceived": "सन्देशहरू प्राप्त भयो",
+ "statisticsMessagesStored": "सन्देशहरू भण्डारण गरियो",
+ "statisticsGeocoder": "जियोकोडर अनुरोधहरू",
+ "statisticsGeolocation": "भौगोलिक स्थान अनुरोधहरू",
+ "categoryArrow": "तीर",
+ "categoryDefault": "पुर्वनिर्धारित",
+ "categoryAnimal": "जनावर ",
+ "categoryBicycle": "साइकल ",
+ "categoryBoat": "डुंगा ",
+ "categoryBus": "बस ",
+ "categoryCar": "कार ",
+ "categoryCamper": "Camper",
+ "categoryCrane": "क्रेन ",
+ "categoryHelicopter": "हेलिकप्टर ",
+ "categoryMotorcycle": "मोटरसाइकल ",
+ "categoryOffroad": "कच्ची बाटो ",
+ "categoryPerson": "व्यक्ति ",
+ "categoryPickup": "पिकअप ",
+ "categoryPlane": "हवाइजहाज ",
+ "categoryShip": "जहाज ",
+ "categoryTractor": "ट्रयाक्टर",
+ "categoryTrain": "रेल",
+ "categoryTram": "ट्राम",
+ "categoryTrolleybus": "ट्रलिबस",
+ "categoryTruck": "ट्रक ",
+ "categoryVan": "भ्यान ",
+ "categoryScooter": "स्कुटर",
+ "maintenanceStart": "शुरु",
+ "maintenancePeriod": "अवधि"
+} \ No newline at end of file
diff --git a/src/resources/l10n/nl.json b/src/resources/l10n/nl.json
new file mode 100644
index 00000000..224adc26
--- /dev/null
+++ b/src/resources/l10n/nl.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "Laden...",
+ "sharedHide": "Verberg",
+ "sharedSave": "Opslaan",
+ "sharedUpload": "Upload",
+ "sharedSet": "Instellen",
+ "sharedCancel": "Annuleren",
+ "sharedCopy": "Copy",
+ "sharedAdd": "Toevoegen",
+ "sharedEdit": "Bewerken",
+ "sharedRemove": "Verwijderen",
+ "sharedRemoveConfirm": "Item verwijderen?",
+ "sharedNoData": "Geen gegevens",
+ "sharedSubject": "Subject",
+ "sharedYes": "Ja",
+ "sharedNo": "Nee",
+ "sharedKm": "km",
+ "sharedMi": "mijl",
+ "sharedNmi": "nmi",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "knopen",
+ "sharedKmh": "km/h",
+ "sharedMph": "mijl per uur",
+ "sharedHour": "Uur",
+ "sharedMinute": "Minuut",
+ "sharedSecond": "Seconde",
+ "sharedDays": "dagen",
+ "sharedHours": "uren",
+ "sharedMinutes": "minuten",
+ "sharedDecimalDegrees": "Decimale graden",
+ "sharedDegreesDecimalMinutes": "Graden decimale minuten",
+ "sharedDegreesMinutesSeconds": "Graden minuten/seconden",
+ "sharedName": "Naam",
+ "sharedDescription": "Omschrijving",
+ "sharedSearch": "Zoeken",
+ "sharedIconScale": "Schaal van iconen",
+ "sharedGeofence": "Geografisch gebied",
+ "sharedGeofences": "Geografische gebieden",
+ "sharedCreateGeofence": "Maak geofence",
+ "sharedNotifications": "Notificatie",
+ "sharedNotification": "Notificatie",
+ "sharedAttributes": "Attributen",
+ "sharedAttribute": "Attribuut",
+ "sharedDrivers": "Bestuurders",
+ "sharedDriver": "Bestuurder",
+ "sharedArea": "Gebied",
+ "sharedSound": "Notificatiegeluid",
+ "sharedType": "Type",
+ "sharedDistance": "Afstand",
+ "sharedHourAbbreviation": "u",
+ "sharedMinuteAbbreviation": "m",
+ "sharedSecondAbbreviation": "s",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "l",
+ "sharedGallonAbbreviation": "gal",
+ "sharedLiter": "liter",
+ "sharedImpGallon": "imperial gallon",
+ "sharedUsGallon": "U.S. gallon",
+ "sharedLiterPerHourAbbreviation": "l/u",
+ "sharedGetMapState": "Haal kaartstatus op",
+ "sharedComputedAttribute": "Berekend attribuut",
+ "sharedComputedAttributes": "Berekende attributen",
+ "sharedCheckComputedAttribute": "Controleer berekend attribuut",
+ "sharedExpression": "Uitdrukking",
+ "sharedDevice": "Apparaat",
+ "sharedTest": "Test",
+ "sharedTestNotification": "Stuur testnotificatie",
+ "sharedTestNotificators": "Testkanalen",
+ "sharedTestExpression": "Testexpressie",
+ "sharedCalendar": "Kalender",
+ "sharedCalendars": "Kalenders",
+ "sharedFile": "Bestand",
+ "sharedSearchDevices": "Zoek apparaten",
+ "sharedSortBy": "Sorteer op",
+ "sharedFilterMap": "Filter op kaart",
+ "sharedSelectFile": "Selecteer bestand",
+ "sharedPhone": "Telefoon",
+ "sharedRequired": "Verplicht",
+ "sharedPreferences": "Voorkeuren",
+ "sharedPermissions": "Rechten",
+ "sharedConnections": "Connecties",
+ "sharedExtra": "Extra",
+ "sharedPrimary": "Primair",
+ "sharedSecondary": "Secundair",
+ "sharedTypeString": "Tekst",
+ "sharedTypeNumber": "Getal",
+ "sharedTypeBoolean": "Boolean",
+ "sharedTimezone": "Tijdzone",
+ "sharedInfoTitle": "Info",
+ "sharedSavedCommand": "Opgeslagen commando",
+ "sharedSavedCommands": "Opgeslagen commando's",
+ "sharedNew": "Nieuw...",
+ "sharedShowAddress": "Toon adres",
+ "sharedShowDetails": "Meer details",
+ "sharedDisabled": "Uitgeschakeld",
+ "sharedMaintenance": "Onderhoud",
+ "sharedDeviceAccumulators": "Accumulators",
+ "sharedAlarms": "Alarmen",
+ "sharedLocation": "Locatie",
+ "sharedImport": "Importeren",
+ "sharedColumns": "Kolommen",
+ "sharedDropzoneText": "Sleep en plaats hier een bestand of klik hier",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "Eenvoudig",
+ "calendarRecurrence": "Herhaling",
+ "calendarOnce": "Eenmalig",
+ "calendarDaily": "Dagelijks",
+ "calendarWeekly": "Wekelijks",
+ "calendarMonthly": "Maandelijks",
+ "calendarDays": "Dagen",
+ "calendarSunday": "Zondag",
+ "calendarMonday": "Maandag",
+ "calendarTuesday": "Dinsdag",
+ "calendarWednesday": "Woensdag",
+ "calendarThursday": "Donderdag",
+ "calendarFriday": "Vrijdag",
+ "calendarSaturday": "Zaterdag",
+ "attributeShowGeofences": "Toon geofences",
+ "attributeSpeedLimit": "Snelheidslimiet",
+ "attributeFuelDropThreshold": "Brandstofafname grens",
+ "attributeFuelIncreaseThreshold": "Brandstoftoename grens",
+ "attributePolylineDistance": "Polylijn afstand",
+ "attributeReportIgnoreOdometer": "Rapport: negeer odometer",
+ "attributeWebReportColor": "Web: rapportkleur",
+ "attributeDevicePassword": "Apparaatwachtwoord",
+ "attributeDeviceImage": "Apparaatafbeelding",
+ "attributeDeviceInactivityStart": "Start van inactiviteit",
+ "attributeDeviceInactivityPeriod": "Inactiviteitperiode",
+ "attributeProcessingCopyAttributes": "Verwerking: kopieer attributen",
+ "attributeColor": "Kleur",
+ "attributeWebLiveRouteLength": "Web: live routelengte",
+ "attributeWebSelectZoom": "Web: zoomen tijdens selecteren",
+ "attributeWebMaxZoom": "Web: maximaal zoomniveau",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Pushover gebruikerssleutel",
+ "attributePushoverDeviceNames": "Pushover apparaatnamen",
+ "attributeMailSmtpHost": "Mail: SMTP host",
+ "attributeMailSmtpPort": "Mail: SMTP poort",
+ "attributeMailSmtpStarttlsEnable": "Mail: SMTP STARTTL ingeschakeld",
+ "attributeMailSmtpStarttlsRequired": "Mail: SMTP STARTTL vereist",
+ "attributeMailSmtpSslEnable": "Mail: SMTP SSL ingeschakeld",
+ "attributeMailSmtpSslTrust": "Mail: SMTP SSL vertrouwd",
+ "attributeMailSmtpSslProtocols": "Mail: SMTP SSL protocollen",
+ "attributeMailSmtpFrom": "Mail: SMTP van",
+ "attributeMailSmtpAuth": "Mail: SMTP authenticatie ingeschakeld",
+ "attributeMailSmtpUsername": "Mail: SMTP gebruikersnaam",
+ "attributeMailSmtpPassword": "Mail: SMTP wachtwoord",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: attributen uitschakelen",
+ "attributeUiDisableGroups": "UI: groepen uitschakelen",
+ "attributeUiDisableEvents": "UI: Gebeurtenissen uitschakelen",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "UI: bestuurders uitschakelen",
+ "attributeUiDisableComputedAttributes": "UI: berekende attributen uitschakelen",
+ "attributeUiDisableCalendars": "UI: kalenders uitschakelen",
+ "attributeUiDisableMaintenance": "UI: Onderhoud uitschakelen",
+ "attributeUiHidePositionAttributes": "UI: Verberg positie-eigenschappen",
+ "attributeUiDisableLoginLanguage": "UI: logintaal uitschakelen",
+ "attributeNotificationTokens": "Notificatietokens",
+ "attributePopupInfo": "Popup info",
+ "errorTitle": "Fout",
+ "errorGeneral": "Ongeldige parameters of overschrijding van beperkingen",
+ "errorConnection": "Verbindingsfout",
+ "errorSocket": "Web socket verbindingsfout",
+ "errorZero": "Mag niet nul zijn",
+ "userEmail": "E-mail",
+ "userPassword": "Wachtwoord",
+ "userAdmin": "Administrator",
+ "userRemember": "Onthouden",
+ "userExpirationTime": "Verloopdatum",
+ "userDeviceLimit": "Apparaatlimiet",
+ "userUserLimit": "Gebruikerlimiet",
+ "userDeviceReadonly": "Apparaat alleen-lezen",
+ "userLimitCommands": "Limiteer commando's",
+ "userDisableReports": "Rapporten uitschakelen",
+ "userFixedEmail": "Geen e-mail aanpassing",
+ "userToken": "Token",
+ "userDeleteAccount": "Geen account",
+ "userTemporary": "Temporary",
+ "loginTitle": "Inloggen",
+ "loginLanguage": "Taal",
+ "loginReset": "Wachtwoord herstellen",
+ "loginRegister": "Registreren",
+ "loginLogin": "Inloggen",
+ "loginOpenId": "Inloggen met OpenID",
+ "loginFailed": "Onjuist e-mailadres of wachtwoord",
+ "loginCreated": "De nieuwe gebruiker is geregistreerd",
+ "loginResetSuccess": "Check je e-mail",
+ "loginUpdateSuccess": "Het wachtwoord is ingesteld",
+ "loginLogout": "Afmelden",
+ "loginLogo": "Logo",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "Apparaten en status",
+ "deviceSelected": "Geselecteerd apparaat",
+ "deviceTitle": "Apparaten",
+ "devicePrimaryInfo": "Apparaattitel",
+ "deviceSecondaryInfo": "Apparaatdetail",
+ "deviceIdentifier": "Identifier",
+ "deviceModel": "Model",
+ "deviceContact": "Contact",
+ "deviceCategory": "Categorie",
+ "deviceLastUpdate": "Laatste update",
+ "deviceCommand": "Commando",
+ "deviceFollow": "Volgen",
+ "deviceTotalDistance": "Totale afstand",
+ "deviceStatus": "Status",
+ "deviceStatusOnline": "Online",
+ "deviceStatusOffline": "Offline",
+ "deviceStatusUnknown": "Onbekend",
+ "deviceRegisterFirst": "Registreer je eerste apparaat",
+ "deviceIdentifierHelp": "IMEI, serienummer of ander id. Het moet overeenkomen met het identificatienummer dat het apparaat rapporteert aan de server.",
+ "deviceShare": "Share Device",
+ "groupDialog": "Groep",
+ "groupParent": "Groep",
+ "groupNoGroup": "Geen groep",
+ "settingsTitle": "Instellingen",
+ "settingsUser": "Account",
+ "settingsGroups": "Groepen",
+ "settingsServer": "Server",
+ "settingsUsers": "Gebruikers",
+ "settingsDistanceUnit": "Afstandseenheid",
+ "settingsAltitudeUnit": "Hoogte eenheid",
+ "settingsSpeedUnit": "Snelheidseenheid",
+ "settingsVolumeUnit": "Volume-eenheid",
+ "settingsTwelveHourFormat": "12-uurs indeling",
+ "settingsCoordinateFormat": "Coördinatenformaat ",
+ "settingsServerVersion": "Serverversie",
+ "settingsAppVersion": "Appversie",
+ "settingsConnection": "Verbinding",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "Rapportages",
+ "reportScheduled": "Scheduled Reports",
+ "reportDevice": "Apparaat",
+ "reportGroup": "Groep",
+ "reportFrom": "Van",
+ "reportTo": "Naar",
+ "reportShow": "Laat zien",
+ "reportClear": "Leegmaken",
+ "linkGoogleMaps": "Google Maps",
+ "linkAppleMaps": "Apple Maps",
+ "linkStreetView": "Street View",
+ "positionFixTime": "Tijd aanpassen",
+ "positionDeviceTime": "Tijd van apparaat",
+ "positionServerTime": "Tijd van server",
+ "positionValid": "Geldig",
+ "positionAccuracy": "Nauwkeurigheid",
+ "positionLatitude": "Breedtegraad",
+ "positionLongitude": "Lengtegraad",
+ "positionAltitude": "Hoogte",
+ "positionSpeed": "Snelheid",
+ "positionCourse": "Koers",
+ "positionAddress": "Adres",
+ "positionProtocol": "Protocol",
+ "positionDistance": "Afstand",
+ "positionRpm": "RPM",
+ "positionFuel": "Brandstof",
+ "positionPower": "Stroom",
+ "positionBattery": "Accu",
+ "positionRaw": "Raw",
+ "positionIndex": "Index",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "Satellieten",
+ "positionSatVisible": "Zichtbare satellieten",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "Roamen",
+ "positionEvent": "Gebeurtenis",
+ "positionAlarm": "Alarm",
+ "positionStatus": "Status",
+ "positionOdometer": "Odometer",
+ "positionServiceOdometer": "Service odometer",
+ "positionTripOdometer": "Reis odometer",
+ "positionHours": "Uur",
+ "positionSteps": "Stappen",
+ "positionInput": "Invoer",
+ "positionHeartRate": "Heart Rate",
+ "positionOutput": "Uitvoer",
+ "positionBatteryLevel": "Accuniveau",
+ "positionFuelConsumption": "Brandstofverbruik",
+ "positionRfid": "RFID",
+ "positionVersionFw": "Firmware versie",
+ "positionVersionHw": "Hardware versie",
+ "positionIgnition": "Contact",
+ "positionFlags": "Vlaggen",
+ "positionCharge": "Lading",
+ "positionIp": "IP",
+ "positionArchive": "Archief",
+ "positionVin": "VIN",
+ "positionApproximate": "Benadering",
+ "positionThrottle": "Throttle",
+ "positionMotion": "Beweging",
+ "positionArmed": "Armed",
+ "positionAcceleration": "Versnelling",
+ "positionTemp": "Temperatuur",
+ "positionDeviceTemp": "Apparaattemperatuur",
+ "positionCoolantTemp": "Coolant Temperature",
+ "positionOperator": "Operator",
+ "positionCommand": "Commando",
+ "positionBlocked": "Geblokkeerd",
+ "positionDtcs": "DTCs",
+ "positionObdSpeed": "OBD snelheid",
+ "positionObdOdometer": "OBD odometer",
+ "positionDrivingTime": "Driving Time",
+ "positionDriverUniqueId": "Bestuurder uniek ID",
+ "positionCard": "Card",
+ "positionImage": "Afbeelding",
+ "positionVideo": "Video",
+ "positionAudio": "Geluid",
+ "serverTitle": "Serverinstellingen",
+ "serverZoom": "Zoom",
+ "serverRegistration": "Registratie",
+ "serverReadonly": "Alleen lezen",
+ "serverForceSettings": "Instellingen forceren",
+ "serverAnnouncement": "Aankondiging",
+ "serverName": "Server Name",
+ "serverDescription": "Server Description",
+ "serverColorPrimary": "Color Primary",
+ "serverColorSecondary": "Color Secondary",
+ "serverLogo": "Logo Image",
+ "serverLogoInverted": "Inverted Logo Image",
+ "serverChangeDisable": "Disable Server Change",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "Kaart",
+ "mapActive": "Actieve kaarten",
+ "mapOverlay": "Kaartlaag",
+ "mapOverlayCustom": "Aangepaste laag",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API sleutel",
+ "mapOpenWeatherClouds": "OpenWeather wolken",
+ "mapOpenWeatherPrecipitation": "OpenWeather neerslag",
+ "mapOpenWeatherPressure": "OpenWeather luchtdruk",
+ "mapOpenWeatherWind": "OpenWeather wind",
+ "mapOpenWeatherTemperature": "OpenWeather temperatuur",
+ "mapLayer": "Kaart laag",
+ "mapCustom": "Aangepast (XYZ)",
+ "mapCustomArcgis": "Aangepast (ArcGIS)",
+ "mapCustomLabel": "Aangepaste map",
+ "mapCarto": "Carto Basemaps",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Bing Maps sleutel",
+ "mapBingRoad": "Bing Maps Wegen",
+ "mapBingAerial": "Bing Maps Luchtfoto",
+ "mapBingHybrid": "Bing Maps hybride",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Yandex kaart",
+ "mapYandexSat": "Yandex satelliet",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox Streets",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox Outdoors",
+ "mapMapboxSatellite": "Mapbox Satellite",
+ "mapMapboxKey": "MapBox toegangsleutel",
+ "mapMapTilerBasic": "MapTiler Basic",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "MapTiler API sleutel",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ toegangsleutel",
+ "mapTomTomBasic": "TomTom basis",
+ "mapTomTomFlow": "TomTom verkeersstromen",
+ "mapTomTomIncidents": "TomTom verkeersincidenten",
+ "mapTomTomKey": "TomTom API sleutel",
+ "mapHereBasic": "Here basis",
+ "mapHereHybrid": "Here hybride",
+ "mapHereSatellite": "Here satelliet",
+ "mapHereFlow": "Here verkeersstromen",
+ "mapHereKey": "Here API sleutel",
+ "mapShapePolygon": "Polygoon",
+ "mapShapeCircle": "Cirkel",
+ "mapShapePolyline": "Polylijn",
+ "mapLiveRoutes": "Live routes",
+ "mapDirection": "Show Direction",
+ "mapCurrentLocation": "Huidige locatie",
+ "mapPoiLayer": "POI laag",
+ "mapClustering": "Markeringen clusteren",
+ "mapOnSelect": "Toon kaart bij selectie",
+ "mapDefault": "Default Map",
+ "stateTitle": "Status",
+ "stateName": "Parameter",
+ "stateValue": "Waarde",
+ "commandTitle": "Commando",
+ "commandSend": "Verstuur",
+ "commandSent": "Commando verstuurd",
+ "commandQueued": "Commando in de wachtrij geplaatst",
+ "commandUnit": "Eenheid",
+ "commandCustom": "Aangepast commando",
+ "commandDeviceIdentification": "Apparaatidentificatie",
+ "commandPositionSingle": "Enkel commando",
+ "commandPositionPeriodic": "Periodiek rapporteren",
+ "commandPositionStop": "Stop rapporteren",
+ "commandEngineStop": "Motor stoppen",
+ "commandEngineResume": "Motor hervatten",
+ "commandAlarmArm": "Alarm aan",
+ "commandAlarmDisarm": "Alarm uit",
+ "commandAlarmDismiss": "Alarm negeren",
+ "commandSetTimezone": "Tijdzone instellen",
+ "commandRequestPhoto": "Vraag foto",
+ "commandPowerOff": "Apparaat uitschakelen",
+ "commandRebootDevice": "Herstart apparaat",
+ "commandFactoryReset": "Naar fabrieksinstellingen",
+ "commandSendSms": "Stuur SMS",
+ "commandSendUssd": "Stuur USDD",
+ "commandSosNumber": "Stel SOS-nummer in",
+ "commandSilenceTime": "Stel 'Stille tijd' in",
+ "commandSetPhonebook": "Bewerk telefoonboek",
+ "commandVoiceMessage": "Spraakbericht",
+ "commandOutputControl": "Stel output in",
+ "commandVoiceMonitoring": "Stemmonitoring",
+ "commandSetAgps": "AGPS instellen",
+ "commandSetIndicator": "Indicator instellen",
+ "commandConfiguration": "Configuratie",
+ "commandGetVersion": "Versie ophalen",
+ "commandFirmwareUpdate": "Firmware bijwerken",
+ "commandSetConnection": "Connectie instellen",
+ "commandSetOdometer": "Odometer instellen",
+ "commandGetModemStatus": "Modemstatus opvragen",
+ "commandGetDeviceStatus": "Apparaatstatus opvragen",
+ "commandSetSpeedLimit": "Snelheidslimiet instellen",
+ "commandModePowerSaving": "Batterijspaarstand",
+ "commandModeDeepSleep": "Diepe slaapstand",
+ "commandAlarmGeofence": "Geofence-alarm instellen",
+ "commandAlarmBattery": "Accualarm instellen",
+ "commandAlarmSos": "SOS-alarm instellen",
+ "commandAlarmRemove": "Verwijderalarm instellen",
+ "commandAlarmClock": "Klokalarm instellen",
+ "commandAlarmSpeed": "Snelheidsalarm instellen",
+ "commandAlarmFall": "Probleemalarm instellen",
+ "commandAlarmVibration": "Trilalarm instellen",
+ "commandFrequency": "Frequentie",
+ "commandTimezone": "Tijdzoneverschil",
+ "commandMessage": "Bericht",
+ "commandRadius": "Radius",
+ "commandEnable": "Inschakelen",
+ "commandData": "Data",
+ "commandIndex": "Index",
+ "commandPhone": "Telefoonnummer",
+ "commandServer": "Server",
+ "commandPort": "Poort",
+ "eventAll": "Alle gebeurtenissen",
+ "eventDeviceOnline": "Status online",
+ "eventDeviceUnknown": "Status onbekend",
+ "eventDeviceOffline": "Status offline",
+ "eventDeviceInactive": "Einde van inactiviteit",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "Apparaat beweegt",
+ "eventDeviceStopped": "Apparaat gestopt",
+ "eventDeviceOverspeed": "Snelheidslimiet overschreden",
+ "eventDeviceFuelDrop": "Brandstofafname",
+ "eventDeviceFuelIncrease": "Fuel increase",
+ "eventCommandResult": "Commando resultaat",
+ "eventGeofenceEnter": "Geofence binnengegaan",
+ "eventGeofenceExit": "Geofence verlaten",
+ "eventAlarm": "Alarm",
+ "eventIgnitionOn": "Ontsteking aan",
+ "eventIgnitionOff": "Ontsteking uit",
+ "eventMaintenance": "Onderhoud vereist",
+ "eventTextMessage": "Tekstbericht ontvangen",
+ "eventDriverChanged": "Bestuurder veranderd",
+ "eventMedia": "Media",
+ "eventsScrollToLast": "Scroll naar laatste",
+ "eventsSoundEvents": "Sound Events",
+ "eventsSoundAlarms": "Sound Alarms",
+ "alarmGeneral": "Algemeen",
+ "alarmSos": "SOS",
+ "alarmVibration": "Trilling",
+ "alarmMovement": "Beweging",
+ "alarmLowspeed": "Lage snelheid",
+ "alarmOverspeed": "Snelheidsoverschreiding",
+ "alarmFallDown": "Gevallen",
+ "alarmLowPower": "Lage stroom",
+ "alarmLowBattery": "Laag accuniveau",
+ "alarmFault": "Fout",
+ "alarmPowerOff": "Uitgeschakeld",
+ "alarmPowerOn": "Ingeschakeld",
+ "alarmDoor": "Deur",
+ "alarmLock": "Afgesloten",
+ "alarmUnlock": "Geopend",
+ "alarmGeofence": "Geofence",
+ "alarmGeofenceEnter": "Geofence binnengegaan",
+ "alarmGeofenceExit": "Geofence verlaten",
+ "alarmGpsAntennaCut": "GPS antenne verbroken",
+ "alarmAccident": "Ongeluk",
+ "alarmTow": "Gesleept",
+ "alarmIdle": "Rust",
+ "alarmHighRpm": "Hoge TPM",
+ "alarmHardAcceleration": "Harde versnelling",
+ "alarmHardBraking": "Harde remming",
+ "alarmHardCornering": "Scherpe bocht",
+ "alarmLaneChange": "Rijbaanwisseling",
+ "alarmFatigueDriving": "Vermoeid rijden",
+ "alarmPowerCut": "Stroomonderbreking",
+ "alarmPowerRestored": "Stroom hersteld",
+ "alarmJamming": "Verstoring",
+ "alarmTemperature": "Temperatuur",
+ "alarmParking": "Parkeren",
+ "alarmBonnet": "Motorkap",
+ "alarmFootBrake": "Voetrem",
+ "alarmFuelLeak": "Brandstoflek",
+ "alarmTampering": "Knoeien",
+ "alarmRemoving": "Verwijderd",
+ "notificationType": "Notificatietype",
+ "notificationAlways": "Alle apparaten",
+ "notificationNotificators": "Kanalen",
+ "notificatorCommand": "Command",
+ "notificatorWeb": "Web",
+ "notificatorMail": "E-mail",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Opnieuw afspelen",
+ "reportCombined": "Combined",
+ "reportRoute": "Route",
+ "reportEvents": "Gebeurtenissen",
+ "reportTrips": "Ritten",
+ "reportStops": "Stops",
+ "reportSummary": "Samenvatting",
+ "reportDaily": "Dagelijkse samenvatting",
+ "reportChart": "Grafiek",
+ "reportConfigure": "Configureer",
+ "reportEventTypes": "Gebeurtenistypen",
+ "reportChartType": "Grafiektype",
+ "reportShowMarkers": "Toon markeringen",
+ "reportExport": "Exporteren",
+ "reportEmail": "E-mailrapport",
+ "reportSchedule": "Schedule",
+ "reportPeriod": "Periode",
+ "reportCustom": "Aangepast",
+ "reportToday": "Vandaag",
+ "reportYesterday": "Gisteren",
+ "reportThisWeek": "Deze week",
+ "reportPreviousWeek": "Vorige week",
+ "reportThisMonth": "Deze maand",
+ "reportPreviousMonth": "Vorige maand",
+ "reportDeviceName": "Apparaatnaam",
+ "reportAverageSpeed": "Gemiddelde snelheid",
+ "reportMaximumSpeed": "Maximale snelheid",
+ "reportEngineHours": "Draaiuren motor",
+ "reportDuration": "Duur",
+ "reportStartDate": "Startdatum",
+ "reportStartTime": "Starttijd",
+ "reportStartAddress": "Beginadres",
+ "reportEndTime": "Eindtijd",
+ "reportEndAddress": "Eindadres",
+ "reportSpentFuel": "Verbruikte brandstof",
+ "reportStartOdometer": "Odometer start",
+ "reportEndOdometer": "Odometer einde",
+ "statisticsTitle": "Statistieken",
+ "statisticsCaptureTime": "Opnametijd",
+ "statisticsActiveUsers": "Actieve gebruikers",
+ "statisticsActiveDevices": "Actieve apparaten",
+ "statisticsRequests": "Verzoeken",
+ "statisticsMessagesReceived": "Ontvangen berichten",
+ "statisticsMessagesStored": "Opgeslagen berichten",
+ "statisticsGeocoder": "Geocoderverzoeken",
+ "statisticsGeolocation": "Geolocatieverzoeken",
+ "categoryArrow": "Aanwijzer",
+ "categoryDefault": "Standaard",
+ "categoryAnimal": "Dier",
+ "categoryBicycle": "Fiets",
+ "categoryBoat": "Boot",
+ "categoryBus": "Bus",
+ "categoryCar": "Auto",
+ "categoryCamper": "Camper",
+ "categoryCrane": "Kraan",
+ "categoryHelicopter": "Helicopter",
+ "categoryMotorcycle": "Motor",
+ "categoryOffroad": "Offroad",
+ "categoryPerson": "Persoon",
+ "categoryPickup": "Pickup",
+ "categoryPlane": "Vliegtuig",
+ "categoryShip": "Schip",
+ "categoryTractor": "Tractor",
+ "categoryTrain": "Trein",
+ "categoryTram": "Tram",
+ "categoryTrolleybus": "Trolleybus",
+ "categoryTruck": "Vrachtwagen",
+ "categoryVan": "Busje",
+ "categoryScooter": "Scooter",
+ "maintenanceStart": "Start",
+ "maintenancePeriod": "Periode"
+} \ No newline at end of file
diff --git a/src/resources/l10n/nn.json b/src/resources/l10n/nn.json
new file mode 100644
index 00000000..2bdc5a9a
--- /dev/null
+++ b/src/resources/l10n/nn.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "Lastar...",
+ "sharedHide": "Gøym",
+ "sharedSave": "Lagre",
+ "sharedUpload": "Upload",
+ "sharedSet": "Sett",
+ "sharedCancel": "Avbryt",
+ "sharedCopy": "Copy",
+ "sharedAdd": "Legg til",
+ "sharedEdit": "Endre",
+ "sharedRemove": "Fjern",
+ "sharedRemoveConfirm": "Fjern element?",
+ "sharedNoData": "No data",
+ "sharedSubject": "Subject",
+ "sharedYes": "Yes",
+ "sharedNo": "No",
+ "sharedKm": "km",
+ "sharedMi": "mi",
+ "sharedNmi": "nm",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "kn",
+ "sharedKmh": "km/t",
+ "sharedMph": "mph",
+ "sharedHour": "Time",
+ "sharedMinute": "Minutt",
+ "sharedSecond": "Sekund",
+ "sharedDays": "dagar",
+ "sharedHours": "timar",
+ "sharedMinutes": "minuttar",
+ "sharedDecimalDegrees": "Disimalgrader",
+ "sharedDegreesDecimalMinutes": "Desimalminutter",
+ "sharedDegreesMinutesSeconds": "Grader minuttar sekund",
+ "sharedName": "Namn",
+ "sharedDescription": "Beskriving",
+ "sharedSearch": "Søk",
+ "sharedIconScale": "Icon Scale",
+ "sharedGeofence": "Geo-gjerde",
+ "sharedGeofences": "Geo-gjerde",
+ "sharedCreateGeofence": "Create Geofence",
+ "sharedNotifications": "Varsel",
+ "sharedNotification": "Varsel",
+ "sharedAttributes": "Eigenskapar",
+ "sharedAttribute": "Eigenskap",
+ "sharedDrivers": "Sjåførar",
+ "sharedDriver": "Sjåfør",
+ "sharedArea": "Område",
+ "sharedSound": "Varslingslyd",
+ "sharedType": "Type",
+ "sharedDistance": "Avstand",
+ "sharedHourAbbreviation": "t",
+ "sharedMinuteAbbreviation": "m",
+ "sharedSecondAbbreviation": "s",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "l",
+ "sharedGallonAbbreviation": "gal",
+ "sharedLiter": "Liter",
+ "sharedImpGallon": "Britisk gallon",
+ "sharedUsGallon": "Amerikansk gallon",
+ "sharedLiterPerHourAbbreviation": "l/t",
+ "sharedGetMapState": "Få karttilstand",
+ "sharedComputedAttribute": "Berekna eigenskap",
+ "sharedComputedAttributes": "Berekna eigenskapar",
+ "sharedCheckComputedAttribute": "Sjekk berekna eigenskaper",
+ "sharedExpression": "Uttrykk",
+ "sharedDevice": "Eining",
+ "sharedTest": "Test",
+ "sharedTestNotification": "Send testvarsel",
+ "sharedTestNotificators": "Test Channels",
+ "sharedTestExpression": "Test Expression",
+ "sharedCalendar": "Kalender",
+ "sharedCalendars": "Kalendrar",
+ "sharedFile": "Fil",
+ "sharedSearchDevices": "Search Devices",
+ "sharedSortBy": "Sort By",
+ "sharedFilterMap": "Filter on Map",
+ "sharedSelectFile": "Velg fil",
+ "sharedPhone": "Telefon",
+ "sharedRequired": "Naudsynt",
+ "sharedPreferences": "Innstillingar",
+ "sharedPermissions": "Løyve",
+ "sharedConnections": "Connections",
+ "sharedExtra": "Ekstra",
+ "sharedPrimary": "Primary",
+ "sharedSecondary": "Secondary",
+ "sharedTypeString": "Streng",
+ "sharedTypeNumber": "Nummer",
+ "sharedTypeBoolean": "Boolsk",
+ "sharedTimezone": "Tidssone",
+ "sharedInfoTitle": "Info",
+ "sharedSavedCommand": "Lagra kommando",
+ "sharedSavedCommands": "Lagra kommandoar",
+ "sharedNew": "Ny...",
+ "sharedShowAddress": "Vis adresse",
+ "sharedShowDetails": "More Details",
+ "sharedDisabled": "Deaktiver",
+ "sharedMaintenance": "Vedlikehald",
+ "sharedDeviceAccumulators": "Akkumulatorar",
+ "sharedAlarms": "Alarmar",
+ "sharedLocation": "Location",
+ "sharedImport": "Import",
+ "sharedColumns": "Columns",
+ "sharedDropzoneText": "Drag and drop a file here or click",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "Simple",
+ "calendarRecurrence": "Recurrence",
+ "calendarOnce": "Once",
+ "calendarDaily": "Daily",
+ "calendarWeekly": "Weekly",
+ "calendarMonthly": "Monthly",
+ "calendarDays": "Days",
+ "calendarSunday": "Sunday",
+ "calendarMonday": "Monday",
+ "calendarTuesday": "Tuesday",
+ "calendarWednesday": "Wednesday",
+ "calendarThursday": "Thursday",
+ "calendarFriday": "Friday",
+ "calendarSaturday": "Saturday",
+ "attributeShowGeofences": "Show Geofences",
+ "attributeSpeedLimit": "Fartsgrense",
+ "attributeFuelDropThreshold": "Fuel Drop Threshold",
+ "attributeFuelIncreaseThreshold": "Fuel Increase Threshold",
+ "attributePolylineDistance": "Polylinjedistanse",
+ "attributeReportIgnoreOdometer": "Rapporter: Ignorer kilometerteljar",
+ "attributeWebReportColor": "Web: Rapportfarge",
+ "attributeDevicePassword": "Einingspassord",
+ "attributeDeviceImage": "Device Image",
+ "attributeDeviceInactivityStart": "Device Inactivity Start",
+ "attributeDeviceInactivityPeriod": "Device Inactivity Period",
+ "attributeProcessingCopyAttributes": "Prosesser: Kopier eigenskapar",
+ "attributeColor": "Farge",
+ "attributeWebLiveRouteLength": "Web: Live rute lengde",
+ "attributeWebSelectZoom": "Web: Skaler ved val",
+ "attributeWebMaxZoom": "Web: Maksimal forstørring",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Pushover User Key",
+ "attributePushoverDeviceNames": "Pushover Device Names",
+ "attributeMailSmtpHost": "E-post: SMTP-vert",
+ "attributeMailSmtpPort": "E-post: SMTP-port",
+ "attributeMailSmtpStarttlsEnable": "E-post: aktiver SMTP STARTTLS",
+ "attributeMailSmtpStarttlsRequired": "E-post: SMTP STARTTLS nøydsynt",
+ "attributeMailSmtpSslEnable": "E-post: aktiver SMTP SSL",
+ "attributeMailSmtpSslTrust": "E-Post: SMTP SSL tillit",
+ "attributeMailSmtpSslProtocols": "E-post: SMTP SSL protokollar",
+ "attributeMailSmtpFrom": "E-post: SMTP Frå",
+ "attributeMailSmtpAuth": "E-post: Aktiver SMTP autentisering",
+ "attributeMailSmtpUsername": "E-post: SMTP brukarnamn",
+ "attributeMailSmtpPassword": "E-post: SMTP passord",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: Disable Attributes",
+ "attributeUiDisableGroups": "UI: Disable Groups",
+ "attributeUiDisableEvents": "UI: Deaktiver hendingar",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "UI: Deaktiver Sjåførar",
+ "attributeUiDisableComputedAttributes": "UI: Deaktiver Berekna eigenskaper",
+ "attributeUiDisableCalendars": "UI: Deaktiver kalendrar",
+ "attributeUiDisableMaintenance": " UI: Deaktiver vedlikehald",
+ "attributeUiHidePositionAttributes": "UI: Gøym posisojonseigenskapar",
+ "attributeUiDisableLoginLanguage": "UI: Disable Login Language",
+ "attributeNotificationTokens": "Notification Tokens",
+ "attributePopupInfo": "Popup Info",
+ "errorTitle": "Feil",
+ "errorGeneral": "ugyldige parameterar eller avgrensingsbrot",
+ "errorConnection": "Forbindelse feila",
+ "errorSocket": "Web socket tilkoplingsfeil",
+ "errorZero": "Kan ikkje vere null",
+ "userEmail": "E-post",
+ "userPassword": "Passord",
+ "userAdmin": "Admin",
+ "userRemember": "Hugs",
+ "userExpirationTime": "Utløp",
+ "userDeviceLimit": "Einingsgrense",
+ "userUserLimit": "Brukargrense",
+ "userDeviceReadonly": "Eining skrivebeskytta",
+ "userLimitCommands": "Avgrens kommandoar",
+ "userDisableReports": "Disable Reports",
+ "userFixedEmail": "No Email Change",
+ "userToken": "Symbol",
+ "userDeleteAccount": "Delete Account",
+ "userTemporary": "Temporary",
+ "loginTitle": "Logg inn",
+ "loginLanguage": "Språk",
+ "loginReset": "Reset Password",
+ "loginRegister": "Registrer",
+ "loginLogin": "Logg inn",
+ "loginOpenId": "Login with OpenID",
+ "loginFailed": "Feil e-post eller passord",
+ "loginCreated": "Ny brukar har blitt registrert",
+ "loginResetSuccess": "Check your email",
+ "loginUpdateSuccess": "New password is set",
+ "loginLogout": "Logg ut",
+ "loginLogo": "Logo",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "Einingar og status",
+ "deviceSelected": "Selected Device",
+ "deviceTitle": "Einingar",
+ "devicePrimaryInfo": "Device Title",
+ "deviceSecondaryInfo": "Device Detail",
+ "deviceIdentifier": "Identifikator",
+ "deviceModel": "Model",
+ "deviceContact": "Kontakt",
+ "deviceCategory": "Kategori",
+ "deviceLastUpdate": "Sist oppdatert",
+ "deviceCommand": "Kommando",
+ "deviceFollow": "Følj",
+ "deviceTotalDistance": "Total avstand",
+ "deviceStatus": "Status",
+ "deviceStatusOnline": "Tilkopla",
+ "deviceStatusOffline": "Fråkopla",
+ "deviceStatusUnknown": "Ukjend",
+ "deviceRegisterFirst": "Register your first device",
+ "deviceIdentifierHelp": "IMEI, serial number or other id. It has to match the identifier device reports to the server.",
+ "deviceShare": "Share Device",
+ "groupDialog": "Gruppe",
+ "groupParent": "Gruppe",
+ "groupNoGroup": "Inga gruppe",
+ "settingsTitle": "Innstillingar",
+ "settingsUser": "Konto",
+ "settingsGroups": "Gruppar",
+ "settingsServer": "Tenar",
+ "settingsUsers": "Brukarar",
+ "settingsDistanceUnit": "Avstanseining",
+ "settingsAltitudeUnit": "Altitude Unit",
+ "settingsSpeedUnit": "Hastighetseining",
+ "settingsVolumeUnit": "Volumeining",
+ "settingsTwelveHourFormat": "Tolvtimersformat",
+ "settingsCoordinateFormat": "Koordinatformat",
+ "settingsServerVersion": "Server Version",
+ "settingsAppVersion": "App Version",
+ "settingsConnection": "Connection",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "Rapportar",
+ "reportScheduled": "Scheduled Reports",
+ "reportDevice": "Eining",
+ "reportGroup": "Gruppe",
+ "reportFrom": "Frå",
+ "reportTo": "Til",
+ "reportShow": "Syn",
+ "reportClear": "Nullstill",
+ "linkGoogleMaps": "Google Maps",
+ "linkAppleMaps": "Apple Maps",
+ "linkStreetView": "Street View",
+ "positionFixTime": "Fix Time",
+ "positionDeviceTime": "Device Time",
+ "positionServerTime": "Server Time",
+ "positionValid": "Gyldig",
+ "positionAccuracy": "Nøyaktigheit",
+ "positionLatitude": "Breddegrad",
+ "positionLongitude": "Lengdegrad",
+ "positionAltitude": "Høgde",
+ "positionSpeed": "Hastigheit",
+ "positionCourse": "Retning",
+ "positionAddress": "Adresse",
+ "positionProtocol": "Protokoll",
+ "positionDistance": "Avstand",
+ "positionRpm": "RPM",
+ "positionFuel": "Drivstoff",
+ "positionPower": "Kraft",
+ "positionBattery": "Batteri",
+ "positionRaw": "Rå",
+ "positionIndex": "Register",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "Satelittar",
+ "positionSatVisible": "Synlege satelittar",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "Nettveksling (Roaming)",
+ "positionEvent": "Hending",
+ "positionAlarm": "Alarm",
+ "positionStatus": "Status",
+ "positionOdometer": "Kilometerteljar",
+ "positionServiceOdometer": "Service kilometerteljar",
+ "positionTripOdometer": "Turmålar",
+ "positionHours": "Timar",
+ "positionSteps": "Steg",
+ "positionInput": "Inngang",
+ "positionHeartRate": "Heart Rate",
+ "positionOutput": "Utgang",
+ "positionBatteryLevel": "Batterinivå",
+ "positionFuelConsumption": "Drivstoff-forbruk",
+ "positionRfid": "RFID",
+ "positionVersionFw": "Fastvareversjon",
+ "positionVersionHw": "Maskinvareversjon",
+ "positionIgnition": "Tenning",
+ "positionFlags": "Status indikatorar",
+ "positionCharge": "Lade",
+ "positionIp": "IP",
+ "positionArchive": "Arkiv",
+ "positionVin": "VIN",
+ "positionApproximate": "Tilnærma",
+ "positionThrottle": "Gasspedal",
+ "positionMotion": "Rørsle",
+ "positionArmed": "Armert",
+ "positionAcceleration": "Akselerasjon",
+ "positionTemp": "Temperature",
+ "positionDeviceTemp": "Einingstemperatur",
+ "positionCoolantTemp": "Coolant Temperature",
+ "positionOperator": "Operatør",
+ "positionCommand": "Kommando",
+ "positionBlocked": "Blokert",
+ "positionDtcs": "Diagnosefeilkoder",
+ "positionObdSpeed": "OBD-hastigheit",
+ "positionObdOdometer": "OBD kilometerteljar",
+ "positionDrivingTime": "Driving Time",
+ "positionDriverUniqueId": "Sjåførunik ID",
+ "positionCard": "Card",
+ "positionImage": "Bilete",
+ "positionVideo": "Video",
+ "positionAudio": "Lyd",
+ "serverTitle": "Tenarinnstillingar",
+ "serverZoom": "Zoom",
+ "serverRegistration": "Registering",
+ "serverReadonly": "Skrivebeskytta",
+ "serverForceSettings": "Tving innstillingar",
+ "serverAnnouncement": "Announcement",
+ "serverName": "Server Name",
+ "serverDescription": "Server Description",
+ "serverColorPrimary": "Color Primary",
+ "serverColorSecondary": "Color Secondary",
+ "serverLogo": "Logo Image",
+ "serverLogoInverted": "Inverted Logo Image",
+ "serverChangeDisable": "Disable Server Change",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "Kart",
+ "mapActive": "Active Maps",
+ "mapOverlay": "Map Overlay",
+ "mapOverlayCustom": "Custom Overlay",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API Key",
+ "mapOpenWeatherClouds": "OpenWeather Clouds",
+ "mapOpenWeatherPrecipitation": "OpenWeather Precipitation",
+ "mapOpenWeatherPressure": "OpenWeather Pressure",
+ "mapOpenWeatherWind": "OpenWeather Wind",
+ "mapOpenWeatherTemperature": "OpenWeather Temperature",
+ "mapLayer": "Kartlag",
+ "mapCustom": "Tilpassa (XYZ)",
+ "mapCustomArcgis": "Tilpassa (ArcGIS)",
+ "mapCustomLabel": "Tilpassa kart",
+ "mapCarto": "Carto basiskart",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Bing Maps-nøkkel",
+ "mapBingRoad": "Bing Maps-veg",
+ "mapBingAerial": "Bing Maps-flyfoto",
+ "mapBingHybrid": "Bing kart hybrid",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Yandex kart",
+ "mapYandexSat": "Yandex satellitt",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox Streets",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox Outdoors",
+ "mapMapboxSatellite": "Mapbox Satellite",
+ "mapMapboxKey": "Mapbox Access Token",
+ "mapMapTilerBasic": "MapTiler Basic",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "MapTiler API Key",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ Access Token",
+ "mapTomTomBasic": "TomTom Basic",
+ "mapTomTomFlow": "TomTom Traffic Flow",
+ "mapTomTomIncidents": "TomTom Traffic Incidents",
+ "mapTomTomKey": "TomTom API Key",
+ "mapHereBasic": "Here Basic",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satellite",
+ "mapHereFlow": "Here Traffic Flow",
+ "mapHereKey": "Here API Key",
+ "mapShapePolygon": "Mangekant",
+ "mapShapeCircle": "Sirkel",
+ "mapShapePolyline": "Polylinje",
+ "mapLiveRoutes": "Live ruter",
+ "mapDirection": "Show Direction",
+ "mapCurrentLocation": "Current Location",
+ "mapPoiLayer": "POI-lag",
+ "mapClustering": "Markers Clustering",
+ "mapOnSelect": "Show Map on Selection",
+ "mapDefault": "Default Map",
+ "stateTitle": "Status",
+ "stateName": "Eigenskap",
+ "stateValue": "Verdi",
+ "commandTitle": "Kommando",
+ "commandSend": "Send",
+ "commandSent": "Kommando sendt",
+ "commandQueued": " Kommando lagt i kø",
+ "commandUnit": "Eining",
+ "commandCustom": "Eigendefinert kommando",
+ "commandDeviceIdentification": "Einingsidentifikasjon",
+ "commandPositionSingle": "Enkel-rapportering",
+ "commandPositionPeriodic": "Periodisk rapportering",
+ "commandPositionStop": "Stopp rapportering",
+ "commandEngineStop": "Stopp motor",
+ "commandEngineResume": "Fortsett motor",
+ "commandAlarmArm": "Slå alarm på",
+ "commandAlarmDisarm": "Slå alarm av",
+ "commandAlarmDismiss": "Dismiss Alarm",
+ "commandSetTimezone": "Sett opp tidssone",
+ "commandRequestPhoto": "Be om foto",
+ "commandPowerOff": "Skru av eining",
+ "commandRebootDevice": "Omstart eining",
+ "commandFactoryReset": "Factory Reset",
+ "commandSendSms": "Send SMS",
+ "commandSendUssd": "Send USSD",
+ "commandSosNumber": "Set SMS-nummer",
+ "commandSilenceTime": "Sett stilletid",
+ "commandSetPhonebook": "Sett telefonkatalog",
+ "commandVoiceMessage": "Talemelding",
+ "commandOutputControl": "Utgangkontroll",
+ "commandVoiceMonitoring": "Stemmeovervaking",
+ "commandSetAgps": "Sett AGPS",
+ "commandSetIndicator": "Sett indikator",
+ "commandConfiguration": "Oppsett",
+ "commandGetVersion": "Syn versjon",
+ "commandFirmwareUpdate": "Oppdater fastvare",
+ "commandSetConnection": "Sett tilkopling",
+ "commandSetOdometer": "Set kilometerteljar",
+ "commandGetModemStatus": "Syn Modemstatus",
+ "commandGetDeviceStatus": "Synd einingstatus",
+ "commandSetSpeedLimit": "Set Speed Limit",
+ "commandModePowerSaving": "Power Saving Mode",
+ "commandModeDeepSleep": "Deep Sleep Mode",
+ "commandAlarmGeofence": "Set Geofence Alarm",
+ "commandAlarmBattery": "Set Battery Alarm",
+ "commandAlarmSos": "Set SOS Alarm",
+ "commandAlarmRemove": "Set Remove Alarm",
+ "commandAlarmClock": "Set Clock Alarm",
+ "commandAlarmSpeed": "Set Speed Alarm",
+ "commandAlarmFall": "Set Fall Alarm",
+ "commandAlarmVibration": "Set Vibration Alarm",
+ "commandFrequency": "Frekvens",
+ "commandTimezone": "Tidszoneforskyving",
+ "commandMessage": "Melding",
+ "commandRadius": "Radius",
+ "commandEnable": "Aktiver",
+ "commandData": "Data",
+ "commandIndex": "Register",
+ "commandPhone": "Telefonnummer",
+ "commandServer": "Tenar",
+ "commandPort": "Port",
+ "eventAll": "Alle hendingar",
+ "eventDeviceOnline": "Status tilkopla",
+ "eventDeviceUnknown": "Status ukjend",
+ "eventDeviceOffline": "Status fråkopla",
+ "eventDeviceInactive": "Device inactive",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "Eining rører seg",
+ "eventDeviceStopped": "Eining stoppa",
+ "eventDeviceOverspeed": "Fartsgrense overstige",
+ "eventDeviceFuelDrop": "Fall i drivstoff",
+ "eventDeviceFuelIncrease": "Fuel increase",
+ "eventCommandResult": "Kommandoresultat",
+ "eventGeofenceEnter": "Innkommen Geo-gjerde",
+ "eventGeofenceExit": "Forlate Geo-gjerde",
+ "eventAlarm": "Alarm",
+ "eventIgnitionOn": "Tenning på",
+ "eventIgnitionOff": "Tenning av",
+ "eventMaintenance": "Vedlikehald nøydsynt",
+ "eventTextMessage": "Tekstmelding motteken",
+ "eventDriverChanged": "Sjåfør endra",
+ "eventMedia": "Media",
+ "eventsScrollToLast": "Syn siste",
+ "eventsSoundEvents": "Sound Events",
+ "eventsSoundAlarms": "Sound Alarms",
+ "alarmGeneral": "Generell",
+ "alarmSos": "SOS",
+ "alarmVibration": "Vibrasjon",
+ "alarmMovement": "Rørsle",
+ "alarmLowspeed": "Låg hastigheit",
+ "alarmOverspeed": "Høgt turtal",
+ "alarmFallDown": "Fall",
+ "alarmLowPower": "Lite straum",
+ "alarmLowBattery": "Lågt batterinivå",
+ "alarmFault": "Feil",
+ "alarmPowerOff": "Straum fråkopla",
+ "alarmPowerOn": "Straum tilkopla",
+ "alarmDoor": "Dør",
+ "alarmLock": "Lås",
+ "alarmUnlock": "Lås opp",
+ "alarmGeofence": "Geo-gjerde",
+ "alarmGeofenceEnter": "Innanfor Geo-gjerde",
+ "alarmGeofenceExit": "Utanfor Geo-gjerde",
+ "alarmGpsAntennaCut": "GPS-antenne fjerna",
+ "alarmAccident": "Ulykke",
+ "alarmTow": "Tauing",
+ "alarmIdle": "Tomgang",
+ "alarmHighRpm": "Høgt turtal",
+ "alarmHardAcceleration": "Hard akselerasjon",
+ "alarmHardBraking": "Hard oppbremsing",
+ "alarmHardCornering": "Hard vending",
+ "alarmLaneChange": "Filskifte",
+ "alarmFatigueDriving": "Trøyttleikskøyring",
+ "alarmPowerCut": "Straum fråkopla",
+ "alarmPowerRestored": " Straumtilførsel atterreist",
+ "alarmJamming": "Jamming",
+ "alarmTemperature": "Temperatur",
+ "alarmParking": "Parkering",
+ "alarmBonnet": "Motorpanser",
+ "alarmFootBrake": "Bremsepedal",
+ "alarmFuelLeak": "Drivstofflekkasje",
+ "alarmTampering": "Tukling",
+ "alarmRemoving": "Fjerning",
+ "notificationType": "Varseltype",
+ "notificationAlways": "Alle einingar",
+ "notificationNotificators": "Kanalar",
+ "notificatorCommand": "Command",
+ "notificatorWeb": "Vev",
+ "notificatorMail": "E-post",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Replay",
+ "reportCombined": "Combined",
+ "reportRoute": "Rute",
+ "reportEvents": "Hendingar",
+ "reportTrips": "Turar",
+ "reportStops": "Stopp",
+ "reportSummary": "Oppsumering",
+ "reportDaily": "Daily Summary",
+ "reportChart": "Graf",
+ "reportConfigure": "Set opp",
+ "reportEventTypes": "Hendingstypar",
+ "reportChartType": "Graf type",
+ "reportShowMarkers": "Syn markørar",
+ "reportExport": "Eksporter",
+ "reportEmail": "E-post-rapport",
+ "reportSchedule": "Schedule",
+ "reportPeriod": "Periode",
+ "reportCustom": "Tilpassa",
+ "reportToday": "Idag",
+ "reportYesterday": "I går",
+ "reportThisWeek": "Denne veka",
+ "reportPreviousWeek": "Forrige veke",
+ "reportThisMonth": "Denne månaden",
+ "reportPreviousMonth": "Forrige månad",
+ "reportDeviceName": "Einingsnamn",
+ "reportAverageSpeed": "Gjennomsnittshastighet",
+ "reportMaximumSpeed": "Maksimumshastighet",
+ "reportEngineHours": "Motortimar",
+ "reportDuration": "Varigheit",
+ "reportStartDate": "Start Date",
+ "reportStartTime": "Starttidspunkt",
+ "reportStartAddress": "Startadresse",
+ "reportEndTime": "Sluttidspunkt",
+ "reportEndAddress": "Sluttadresse",
+ "reportSpentFuel": "Brukt drivstoff",
+ "reportStartOdometer": "Kilometerteljar start",
+ "reportEndOdometer": "Kilometerteljar stop",
+ "statisticsTitle": "Statistikk",
+ "statisticsCaptureTime": "Opptakstid",
+ "statisticsActiveUsers": "Aktive brukarar",
+ "statisticsActiveDevices": "Aktive einingar",
+ "statisticsRequests": "Førespurnadar",
+ "statisticsMessagesReceived": "Meldingar motteke",
+ "statisticsMessagesStored": "Meldingar lagra",
+ "statisticsGeocoder": "Geocoder-førespurnader",
+ "statisticsGeolocation": "Geolocation-førespurnader",
+ "categoryArrow": "Pil",
+ "categoryDefault": "Standard",
+ "categoryAnimal": "Dyr",
+ "categoryBicycle": "Sykkel",
+ "categoryBoat": "Båt",
+ "categoryBus": "Buss",
+ "categoryCar": "Bil",
+ "categoryCamper": "Camper",
+ "categoryCrane": "Kran",
+ "categoryHelicopter": "Helikopter",
+ "categoryMotorcycle": "Motorsykkel",
+ "categoryOffroad": "Offroad",
+ "categoryPerson": "Person",
+ "categoryPickup": "Pickup",
+ "categoryPlane": "Fly",
+ "categoryShip": "Skip",
+ "categoryTractor": "Traktor",
+ "categoryTrain": "Tog",
+ "categoryTram": "Trikk",
+ "categoryTrolleybus": "Trolleybuss",
+ "categoryTruck": "Lastebil",
+ "categoryVan": "Varebil",
+ "categoryScooter": "Scooter",
+ "maintenanceStart": "Start",
+ "maintenancePeriod": "Periode"
+} \ No newline at end of file
diff --git a/src/resources/l10n/pl.json b/src/resources/l10n/pl.json
new file mode 100644
index 00000000..6e0a57d4
--- /dev/null
+++ b/src/resources/l10n/pl.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "Wczytywanie...",
+ "sharedHide": "Ukryj",
+ "sharedSave": "Zapisz",
+ "sharedUpload": "Wyślij",
+ "sharedSet": "Ustaw",
+ "sharedCancel": "Anuluj",
+ "sharedCopy": "Copy",
+ "sharedAdd": "Dodaj",
+ "sharedEdit": "Edytuj",
+ "sharedRemove": "Usuń",
+ "sharedRemoveConfirm": "Usunąć obiekt?",
+ "sharedNoData": "Brak danych",
+ "sharedSubject": "Subject",
+ "sharedYes": "Tak",
+ "sharedNo": "Nie",
+ "sharedKm": "km",
+ "sharedMi": "mi",
+ "sharedNmi": "Mm",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "w",
+ "sharedKmh": "km/h",
+ "sharedMph": "mph",
+ "sharedHour": "Godzina",
+ "sharedMinute": "Minuta",
+ "sharedSecond": "Sekunda",
+ "sharedDays": "dni",
+ "sharedHours": "godziny",
+ "sharedMinutes": "minuty",
+ "sharedDecimalDegrees": "Stopnie Dziesiętne",
+ "sharedDegreesDecimalMinutes": "Stopnie Dziesiętne Minuty",
+ "sharedDegreesMinutesSeconds": "Stopnie Minuty Sekundy",
+ "sharedName": "Nazwa",
+ "sharedDescription": "Opis",
+ "sharedSearch": "Szukaj",
+ "sharedIconScale": "Skala ikon",
+ "sharedGeofence": "Obszar monitorowany",
+ "sharedGeofences": "Obszary monitorowane",
+ "sharedCreateGeofence": "Utwórz obszar monitorowany",
+ "sharedNotifications": "Powiadomienia",
+ "sharedNotification": "Powiadomienie",
+ "sharedAttributes": "Atrybuty",
+ "sharedAttribute": "Atrybut",
+ "sharedDrivers": "Kierowcy",
+ "sharedDriver": "Kierowca",
+ "sharedArea": "Strefa",
+ "sharedSound": "Dźwięk powiadomienia",
+ "sharedType": "Typ",
+ "sharedDistance": "Odległość",
+ "sharedHourAbbreviation": "g",
+ "sharedMinuteAbbreviation": "m",
+ "sharedSecondAbbreviation": "s",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "l",
+ "sharedGallonAbbreviation": "gal",
+ "sharedLiter": "Litr",
+ "sharedImpGallon": "Galon Ang.",
+ "sharedUsGallon": "Galon U.S.",
+ "sharedLiterPerHourAbbreviation": "l/h",
+ "sharedGetMapState": "Pobierz stan mapy",
+ "sharedComputedAttribute": "Obliczony atrybut",
+ "sharedComputedAttributes": "Obliczone atrybuty",
+ "sharedCheckComputedAttribute": "Sprawdź obliczony atrybut",
+ "sharedExpression": "Wyrażenie",
+ "sharedDevice": "Urządzenie",
+ "sharedTest": "Test",
+ "sharedTestNotification": "Wyślij powiadomienie testowe",
+ "sharedTestNotificators": "Kanały testowe",
+ "sharedTestExpression": "Wyrażenie testowe",
+ "sharedCalendar": "Kalendarz",
+ "sharedCalendars": "Kalendarze",
+ "sharedFile": "Plik",
+ "sharedSearchDevices": "Szukaj urządzeń",
+ "sharedSortBy": "Sortuj według",
+ "sharedFilterMap": "Filtruj na mapie",
+ "sharedSelectFile": "Wybierz plik",
+ "sharedPhone": "Telefon",
+ "sharedRequired": "Wymagane",
+ "sharedPreferences": "Preferencje",
+ "sharedPermissions": "Uprawnienia",
+ "sharedConnections": "Połączenia",
+ "sharedExtra": "Dodatkowe",
+ "sharedPrimary": "Podstawowy",
+ "sharedSecondary": "Drugorzędny",
+ "sharedTypeString": "Tekst",
+ "sharedTypeNumber": "Liczba",
+ "sharedTypeBoolean": "Wartość binarna",
+ "sharedTimezone": "Strefa czasowa",
+ "sharedInfoTitle": "Informacja",
+ "sharedSavedCommand": "Zapisane polecenie",
+ "sharedSavedCommands": "Zapisane polecenia",
+ "sharedNew": "Nowy...",
+ "sharedShowAddress": "Pokaż adres",
+ "sharedShowDetails": "Więcej szczegółów",
+ "sharedDisabled": "Wyłączony",
+ "sharedMaintenance": "Konserwacja",
+ "sharedDeviceAccumulators": "Akumulatory",
+ "sharedAlarms": "Alarmy",
+ "sharedLocation": "Lokalizacja",
+ "sharedImport": "Importuj",
+ "sharedColumns": "Kolumny",
+ "sharedDropzoneText": "Przeciągnij i upuść plik tutaj lub kliknij",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "Prosty",
+ "calendarRecurrence": "Powtarzalny",
+ "calendarOnce": "Jednorazowy",
+ "calendarDaily": "Codzienny",
+ "calendarWeekly": "Tygodniowy",
+ "calendarMonthly": "Miesięczny",
+ "calendarDays": "Dzienny",
+ "calendarSunday": "Niedziela",
+ "calendarMonday": "Poniedziałek",
+ "calendarTuesday": "Wtorek",
+ "calendarWednesday": "Środa",
+ "calendarThursday": "Czwartek",
+ "calendarFriday": "Piątek",
+ "calendarSaturday": "Sobota",
+ "attributeShowGeofences": "Pokaż obszar monitorowany",
+ "attributeSpeedLimit": "Ograniczenie prędkości",
+ "attributeFuelDropThreshold": "Próg spadku paliwa",
+ "attributeFuelIncreaseThreshold": "Próg wzrostu paliwa",
+ "attributePolylineDistance": "Dystans łamanej",
+ "attributeReportIgnoreOdometer": "Raport: Ignoruj licznik kilometrów",
+ "attributeWebReportColor": "Web: Kolor raportu",
+ "attributeDevicePassword": "Hasło urządzenia",
+ "attributeDeviceImage": "Zdjęcie urządzenia",
+ "attributeDeviceInactivityStart": "Początek nieaktywności urządzenia",
+ "attributeDeviceInactivityPeriod": "Okres nieaktywności urządzenia",
+ "attributeProcessingCopyAttributes": "Przetwarzanie: Kopiuj atrybuty",
+ "attributeColor": "Kolor",
+ "attributeWebLiveRouteLength": "Web: Długość ścieżki na żywo",
+ "attributeWebSelectZoom": "Sieć: Powiększ po zaznaczeniu",
+ "attributeWebMaxZoom": "Strona: Maksymalne przybliżenie",
+ "attributeTelegramChatId": "Identyfikator czatu Telegram",
+ "attributePushoverUserKey": "Klucz użytkownika Pushover",
+ "attributePushoverDeviceNames": "Nazwy urządzeń Pushover",
+ "attributeMailSmtpHost": "Poczta: Nazwa hosta SMTP",
+ "attributeMailSmtpPort": "Poczta: Port SMTP",
+ "attributeMailSmtpStarttlsEnable": "Poczta: SMTP STARTTLS - Włącz",
+ "attributeMailSmtpStarttlsRequired": "Poczta: SMTP STARTTLS - Wymagane ",
+ "attributeMailSmtpSslEnable": "Poczta: SMTP SSL - Włącz",
+ "attributeMailSmtpSslTrust": "Poczta: SMTP SSL - Ufaj ",
+ "attributeMailSmtpSslProtocols": "Poczta: SMTP SSL - Protokoły ",
+ "attributeMailSmtpFrom": "Poczta: SMTP - Od",
+ "attributeMailSmtpAuth": "Poczta: SMTP - Włącz autoryzację",
+ "attributeMailSmtpUsername": "Poczta: SMTP - Nazwa użytkownika",
+ "attributeMailSmtpPassword": "Poczta: SMTP - Hasło",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: Wyłącz atrybuty",
+ "attributeUiDisableGroups": "UI: Wyłącz grupy",
+ "attributeUiDisableEvents": "UI: Wyłącz zdarzenia",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "UI: Wyłącz kierowców",
+ "attributeUiDisableComputedAttributes": "UI: Wyłącz obliczone atrybuty",
+ "attributeUiDisableCalendars": "UI: Wyłącz kalendarze",
+ "attributeUiDisableMaintenance": "UI: Wyłącz konserwację",
+ "attributeUiHidePositionAttributes": "UI: Ukryj atrybuty pozycji",
+ "attributeUiDisableLoginLanguage": "UI: Wyłącz język logowania",
+ "attributeNotificationTokens": "Tokeny powiadomień",
+ "attributePopupInfo": "Wyskakujące informacje",
+ "errorTitle": "Błąd",
+ "errorGeneral": "Nieprawidłowe parametry lub naruszenie ograniczeń",
+ "errorConnection": "Błąd przy połączeniu",
+ "errorSocket": "Błąd połączenia gniazda sieciowego",
+ "errorZero": "Nie może być zerem",
+ "userEmail": "E-mail",
+ "userPassword": "Hasło",
+ "userAdmin": "Administrator",
+ "userRemember": "Zapamiętaj",
+ "userExpirationTime": "Wygaśnięcie",
+ "userDeviceLimit": "Limit urządzeń",
+ "userUserLimit": "Limit użytkownika",
+ "userDeviceReadonly": "Urządzenie do odczytu",
+ "userLimitCommands": "Ogranicz polecenia",
+ "userDisableReports": "Wyłącz raporty",
+ "userFixedEmail": "Bez zmiany adresu e-mail",
+ "userToken": "Token",
+ "userDeleteAccount": "Usuń konto",
+ "userTemporary": "Temporary",
+ "loginTitle": "Login",
+ "loginLanguage": "Język",
+ "loginReset": "Zresetuj hasło",
+ "loginRegister": "Rejestracja",
+ "loginLogin": "Zaloguj",
+ "loginOpenId": "Zaloguj się za pomocą OpenID",
+ "loginFailed": "Nieprawidłowy adres e-mail lub hasło",
+ "loginCreated": "Nowy użytkownik został zarejestrowany",
+ "loginResetSuccess": "Sprawdź swój e-mail",
+ "loginUpdateSuccess": "Nowe hasło zostało ustawione",
+ "loginLogout": "Wyloguj",
+ "loginLogo": "Logo",
+ "loginTotpCode": "Kod hasła jednorazowego",
+ "loginTotpKey": "Klucz hasła jednorazowego",
+ "devicesAndState": "Urządzenia i stan",
+ "deviceSelected": "Wybrane urządzenie",
+ "deviceTitle": "Urządzenia",
+ "devicePrimaryInfo": "Tytuł urządzenia",
+ "deviceSecondaryInfo": "Szczegóły urządzenia",
+ "deviceIdentifier": "Identyfikator",
+ "deviceModel": "Model",
+ "deviceContact": "Kontakt",
+ "deviceCategory": "Kategoria",
+ "deviceLastUpdate": "Ostatnia aktualizacja",
+ "deviceCommand": "Komenda",
+ "deviceFollow": "Podążaj",
+ "deviceTotalDistance": "Całkowity dystans",
+ "deviceStatus": "Status",
+ "deviceStatusOnline": "Online",
+ "deviceStatusOffline": "Offline",
+ "deviceStatusUnknown": "Nieznany",
+ "deviceRegisterFirst": "Zarejestruj pierwsze urządzenie",
+ "deviceIdentifierHelp": "IMEI, numer seryjny lub inny identyfikator. Musi być zgodny z identyfikatorem raportującego urządzenia",
+ "deviceShare": "Share Device",
+ "groupDialog": "Grupa",
+ "groupParent": "Grupa",
+ "groupNoGroup": "Brak grupy",
+ "settingsTitle": "Ustawienia",
+ "settingsUser": "Konto",
+ "settingsGroups": "Grupy",
+ "settingsServer": "Serwer",
+ "settingsUsers": "Użytkownicy",
+ "settingsDistanceUnit": "Jednostka odległości",
+ "settingsAltitudeUnit": "Jednostka wysokości",
+ "settingsSpeedUnit": "Jednostka prędkości",
+ "settingsVolumeUnit": "Jednostka objętości",
+ "settingsTwelveHourFormat": "Format 12-godz.",
+ "settingsCoordinateFormat": "Format współrzędnych",
+ "settingsServerVersion": "Wersja serwera",
+ "settingsAppVersion": "Wersja aplikacji",
+ "settingsConnection": "Połączenie",
+ "settingsDarkMode": "Tryb ciemny",
+ "settingsTotpEnable": "Włącz hasło jednorazowe",
+ "settingsTotpForce": "Wymuś hasło jednorazowe",
+ "settingsServiceWorkerUpdateInterval": "Interwał aktualizacji ServiceWorkera",
+ "settingsUpdateAvailable": "Dostępna jest aktualizacja.",
+ "settingsSupport": "Support",
+ "reportTitle": "Raporty",
+ "reportScheduled": "Zaplanowane raporty",
+ "reportDevice": "Urządzenie",
+ "reportGroup": "Grupa",
+ "reportFrom": "Z",
+ "reportTo": "Do",
+ "reportShow": "Pokaż",
+ "reportClear": "Wyczyść",
+ "linkGoogleMaps": "Mapy Google",
+ "linkAppleMaps": "Mapy Apple",
+ "linkStreetView": "Street View",
+ "positionFixTime": "Czas ustalenia pozycji",
+ "positionDeviceTime": "Czas urządzenia",
+ "positionServerTime": "Czas serwera",
+ "positionValid": "Aktywny",
+ "positionAccuracy": "Dokładność",
+ "positionLatitude": "Szerokość",
+ "positionLongitude": "Długość",
+ "positionAltitude": "Wysokość",
+ "positionSpeed": "Prędkość",
+ "positionCourse": "Kurs",
+ "positionAddress": "Adres",
+ "positionProtocol": "Protokół",
+ "positionDistance": "Odległość",
+ "positionRpm": "RPM",
+ "positionFuel": "Paliwo",
+ "positionPower": "Moc",
+ "positionBattery": "Bateria",
+ "positionRaw": "Surowy",
+ "positionIndex": "Indeks",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "Satelity",
+ "positionSatVisible": "Widoczne satelity",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "Roaming",
+ "positionEvent": "Zdarzenie",
+ "positionAlarm": "Alarm",
+ "positionStatus": "Status",
+ "positionOdometer": "Drogomierz",
+ "positionServiceOdometer": "Drogomierz usługi",
+ "positionTripOdometer": "Drogomierz podróży",
+ "positionHours": "Godziny",
+ "positionSteps": "Kroki",
+ "positionInput": "Wejście",
+ "positionHeartRate": "Tętno",
+ "positionOutput": "Wyjście",
+ "positionBatteryLevel": "Poziom baterii",
+ "positionFuelConsumption": "Zużycie paliwa",
+ "positionRfid": "RFID",
+ "positionVersionFw": "Wersja firmware",
+ "positionVersionHw": "Wersja sprzętu",
+ "positionIgnition": "Zapłon",
+ "positionFlags": "Flagi",
+ "positionCharge": "Ładowanie",
+ "positionIp": "IP",
+ "positionArchive": "Archiwum",
+ "positionVin": "VIN",
+ "positionApproximate": "Przybliżony",
+ "positionThrottle": "Przepustnica",
+ "positionMotion": "Ruch",
+ "positionArmed": "Uzbrojony",
+ "positionAcceleration": "Przyśpieszenie",
+ "positionTemp": "Temperatura",
+ "positionDeviceTemp": "Temperatura urządzenia",
+ "positionCoolantTemp": "Temperatura płynu chłodzącego",
+ "positionOperator": "Operator",
+ "positionCommand": "Polecenie",
+ "positionBlocked": "Zablokowany",
+ "positionDtcs": "DTCs",
+ "positionObdSpeed": "Prędkość OBD",
+ "positionObdOdometer": "Drogomierz OBD",
+ "positionDrivingTime": "Czas podróży",
+ "positionDriverUniqueId": "Unikalny ID kierowcy",
+ "positionCard": "Karta",
+ "positionImage": "Obraz",
+ "positionVideo": "Wideo",
+ "positionAudio": "Dźwięk",
+ "serverTitle": "Ustawienia serwera",
+ "serverZoom": "Powiększenie",
+ "serverRegistration": "Rejestracja",
+ "serverReadonly": "Tylko do odczytu",
+ "serverForceSettings": "Wymuś ustawienia",
+ "serverAnnouncement": "Komunikat",
+ "serverName": "Nazwa serwera",
+ "serverDescription": "Opis serwera",
+ "serverColorPrimary": "Kolor podstawowy",
+ "serverColorSecondary": "Kolor drugorzędny",
+ "serverLogo": "Obraz logo",
+ "serverLogoInverted": "Odwrócony obraz logo",
+ "serverChangeDisable": "Wyłącz zmianę serwera",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "Mapa",
+ "mapActive": "Aktywne mapy",
+ "mapOverlay": "Nakładka mapy",
+ "mapOverlayCustom": "Niestandardowa nakładka",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "Klucz API OpenWeather",
+ "mapOpenWeatherClouds": "Chmury OpenWeather",
+ "mapOpenWeatherPrecipitation": "Opady OpenWeather",
+ "mapOpenWeatherPressure": "Ciśnienie OpenWeather",
+ "mapOpenWeatherWind": "Wiatr OpenWeather",
+ "mapOpenWeatherTemperature": "Temperatura OpenWeather",
+ "mapLayer": "Rodzaj mapy",
+ "mapCustom": "Własny (XYZ)",
+ "mapCustomArcgis": "Własny (ArcGIS)",
+ "mapCustomLabel": "Własna mapa",
+ "mapCarto": "Carto Basemaps",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Bing Maps Key",
+ "mapBingRoad": "Bing Maps Road",
+ "mapBingAerial": "Bing Maps Aerial",
+ "mapBingHybrid": "Bing Maps Hybrid",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Yandex Map",
+ "mapYandexSat": "Yandex Satellite",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Spis uzbrojenia",
+ "mapMapboxStreets": "Mapbox Streets",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox Outdoors",
+ "mapMapboxSatellite": "Mapbox Satellite",
+ "mapMapboxKey": "Token dostępu do Mapboxa",
+ "mapMapTilerBasic": "MapTiler Basic",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "Klucz API MapTiler",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "Token dostępu LocationIQ",
+ "mapTomTomBasic": "Podstawowy TomTom",
+ "mapTomTomFlow": "Ruch drogowy TomTom",
+ "mapTomTomIncidents": "Wypadki drogowe TomTom",
+ "mapTomTomKey": "Klucz API TomTom",
+ "mapHereBasic": "Podstawowy Here",
+ "mapHereHybrid": "Hybrydowy Here",
+ "mapHereSatellite": "Satelitarny Here",
+ "mapHereFlow": "Ruch drogowy Here",
+ "mapHereKey": "Klucz API Here",
+ "mapShapePolygon": "Wielokąt",
+ "mapShapeCircle": "Okrąg",
+ "mapShapePolyline": "Krzywa",
+ "mapLiveRoutes": "Ścieżki na żywo",
+ "mapDirection": "Pokaż kierunek",
+ "mapCurrentLocation": "Aktualna lokalizacja",
+ "mapPoiLayer": "Warstwa POI",
+ "mapClustering": "Grupowanie znaczników",
+ "mapOnSelect": "Pokaż mapę w zaznaczeniu",
+ "mapDefault": "Mapa domyślna",
+ "stateTitle": "Stan i lokalizacja",
+ "stateName": "Właściwość",
+ "stateValue": "Wartość",
+ "commandTitle": "Komenda",
+ "commandSend": "Wyślij",
+ "commandSent": "Polecenie wysłane",
+ "commandQueued": "Polecenie zakolejkowane",
+ "commandUnit": "Jednostka",
+ "commandCustom": "Własna komenda",
+ "commandDeviceIdentification": "Identyfikacja urządzenia",
+ "commandPositionSingle": "Pojedyncze raportowanie",
+ "commandPositionPeriodic": "Okresowy raport",
+ "commandPositionStop": "Zatrzymaj raportowanie",
+ "commandEngineStop": "Silnik - Stop",
+ "commandEngineResume": "Silnik - Wznów",
+ "commandAlarmArm": "Włączanie alarmu",
+ "commandAlarmDisarm": "Wyłączanie alarmu",
+ "commandAlarmDismiss": "Odwołaj alarm",
+ "commandSetTimezone": "Ustaw strefę czasową",
+ "commandRequestPhoto": "Żądanie zdjęcia\n",
+ "commandPowerOff": "Wyłącz urządzenie",
+ "commandRebootDevice": "Zresetuj urządzenie",
+ "commandFactoryReset": "Przywrócenie ustawień fabrycznych",
+ "commandSendSms": "Wyślij SMS",
+ "commandSendUssd": "Wyślij USSD",
+ "commandSosNumber": "Ustaw numer SOS",
+ "commandSilenceTime": "Ustaw czas milczenia",
+ "commandSetPhonebook": "Ustaw książkę adresową",
+ "commandVoiceMessage": "Wiadomość głosowa",
+ "commandOutputControl": "Kontrola wyjścia",
+ "commandVoiceMonitoring": "Monitorowanie głosu",
+ "commandSetAgps": "Ustaw AGPS",
+ "commandSetIndicator": "Ustaw znacznik",
+ "commandConfiguration": "Konfiguracja",
+ "commandGetVersion": "Pobierz wersję",
+ "commandFirmwareUpdate": "Aktualizuj firmware",
+ "commandSetConnection": "Ustaw połączenie",
+ "commandSetOdometer": "Ustaw drogomierz",
+ "commandGetModemStatus": "Pobierz status modemu",
+ "commandGetDeviceStatus": "Pobierz status urządzenia",
+ "commandSetSpeedLimit": "Ustaw ograniczenie prędkości",
+ "commandModePowerSaving": "Tryb oszczędzania energii",
+ "commandModeDeepSleep": "Tryb głębokiego uśpienia",
+ "commandAlarmGeofence": "Ustaw alarm obszaru monitorowanego",
+ "commandAlarmBattery": "Ustaw alarm baterii",
+ "commandAlarmSos": "Ustaw alarm SOS",
+ "commandAlarmRemove": "Ustaw usunięcie alarmu",
+ "commandAlarmClock": "Ustaw alarm zegara",
+ "commandAlarmSpeed": "Ustaw alarm prędkości",
+ "commandAlarmFall": "Ustaw alarm upadku",
+ "commandAlarmVibration": "Ustaw alarm wibracji",
+ "commandFrequency": "Częstotliwość",
+ "commandTimezone": "Przesunięcie strefy czasowej",
+ "commandMessage": "Wiadomość",
+ "commandRadius": "Promień",
+ "commandEnable": "Włącz",
+ "commandData": "Dane",
+ "commandIndex": "Index",
+ "commandPhone": "Numer telefonu",
+ "commandServer": "Serwer",
+ "commandPort": "Port",
+ "eventAll": "Wszystkie zdarzenia",
+ "eventDeviceOnline": "Status online",
+ "eventDeviceUnknown": "Status nieznany",
+ "eventDeviceOffline": "Status offline",
+ "eventDeviceInactive": "Urządzenie nieaktywne",
+ "eventQueuedCommandSent": "Wysłano polecenie z kolejki",
+ "eventDeviceMoving": "Urządzenie się porusza",
+ "eventDeviceStopped": "Urządzenie zatrzymane",
+ "eventDeviceOverspeed": "Przekroczono ograniczenie prędkości",
+ "eventDeviceFuelDrop": "Utrata paliwa",
+ "eventDeviceFuelIncrease": "Wzrost paliwa",
+ "eventCommandResult": "Rezultat polecenia",
+ "eventGeofenceEnter": "Wkroczono w obszar monitorowany",
+ "eventGeofenceExit": "Opuszczono obszar monitorowany",
+ "eventAlarm": "Alarm",
+ "eventIgnitionOn": "Zapłon włączony",
+ "eventIgnitionOff": "Zapłon wyłączony",
+ "eventMaintenance": "Wymagana konserwacja",
+ "eventTextMessage": "Odebrano wiadomość tekstową",
+ "eventDriverChanged": "Zmieniana kierowcy",
+ "eventMedia": "Media",
+ "eventsScrollToLast": "Przewiń do ostatniego",
+ "eventsSoundEvents": "Dźwięk wydarzeń",
+ "eventsSoundAlarms": "Dźwięk alarmów",
+ "alarmGeneral": "Ogólny",
+ "alarmSos": "SOS",
+ "alarmVibration": "WIbracja",
+ "alarmMovement": "Ruch",
+ "alarmLowspeed": "Niska prędkość",
+ "alarmOverspeed": "Przekroczenie prędkości",
+ "alarmFallDown": "Upadek",
+ "alarmLowPower": "Mało energii",
+ "alarmLowBattery": "Niski stan baterii",
+ "alarmFault": "Błąd",
+ "alarmPowerOff": "Wyłączenie zasilania",
+ "alarmPowerOn": "Włączenie zasilania",
+ "alarmDoor": "Drzwi",
+ "alarmLock": "Zamknięty",
+ "alarmUnlock": "Otwarty",
+ "alarmGeofence": "Obszar monitorowany",
+ "alarmGeofenceEnter": "Obszar monitorowany wejście",
+ "alarmGeofenceExit": "Obszar monitorowany wyjście",
+ "alarmGpsAntennaCut": "Odłączenie anteny GPS",
+ "alarmAccident": "Wypadek",
+ "alarmTow": "Holowanie",
+ "alarmIdle": "Bezczynność",
+ "alarmHighRpm": "Wysokie obroty silnika",
+ "alarmHardAcceleration": "Mocne przyśpiesznie",
+ "alarmHardBraking": "Mocne hamowanie",
+ "alarmHardCornering": "Ostre zakręty",
+ "alarmLaneChange": "Zmiana pasa ruchu",
+ "alarmFatigueDriving": "Zmęczenie",
+ "alarmPowerCut": "Odcięcie zasilania",
+ "alarmPowerRestored": "Zasilanie przywrócone",
+ "alarmJamming": "Zakłócanie",
+ "alarmTemperature": "Temperatura",
+ "alarmParking": "Parkowanie",
+ "alarmBonnet": "Maska",
+ "alarmFootBrake": "Hamulec nożny",
+ "alarmFuelLeak": "Wyciek paliwa",
+ "alarmTampering": "Manipulacja",
+ "alarmRemoving": "Usuwanie",
+ "notificationType": "Rodzaj powiadomienia",
+ "notificationAlways": "Wszystkie urządzenia",
+ "notificationNotificators": "Kanały",
+ "notificatorCommand": "Komenda",
+ "notificatorWeb": "Sieć",
+ "notificatorMail": "Poczta",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Odtwórz",
+ "reportCombined": "Połączone",
+ "reportRoute": "Trasa",
+ "reportEvents": "Zdarzenia",
+ "reportTrips": "Podróże",
+ "reportStops": "Zatrzymania",
+ "reportSummary": "Podsumowanie",
+ "reportDaily": "Podsumowanie dzienne",
+ "reportChart": "Wykres",
+ "reportConfigure": "Konfiguracja",
+ "reportEventTypes": "Rodzaje zdarzeń",
+ "reportChartType": "Typ wykresu",
+ "reportShowMarkers": "Pokaż znaczniki",
+ "reportExport": "Eksportuj",
+ "reportEmail": "Raport e-mailowy",
+ "reportSchedule": "Zaplanuj",
+ "reportPeriod": "Okres",
+ "reportCustom": "Własny",
+ "reportToday": "Dzisiaj",
+ "reportYesterday": "Wczoraj",
+ "reportThisWeek": "W tym tygodniu",
+ "reportPreviousWeek": "W poprzednim tygodniu",
+ "reportThisMonth": "W tym miesiącu",
+ "reportPreviousMonth": "W poprzednim miesiącu",
+ "reportDeviceName": "Nazwa urządzenia",
+ "reportAverageSpeed": "Średnia prędkość",
+ "reportMaximumSpeed": "Maksymalna prędkość",
+ "reportEngineHours": "Czas pracy silnika",
+ "reportDuration": "Czas trwania",
+ "reportStartDate": "Data początkowa",
+ "reportStartTime": "Czas uruchomienia",
+ "reportStartAddress": "Adres początkowy",
+ "reportEndTime": "Czas końcowy",
+ "reportEndAddress": "Adres końcowy",
+ "reportSpentFuel": "Zużyte paliwo",
+ "reportStartOdometer": "Start drogomierza",
+ "reportEndOdometer": "Stop drogomierza",
+ "statisticsTitle": "Statystyki",
+ "statisticsCaptureTime": "Czas zapisu",
+ "statisticsActiveUsers": "Aktywni użytkownicy",
+ "statisticsActiveDevices": "Aktywne urządzenia",
+ "statisticsRequests": "Żądania",
+ "statisticsMessagesReceived": "Odebrane wiadomości",
+ "statisticsMessagesStored": "Zapisane wiadomości",
+ "statisticsGeocoder": "Żądania Geokodera",
+ "statisticsGeolocation": "Żądania geolokacji",
+ "categoryArrow": "Strzałka",
+ "categoryDefault": "Domyślny",
+ "categoryAnimal": "Zwierzę",
+ "categoryBicycle": "Rower",
+ "categoryBoat": "Łódź",
+ "categoryBus": "Autobus",
+ "categoryCar": "Samochód",
+ "categoryCamper": "Camper",
+ "categoryCrane": "Dźwig",
+ "categoryHelicopter": "Śmigłowiec",
+ "categoryMotorcycle": "Motocykl",
+ "categoryOffroad": "Pojazd terenowy",
+ "categoryPerson": "Osoba",
+ "categoryPickup": "Pickup",
+ "categoryPlane": "Samolot",
+ "categoryShip": "Statek",
+ "categoryTractor": "Traktor",
+ "categoryTrain": "Pociąg",
+ "categoryTram": "Tramwaj",
+ "categoryTrolleybus": "Trolejbus",
+ "categoryTruck": "Ciężarówka",
+ "categoryVan": "Van",
+ "categoryScooter": "Skuter",
+ "maintenanceStart": "Start",
+ "maintenancePeriod": "Okres"
+} \ No newline at end of file
diff --git a/src/resources/l10n/pt.json b/src/resources/l10n/pt.json
new file mode 100644
index 00000000..66181d39
--- /dev/null
+++ b/src/resources/l10n/pt.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "A Carregar...",
+ "sharedHide": "Ocultar",
+ "sharedSave": "Guardar",
+ "sharedUpload": "Upload",
+ "sharedSet": "Conjunto",
+ "sharedCancel": "Cancelar",
+ "sharedCopy": "Copy",
+ "sharedAdd": "Adicionar",
+ "sharedEdit": "Editar",
+ "sharedRemove": "Remover",
+ "sharedRemoveConfirm": "Remover item?",
+ "sharedNoData": "No data",
+ "sharedSubject": "Subject",
+ "sharedYes": "Sim",
+ "sharedNo": "Não",
+ "sharedKm": "Km",
+ "sharedMi": "Mi",
+ "sharedNmi": "Nmi",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "Km/h",
+ "sharedKmh": "Km/h",
+ "sharedMph": "Mph",
+ "sharedHour": "Hora",
+ "sharedMinute": "Minuto",
+ "sharedSecond": "Segundo",
+ "sharedDays": "Dias",
+ "sharedHours": "Horas",
+ "sharedMinutes": "Minutos",
+ "sharedDecimalDegrees": "Graus Decimais",
+ "sharedDegreesDecimalMinutes": "Graus Decimais Minutos",
+ "sharedDegreesMinutesSeconds": "Graus Decimais Segundos",
+ "sharedName": "Nome",
+ "sharedDescription": "Descrição",
+ "sharedSearch": "Pesquisar",
+ "sharedIconScale": "Icon Scale",
+ "sharedGeofence": "Cerca Geográfica",
+ "sharedGeofences": "Cercas Geográficas",
+ "sharedCreateGeofence": "Create Geofence",
+ "sharedNotifications": "Notificações",
+ "sharedNotification": "Notificação",
+ "sharedAttributes": "Atributos",
+ "sharedAttribute": "Atributo",
+ "sharedDrivers": "Condutores",
+ "sharedDriver": "Condutor",
+ "sharedArea": "Área",
+ "sharedSound": "Notificação Sonora",
+ "sharedType": "Tipo",
+ "sharedDistance": "Distância",
+ "sharedHourAbbreviation": "h",
+ "sharedMinuteAbbreviation": "m",
+ "sharedSecondAbbreviation": "s",
+ "sharedVoltAbbreviation": "Volts",
+ "sharedLiterAbbreviation": "Litros",
+ "sharedGallonAbbreviation": "Gal",
+ "sharedLiter": "Litro",
+ "sharedImpGallon": "Galão Imp.",
+ "sharedUsGallon": "Galão U.S.",
+ "sharedLiterPerHourAbbreviation": "Litros/Hora",
+ "sharedGetMapState": "Obter Estado do Mapa",
+ "sharedComputedAttribute": "Atributo",
+ "sharedComputedAttributes": "Atributos",
+ "sharedCheckComputedAttribute": "Verificar Atributos",
+ "sharedExpression": "Expressão",
+ "sharedDevice": "Dispositivo",
+ "sharedTest": "Test",
+ "sharedTestNotification": "Enviar Notificação Teste",
+ "sharedTestNotificators": "Test Channels",
+ "sharedTestExpression": "Test Expression",
+ "sharedCalendar": "Calendário",
+ "sharedCalendars": "Calendários",
+ "sharedFile": "Ficheiro",
+ "sharedSearchDevices": "Search Devices",
+ "sharedSortBy": "Sort By",
+ "sharedFilterMap": "Filter on Map",
+ "sharedSelectFile": "Seleccionar Ficheiro",
+ "sharedPhone": "Telefone",
+ "sharedRequired": "Exigido",
+ "sharedPreferences": "Preferências",
+ "sharedPermissions": "Permissões",
+ "sharedConnections": "Conexões",
+ "sharedExtra": "Extra",
+ "sharedPrimary": "Primary",
+ "sharedSecondary": "Secondary",
+ "sharedTypeString": "Corda",
+ "sharedTypeNumber": "Número",
+ "sharedTypeBoolean": "Boleano",
+ "sharedTimezone": "Fuso-Horário",
+ "sharedInfoTitle": "Informação",
+ "sharedSavedCommand": "Comando Gravado",
+ "sharedSavedCommands": "Comandos Gravados",
+ "sharedNew": "Novo...",
+ "sharedShowAddress": "Mostrar Morada",
+ "sharedShowDetails": "Mais Detalhes",
+ "sharedDisabled": "Desativado",
+ "sharedMaintenance": "Manutenção",
+ "sharedDeviceAccumulators": "Acumuladores",
+ "sharedAlarms": "Alarmes",
+ "sharedLocation": "Localização",
+ "sharedImport": "Importar",
+ "sharedColumns": "Columns",
+ "sharedDropzoneText": "Drag and drop a file here or click",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "Simple",
+ "calendarRecurrence": "Recurrence",
+ "calendarOnce": "Once",
+ "calendarDaily": "Daily",
+ "calendarWeekly": "Weekly",
+ "calendarMonthly": "Monthly",
+ "calendarDays": "Days",
+ "calendarSunday": "Sunday",
+ "calendarMonday": "Monday",
+ "calendarTuesday": "Tuesday",
+ "calendarWednesday": "Wednesday",
+ "calendarThursday": "Thursday",
+ "calendarFriday": "Friday",
+ "calendarSaturday": "Saturday",
+ "attributeShowGeofences": "Show Geofences",
+ "attributeSpeedLimit": "Limite de Velocidade",
+ "attributeFuelDropThreshold": "Fuel Drop Threshold",
+ "attributeFuelIncreaseThreshold": "Fuel Increase Threshold",
+ "attributePolylineDistance": "Distância Polilinha",
+ "attributeReportIgnoreOdometer": "Relatório: Ignorar Conta-Quilómetros",
+ "attributeWebReportColor": "Web: Cor do Relatório",
+ "attributeDevicePassword": "Password do Dispositivo",
+ "attributeDeviceImage": "Device Image",
+ "attributeDeviceInactivityStart": "Inicio da Inatividade do Dispositivo",
+ "attributeDeviceInactivityPeriod": "Período de Inatividade do Dispositivo",
+ "attributeProcessingCopyAttributes": "Processando: Cópia dos Atributos",
+ "attributeColor": "Cor",
+ "attributeWebLiveRouteLength": "Web: Comprimento da Rota ao Vivo",
+ "attributeWebSelectZoom": "Web: Zoom na Selecção",
+ "attributeWebMaxZoom": "Web: Zoom Máximo",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Pushover User Key",
+ "attributePushoverDeviceNames": "Pushover Device Names",
+ "attributeMailSmtpHost": "Mail: Alojamento SMTP",
+ "attributeMailSmtpPort": "Mail: Porta SMTP",
+ "attributeMailSmtpStarttlsEnable": "Mail: SMTP STARTTLS Activo",
+ "attributeMailSmtpStarttlsRequired": "Mail: SMTP STARTTLS Necessário",
+ "attributeMailSmtpSslEnable": "Mail: SMTP SSL Activo",
+ "attributeMailSmtpSslTrust": "Mail: SMTP SSL Verificado",
+ "attributeMailSmtpSslProtocols": "Mail: SMTP Protocolos SSL",
+ "attributeMailSmtpFrom": "Mail: SMTP de",
+ "attributeMailSmtpAuth": "Mail: SMTP Autenticação Activa",
+ "attributeMailSmtpUsername": "Mail: Utilizador SMTP",
+ "attributeMailSmtpPassword": "Mail: Password SMTP",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: Disable Attributes",
+ "attributeUiDisableGroups": "UI: Disable Groups",
+ "attributeUiDisableEvents": "UI: Desativar Eventos",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "UI: Desativar Condutores",
+ "attributeUiDisableComputedAttributes": "UI: Desativar Atributos",
+ "attributeUiDisableCalendars": "UI: Desativar Calendários",
+ "attributeUiDisableMaintenance": "Desativar Manutenção",
+ "attributeUiHidePositionAttributes": "UI: Ocultar Atributos de Localização",
+ "attributeUiDisableLoginLanguage": "UI: Disable Login Language",
+ "attributeNotificationTokens": "Tokens de Notificação",
+ "attributePopupInfo": "Popup Info",
+ "errorTitle": "Erro",
+ "errorGeneral": "Parâmetros Inválidos",
+ "errorConnection": "Erro de conexão",
+ "errorSocket": "Erro de Conexão",
+ "errorZero": "Não pode ser zero",
+ "userEmail": "Utilizador",
+ "userPassword": "Senha",
+ "userAdmin": "Administrador",
+ "userRemember": "Relembrar",
+ "userExpirationTime": "Expira em:",
+ "userDeviceLimit": "Limite de Dispositivos",
+ "userUserLimit": "Limite de Utilizadores",
+ "userDeviceReadonly": "Leitura de Dispositivo",
+ "userLimitCommands": "Limite de Comandos",
+ "userDisableReports": "Disable Reports",
+ "userFixedEmail": "No Email Change",
+ "userToken": "Símbolo",
+ "userDeleteAccount": "Delete Account",
+ "userTemporary": "Temporary",
+ "loginTitle": "Entrar",
+ "loginLanguage": "Idioma",
+ "loginReset": "Reset Password",
+ "loginRegister": "Registar",
+ "loginLogin": "Entrar",
+ "loginOpenId": "Login with OpenID",
+ "loginFailed": "Dados incorrectos ou inexistentes",
+ "loginCreated": "Novo utilizador foi registado",
+ "loginResetSuccess": "Check your email",
+ "loginUpdateSuccess": "New password is set",
+ "loginLogout": "Sair",
+ "loginLogo": "Logótipo",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "Dispositivos e Estados",
+ "deviceSelected": "Selected Device",
+ "deviceTitle": "Dispositivos",
+ "devicePrimaryInfo": "Device Title",
+ "deviceSecondaryInfo": "Device Detail",
+ "deviceIdentifier": "Identificador",
+ "deviceModel": "Modelo",
+ "deviceContact": "Contacto",
+ "deviceCategory": "Categoria",
+ "deviceLastUpdate": "Ultima Actualização",
+ "deviceCommand": "Comando",
+ "deviceFollow": "Seguir",
+ "deviceTotalDistance": "Distancia Total",
+ "deviceStatus": "Estado",
+ "deviceStatusOnline": "Online",
+ "deviceStatusOffline": "Offline",
+ "deviceStatusUnknown": "Desconhecido",
+ "deviceRegisterFirst": "Register your first device",
+ "deviceIdentifierHelp": "IMEI, serial number or other id. It has to match the identifier device reports to the server.",
+ "deviceShare": "Share Device",
+ "groupDialog": "Grupo",
+ "groupParent": "Grupo",
+ "groupNoGroup": "Sem Grupo",
+ "settingsTitle": "Configurações",
+ "settingsUser": "Conta",
+ "settingsGroups": "Grupos",
+ "settingsServer": "Servidor",
+ "settingsUsers": "Utilizadores",
+ "settingsDistanceUnit": "Unidade de Distância",
+ "settingsAltitudeUnit": "Altitude Unit",
+ "settingsSpeedUnit": "Unidade de Velocidade",
+ "settingsVolumeUnit": "Unidade de Volume",
+ "settingsTwelveHourFormat": "Formato 12 Horas",
+ "settingsCoordinateFormat": "Formato das Coordenadas",
+ "settingsServerVersion": "Server Version",
+ "settingsAppVersion": "App Version",
+ "settingsConnection": "Connection",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "Relatórios",
+ "reportScheduled": "Scheduled Reports",
+ "reportDevice": "Dispositivo",
+ "reportGroup": "Grupo",
+ "reportFrom": "De",
+ "reportTo": "Para",
+ "reportShow": "Mostrar",
+ "reportClear": "Limpar",
+ "linkGoogleMaps": "Google Maps",
+ "linkAppleMaps": "Apple Maps",
+ "linkStreetView": "Street View",
+ "positionFixTime": "Fix Time",
+ "positionDeviceTime": "Device Time",
+ "positionServerTime": "Server Time",
+ "positionValid": "Válido",
+ "positionAccuracy": "Precisão",
+ "positionLatitude": "Latitude",
+ "positionLongitude": "Longitude",
+ "positionAltitude": "Altitude",
+ "positionSpeed": "Velocidade",
+ "positionCourse": "Direcção",
+ "positionAddress": "Morada",
+ "positionProtocol": "Protocolo",
+ "positionDistance": "Distância",
+ "positionRpm": "Rpm",
+ "positionFuel": "Combustível",
+ "positionPower": "Potência",
+ "positionBattery": "Bateria",
+ "positionRaw": "Aberto",
+ "positionIndex": "Início",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "Satelites",
+ "positionSatVisible": "Satelites Visiveis",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "Roaming",
+ "positionEvent": "Evento",
+ "positionAlarm": "Alarme",
+ "positionStatus": "Estado",
+ "positionOdometer": "Conta-Quilómetros",
+ "positionServiceOdometer": "Serviço Conta-Quilómetros",
+ "positionTripOdometer": "Conta-Quilómetros de Viagem",
+ "positionHours": "Horas",
+ "positionSteps": "Posições",
+ "positionInput": "Dentro",
+ "positionHeartRate": "Heart Rate",
+ "positionOutput": "Fora",
+ "positionBatteryLevel": "Nível da Bateria",
+ "positionFuelConsumption": "Consumo de Combustível",
+ "positionRfid": "RFID",
+ "positionVersionFw": "Versão de Firmware",
+ "positionVersionHw": "Versão de Hardware",
+ "positionIgnition": "Ignição",
+ "positionFlags": "Bandeiras",
+ "positionCharge": "Carregar",
+ "positionIp": "IP",
+ "positionArchive": "Arquivo",
+ "positionVin": "VIN",
+ "positionApproximate": "Aproximação",
+ "positionThrottle": "Acelerador",
+ "positionMotion": "Movimento",
+ "positionArmed": "Armado",
+ "positionAcceleration": "Aceleração",
+ "positionTemp": "Temperature",
+ "positionDeviceTemp": "Temperatura do Dispositivo",
+ "positionCoolantTemp": "Coolant Temperature",
+ "positionOperator": "Operador",
+ "positionCommand": "Comando",
+ "positionBlocked": "Bloqueado",
+ "positionDtcs": "DTCs",
+ "positionObdSpeed": "Velocidade OBD",
+ "positionObdOdometer": "Conta-Quilómetros OBD",
+ "positionDrivingTime": "Driving Time",
+ "positionDriverUniqueId": "Identificação do Condutor",
+ "positionCard": "Card",
+ "positionImage": "Imagem",
+ "positionVideo": "Video",
+ "positionAudio": "Audio",
+ "serverTitle": "Configurações do Servidor",
+ "serverZoom": "Zoom",
+ "serverRegistration": "Registo",
+ "serverReadonly": "Apenas Leitura",
+ "serverForceSettings": "Forçar Configurações",
+ "serverAnnouncement": "Anúncio",
+ "serverName": "Server Name",
+ "serverDescription": "Server Description",
+ "serverColorPrimary": "Color Primary",
+ "serverColorSecondary": "Color Secondary",
+ "serverLogo": "Logo Image",
+ "serverLogoInverted": "Inverted Logo Image",
+ "serverChangeDisable": "Disable Server Change",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "Mapa",
+ "mapActive": "Active Maps",
+ "mapOverlay": "Map Overlay",
+ "mapOverlayCustom": "Custom Overlay",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API Key",
+ "mapOpenWeatherClouds": "OpenWeather Clouds",
+ "mapOpenWeatherPrecipitation": "OpenWeather Precipitation",
+ "mapOpenWeatherPressure": "OpenWeather Pressure",
+ "mapOpenWeatherWind": "OpenWeather Wind",
+ "mapOpenWeatherTemperature": "OpenWeather Temperature",
+ "mapLayer": "Aspecto do Mapa",
+ "mapCustom": "Padrão (XYZ)",
+ "mapCustomArcgis": "Padrão (ArcGIS)",
+ "mapCustomLabel": "Mapa Personalizado",
+ "mapCarto": "Mapa Carto",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Mapa Bing Key",
+ "mapBingRoad": "Mapa Bing Road",
+ "mapBingAerial": "Mapa Bing Aéreo",
+ "mapBingHybrid": "Mapa Bing Híbrido",
+ "mapBaidu": "Mapa Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Mapa Yandex",
+ "mapYandexSat": "Mapa Yandex Satélite",
+ "mapWikimedia": "Mapa Wikimedia",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox Ruas",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox ao ar livre",
+ "mapMapboxSatellite": "Mapbox Satélite",
+ "mapMapboxKey": "Mapbox Access Token",
+ "mapMapTilerBasic": "MapTiler Basic",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "MapTiler API Key",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ Access Token",
+ "mapTomTomBasic": "TomTom Basic",
+ "mapTomTomFlow": "TomTom Traffic Flow",
+ "mapTomTomIncidents": "TomTom Traffic Incidents",
+ "mapTomTomKey": "TomTom API Key",
+ "mapHereBasic": "Here Basic",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satellite",
+ "mapHereFlow": "Here Traffic Flow",
+ "mapHereKey": "Here API Key",
+ "mapShapePolygon": "Polígono",
+ "mapShapeCircle": "Circulo",
+ "mapShapePolyline": "Linha Polígono",
+ "mapLiveRoutes": "Rotas ao Vivo",
+ "mapDirection": "Show Direction",
+ "mapCurrentLocation": "Current Location",
+ "mapPoiLayer": "Camada POI",
+ "mapClustering": "Markers Clustering",
+ "mapOnSelect": "Show Map on Selection",
+ "mapDefault": "Default Map",
+ "stateTitle": "Estado",
+ "stateName": "Parâmetro",
+ "stateValue": "Valor",
+ "commandTitle": "Comando",
+ "commandSend": "Enviar",
+ "commandSent": "Comando Enviado",
+ "commandQueued": "Comando foi para a fila",
+ "commandUnit": "Unidade",
+ "commandCustom": "Comando Personalizado",
+ "commandDeviceIdentification": "Identificação do Dispositivo",
+ "commandPositionSingle": "Relatórios Únicos",
+ "commandPositionPeriodic": "Relatório Periódico",
+ "commandPositionStop": "Parar Posição",
+ "commandEngineStop": "Parar Motor",
+ "commandEngineResume": "Desbloqueio do Motor",
+ "commandAlarmArm": "Armar Alarme",
+ "commandAlarmDisarm": "Desarmar Alarme",
+ "commandAlarmDismiss": "Alarme Dispensado",
+ "commandSetTimezone": "Definir Fuso Horário",
+ "commandRequestPhoto": "Solicitar Foto",
+ "commandPowerOff": "Dispositivo Desligado",
+ "commandRebootDevice": "Reiniciar Dispositivo",
+ "commandFactoryReset": "Factory Reset",
+ "commandSendSms": "Enviar SMS",
+ "commandSendUssd": "Enviar USSD",
+ "commandSosNumber": "Definir Numero SOS",
+ "commandSilenceTime": "Definir Tempo de Silencio",
+ "commandSetPhonebook": "Definir Agenda",
+ "commandVoiceMessage": "Mensagem de Voz",
+ "commandOutputControl": "Controlo de Saída",
+ "commandVoiceMonitoring": "Monitorização de Voz",
+ "commandSetAgps": "Definir AGPS",
+ "commandSetIndicator": "Definir Indicador",
+ "commandConfiguration": "Configuração",
+ "commandGetVersion": "Obter Versão",
+ "commandFirmwareUpdate": "Actualizar Firmware",
+ "commandSetConnection": "Estabelecer Ligação",
+ "commandSetOdometer": "Conta-Quilómetros",
+ "commandGetModemStatus": "Estado do Modem",
+ "commandGetDeviceStatus": "Obter Estado do Dispositivos",
+ "commandSetSpeedLimit": "Set Speed Limit",
+ "commandModePowerSaving": "Power Saving Mode",
+ "commandModeDeepSleep": "Deep Sleep Mode",
+ "commandAlarmGeofence": "Set Geofence Alarm",
+ "commandAlarmBattery": "Set Battery Alarm",
+ "commandAlarmSos": "Set SOS Alarm",
+ "commandAlarmRemove": "Set Remove Alarm",
+ "commandAlarmClock": "Set Clock Alarm",
+ "commandAlarmSpeed": "Set Speed Alarm",
+ "commandAlarmFall": "Set Fall Alarm",
+ "commandAlarmVibration": "Set Vibration Alarm",
+ "commandFrequency": "Frequência",
+ "commandTimezone": "Deslocamento do Fuso Horário",
+ "commandMessage": "Mensagem",
+ "commandRadius": "Radiação",
+ "commandEnable": "Ativar",
+ "commandData": "Dados",
+ "commandIndex": "Inicio",
+ "commandPhone": "Número de Telefone",
+ "commandServer": "Servidor",
+ "commandPort": "Porta",
+ "eventAll": "Todos os Eventos",
+ "eventDeviceOnline": "Dispositivo Conectado",
+ "eventDeviceUnknown": "Estado do dispositivo desconhecido",
+ "eventDeviceOffline": "Estado Desligado",
+ "eventDeviceInactive": "Dispositivo Inativo",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "Dispositivo em Movimento",
+ "eventDeviceStopped": "Dispositivo Parado",
+ "eventDeviceOverspeed": "Atingido o Excesso de Velocidade",
+ "eventDeviceFuelDrop": "Perda de Combústivel",
+ "eventDeviceFuelIncrease": "Fuel increase",
+ "eventCommandResult": "Resultado do Comando",
+ "eventGeofenceEnter": "Entrou na Cerca Geográfica",
+ "eventGeofenceExit": "Saiu da Cerca Geográfica",
+ "eventAlarm": "Alarme",
+ "eventIgnitionOn": "Ignição Ligada",
+ "eventIgnitionOff": "Ignição Desligada",
+ "eventMaintenance": "Necessária Manutenção",
+ "eventTextMessage": "Mensagem de Texto Recebida",
+ "eventDriverChanged": "Alteração de Condutor",
+ "eventMedia": "Media",
+ "eventsScrollToLast": "Puxar para o Fim",
+ "eventsSoundEvents": "Sound Events",
+ "eventsSoundAlarms": "Sound Alarms",
+ "alarmGeneral": "Geral",
+ "alarmSos": "SOS",
+ "alarmVibration": "Vibração",
+ "alarmMovement": "Movimento",
+ "alarmLowspeed": "Baixa Velocidade",
+ "alarmOverspeed": "Excesso de Velocidade",
+ "alarmFallDown": "Queda",
+ "alarmLowPower": "Potência Baixa",
+ "alarmLowBattery": "Bateria Fraca",
+ "alarmFault": "Falha",
+ "alarmPowerOff": "Desligado",
+ "alarmPowerOn": "Ligado",
+ "alarmDoor": "Porta",
+ "alarmLock": "Bloqueio",
+ "alarmUnlock": "Desbloqueio",
+ "alarmGeofence": "Cerca Geográfica",
+ "alarmGeofenceEnter": "Entrada na Cerca Geográfica",
+ "alarmGeofenceExit": "Saída da Cerca Geográfica",
+ "alarmGpsAntennaCut": "Corte de Antena GPS",
+ "alarmAccident": "Acidente",
+ "alarmTow": "Reboque",
+ "alarmIdle": "Inútil",
+ "alarmHighRpm": "RPM Alta",
+ "alarmHardAcceleration": "Aceleração Brusca",
+ "alarmHardBraking": "Travagem Brusca",
+ "alarmHardCornering": "Viragem Brusca",
+ "alarmLaneChange": "Mudança de Faixa",
+ "alarmFatigueDriving": "Condução Cansada",
+ "alarmPowerCut": "Alarme de Corte de Corrente",
+ "alarmPowerRestored": "Alarme de Energia Restaurado",
+ "alarmJamming": "Alarme de Bloqueio",
+ "alarmTemperature": "Temperatura",
+ "alarmParking": "Estacionamento",
+ "alarmBonnet": "Alarme do Capô",
+ "alarmFootBrake": "Alarme de Travão de Mão",
+ "alarmFuelLeak": "Perda de Combustivel",
+ "alarmTampering": "Adulteração",
+ "alarmRemoving": "Removendo",
+ "notificationType": "Tipo de Notificação",
+ "notificationAlways": "Todos os Dispositivos",
+ "notificationNotificators": "Vias",
+ "notificatorCommand": "Command",
+ "notificatorWeb": "Web",
+ "notificatorMail": "E-Mail",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Replay",
+ "reportCombined": "Combined",
+ "reportRoute": "Histórico de Rotas",
+ "reportEvents": "Eventos",
+ "reportTrips": "Viagens",
+ "reportStops": "Paragens",
+ "reportSummary": "Resumo",
+ "reportDaily": "Resumo Diário",
+ "reportChart": "Gráfico",
+ "reportConfigure": "Configurar",
+ "reportEventTypes": "Tipos de Eventos",
+ "reportChartType": "Tipo de Gráfico",
+ "reportShowMarkers": "Mostrar Marcadores",
+ "reportExport": "Exportar",
+ "reportEmail": "Relatório de Email",
+ "reportSchedule": "Schedule",
+ "reportPeriod": "Periodo",
+ "reportCustom": "Personalização",
+ "reportToday": "Hoje",
+ "reportYesterday": "Ontem",
+ "reportThisWeek": "Esta Semana",
+ "reportPreviousWeek": "Semana Passada",
+ "reportThisMonth": "Este Mês",
+ "reportPreviousMonth": "Mês Passado",
+ "reportDeviceName": "Nome do Dispositivo",
+ "reportAverageSpeed": "Velocidade Média",
+ "reportMaximumSpeed": "Velocidade Máxima",
+ "reportEngineHours": "Duração Ligado",
+ "reportDuration": "Duração",
+ "reportStartDate": "Data de Início",
+ "reportStartTime": "Hora de Inicio",
+ "reportStartAddress": "Morada Inicial",
+ "reportEndTime": "Hora de Fim",
+ "reportEndAddress": "Morada Final",
+ "reportSpentFuel": "Combustível Gasto",
+ "reportStartOdometer": "Início do Conta-Quilómetros",
+ "reportEndOdometer": "Fim do Conta-Quilómetros",
+ "statisticsTitle": "Estatísticas",
+ "statisticsCaptureTime": "Data",
+ "statisticsActiveUsers": "Utilizadores Activos",
+ "statisticsActiveDevices": "Dispositivos Activos",
+ "statisticsRequests": "Pedidos",
+ "statisticsMessagesReceived": "Mensagens Recebidas",
+ "statisticsMessagesStored": "Mensagens Armazenadas",
+ "statisticsGeocoder": "Pedidos de Codificador Geográfico",
+ "statisticsGeolocation": "Pedidos de Geo Localização",
+ "categoryArrow": "Seta",
+ "categoryDefault": "Padrão",
+ "categoryAnimal": "Animal",
+ "categoryBicycle": "Bicicleta",
+ "categoryBoat": "Barco",
+ "categoryBus": "Autocarro",
+ "categoryCar": "Carro",
+ "categoryCamper": "Camper",
+ "categoryCrane": "Guindaste",
+ "categoryHelicopter": "Helicóptero",
+ "categoryMotorcycle": "Mota",
+ "categoryOffroad": "Jipe",
+ "categoryPerson": "Pessoa",
+ "categoryPickup": "Pickup",
+ "categoryPlane": "Avião",
+ "categoryShip": "Barco",
+ "categoryTractor": "Tractor",
+ "categoryTrain": "Comboio",
+ "categoryTram": "Eléctrico",
+ "categoryTrolleybus": "Autocarro Eléctrico",
+ "categoryTruck": "Camião",
+ "categoryVan": "Caravana",
+ "categoryScooter": "Scooter",
+ "maintenanceStart": "Iniciar",
+ "maintenancePeriod": "Período"
+} \ No newline at end of file
diff --git a/src/resources/l10n/pt_BR.json b/src/resources/l10n/pt_BR.json
new file mode 100644
index 00000000..7f47050f
--- /dev/null
+++ b/src/resources/l10n/pt_BR.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "Carregando...",
+ "sharedHide": "oculto",
+ "sharedSave": "Salvar",
+ "sharedUpload": "Enviar",
+ "sharedSet": "Aplicar",
+ "sharedCancel": "Cancelar",
+ "sharedCopy": "Copy",
+ "sharedAdd": "Adicionar",
+ "sharedEdit": "Editar",
+ "sharedRemove": "Remover",
+ "sharedRemoveConfirm": "Remover item?",
+ "sharedNoData": "Sem dados",
+ "sharedSubject": "Subject",
+ "sharedYes": "Sim",
+ "sharedNo": "Não",
+ "sharedKm": "km",
+ "sharedMi": "mi",
+ "sharedNmi": "Milhas Náuticas",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "kn",
+ "sharedKmh": "km/h",
+ "sharedMph": "mph",
+ "sharedHour": "Hora",
+ "sharedMinute": "Minuto",
+ "sharedSecond": "Segundo",
+ "sharedDays": "dias",
+ "sharedHours": "horas",
+ "sharedMinutes": "minutos",
+ "sharedDecimalDegrees": "Graus Decimais",
+ "sharedDegreesDecimalMinutes": "Graus Minutos Decimais",
+ "sharedDegreesMinutesSeconds": "Graus Minutos Segundos",
+ "sharedName": "Nome",
+ "sharedDescription": "Descrição",
+ "sharedSearch": "Busca",
+ "sharedIconScale": "Escala do ícone",
+ "sharedGeofence": "Geocerca",
+ "sharedGeofences": "Cerca Virtual",
+ "sharedCreateGeofence": "Criar cerca",
+ "sharedNotifications": "Notificações",
+ "sharedNotification": "Notificação",
+ "sharedAttributes": "Atributos",
+ "sharedAttribute": "Atributo",
+ "sharedDrivers": "Motoristas",
+ "sharedDriver": "Motorista",
+ "sharedArea": "Área",
+ "sharedSound": "Som de notificação",
+ "sharedType": "Tipo",
+ "sharedDistance": "Distância",
+ "sharedHourAbbreviation": "h",
+ "sharedMinuteAbbreviation": "m",
+ "sharedSecondAbbreviation": "s",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "L",
+ "sharedGallonAbbreviation": "Galão",
+ "sharedLiter": "Litro",
+ "sharedImpGallon": "Imp. Galão",
+ "sharedUsGallon": "U.S. Galão",
+ "sharedLiterPerHourAbbreviation": "L/h",
+ "sharedGetMapState": "Obter estado do mapa",
+ "sharedComputedAttribute": "Atributo Calculado",
+ "sharedComputedAttributes": "Atributos Calculados",
+ "sharedCheckComputedAttribute": "Verificar Atributo Calculado",
+ "sharedExpression": "Expressão",
+ "sharedDevice": "Dispositivo",
+ "sharedTest": "Teste",
+ "sharedTestNotification": "Enviar notificação de teste",
+ "sharedTestNotificators": "Canais de Teste",
+ "sharedTestExpression": "Expressão de teste",
+ "sharedCalendar": "Calendário",
+ "sharedCalendars": "Calendários",
+ "sharedFile": "Arquivo",
+ "sharedSearchDevices": "Pesquisar Dispositivos",
+ "sharedSortBy": "Organizar por",
+ "sharedFilterMap": "Filtrar no mapa",
+ "sharedSelectFile": "Selecionar Arquivo",
+ "sharedPhone": "Telefone",
+ "sharedRequired": "Necessário",
+ "sharedPreferences": "Preferências",
+ "sharedPermissions": "Permissões",
+ "sharedConnections": "Conexões",
+ "sharedExtra": "Adicional",
+ "sharedPrimary": "Primário",
+ "sharedSecondary": "Secundário",
+ "sharedTypeString": "Texto",
+ "sharedTypeNumber": "Número",
+ "sharedTypeBoolean": "Lógico",
+ "sharedTimezone": "Fuso horário",
+ "sharedInfoTitle": "Informação",
+ "sharedSavedCommand": "Comando Salvo",
+ "sharedSavedCommands": "Comandos Salvos",
+ "sharedNew": "Novo...",
+ "sharedShowAddress": "Mostrar Endereço",
+ "sharedShowDetails": "Mais Detalhes",
+ "sharedDisabled": "Desativado",
+ "sharedMaintenance": "Manutenção",
+ "sharedDeviceAccumulators": "Contadores",
+ "sharedAlarms": "Alarmes",
+ "sharedLocation": "Localização",
+ "sharedImport": "Importar",
+ "sharedColumns": "Colunas",
+ "sharedDropzoneText": "Arraste e solte um arquivo aqui ou clique",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "Simples",
+ "calendarRecurrence": "Recorrência",
+ "calendarOnce": "Uma vez",
+ "calendarDaily": "Diariamente",
+ "calendarWeekly": "Semanalmente",
+ "calendarMonthly": "Mensalmente",
+ "calendarDays": "Dias",
+ "calendarSunday": "Domingo",
+ "calendarMonday": "Segunda-feira",
+ "calendarTuesday": "Terça-feira",
+ "calendarWednesday": "Quarta-feira",
+ "calendarThursday": "Quinta-feira",
+ "calendarFriday": "Sexta-feira",
+ "calendarSaturday": "Sábado",
+ "attributeShowGeofences": "Exibir Cerca Virtual",
+ "attributeSpeedLimit": "Limite de Velocidade",
+ "attributeFuelDropThreshold": "Limite de decréscimo de combustível",
+ "attributeFuelIncreaseThreshold": "Limite de acréscimo de combustível",
+ "attributePolylineDistance": "Distância da Polilinha",
+ "attributeReportIgnoreOdometer": "Relatório: Ignorar Odômetro",
+ "attributeWebReportColor": "Web: Cor do Relatório",
+ "attributeDevicePassword": "Senha do Dispositivo",
+ "attributeDeviceImage": "Imagem",
+ "attributeDeviceInactivityStart": "Início de inatividade do dispositivo",
+ "attributeDeviceInactivityPeriod": "Período de inatividade do dispositivo",
+ "attributeProcessingCopyAttributes": "Processamento: Copiar Atributos",
+ "attributeColor": "Cor",
+ "attributeWebLiveRouteLength": "Web: Comprimento da Rota ao Vivo",
+ "attributeWebSelectZoom": "Web: Zoom Selecionado",
+ "attributeWebMaxZoom": "Web: Zoom Máximo",
+ "attributeTelegramChatId": "ID do Chat do Telegram",
+ "attributePushoverUserKey": "Chave de usuário do Pushover",
+ "attributePushoverDeviceNames": "Nomes de dispositivos do Pushover",
+ "attributeMailSmtpHost": "Email: Host SMTP",
+ "attributeMailSmtpPort": "Email: Porta SMTP",
+ "attributeMailSmtpStarttlsEnable": "Email: Ativar STARTTLS (SMTP)",
+ "attributeMailSmtpStarttlsRequired": "Email: SMTP START TLS Obrigatório",
+ "attributeMailSmtpSslEnable": "Email: Ativar SSL (SMTP)",
+ "attributeMailSmtpSslTrust": "Email: Certificado SSL (SMTP)",
+ "attributeMailSmtpSslProtocols": "Email: Protocolo SSL (SMTP)",
+ "attributeMailSmtpFrom": "Email: Remetente",
+ "attributeMailSmtpAuth": "Email: Ativar Autenticação (SMTP)",
+ "attributeMailSmtpUsername": "Email: Nome de Usuário (SMTP)",
+ "attributeMailSmtpPassword": "Email: Senha (SMTP)",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: Desativar atributos",
+ "attributeUiDisableGroups": "UI: Desabilitar Grupos",
+ "attributeUiDisableEvents": "UI: Eventos Desativados",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "UI: Desativar Motoristas",
+ "attributeUiDisableComputedAttributes": "UI: Desativar Atributos Calculados",
+ "attributeUiDisableCalendars": "UI: Desativar Calendários",
+ "attributeUiDisableMaintenance": "UI: Desativar Manutenção",
+ "attributeUiHidePositionAttributes": "UI: Ocultar os atributos de posição",
+ "attributeUiDisableLoginLanguage": "UI: Desativar idioma de login",
+ "attributeNotificationTokens": "Token de Notificação",
+ "attributePopupInfo": "Informações no Pop-up",
+ "errorTitle": "Erro",
+ "errorGeneral": "Parâmetros inválidos ou violação de restrições",
+ "errorConnection": "Erro de conexão",
+ "errorSocket": "Erro na conexão com Web Socket",
+ "errorZero": "Não pode ser zero",
+ "userEmail": "Email",
+ "userPassword": "Senha",
+ "userAdmin": "Admin",
+ "userRemember": "Lembrar",
+ "userExpirationTime": "Validade",
+ "userDeviceLimit": "Limite de dispositivos",
+ "userUserLimit": "Limite de Usuários",
+ "userDeviceReadonly": "Dispositivos somente leitura",
+ "userLimitCommands": "Limite de Comandos",
+ "userDisableReports": "Desabilitar Relatórios",
+ "userFixedEmail": "Sem alteração de e-mail",
+ "userToken": "Token",
+ "userDeleteAccount": "Deletar conta",
+ "userTemporary": "Temporary",
+ "loginTitle": "Login",
+ "loginLanguage": "Idioma",
+ "loginReset": "Redefinir senha",
+ "loginRegister": "Registrar",
+ "loginLogin": "Entrar",
+ "loginOpenId": "Logar com OpenID",
+ "loginFailed": "Email ou senha incorretos",
+ "loginCreated": "Novo usuário registrado",
+ "loginResetSuccess": "Verifique seu e-mail",
+ "loginUpdateSuccess": "Nova senha definida",
+ "loginLogout": "Sair",
+ "loginLogo": "Logotipo",
+ "loginTotpCode": "Código One-time Password",
+ "loginTotpKey": "Chave One-time Password",
+ "devicesAndState": "Dispositivo e Estado",
+ "deviceSelected": "Dispositivo selecionado",
+ "deviceTitle": "Dispositivos",
+ "devicePrimaryInfo": "Título do dispositivo",
+ "deviceSecondaryInfo": "Detalhes do dispositivo",
+ "deviceIdentifier": "Identificador",
+ "deviceModel": "Modelo",
+ "deviceContact": "Contato",
+ "deviceCategory": "Categoria",
+ "deviceLastUpdate": "Última Atualização",
+ "deviceCommand": "Comando",
+ "deviceFollow": "Seguir",
+ "deviceTotalDistance": "Distância total",
+ "deviceStatus": "Estado",
+ "deviceStatusOnline": "Conectado",
+ "deviceStatusOffline": "Desconectado",
+ "deviceStatusUnknown": "Desconhecido",
+ "deviceRegisterFirst": "Registre seu primeiro dispositivo",
+ "deviceIdentifierHelp": "IMEI, número de serial ou outro ID. Precisa ser um identificador único para os relatórios do dispositivo no servidor.",
+ "deviceShare": "Dispositivo Compartilhado",
+ "groupDialog": "Grupo",
+ "groupParent": "Grupo",
+ "groupNoGroup": "Sem Grupo",
+ "settingsTitle": "Configurações",
+ "settingsUser": "Conta",
+ "settingsGroups": "Grupos",
+ "settingsServer": "Servidor",
+ "settingsUsers": "Usuários",
+ "settingsDistanceUnit": "Unidade de Distância",
+ "settingsAltitudeUnit": "Altitude Unidade",
+ "settingsSpeedUnit": "Unidade de Velocidade",
+ "settingsVolumeUnit": "Unidade de Volume",
+ "settingsTwelveHourFormat": "Formato de 12 Horas",
+ "settingsCoordinateFormat": "Formato de coordenadas",
+ "settingsServerVersion": "Versão do servidor",
+ "settingsAppVersion": "Versão do aplicativo",
+ "settingsConnection": "Conexão",
+ "settingsDarkMode": "Modo Escuro",
+ "settingsTotpEnable": "Habilitar One-time Password",
+ "settingsTotpForce": "Forçar One-time Password",
+ "settingsServiceWorkerUpdateInterval": "Intervalo de atualização do ServiceWorker",
+ "settingsUpdateAvailable": "Existe uma atualização disponível.",
+ "settingsSupport": "Support",
+ "reportTitle": "Relatórios",
+ "reportScheduled": "Agendamento de relatórios",
+ "reportDevice": "Dispositivo",
+ "reportGroup": "Grupo",
+ "reportFrom": "De",
+ "reportTo": "Para",
+ "reportShow": "Mostrar",
+ "reportClear": "Limpar",
+ "linkGoogleMaps": "Google Maps",
+ "linkAppleMaps": "Apple Maps",
+ "linkStreetView": "Street View",
+ "positionFixTime": "Definir tempo",
+ "positionDeviceTime": "Hora do dispositivo",
+ "positionServerTime": "Hora do servidor",
+ "positionValid": "Válido",
+ "positionAccuracy": "Precisão",
+ "positionLatitude": "Latitude",
+ "positionLongitude": "Longitude",
+ "positionAltitude": "Altitude",
+ "positionSpeed": "Velocidade",
+ "positionCourse": "Direção",
+ "positionAddress": "Endereço",
+ "positionProtocol": "Protocolo",
+ "positionDistance": "Distância",
+ "positionRpm": "RPM",
+ "positionFuel": "Combustível",
+ "positionPower": "Potência",
+ "positionBattery": "Bateria",
+ "positionRaw": "Bruto",
+ "positionIndex": "Índice",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "Sátelites",
+ "positionSatVisible": "Satélites Visíveis",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "Roaming",
+ "positionEvent": "Evento",
+ "positionAlarm": "Alarme",
+ "positionStatus": "Estado",
+ "positionOdometer": "Odômetro",
+ "positionServiceOdometer": "Odômetro de Serviço",
+ "positionTripOdometer": "Odômetro de Viagem",
+ "positionHours": "Horas",
+ "positionSteps": "Passos",
+ "positionInput": "Entrada",
+ "positionHeartRate": "Frequência Cardíaca",
+ "positionOutput": "Saída",
+ "positionBatteryLevel": "Nível de Bateria",
+ "positionFuelConsumption": "Consumo de Combustível",
+ "positionRfid": "RFID",
+ "positionVersionFw": "Versão de Firmware",
+ "positionVersionHw": "Versão de Hardware",
+ "positionIgnition": "Ignição",
+ "positionFlags": "Sinais",
+ "positionCharge": "Carga",
+ "positionIp": "IP",
+ "positionArchive": "Arquivar",
+ "positionVin": "VIN",
+ "positionApproximate": "Estimado",
+ "positionThrottle": "Acelerador",
+ "positionMotion": "Movimento",
+ "positionArmed": "Alarme Ativado",
+ "positionAcceleration": "Aceleração",
+ "positionTemp": "Temperatura",
+ "positionDeviceTemp": "Temperatura do Dispositivo",
+ "positionCoolantTemp": "Temperatura do líquido de arrefecimento",
+ "positionOperator": "Operadora",
+ "positionCommand": "Comando",
+ "positionBlocked": "Bloqueado",
+ "positionDtcs": "DTCs",
+ "positionObdSpeed": "Velocidade OBD",
+ "positionObdOdometer": "Odômetro OBD",
+ "positionDrivingTime": "Tempo de direção",
+ "positionDriverUniqueId": "Identificador Único Motorista",
+ "positionCard": "Cartão",
+ "positionImage": "Imagem",
+ "positionVideo": "Video",
+ "positionAudio": "Áudio",
+ "serverTitle": "Configurações do Servidor",
+ "serverZoom": "Zoom",
+ "serverRegistration": "Registro",
+ "serverReadonly": "Somente leitura",
+ "serverForceSettings": "Forçar configurações",
+ "serverAnnouncement": "Anúncio",
+ "serverName": "Nome do Servidor",
+ "serverDescription": "Descrição do Servidor",
+ "serverColorPrimary": "Cor Primária",
+ "serverColorSecondary": "Cor Secundária",
+ "serverLogo": "Imagem do Logo",
+ "serverLogoInverted": "Imagem do logo invertida",
+ "serverChangeDisable": "Desabilitar mudança de servidor",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "Mapa",
+ "mapActive": "Mapas ativos",
+ "mapOverlay": "Sobreposição de mapa",
+ "mapOverlayCustom": "Sobreposição personalizada",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API Key",
+ "mapOpenWeatherClouds": "OpenWeather Nuvens",
+ "mapOpenWeatherPrecipitation": "OpenWeather Precipitação",
+ "mapOpenWeatherPressure": "OpenWeather Pressão",
+ "mapOpenWeatherWind": "OpenWeather Vento",
+ "mapOpenWeatherTemperature": "OpenWeather Temperatura",
+ "mapLayer": "Camada de Mapa",
+ "mapCustom": "Custom (XYZ)",
+ "mapCustomArcgis": "Custom (ArcGIS)",
+ "mapCustomLabel": "Personalizar mapa",
+ "mapCarto": "Carto Basemaps",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Estrada",
+ "mapGoogleHybrid": "Google Híbrido",
+ "mapGoogleSatellite": "Google Satélite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Bing Mapas API Key",
+ "mapBingRoad": "Bing Mapas Estradas",
+ "mapBingAerial": "Bing Mapas Aéreo",
+ "mapBingHybrid": "Bing Mapas Híbrido",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Mapa Yandex",
+ "mapYandexSat": "Satélite Yandex",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Levantamento de artilharia",
+ "mapMapboxStreets": "Mapbox Ruas",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox ao ar livre",
+ "mapMapboxSatellite": "Mapbox Satélite",
+ "mapMapboxKey": "Mapbox Token de Acesso",
+ "mapMapTilerBasic": "MapTiler Básico",
+ "mapMapTilerHybrid": "MapTiler Híbrido",
+ "mapMapTilerKey": "MapTiler Chave API",
+ "mapLocationIqStreets": "LocationIQ Ruas",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ Token de Acesso",
+ "mapTomTomBasic": "TomTom Basic",
+ "mapTomTomFlow": "TomTom Fluxo de Tráfego",
+ "mapTomTomIncidents": "TomTom Incidentes de Tráfego",
+ "mapTomTomKey": "TomTom API Key",
+ "mapHereBasic": "Here Basico",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satellite",
+ "mapHereFlow": "Here Fluxo de Tráfego",
+ "mapHereKey": "Here API Key",
+ "mapShapePolygon": "Polígono",
+ "mapShapeCircle": "Círculo",
+ "mapShapePolyline": "Polilinha",
+ "mapLiveRoutes": "Rotas ao Vivo",
+ "mapDirection": "Exibir direção",
+ "mapCurrentLocation": "Localização atual",
+ "mapPoiLayer": "Camada POI",
+ "mapClustering": "Agrupamento de marcadores",
+ "mapOnSelect": "Mostrar mapa na seleção",
+ "mapDefault": "Mapa padrão",
+ "stateTitle": "Estado",
+ "stateName": "Atributo",
+ "stateValue": "Valor",
+ "commandTitle": "Comando",
+ "commandSend": "Enviar",
+ "commandSent": "Comando enviado",
+ "commandQueued": "Comando na fila",
+ "commandUnit": "Unidade",
+ "commandCustom": "Comando personalizado",
+ "commandDeviceIdentification": "Identificação do dispositivo",
+ "commandPositionSingle": "Relatório avulso",
+ "commandPositionPeriodic": "Atualização Periódica",
+ "commandPositionStop": "Parar Atualização",
+ "commandEngineStop": "Desligar Motor",
+ "commandEngineResume": "Religar Motor",
+ "commandAlarmArm": "Ativar Alarme",
+ "commandAlarmDisarm": "Desativar Alarme",
+ "commandAlarmDismiss": "Ignorar Alarme",
+ "commandSetTimezone": "Definir fuso horário",
+ "commandRequestPhoto": "Pegar foto",
+ "commandPowerOff": "Desligar o Dispositivo",
+ "commandRebootDevice": "Reiniciar dispositivo",
+ "commandFactoryReset": "Restauração de fábrica",
+ "commandSendSms": "Enviar SMS",
+ "commandSendUssd": "Enviar USSD",
+ "commandSosNumber": "Definir numero SOS",
+ "commandSilenceTime": "Definir Tempo no Silencioso",
+ "commandSetPhonebook": "Definir lista telefônica",
+ "commandVoiceMessage": "Mensagem de voz",
+ "commandOutputControl": "Controle de saída",
+ "commandVoiceMonitoring": "Monitoramento de Voz",
+ "commandSetAgps": "Definir AGPS",
+ "commandSetIndicator": "Definir Indicador",
+ "commandConfiguration": "Configuração",
+ "commandGetVersion": "Obter Versão",
+ "commandFirmwareUpdate": "Atualizar Firmware",
+ "commandSetConnection": "Definir Conexão",
+ "commandSetOdometer": "Definir Odômetro",
+ "commandGetModemStatus": "Obter Status do Modem",
+ "commandGetDeviceStatus": "Obter status do dispositivo",
+ "commandSetSpeedLimit": "Definir limite de velocidade",
+ "commandModePowerSaving": "Modo de economia de energia",
+ "commandModeDeepSleep": "Modo de Sono Profundo",
+ "commandAlarmGeofence": "Definir Alarme de Geofence",
+ "commandAlarmBattery": "Definir Alarme de Bateria",
+ "commandAlarmSos": "Definir Alarme SOS",
+ "commandAlarmRemove": "Definir Remoção de Alarme",
+ "commandAlarmClock": "Definir Horário de Alarme",
+ "commandAlarmSpeed": "Definir Velocidade de Alarme",
+ "commandAlarmFall": "Definir Falha de Alarme",
+ "commandAlarmVibration": "Definir Alarme de Vibração",
+ "commandFrequency": "Frequência",
+ "commandTimezone": "Ajuste de Fuso horário",
+ "commandMessage": "Mensagem",
+ "commandRadius": "Raio",
+ "commandEnable": "Habilitar",
+ "commandData": "Data",
+ "commandIndex": "Índice",
+ "commandPhone": "Número de Telefone",
+ "commandServer": "Servidor",
+ "commandPort": "Porta",
+ "eventAll": "Todos Eventos",
+ "eventDeviceOnline": "Status online",
+ "eventDeviceUnknown": "Status desconhecido",
+ "eventDeviceOffline": "Status offline",
+ "eventDeviceInactive": "Dispositivo inativo",
+ "eventQueuedCommandSent": "Comando na fila enviado",
+ "eventDeviceMoving": "Dispositivo movendo",
+ "eventDeviceStopped": "Dispositivo parado",
+ "eventDeviceOverspeed": "Excedido o limite de velocidade",
+ "eventDeviceFuelDrop": "Queda de combustível",
+ "eventDeviceFuelIncrease": "Acréscimo de combustível",
+ "eventCommandResult": "Resultado do comando",
+ "eventGeofenceEnter": "Entrada na cerca virtual",
+ "eventGeofenceExit": "Saída da cerca virtual",
+ "eventAlarm": "Alarme",
+ "eventIgnitionOn": "Ignição ligada",
+ "eventIgnitionOff": "Ignição desligada",
+ "eventMaintenance": "Manutenção necessária",
+ "eventTextMessage": "Mensagem de texto recebida",
+ "eventDriverChanged": "Condutor alterado",
+ "eventMedia": "Media",
+ "eventsScrollToLast": "Rolar para o último",
+ "eventsSoundEvents": "Som de eventos",
+ "eventsSoundAlarms": "Som de alarmes",
+ "alarmGeneral": "Geral",
+ "alarmSos": "SOS",
+ "alarmVibration": "Vibrando",
+ "alarmMovement": "Movendo",
+ "alarmLowspeed": "Velocidade baixa",
+ "alarmOverspeed": "Alta velocidade",
+ "alarmFallDown": "Cair",
+ "alarmLowPower": "Carga Baixa",
+ "alarmLowBattery": "Bateria baixa",
+ "alarmFault": "Falha",
+ "alarmPowerOff": "Alimentação desligada",
+ "alarmPowerOn": "Alimentação ligada",
+ "alarmDoor": "Porta",
+ "alarmLock": "Bloqueado",
+ "alarmUnlock": "Desbloqueado",
+ "alarmGeofence": "Cerca virtual",
+ "alarmGeofenceEnter": "Entrando na cerca virtual",
+ "alarmGeofenceExit": "Saiu da Cerca virtual",
+ "alarmGpsAntennaCut": "Antena de GPS cortada",
+ "alarmAccident": "Acidente",
+ "alarmTow": "Rebocar",
+ "alarmIdle": "Ocioso",
+ "alarmHighRpm": "Alta Rotação",
+ "alarmHardAcceleration": "Aceleração brusca",
+ "alarmHardBraking": "Frenagem brusca",
+ "alarmHardCornering": "Curva Acentuada",
+ "alarmLaneChange": "Mudança de Faixa",
+ "alarmFatigueDriving": "Condutor Cansado",
+ "alarmPowerCut": "Alimentação cortada",
+ "alarmPowerRestored": "Alimentação restaurada",
+ "alarmJamming": "Interferência",
+ "alarmTemperature": "Temperatura",
+ "alarmParking": "Estacionamento",
+ "alarmBonnet": "Capô",
+ "alarmFootBrake": "Freio de mão",
+ "alarmFuelLeak": "Vazamento de combustível",
+ "alarmTampering": "Manipulando",
+ "alarmRemoving": "Removendo",
+ "notificationType": "Tipo de Notificação",
+ "notificationAlways": "Todos os Dispositivos",
+ "notificationNotificators": "Canais",
+ "notificatorCommand": "Comando",
+ "notificatorWeb": "Web",
+ "notificatorMail": "E-Mail",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Empurrar",
+ "reportReplay": "Replay",
+ "reportCombined": "Combinado",
+ "reportRoute": "Rota",
+ "reportEvents": "Eventos",
+ "reportTrips": "Viagens",
+ "reportStops": "Paradas",
+ "reportSummary": "Resumo",
+ "reportDaily": "Resumo Diário",
+ "reportChart": "Gráfico",
+ "reportConfigure": "Configurar",
+ "reportEventTypes": "Tipos de Eventos",
+ "reportChartType": "Tipo do Gráfico",
+ "reportShowMarkers": "Mostrar Marcadores",
+ "reportExport": "Exportar",
+ "reportEmail": "Relatório de E-mail",
+ "reportSchedule": "Agenda",
+ "reportPeriod": "Período",
+ "reportCustom": "Personalizadas",
+ "reportToday": "Hoje",
+ "reportYesterday": "Ontem",
+ "reportThisWeek": "Esta Semana",
+ "reportPreviousWeek": "Semana Anterior",
+ "reportThisMonth": "Este Mês",
+ "reportPreviousMonth": "Mês Anterior",
+ "reportDeviceName": "Nome do Dispositivo",
+ "reportAverageSpeed": "Velocidade Média",
+ "reportMaximumSpeed": "Velocidade Máxima",
+ "reportEngineHours": "Horas ligado",
+ "reportDuration": "Duração",
+ "reportStartDate": "Data de Início",
+ "reportStartTime": "Hora inicial",
+ "reportStartAddress": "Endereço inicial",
+ "reportEndTime": "Hora final",
+ "reportEndAddress": "Endereço final",
+ "reportSpentFuel": "Gasto de Combustível",
+ "reportStartOdometer": "Início do odômetro",
+ "reportEndOdometer": "Fim do odômetro",
+ "statisticsTitle": "Estatísticas",
+ "statisticsCaptureTime": "Tempo de Captura",
+ "statisticsActiveUsers": "Usuários ativos",
+ "statisticsActiveDevices": "Dispositivos ativos",
+ "statisticsRequests": "Pedidos",
+ "statisticsMessagesReceived": "Mensagens Recebidas",
+ "statisticsMessagesStored": "Mensagens armazenadas",
+ "statisticsGeocoder": "Solicitações de Geocódigo",
+ "statisticsGeolocation": "Solicitações de Geolocalização",
+ "categoryArrow": "Seta",
+ "categoryDefault": "Padrão",
+ "categoryAnimal": "Animal",
+ "categoryBicycle": "Bicicleta",
+ "categoryBoat": "Barco",
+ "categoryBus": "Ônibus",
+ "categoryCar": "Carro",
+ "categoryCamper": "Campista",
+ "categoryCrane": "Guindaste",
+ "categoryHelicopter": "Helicóptero",
+ "categoryMotorcycle": "Motocicleta",
+ "categoryOffroad": "Offroad",
+ "categoryPerson": "Pessoa",
+ "categoryPickup": "Pick-up",
+ "categoryPlane": "Avião",
+ "categoryShip": "Navio",
+ "categoryTractor": "Trator",
+ "categoryTrain": "Trem",
+ "categoryTram": "Bonde",
+ "categoryTrolleybus": "Ônibus Elétrico",
+ "categoryTruck": "Caminhão",
+ "categoryVan": "Van",
+ "categoryScooter": "Patinete Elétrico",
+ "maintenanceStart": "Começar",
+ "maintenancePeriod": "Período"
+} \ No newline at end of file
diff --git a/src/resources/l10n/ro.json b/src/resources/l10n/ro.json
new file mode 100644
index 00000000..84367c5b
--- /dev/null
+++ b/src/resources/l10n/ro.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "Se încarcă...",
+ "sharedHide": "Ascunde",
+ "sharedSave": "Salvează",
+ "sharedUpload": "Încarcă",
+ "sharedSet": "Configurare",
+ "sharedCancel": "Anulează",
+ "sharedCopy": "Copy",
+ "sharedAdd": "Adaugă",
+ "sharedEdit": "Modifică",
+ "sharedRemove": "Elimină",
+ "sharedRemoveConfirm": "Ștergeți obiectul?",
+ "sharedNoData": "Fără date",
+ "sharedSubject": "Subject",
+ "sharedYes": "Da",
+ "sharedNo": "Nu",
+ "sharedKm": "km",
+ "sharedMi": "mi",
+ "sharedNmi": "nmi",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "kn",
+ "sharedKmh": "km/h",
+ "sharedMph": "mph",
+ "sharedHour": "Oră",
+ "sharedMinute": "Minut",
+ "sharedSecond": "Secundă",
+ "sharedDays": "zile",
+ "sharedHours": "ore",
+ "sharedMinutes": "minute",
+ "sharedDecimalDegrees": "Grade Zecimale",
+ "sharedDegreesDecimalMinutes": "Grade Zecimale Minute",
+ "sharedDegreesMinutesSeconds": "Grade minute secunde",
+ "sharedName": "Nume",
+ "sharedDescription": "Descriere",
+ "sharedSearch": "Căutare",
+ "sharedIconScale": "Scală pentru Iconiță",
+ "sharedGeofence": "Perimetru Restricționat",
+ "sharedGeofences": "Perimetre Restricționate",
+ "sharedCreateGeofence": "Crează Perimetru Restricționat",
+ "sharedNotifications": "Notificări",
+ "sharedNotification": "Notificare",
+ "sharedAttributes": "Atribute",
+ "sharedAttribute": "Atribut",
+ "sharedDrivers": "Șoferi",
+ "sharedDriver": "Șofer",
+ "sharedArea": "Suprafață",
+ "sharedSound": "Sunet Notificare",
+ "sharedType": "Tip",
+ "sharedDistance": "Distanţa",
+ "sharedHourAbbreviation": "h",
+ "sharedMinuteAbbreviation": "m",
+ "sharedSecondAbbreviation": "s",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "l",
+ "sharedGallonAbbreviation": "gal",
+ "sharedLiter": "Litru",
+ "sharedImpGallon": "Galon Imperial",
+ "sharedUsGallon": "Galon US",
+ "sharedLiterPerHourAbbreviation": "l/h",
+ "sharedGetMapState": "Obține Starea Hărții",
+ "sharedComputedAttribute": "Atribut Calculat",
+ "sharedComputedAttributes": "Atribute Calculate",
+ "sharedCheckComputedAttribute": "Verifică Atributul Calculat",
+ "sharedExpression": "Formulă",
+ "sharedDevice": "Dispozitiv",
+ "sharedTest": "Test",
+ "sharedTestNotification": "Trimite Notificare de Test",
+ "sharedTestNotificators": "Canale de Test",
+ "sharedTestExpression": "Formulă de Test",
+ "sharedCalendar": "Calendar",
+ "sharedCalendars": "Calendare",
+ "sharedFile": "Fișier",
+ "sharedSearchDevices": "Caută Echipamente",
+ "sharedSortBy": "Sortat După",
+ "sharedFilterMap": "Filtru pe Hartă",
+ "sharedSelectFile": "Selectează Fișier",
+ "sharedPhone": "Telefon",
+ "sharedRequired": "Obligatorii",
+ "sharedPreferences": "Preferințe",
+ "sharedPermissions": "Permisiuni",
+ "sharedConnections": "Conexiuni",
+ "sharedExtra": "Suplimentar",
+ "sharedPrimary": "Primar",
+ "sharedSecondary": "Secundar",
+ "sharedTypeString": "Șir",
+ "sharedTypeNumber": "Număr",
+ "sharedTypeBoolean": "Logic",
+ "sharedTimezone": "Fus orar",
+ "sharedInfoTitle": "Informații",
+ "sharedSavedCommand": "Comandă Salvată",
+ "sharedSavedCommands": "Comenzi Salvate",
+ "sharedNew": "Nou...",
+ "sharedShowAddress": "Afișează Adresa",
+ "sharedShowDetails": "Mai Multe Detalii",
+ "sharedDisabled": "Dezactivat",
+ "sharedMaintenance": "Întreținere",
+ "sharedDeviceAccumulators": "Acumulatori",
+ "sharedAlarms": "Alarme",
+ "sharedLocation": "Locație",
+ "sharedImport": "Import",
+ "sharedColumns": "Coloane",
+ "sharedDropzoneText": "Drag and drop un fișier aici ori click",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "Simplu",
+ "calendarRecurrence": "Recurent",
+ "calendarOnce": "O singură dată",
+ "calendarDaily": "Zilnic",
+ "calendarWeekly": "Săptămânal",
+ "calendarMonthly": "Lunar",
+ "calendarDays": "Zile",
+ "calendarSunday": "Duminică",
+ "calendarMonday": "Luni",
+ "calendarTuesday": "Marți",
+ "calendarWednesday": "Miercuri",
+ "calendarThursday": "Joi",
+ "calendarFriday": "Vineri",
+ "calendarSaturday": "Sâmbătă",
+ "attributeShowGeofences": "Afișează Perimetre Restricționate",
+ "attributeSpeedLimit": "Limita de viteza",
+ "attributeFuelDropThreshold": "Prag de Cădere a Combustibilului",
+ "attributeFuelIncreaseThreshold": "Prag de Creștere a Combustibilului",
+ "attributePolylineDistance": "Distanță Polilinie",
+ "attributeReportIgnoreOdometer": "Raport: Ignoră Kilometraj",
+ "attributeWebReportColor": "Web: Culoare Raport",
+ "attributeDevicePassword": "Parolă Dispozitiv",
+ "attributeDeviceImage": "Imagine Dispozitiv",
+ "attributeDeviceInactivityStart": "Dată Start Dispozitiv Inactiv",
+ "attributeDeviceInactivityPeriod": "Perioadă Dispozitiv Inactiv",
+ "attributeProcessingCopyAttributes": "Procesare: Copiază Atribute",
+ "attributeColor": "Culoare",
+ "attributeWebLiveRouteLength": "Web: Lungime Traseu în Timp Real",
+ "attributeWebSelectZoom": "Web: Zoom la Selecție",
+ "attributeWebMaxZoom": "Web: Zoom Maxim",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Cheie Utilizator Pushover",
+ "attributePushoverDeviceNames": "Nume Dispozitiv Pushover",
+ "attributeMailSmtpHost": "Mail: SMTP Host",
+ "attributeMailSmtpPort": "Mail: SMTP Port",
+ "attributeMailSmtpStarttlsEnable": "Mail: Activeaza SMTP STARTTLS ",
+ "attributeMailSmtpStarttlsRequired": "Mail: SMTP STARTTLS Obligatoriu",
+ "attributeMailSmtpSslEnable": "Mail: Activează SMTP SSL",
+ "attributeMailSmtpSslTrust": "Mail: SMTP SSL Trust",
+ "attributeMailSmtpSslProtocols": "Mail: Protocol SMTP SSL",
+ "attributeMailSmtpFrom": "Mail: SMTP Expeditor",
+ "attributeMailSmtpAuth": "Mail: Activează SMTP Auth",
+ "attributeMailSmtpUsername": "Mail: Utilizator SMTP",
+ "attributeMailSmtpPassword": "Mail: Parolă SMTP",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: Dezactivare Atribute",
+ "attributeUiDisableGroups": "UI: Dezactivare Grupuri",
+ "attributeUiDisableEvents": "UI: Dezactivare Evenimente",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "UI: Dezactivare Șoferi",
+ "attributeUiDisableComputedAttributes": "UI: Dezactivare Atribute Calculate",
+ "attributeUiDisableCalendars": "UI: Dezactivare Calendar",
+ "attributeUiDisableMaintenance": "UI: Dezactivare Întreținere",
+ "attributeUiHidePositionAttributes": "UI: Ascunde Atributele de Poziționare",
+ "attributeUiDisableLoginLanguage": "UI: Dezactivare Limbă Autentificare",
+ "attributeNotificationTokens": "Token-uri Notificări",
+ "attributePopupInfo": "Informații Popup",
+ "errorTitle": "Eroare",
+ "errorGeneral": "Parametri invalizi sau încălcare a restricțiilor",
+ "errorConnection": "Eroare de conectare",
+ "errorSocket": "Eroare conectare web socket",
+ "errorZero": "Nu poate fi zero",
+ "userEmail": "Email",
+ "userPassword": "Parolă",
+ "userAdmin": "Admin",
+ "userRemember": "Ţine minte",
+ "userExpirationTime": "Expirare",
+ "userDeviceLimit": "Limită Dispozitiv",
+ "userUserLimit": "Limită Utilizator",
+ "userDeviceReadonly": "Dispozitiv Doar pentru Citire",
+ "userLimitCommands": "Limită Comenzi",
+ "userDisableReports": "Dezactivare Rapoarte",
+ "userFixedEmail": "Schimbare Adresă Email Interzisă",
+ "userToken": "Token",
+ "userDeleteAccount": "Șterge Cont",
+ "userTemporary": "Temporary",
+ "loginTitle": "Autentificare",
+ "loginLanguage": "Limbă",
+ "loginReset": "Reconfigurare Parolă",
+ "loginRegister": "Înregistrare",
+ "loginLogin": "Autentificare",
+ "loginOpenId": "Autentificare cu OpenID",
+ "loginFailed": "Email sau parolă incorectă",
+ "loginCreated": "Un nou utilizator a fost înregistrat",
+ "loginResetSuccess": "Verifică email",
+ "loginUpdateSuccess": "Noua parolă a fost configurată",
+ "loginLogout": "Deconectare",
+ "loginLogo": "Logo",
+ "loginTotpCode": "Cod Parolă One-time",
+ "loginTotpKey": "Cheie Parolă One-time",
+ "devicesAndState": "Dispozitive și Stare",
+ "deviceSelected": "Dispozitiv Selectat",
+ "deviceTitle": "Dispozitive",
+ "devicePrimaryInfo": "Titlu Dispozitiv",
+ "deviceSecondaryInfo": "Detaliu Dispozitiv",
+ "deviceIdentifier": "Identificator",
+ "deviceModel": "Model",
+ "deviceContact": "Contact",
+ "deviceCategory": "Categorie",
+ "deviceLastUpdate": "Ultima actualizare",
+ "deviceCommand": "Comandă",
+ "deviceFollow": "Urmărește",
+ "deviceTotalDistance": "Distanță Totală",
+ "deviceStatus": "Stare",
+ "deviceStatusOnline": "Conectat",
+ "deviceStatusOffline": "Deconectat",
+ "deviceStatusUnknown": "Necunoscut",
+ "deviceRegisterFirst": "Inregistrează primul tău dispozitiv",
+ "deviceIdentifierHelp": "IMEI, serial number ori alt ID. Trebuie sa coincidă cu identificatorul dispozitivului raportat la server.",
+ "deviceShare": "Partajează Dispozitiv",
+ "groupDialog": "Grup",
+ "groupParent": "Grup",
+ "groupNoGroup": "Fără Grup",
+ "settingsTitle": "Configurări",
+ "settingsUser": "Cont",
+ "settingsGroups": "Grupuri",
+ "settingsServer": "Server",
+ "settingsUsers": "Utilizatori",
+ "settingsDistanceUnit": "Unitatea de Măsură Distanță",
+ "settingsAltitudeUnit": "Unitate de Măsură Altitudine",
+ "settingsSpeedUnit": "Unitate de Măsură Viteză",
+ "settingsVolumeUnit": "Unitate de Măsură Volum",
+ "settingsTwelveHourFormat": "Format 12-ore",
+ "settingsCoordinateFormat": "Format Coordonate",
+ "settingsServerVersion": "Versiune Server",
+ "settingsAppVersion": "Versiune Aplicație",
+ "settingsConnection": "Conexiune",
+ "settingsDarkMode": "Mod Întunecat",
+ "settingsTotpEnable": "Activează Parolă One-time",
+ "settingsTotpForce": "Forțează Parolă One-time",
+ "settingsServiceWorkerUpdateInterval": "Interval Actualizare ServiceWorker",
+ "settingsUpdateAvailable": "Există o actualizare disponibilă.",
+ "settingsSupport": "Support",
+ "reportTitle": "Rapoarte",
+ "reportScheduled": "Rapoarte Programate",
+ "reportDevice": "Dispozitiv",
+ "reportGroup": "Grup",
+ "reportFrom": "De la ",
+ "reportTo": "Până la",
+ "reportShow": "Afișează",
+ "reportClear": "Sterge",
+ "linkGoogleMaps": "Google Maps",
+ "linkAppleMaps": "Apple Maps",
+ "linkStreetView": "Street View",
+ "positionFixTime": "Ora Fix-ului",
+ "positionDeviceTime": "Timpul Dispozitivului",
+ "positionServerTime": "Timpul Serverului",
+ "positionValid": "Valabil",
+ "positionAccuracy": "Precizie",
+ "positionLatitude": "Latitudine",
+ "positionLongitude": "Longitudine",
+ "positionAltitude": "Altitudine",
+ "positionSpeed": "Viteză",
+ "positionCourse": "Curs",
+ "positionAddress": "Adresă",
+ "positionProtocol": "Protocol",
+ "positionDistance": "Distanța",
+ "positionRpm": "Turatia",
+ "positionFuel": "Nivel combustibil",
+ "positionPower": "Alimentare",
+ "positionBattery": "Baterie",
+ "positionRaw": "Brut",
+ "positionIndex": "Index",
+ "positionHdop": "HDOP Eroare pe orizontala",
+ "positionVdop": "VDOP Eroare pe verticala",
+ "positionPdop": "PDOP Eroarea pozitiei",
+ "positionSat": "Sateliti",
+ "positionSatVisible": "Sateliti Vizibili",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "Migrare",
+ "positionEvent": "Eveniment",
+ "positionAlarm": "Alarma",
+ "positionStatus": "Stare",
+ "positionOdometer": "Kilometraj",
+ "positionServiceOdometer": "Kilometraj Service",
+ "positionTripOdometer": "Kilometraj Călătorie",
+ "positionHours": "Ore",
+ "positionSteps": "Pasi",
+ "positionInput": "Intrare",
+ "positionHeartRate": "Ritmul Cardiac",
+ "positionOutput": "Iesire",
+ "positionBatteryLevel": "Nivel baterie",
+ "positionFuelConsumption": "Consum combustibil",
+ "positionRfid": "RFID",
+ "positionVersionFw": "Vesiune firmware",
+ "positionVersionHw": "Versiune hardware",
+ "positionIgnition": "Contact motor",
+ "positionFlags": "Marcaj",
+ "positionCharge": "Incarca",
+ "positionIp": "Adresa IP",
+ "positionArchive": "Arhiva",
+ "positionVin": "Numar identificare vehicul (VIN)",
+ "positionApproximate": "Aproximeaza",
+ "positionThrottle": "Pedala acceleratie",
+ "positionMotion": "Miscare",
+ "positionArmed": "Armat",
+ "positionAcceleration": "Acceleratie",
+ "positionTemp": "Temperatură",
+ "positionDeviceTemp": "Temperatura Dispozitivului",
+ "positionCoolantTemp": "Temperatuă Lichid de Răcire",
+ "positionOperator": "Operator",
+ "positionCommand": "Comenzi",
+ "positionBlocked": "Blocat",
+ "positionDtcs": "DTCs",
+ "positionObdSpeed": "Viteza OBD",
+ "positionObdOdometer": "Kilometraj OBD",
+ "positionDrivingTime": "Timp de Conducere",
+ "positionDriverUniqueId": "Identificator Unic Șofer",
+ "positionCard": "Card",
+ "positionImage": "Imagine",
+ "positionVideo": "Video",
+ "positionAudio": "Audio",
+ "serverTitle": "Configurări Server",
+ "serverZoom": "Zoom",
+ "serverRegistration": "Înregistrare",
+ "serverReadonly": "Doar pentru Citire",
+ "serverForceSettings": "Forțează Configurări",
+ "serverAnnouncement": "Anunț",
+ "serverName": "Nume Server",
+ "serverDescription": "Descriere Server",
+ "serverColorPrimary": "Culoare Primară",
+ "serverColorSecondary": "Culoare Secundară",
+ "serverLogo": "Imagine Logo",
+ "serverLogoInverted": "Imagine Logo Inversată",
+ "serverChangeDisable": "Dezactivează Schimbarea Serverului",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "Hartă",
+ "mapActive": "Hărți Active",
+ "mapOverlay": "Suprapunere Hartă",
+ "mapOverlayCustom": "Suprapunere Personalizată",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "Cheie API OpenWeather",
+ "mapOpenWeatherClouds": "Nori OpenWeather",
+ "mapOpenWeatherPrecipitation": "Precipitații OpenWeather",
+ "mapOpenWeatherPressure": "Presiune OpenWeather",
+ "mapOpenWeatherWind": "Vânt OpenWeather",
+ "mapOpenWeatherTemperature": "Temperatură OpenWeather",
+ "mapLayer": "Strat Hartă",
+ "mapCustom": "Personalizat (XYX)",
+ "mapCustomArcgis": "Personalizat (ArcGIS)",
+ "mapCustomLabel": "Hartă personalizată",
+ "mapCarto": "Harta Carto Basemaps",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Cheie Bing Maps",
+ "mapBingRoad": "Bing Hartă Drumuri",
+ "mapBingAerial": "Bing Hartă Aeriană",
+ "mapBingHybrid": "Bing Maps Hybrid",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Harta Yandex",
+ "mapYandexSat": "Satelit Yandex",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox Streets",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox Outdoors",
+ "mapMapboxSatellite": "Mapbox Satellite",
+ "mapMapboxKey": "Token Acces MapBox",
+ "mapMapTilerBasic": "MapTiler Basic",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "Cheie API MapTiler",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "Token Acces LocationIQ",
+ "mapTomTomBasic": "TomTom Basic",
+ "mapTomTomFlow": "TomTom Traffic Flow",
+ "mapTomTomIncidents": "TomTom Traffic Incidents",
+ "mapTomTomKey": "Cheie API TomTom",
+ "mapHereBasic": "Here Basic",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satellite",
+ "mapHereFlow": "Here Traffic Flow",
+ "mapHereKey": "API Key Here",
+ "mapShapePolygon": "Poligon",
+ "mapShapeCircle": "Cerc",
+ "mapShapePolyline": "Polilinie",
+ "mapLiveRoutes": "Trasee în Timp Real",
+ "mapDirection": "Afișează Direcție",
+ "mapCurrentLocation": "Locația Curentă",
+ "mapPoiLayer": "POI ",
+ "mapClustering": "Clustere de Marcatori",
+ "mapOnSelect": "Afișează Harta la Selectare",
+ "mapDefault": "Hartă Implicită",
+ "stateTitle": "Stare",
+ "stateName": "Atribut",
+ "stateValue": "Valoare",
+ "commandTitle": "Comandă",
+ "commandSend": "Trimite",
+ "commandSent": "Comanda trimisa",
+ "commandQueued": "Comanda adaugata in coada",
+ "commandUnit": "Unitate",
+ "commandCustom": "Comandă personalizată",
+ "commandDeviceIdentification": "Identificare Dispozitiv",
+ "commandPositionSingle": "Raportarea unică",
+ "commandPositionPeriodic": "Raportarea Periodică",
+ "commandPositionStop": "Oprire Raportare",
+ "commandEngineStop": "Blocare Motor",
+ "commandEngineResume": "Deblocare Motor",
+ "commandAlarmArm": "Activare Alarmă",
+ "commandAlarmDisarm": "Dezactivare alarmă",
+ "commandAlarmDismiss": "Închide Alarmă",
+ "commandSetTimezone": "Configurare Fus Orar",
+ "commandRequestPhoto": "Solicitare Foto",
+ "commandPowerOff": "Oprire Dispozitiv",
+ "commandRebootDevice": "Repornire Dispozitiv",
+ "commandFactoryReset": "Reconfigurare la Parametrii de Fabrică",
+ "commandSendSms": "Trimite SMS",
+ "commandSendUssd": "Trimite USSD",
+ "commandSosNumber": "Configurare Număr SOS",
+ "commandSilenceTime": "Configurare Timp Silențios",
+ "commandSetPhonebook": "Configurare Agendă Telefonică",
+ "commandVoiceMessage": "Mesaj Vocal",
+ "commandOutputControl": "Controlul de ieșire",
+ "commandVoiceMonitoring": "Monitorizare Audio",
+ "commandSetAgps": "Configurare AGPS",
+ "commandSetIndicator": "Configurare Indicator",
+ "commandConfiguration": "Configurare",
+ "commandGetVersion": "Obține Versiunea",
+ "commandFirmwareUpdate": "Actualizare Firmware",
+ "commandSetConnection": "Configurare Conexiune",
+ "commandSetOdometer": "Configurare Kilometraj",
+ "commandGetModemStatus": "Obține Starea Modemului",
+ "commandGetDeviceStatus": "Obține Starea Dispozitivului",
+ "commandSetSpeedLimit": "Configurare Limită de Viteză",
+ "commandModePowerSaving": "Mod Salvare Energie",
+ "commandModeDeepSleep": "Mod Somn Adânc",
+ "commandAlarmGeofence": "Configurare Alarmă Perimetru Restricționat",
+ "commandAlarmBattery": "Configurare Alarmă Baterie",
+ "commandAlarmSos": "Configurare Alarmă SOS",
+ "commandAlarmRemove": "Configurare Eliminare Alarmă",
+ "commandAlarmClock": "Configurare Alarmă Ceas",
+ "commandAlarmSpeed": "Configurare Alarmă Viteză",
+ "commandAlarmFall": "Configurare Alarmă pentru Cădere",
+ "commandAlarmVibration": "Configurare Alarmă Vibrație",
+ "commandFrequency": "Frecvenţă",
+ "commandTimezone": "Diferență Fus Orar",
+ "commandMessage": "Mesaj",
+ "commandRadius": "Rază",
+ "commandEnable": "Activează",
+ "commandData": "Informații",
+ "commandIndex": "Index",
+ "commandPhone": "Telefon",
+ "commandServer": "Server",
+ "commandPort": "Port",
+ "eventAll": "Toate Evenimentele",
+ "eventDeviceOnline": "Stare pornit",
+ "eventDeviceUnknown": "Stare necunoscută",
+ "eventDeviceOffline": "Stare oprit",
+ "eventDeviceInactive": "Dispozitiv inactiv",
+ "eventQueuedCommandSent": "Comandă Pusă la Coadă",
+ "eventDeviceMoving": "Dispozitiv in mișcare",
+ "eventDeviceStopped": "Dispozitiv oprit",
+ "eventDeviceOverspeed": "Viteza maxima depasita",
+ "eventDeviceFuelDrop": "Scadere nivel carburant",
+ "eventDeviceFuelIncrease": "Creștere a Combustibilului",
+ "eventCommandResult": "Rezultat comandă",
+ "eventGeofenceEnter": "Intare perimetru restricționat",
+ "eventGeofenceExit": "Ieșire perimetru restricționat",
+ "eventAlarm": "Alarma",
+ "eventIgnitionOn": "Contact pornit",
+ "eventIgnitionOff": "Contact oprit",
+ "eventMaintenance": "Necesită întreținere",
+ "eventTextMessage": "Mesaj text recepționat",
+ "eventDriverChanged": "Șofer schimbat",
+ "eventMedia": "Mediu",
+ "eventsScrollToLast": "Deruleaza la sfarsit",
+ "eventsSoundEvents": "Sunet Evenimente",
+ "eventsSoundAlarms": "Sunet Alarme",
+ "alarmGeneral": "General",
+ "alarmSos": "SOS",
+ "alarmVibration": "Vibratii",
+ "alarmMovement": "Miscare",
+ "alarmLowspeed": "Viteza redusa",
+ "alarmOverspeed": "Viteza depasita",
+ "alarmFallDown": "Reducere",
+ "alarmLowPower": "Putere redusa",
+ "alarmLowBattery": "Nivel scazut baterie",
+ "alarmFault": "Eroare",
+ "alarmPowerOff": "Pornit",
+ "alarmPowerOn": "Oprit",
+ "alarmDoor": "Usa",
+ "alarmLock": "Blocare",
+ "alarmUnlock": "Deblocare",
+ "alarmGeofence": "Perimetru Restricționat",
+ "alarmGeofenceEnter": "Intrare Perimetru Restricționat",
+ "alarmGeofenceExit": "Ieșire Perimetru Restricționat",
+ "alarmGpsAntennaCut": "Antena GPS taiata",
+ "alarmAccident": "Accident",
+ "alarmTow": "Tractare",
+ "alarmIdle": "In asteptare",
+ "alarmHighRpm": "Turatie ridicata",
+ "alarmHardAcceleration": "Accelerare Puternica",
+ "alarmHardBraking": "Frana de Urgenta",
+ "alarmHardCornering": "Viraj Brusc",
+ "alarmLaneChange": "Schimbare Banda",
+ "alarmFatigueDriving": "Condus in stare de oboseala",
+ "alarmPowerCut": "Taiere Alimentare",
+ "alarmPowerRestored": "Alimentare restabilita",
+ "alarmJamming": "Bruiaj",
+ "alarmTemperature": "Temperatura",
+ "alarmParking": "Parcare",
+ "alarmBonnet": "Capota",
+ "alarmFootBrake": "Frana de serviciu",
+ "alarmFuelLeak": "Pierdere combustibil",
+ "alarmTampering": "Acces neautorizat",
+ "alarmRemoving": "Indepartare",
+ "notificationType": "Tip de Notificare",
+ "notificationAlways": "Toate Dispozitivele",
+ "notificationNotificators": "Canale",
+ "notificatorCommand": "Comandă",
+ "notificatorWeb": "Web",
+ "notificatorMail": "Mail",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Reluare",
+ "reportCombined": "Combinat",
+ "reportRoute": "Trasee",
+ "reportEvents": "Evenimente",
+ "reportTrips": "Călătorii",
+ "reportStops": "Opriri",
+ "reportSummary": "Sumar",
+ "reportDaily": "Sumar Zilnic",
+ "reportChart": "Grafic",
+ "reportConfigure": "Configureaza",
+ "reportEventTypes": "Tipuri Evenimente",
+ "reportChartType": "Tip Grafic",
+ "reportShowMarkers": "Afișează Marcatori",
+ "reportExport": "Exporta",
+ "reportEmail": "Raport Email",
+ "reportSchedule": "Program",
+ "reportPeriod": "Perioada",
+ "reportCustom": "Personalizat",
+ "reportToday": "Astăzi",
+ "reportYesterday": "Ieri",
+ "reportThisWeek": "Săptămâna Curentă",
+ "reportPreviousWeek": "Săptămâna Precedentă",
+ "reportThisMonth": "Luna Aceasta",
+ "reportPreviousMonth": "Luna Precedentă",
+ "reportDeviceName": "Nume Dispozitiv",
+ "reportAverageSpeed": "Viteza medie",
+ "reportMaximumSpeed": "Viteză Maximă",
+ "reportEngineHours": "Ore motor",
+ "reportDuration": "Durata",
+ "reportStartDate": "Data Start",
+ "reportStartTime": "Oră Start",
+ "reportStartAddress": "Adresa de start",
+ "reportEndTime": "Oră Sfârșit",
+ "reportEndAddress": "Adresa Sfârșit",
+ "reportSpentFuel": "Combustibil consumat",
+ "reportStartOdometer": "Kilometraj Start",
+ "reportEndOdometer": "Kilomteraj Sfârșit",
+ "statisticsTitle": "Statistici",
+ "statisticsCaptureTime": "Timpul ",
+ "statisticsActiveUsers": "Utilizatori Activi",
+ "statisticsActiveDevices": "Dispozitive Active",
+ "statisticsRequests": "Solicitări",
+ "statisticsMessagesReceived": "Mesaje Primite",
+ "statisticsMessagesStored": "Mesaje Salvate",
+ "statisticsGeocoder": "Solicitări de Geocodare",
+ "statisticsGeolocation": "Solicitări de Geolocare",
+ "categoryArrow": "Sageata",
+ "categoryDefault": "Implicit",
+ "categoryAnimal": "Animal",
+ "categoryBicycle": "Bicicleta",
+ "categoryBoat": "Barca",
+ "categoryBus": "Autobuz",
+ "categoryCar": "Masina",
+ "categoryCamper": "Rulotă",
+ "categoryCrane": "Macara",
+ "categoryHelicopter": "Elicopter",
+ "categoryMotorcycle": "Motocicleta",
+ "categoryOffroad": "Teren Accidentat",
+ "categoryPerson": "Persoana",
+ "categoryPickup": "Papuc",
+ "categoryPlane": "Avion",
+ "categoryShip": "Nava",
+ "categoryTractor": "Tractor",
+ "categoryTrain": "Tren",
+ "categoryTram": "Tramvai",
+ "categoryTrolleybus": "Trolebuz",
+ "categoryTruck": "Camion",
+ "categoryVan": "Duba",
+ "categoryScooter": "Scooter",
+ "maintenanceStart": "Start",
+ "maintenancePeriod": "Perioada"
+} \ No newline at end of file
diff --git a/src/resources/l10n/ru.json b/src/resources/l10n/ru.json
new file mode 100644
index 00000000..4d70757d
--- /dev/null
+++ b/src/resources/l10n/ru.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "Загрузка...",
+ "sharedHide": "Скрыть",
+ "sharedSave": "Сохранить",
+ "sharedUpload": "Upload",
+ "sharedSet": "Установить",
+ "sharedCancel": "Отмена",
+ "sharedCopy": "Copy",
+ "sharedAdd": "Добавить",
+ "sharedEdit": "Редактировать",
+ "sharedRemove": "Удалить",
+ "sharedRemoveConfirm": "Удалить элемент?",
+ "sharedNoData": "Нет данных",
+ "sharedSubject": "Subject",
+ "sharedYes": "Да",
+ "sharedNo": "Нет",
+ "sharedKm": "км",
+ "sharedMi": "мили",
+ "sharedNmi": "м.мили",
+ "sharedMeters": "м",
+ "sharedFeet": "фт",
+ "sharedKn": "уз",
+ "sharedKmh": "км/ч",
+ "sharedMph": "миль/ч",
+ "sharedHour": "Часы",
+ "sharedMinute": "Минуты",
+ "sharedSecond": "Секунды",
+ "sharedDays": "дней",
+ "sharedHours": "часов",
+ "sharedMinutes": "минут",
+ "sharedDecimalDegrees": "Десятичные градусы",
+ "sharedDegreesDecimalMinutes": "Градусы Десятичные минуты",
+ "sharedDegreesMinutesSeconds": "Градусы Минуты Секунды",
+ "sharedName": "Имя",
+ "sharedDescription": "Описание",
+ "sharedSearch": "Поиск",
+ "sharedIconScale": "Иконка масштаба",
+ "sharedGeofence": "Геозона",
+ "sharedGeofences": "Геозоны",
+ "sharedCreateGeofence": "Создать геозону",
+ "sharedNotifications": "Уведомления",
+ "sharedNotification": "Уведомление",
+ "sharedAttributes": "Атрибуты",
+ "sharedAttribute": "Атрибут",
+ "sharedDrivers": "Водители",
+ "sharedDriver": "Водитель",
+ "sharedArea": "Область",
+ "sharedSound": "Звуковое уведомление",
+ "sharedType": "Тип",
+ "sharedDistance": "Расстояние",
+ "sharedHourAbbreviation": "ч",
+ "sharedMinuteAbbreviation": "м",
+ "sharedSecondAbbreviation": "с",
+ "sharedVoltAbbreviation": "В",
+ "sharedLiterAbbreviation": "л",
+ "sharedGallonAbbreviation": "галлон",
+ "sharedLiter": "Литр",
+ "sharedImpGallon": "Имп. галлон",
+ "sharedUsGallon": "Галлон США",
+ "sharedLiterPerHourAbbreviation": "л/ч",
+ "sharedGetMapState": "Получить состояние карты",
+ "sharedComputedAttribute": "Вычисляемый атрибут",
+ "sharedComputedAttributes": "Вычисляемые атрибуты",
+ "sharedCheckComputedAttribute": "Проверить вычисляемый атрибут",
+ "sharedExpression": "Выражение",
+ "sharedDevice": "Устройство",
+ "sharedTest": "Тест",
+ "sharedTestNotification": "Отправить тестовое уведомление",
+ "sharedTestNotificators": "Тестовый канал",
+ "sharedTestExpression": "Тестовое выражение",
+ "sharedCalendar": "Календарь",
+ "sharedCalendars": "Календари",
+ "sharedFile": "Файл",
+ "sharedSearchDevices": "Поиск устройств",
+ "sharedSortBy": "Сортировать по",
+ "sharedFilterMap": "Фильтр на карте",
+ "sharedSelectFile": "Выбрать файл",
+ "sharedPhone": "Телефон",
+ "sharedRequired": "Обязательные",
+ "sharedPreferences": "Настройки",
+ "sharedPermissions": "Разрешения",
+ "sharedConnections": "Соединения",
+ "sharedExtra": "Экстра",
+ "sharedPrimary": "Первичный",
+ "sharedSecondary": "Вторичный",
+ "sharedTypeString": "Строка",
+ "sharedTypeNumber": "Число",
+ "sharedTypeBoolean": "Логическое значение",
+ "sharedTimezone": "Часовой пояс",
+ "sharedInfoTitle": "Инфо",
+ "sharedSavedCommand": "Сохраненная команда",
+ "sharedSavedCommands": "Сохраненные команды",
+ "sharedNew": "Новый...",
+ "sharedShowAddress": "Показать адрес",
+ "sharedShowDetails": "Подробнее",
+ "sharedDisabled": "Отключен",
+ "sharedMaintenance": "Обслуживание",
+ "sharedDeviceAccumulators": "Аккумуляторы",
+ "sharedAlarms": "Тревоги",
+ "sharedLocation": "Расположение",
+ "sharedImport": "Импортировать",
+ "sharedColumns": "Колонка",
+ "sharedDropzoneText": "Перетащите файл сюда или нажмите",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "Простой",
+ "calendarRecurrence": "Повторение",
+ "calendarOnce": "Однократно",
+ "calendarDaily": "Ежедневно",
+ "calendarWeekly": "Еженедельно",
+ "calendarMonthly": "Ежемесячно",
+ "calendarDays": "Дни",
+ "calendarSunday": "Воскресенье",
+ "calendarMonday": "Понедельник",
+ "calendarTuesday": "Вторник",
+ "calendarWednesday": "Среда",
+ "calendarThursday": "Четверг",
+ "calendarFriday": "Пятница",
+ "calendarSaturday": "Суббота",
+ "attributeShowGeofences": "Показать геозоны",
+ "attributeSpeedLimit": "Ограничение скорости",
+ "attributeFuelDropThreshold": "Порог снижения расхода топлива",
+ "attributeFuelIncreaseThreshold": "Порог увеличения расхода топлива",
+ "attributePolylineDistance": "Расстояние от линии",
+ "attributeReportIgnoreOdometer": "Отчет: Игнорировать одометер",
+ "attributeWebReportColor": "Веб: Цвет отчета",
+ "attributeDevicePassword": "Пароль устройства",
+ "attributeDeviceImage": "Картинка устройства",
+ "attributeDeviceInactivityStart": "Начало бездействия устройства",
+ "attributeDeviceInactivityPeriod": "Период бездействия устройства",
+ "attributeProcessingCopyAttributes": "Обработка: Копирование атрибутов",
+ "attributeColor": "Цвет",
+ "attributeWebLiveRouteLength": "Веб: Длина онлайн маршрута",
+ "attributeWebSelectZoom": "Веб: Увеличение при выборе",
+ "attributeWebMaxZoom": "Веб: Максимальное увеличение",
+ "attributeTelegramChatId": "ID чата в Телеграмме",
+ "attributePushoverUserKey": "Нажатие пользовательской клавиши",
+ "attributePushoverDeviceNames": "Нажатие имени устройства",
+ "attributeMailSmtpHost": "Почта: SMTP хост",
+ "attributeMailSmtpPort": "Почта: SMTP порт",
+ "attributeMailSmtpStarttlsEnable": "Почта: Включить SMTP STARTTLS",
+ "attributeMailSmtpStarttlsRequired": "Почта: Необходим SMTP STARTTLS",
+ "attributeMailSmtpSslEnable": "Почта: Включить SMTP SSL",
+ "attributeMailSmtpSslTrust": "Почта: Доверие SMTP SSL",
+ "attributeMailSmtpSslProtocols": "Почта: Протоколы SMTP SSL",
+ "attributeMailSmtpFrom": "Почта: SMTP отправитель",
+ "attributeMailSmtpAuth": "Почта: Включить SMTP аутентификацию",
+ "attributeMailSmtpUsername": "Почта: SMTP имя пользователя",
+ "attributeMailSmtpPassword": "Почта: SMTP пароль",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: Выключить атрибуты",
+ "attributeUiDisableGroups": "UI: Выключить группы",
+ "attributeUiDisableEvents": "UI: Отключить события",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "UI: Отключить водителей",
+ "attributeUiDisableComputedAttributes": "UI: Отключить вычисляемые атрибуты",
+ "attributeUiDisableCalendars": "UI: Отключить календари",
+ "attributeUiDisableMaintenance": "UI: Отключить обслуживание",
+ "attributeUiHidePositionAttributes": "UI: Скрывать атрибуты",
+ "attributeUiDisableLoginLanguage": "UI: Отключить выбор языка при входе",
+ "attributeNotificationTokens": "Токены Уведомлений",
+ "attributePopupInfo": "Всплывающая информация",
+ "errorTitle": "Ошибка",
+ "errorGeneral": "Неправильные параметры или нарушение ограничений",
+ "errorConnection": "Ошибка соединения",
+ "errorSocket": "Ошибка web socket соединения",
+ "errorZero": "Не может быть нулевым",
+ "userEmail": "Email",
+ "userPassword": "Пароль",
+ "userAdmin": "Администратор",
+ "userRemember": "Запомнить",
+ "userExpirationTime": "Срок действия",
+ "userDeviceLimit": "Ограничение устройств",
+ "userUserLimit": "Лимит пользователей",
+ "userDeviceReadonly": "Только просмотр устройств",
+ "userLimitCommands": "Ограничение команд",
+ "userDisableReports": "Выключить отчеты",
+ "userFixedEmail": "Не изменять Email",
+ "userToken": "Ключ",
+ "userDeleteAccount": "Удалить аккаунт",
+ "userTemporary": "Temporary",
+ "loginTitle": "Вход",
+ "loginLanguage": "Язык",
+ "loginReset": "Сброс пароля",
+ "loginRegister": "Регистрация",
+ "loginLogin": "Вход",
+ "loginOpenId": "Login with OpenID",
+ "loginFailed": "Неправильный email адрес или пароль",
+ "loginCreated": "Новый пользователь зарегистрирован",
+ "loginResetSuccess": "Проверьте свою электронную почту",
+ "loginUpdateSuccess": "Новый пароль установлен",
+ "loginLogout": "Выход",
+ "loginLogo": "Логотип",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "Устройства и состояния",
+ "deviceSelected": "Выбранное устройство",
+ "deviceTitle": "Устройства",
+ "devicePrimaryInfo": "Название устройства",
+ "deviceSecondaryInfo": "Детали устройства",
+ "deviceIdentifier": "Идентификатор",
+ "deviceModel": "Модель",
+ "deviceContact": "Контакт",
+ "deviceCategory": "Категория",
+ "deviceLastUpdate": "Последнее обновление",
+ "deviceCommand": "Команда",
+ "deviceFollow": "Следовать",
+ "deviceTotalDistance": "Общий пробег",
+ "deviceStatus": "Статус",
+ "deviceStatusOnline": "Онлайн",
+ "deviceStatusOffline": "Оффлайн",
+ "deviceStatusUnknown": "Неизвестный",
+ "deviceRegisterFirst": "Добавьте свое первое устройство",
+ "deviceIdentifierHelp": "IMEI, серийный номер или другой идентификатор. Он должен соответствовать идентификатору, который устройство сообщает серверу.",
+ "deviceShare": "Share Device",
+ "groupDialog": "Группа",
+ "groupParent": "Группа",
+ "groupNoGroup": "Без группы",
+ "settingsTitle": "Настройки",
+ "settingsUser": "Аккаунт",
+ "settingsGroups": "Группы",
+ "settingsServer": "Сервер",
+ "settingsUsers": "Пользователи",
+ "settingsDistanceUnit": "Единица расстояния",
+ "settingsAltitudeUnit": "Единица измерения высоты",
+ "settingsSpeedUnit": "Единица скорости",
+ "settingsVolumeUnit": "Единица объема",
+ "settingsTwelveHourFormat": "12-часовой формат",
+ "settingsCoordinateFormat": "Формат координат",
+ "settingsServerVersion": "Версия сервера",
+ "settingsAppVersion": "Версия приложения",
+ "settingsConnection": "Соединения",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "Отчеты",
+ "reportScheduled": "Запланированные отчеты",
+ "reportDevice": "Устройство",
+ "reportGroup": "Группа",
+ "reportFrom": "От",
+ "reportTo": "До",
+ "reportShow": "Показать",
+ "reportClear": "Очистить",
+ "linkGoogleMaps": "Карты Google",
+ "linkAppleMaps": "Карты Apple",
+ "linkStreetView": "Просмотр улиц",
+ "positionFixTime": "Время определения",
+ "positionDeviceTime": "Время на устройстве",
+ "positionServerTime": "Время на сервере",
+ "positionValid": "Корректность",
+ "positionAccuracy": "Точность",
+ "positionLatitude": "Широта",
+ "positionLongitude": "Долгота",
+ "positionAltitude": "Высота",
+ "positionSpeed": "Скорость",
+ "positionCourse": "Направление",
+ "positionAddress": "Адрес",
+ "positionProtocol": "Протокол",
+ "positionDistance": "Расстояние",
+ "positionRpm": "Обороты",
+ "positionFuel": "Топливо",
+ "positionPower": "Питание",
+ "positionBattery": "Батарея",
+ "positionRaw": "Сырые данные",
+ "positionIndex": "Индекс",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "Спутники",
+ "positionSatVisible": "Видимые спутники",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "Роуминг",
+ "positionEvent": "Событие",
+ "positionAlarm": "Тревога",
+ "positionStatus": "Статус",
+ "positionOdometer": "Одометр",
+ "positionServiceOdometer": "Одометр обслуживания",
+ "positionTripOdometer": "Одометр поездки",
+ "positionHours": "Часы",
+ "positionSteps": "Шаги",
+ "positionInput": "Входы",
+ "positionHeartRate": "Heart Rate",
+ "positionOutput": "Выходы",
+ "positionBatteryLevel": "Уровень заряда батареи",
+ "positionFuelConsumption": "Расход топлива",
+ "positionRfid": "RFID",
+ "positionVersionFw": "Версия прошивки",
+ "positionVersionHw": "Версия устройства",
+ "positionIgnition": "Зажигание",
+ "positionFlags": "Флаги",
+ "positionCharge": "Заряд",
+ "positionIp": "IP",
+ "positionArchive": "Архив",
+ "positionVin": "VIN",
+ "positionApproximate": "Приблизительно",
+ "positionThrottle": "Дроссель",
+ "positionMotion": "Движение",
+ "positionArmed": "На охране",
+ "positionAcceleration": "Ускорение",
+ "positionTemp": "Температура",
+ "positionDeviceTemp": "Температура устройства",
+ "positionCoolantTemp": "Coolant Temperature",
+ "positionOperator": "Оператор",
+ "positionCommand": "Команда",
+ "positionBlocked": "Блокировка",
+ "positionDtcs": "Ошибки",
+ "positionObdSpeed": "OBD скорость",
+ "positionObdOdometer": "OBD одометр",
+ "positionDrivingTime": "Driving Time",
+ "positionDriverUniqueId": "ID водителя",
+ "positionCard": "Card",
+ "positionImage": "Изображение",
+ "positionVideo": "Видео",
+ "positionAudio": "Аудио",
+ "serverTitle": "Настройки сервера",
+ "serverZoom": "Приближение",
+ "serverRegistration": "Регистрация",
+ "serverReadonly": "Только просмотр",
+ "serverForceSettings": "Форсировать настройки",
+ "serverAnnouncement": "Объявление",
+ "serverName": "Server Name",
+ "serverDescription": "Server Description",
+ "serverColorPrimary": "Color Primary",
+ "serverColorSecondary": "Color Secondary",
+ "serverLogo": "Logo Image",
+ "serverLogoInverted": "Inverted Logo Image",
+ "serverChangeDisable": "Disable Server Change",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "Карта",
+ "mapActive": "Активные карты",
+ "mapOverlay": "Слой карты",
+ "mapOverlayCustom": "Собственный слой",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API ключ",
+ "mapOpenWeatherClouds": "OpenWeather Clouds",
+ "mapOpenWeatherPrecipitation": "OpenWeather Precipitation",
+ "mapOpenWeatherPressure": "OpenWeather давление",
+ "mapOpenWeatherWind": "OpenWeather ветер",
+ "mapOpenWeatherTemperature": "OpenWeather температура",
+ "mapLayer": "Слой карты",
+ "mapCustom": "Пользовательский (XYZ)",
+ "mapCustomArcgis": "Пользовательский (ArcGIS)",
+ "mapCustomLabel": "Пользовательская карта",
+ "mapCarto": "Carto Basemaps",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google дороги",
+ "mapGoogleHybrid": "Google гибрид",
+ "mapGoogleSatellite": "Google спутник",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Ключ Bing Maps",
+ "mapBingRoad": "Bing Maps Дороги",
+ "mapBingAerial": "Bing Maps Спутник",
+ "mapBingHybrid": "Bing Maps Гибрид",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Яндекс Карты",
+ "mapYandexSat": "Яндекс Спутник",
+ "mapWikimedia": "Викимедиа",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox Улицы",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox Топография",
+ "mapMapboxSatellite": "Mapbox Спутник",
+ "mapMapboxKey": "Mapbox токен доступа",
+ "mapMapTilerBasic": "MapTiler простое",
+ "mapMapTilerHybrid": "MapTiler гибрид",
+ "mapMapTilerKey": "MapTiler ключ API",
+ "mapLocationIqStreets": "LocationIQ улицы",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ токен доступа",
+ "mapTomTomBasic": "TomTom простое",
+ "mapTomTomFlow": "TomTom движение потока",
+ "mapTomTomIncidents": "TomTom аварии в потоке",
+ "mapTomTomKey": "TomTom ключ API",
+ "mapHereBasic": "Простое",
+ "mapHereHybrid": "Гибрид",
+ "mapHereSatellite": "Спутники",
+ "mapHereFlow": "Движение потока",
+ "mapHereKey": "Ключ API",
+ "mapShapePolygon": "Многоугольник",
+ "mapShapeCircle": "Круг",
+ "mapShapePolyline": "Линия",
+ "mapLiveRoutes": " Живые маршруты",
+ "mapDirection": "Показать направление",
+ "mapCurrentLocation": "Текущее Местоположение",
+ "mapPoiLayer": "Слой POI",
+ "mapClustering": "Кластеризация маркеров",
+ "mapOnSelect": "Показать карту при выборе",
+ "mapDefault": "Карта по-умолчанию",
+ "stateTitle": "Состояние",
+ "stateName": "Параметр",
+ "stateValue": "Значение",
+ "commandTitle": "Команда",
+ "commandSend": "Отправить",
+ "commandSent": "Команда отправлена",
+ "commandQueued": "Команда добавлена в очередь",
+ "commandUnit": "Единицы",
+ "commandCustom": "Пользовательская команда",
+ "commandDeviceIdentification": "Идентификация устройства",
+ "commandPositionSingle": "Разовое отслеживание",
+ "commandPositionPeriodic": "Начать отслеживание",
+ "commandPositionStop": "Отменить отслеживание",
+ "commandEngineStop": "Заблокировать двигатель",
+ "commandEngineResume": "Разблокировать двигатель",
+ "commandAlarmArm": "Активировать сигнализацию",
+ "commandAlarmDisarm": "Деактивировать сигнализацию",
+ "commandAlarmDismiss": "Отключить сигнализацию",
+ "commandSetTimezone": "Настроить часовой пояс",
+ "commandRequestPhoto": "Запросить фото",
+ "commandPowerOff": "Выключить устройство",
+ "commandRebootDevice": "Перезагрузить устройство",
+ "commandFactoryReset": "Сброс к заводским настройкам",
+ "commandSendSms": "Отправить СМС",
+ "commandSendUssd": "Отправить USSD",
+ "commandSosNumber": "Настроить экстренный номер",
+ "commandSilenceTime": "Настроить время тишины",
+ "commandSetPhonebook": "Настроить телефонную книгу",
+ "commandVoiceMessage": "Голосовое сообщение",
+ "commandOutputControl": "Контроль выхода",
+ "commandVoiceMonitoring": "Голосовой контроль",
+ "commandSetAgps": "Настроить AGPS",
+ "commandSetIndicator": "Настроить индикатор",
+ "commandConfiguration": "Конфигурация",
+ "commandGetVersion": "Запросить версию",
+ "commandFirmwareUpdate": "Обновить прошивку",
+ "commandSetConnection": "Настроить соединение",
+ "commandSetOdometer": "Настроить одометр",
+ "commandGetModemStatus": "Запросить состояние модема",
+ "commandGetDeviceStatus": "Запросить состояние устройства",
+ "commandSetSpeedLimit": "Установить ограничение скорости",
+ "commandModePowerSaving": "Режим экономии энергии",
+ "commandModeDeepSleep": "Режим глубокого сна",
+ "commandAlarmGeofence": "Установить сигнал геозоны",
+ "commandAlarmBattery": "Установить сигнал батареи",
+ "commandAlarmSos": "Установить сигнал SOS",
+ "commandAlarmRemove": "Установить сигнал удаления",
+ "commandAlarmClock": "Установить сигнал часов",
+ "commandAlarmSpeed": "Установить сигнал скорости",
+ "commandAlarmFall": "Установить сигнал падения",
+ "commandAlarmVibration": "Установить сигнал вибрации",
+ "commandFrequency": "Частота",
+ "commandTimezone": "Смещение временной зоны",
+ "commandMessage": "Сообщение",
+ "commandRadius": "Радиус",
+ "commandEnable": "Включить",
+ "commandData": "Данные",
+ "commandIndex": "Индекс",
+ "commandPhone": "Номер телефона",
+ "commandServer": "Сервер",
+ "commandPort": "Порт",
+ "eventAll": "Все события",
+ "eventDeviceOnline": "Статус онлайн",
+ "eventDeviceUnknown": "Статус неизвестен",
+ "eventDeviceOffline": "Статус оффлайн",
+ "eventDeviceInactive": "Устройство неактивно",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "Устройство двигается",
+ "eventDeviceStopped": "Устройство остановилось",
+ "eventDeviceOverspeed": "Превышено ограничение скорости",
+ "eventDeviceFuelDrop": "Слив топлива",
+ "eventDeviceFuelIncrease": "Увеличение расхода топлива",
+ "eventCommandResult": "Результат команды",
+ "eventGeofenceEnter": "Вход в геозону",
+ "eventGeofenceExit": "Выход из геозоны",
+ "eventAlarm": "Тревога",
+ "eventIgnitionOn": "Зажигание включено",
+ "eventIgnitionOff": "Зажигание выключено",
+ "eventMaintenance": "Требуется обслуживание",
+ "eventTextMessage": "Текстовое сообщение получено",
+ "eventDriverChanged": "Водитель изменен",
+ "eventMedia": "Медиа",
+ "eventsScrollToLast": "Прокрутка до конца",
+ "eventsSoundEvents": "Звук событий",
+ "eventsSoundAlarms": "Звук тревоги",
+ "alarmGeneral": "Общее",
+ "alarmSos": "SOS",
+ "alarmVibration": "Вибрация",
+ "alarmMovement": "Движение",
+ "alarmLowspeed": "Низкая скорость",
+ "alarmOverspeed": "Превышение скорости",
+ "alarmFallDown": "Падение",
+ "alarmLowPower": "Низкий уровень питания",
+ "alarmLowBattery": "Батарея разряжена",
+ "alarmFault": "Неисправность",
+ "alarmPowerOff": "Выключение",
+ "alarmPowerOn": "Включение",
+ "alarmDoor": "Дверь",
+ "alarmLock": "Закрыто",
+ "alarmUnlock": "Открыто",
+ "alarmGeofence": "Геозона",
+ "alarmGeofenceEnter": "Вход в геозону",
+ "alarmGeofenceExit": "Выход из геозоны",
+ "alarmGpsAntennaCut": "Отключение GPS-антенны",
+ "alarmAccident": "Авария",
+ "alarmTow": "Буксировка",
+ "alarmIdle": "Простой",
+ "alarmHighRpm": "Высокие обороты",
+ "alarmHardAcceleration": "Резкое ускорение",
+ "alarmHardBraking": "Резкое торможение",
+ "alarmHardCornering": "Резкий поворот",
+ "alarmLaneChange": "Смена полосы",
+ "alarmFatigueDriving": "Усталость водителя",
+ "alarmPowerCut": "Отключение питания",
+ "alarmPowerRestored": "Питание восстановлено",
+ "alarmJamming": "Заглушено",
+ "alarmTemperature": "Температура",
+ "alarmParking": "Парковка",
+ "alarmBonnet": "Капот",
+ "alarmFootBrake": "Ножной тормоз",
+ "alarmFuelLeak": "Утечка топлива",
+ "alarmTampering": "Вмешательство",
+ "alarmRemoving": "Извлечение",
+ "notificationType": "Тип уведомления",
+ "notificationAlways": "Все устройства",
+ "notificationNotificators": "Каналы",
+ "notificatorCommand": "Команда",
+ "notificatorWeb": "Веб",
+ "notificatorMail": "Почта",
+ "notificatorSms": "СМС",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Телеграмм",
+ "notificatorPushover": "Нажатие",
+ "reportReplay": "Воспроизвести",
+ "reportCombined": "Совместный",
+ "reportRoute": "Маршрут",
+ "reportEvents": "События",
+ "reportTrips": "Поездки",
+ "reportStops": "Остановки",
+ "reportSummary": "Сводка",
+ "reportDaily": "Ежедневная сводка",
+ "reportChart": "Диаграмма",
+ "reportConfigure": "Конфигурировать",
+ "reportEventTypes": "Тип события",
+ "reportChartType": "Тип диаграммы",
+ "reportShowMarkers": "Показать маркеры",
+ "reportExport": "Экспорт",
+ "reportEmail": "Отчет по почте",
+ "reportSchedule": "Запланированный",
+ "reportPeriod": "Период",
+ "reportCustom": "Пользовательский",
+ "reportToday": "Сегодня",
+ "reportYesterday": "Вчера",
+ "reportThisWeek": "Текущая неделя",
+ "reportPreviousWeek": "Предыдущая неделя",
+ "reportThisMonth": "Текущий месяц",
+ "reportPreviousMonth": "Предыдущий месяц",
+ "reportDeviceName": "Имя устройства",
+ "reportAverageSpeed": "Средняя скорость",
+ "reportMaximumSpeed": "Максимальная скорость",
+ "reportEngineHours": "Моточасы",
+ "reportDuration": "Длительность",
+ "reportStartDate": "Дата начала",
+ "reportStartTime": "Начальное время",
+ "reportStartAddress": "Начальный адрес",
+ "reportEndTime": "Конечное время",
+ "reportEndAddress": "Конечный адрес",
+ "reportSpentFuel": "Использовано топлива",
+ "reportStartOdometer": "Одометр, начало",
+ "reportEndOdometer": "Одометр, окончание",
+ "statisticsTitle": "Статистика",
+ "statisticsCaptureTime": "Время сбора",
+ "statisticsActiveUsers": "Активные пользователи",
+ "statisticsActiveDevices": "Активные устройства",
+ "statisticsRequests": "Запросы",
+ "statisticsMessagesReceived": "Сообщений получено",
+ "statisticsMessagesStored": "Сообщений сохранено",
+ "statisticsGeocoder": "Запросы геокодера",
+ "statisticsGeolocation": "Запросы геолокации",
+ "categoryArrow": "Стрелка",
+ "categoryDefault": "По умолчанию",
+ "categoryAnimal": "Животное",
+ "categoryBicycle": "Велосипед",
+ "categoryBoat": "Лодка",
+ "categoryBus": "Автобус",
+ "categoryCar": "Автомобиль",
+ "categoryCamper": "Camper",
+ "categoryCrane": "Кран",
+ "categoryHelicopter": "Вертолет",
+ "categoryMotorcycle": "Мотоцикл",
+ "categoryOffroad": "Внедорожник",
+ "categoryPerson": "Человек",
+ "categoryPickup": "Пикап",
+ "categoryPlane": "Самолёт",
+ "categoryShip": "Корабль",
+ "categoryTractor": "Трактор",
+ "categoryTrain": "Поезд",
+ "categoryTram": "Трамвай",
+ "categoryTrolleybus": "Троллейбус",
+ "categoryTruck": "Грузовой автомобиль",
+ "categoryVan": "Фургон",
+ "categoryScooter": "Скутер",
+ "maintenanceStart": "Начало",
+ "maintenancePeriod": "Период"
+} \ No newline at end of file
diff --git a/src/resources/l10n/si.json b/src/resources/l10n/si.json
new file mode 100644
index 00000000..9e8ad6af
--- /dev/null
+++ b/src/resources/l10n/si.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "පූරණය ස්ථානයේ...",
+ "sharedHide": "සඟවන්න",
+ "sharedSave": "සුරකින්න",
+ "sharedUpload": "Upload",
+ "sharedSet": "සකසන්න",
+ "sharedCancel": "සිදු කරන්න",
+ "sharedCopy": "Copy",
+ "sharedAdd": "එක් කරන්න",
+ "sharedEdit": "සංස්කරණය",
+ "sharedRemove": "ඉවත් කරන්න",
+ "sharedRemoveConfirm": "අයිතමය ඉවත් කරන්නද?",
+ "sharedNoData": "දත්ත නැත",
+ "sharedSubject": "Subject",
+ "sharedYes": "ඔව්",
+ "sharedNo": "නැත",
+ "sharedKm": "කි.මී.",
+ "sharedMi": "සැතපුම්",
+ "sharedNmi": "nmi",
+ "sharedMeters": "එම්",
+ "sharedFeet": "අඩි",
+ "sharedKn": "kn",
+ "sharedKmh": "කි.මී/පැ",
+ "sharedMph": "mph",
+ "sharedHour": "පැය",
+ "sharedMinute": "විනාඩි",
+ "sharedSecond": "තත්පර",
+ "sharedDays": "දවස්",
+ "sharedHours": "පැය",
+ "sharedMinutes": "විනාඩි",
+ "sharedDecimalDegrees": "දශම සඳහා",
+ "sharedDegreesDecimalMinutes": "අංශක දශම මිනිත්තු",
+ "sharedDegreesMinutesSeconds": "දශම පිළිබඳව තප්පර",
+ "sharedName": "නම",
+ "sharedDescription": "සවිස්තරය",
+ "sharedSearch": "සොයන්න",
+ "sharedIconScale": "Icon Scale",
+ "sharedGeofence": "භූ වැටිය",
+ "sharedGeofences": "භූ වැටි",
+ "sharedCreateGeofence": "Create Geofence",
+ "sharedNotifications": "දැනුම්දීම්",
+ "sharedNotification": "දැනුම්දීම",
+ "sharedAttributes": "උපලක්ෂණ",
+ "sharedAttribute": "උපලක්ෂණය",
+ "sharedDrivers": "රියදුරන්",
+ "sharedDriver": "රියදුරු",
+ "sharedArea": "ප්‍රදේශය",
+ "sharedSound": "දැනුම්දීමේ ශබ්දය",
+ "sharedType": "වර්ගය",
+ "sharedDistance": "දුර",
+ "sharedHourAbbreviation": "පැ.",
+ "sharedMinuteAbbreviation": "මි.",
+ "sharedSecondAbbreviation": "තත්.",
+ "sharedVoltAbbreviation": "වී",
+ "sharedLiterAbbreviation": "එල්",
+ "sharedGallonAbbreviation": "ගැලුම්",
+ "sharedLiter": "ලීටර",
+ "sharedImpGallon": "Imp. ගැලන්",
+ "sharedUsGallon": "එක්සත් ජනපද ගැලෝන්",
+ "sharedLiterPerHourAbbreviation": "l/h",
+ "sharedGetMapState": "සිතියම් තත්ත්වය",
+ "sharedComputedAttribute": "කළ ගුණය",
+ "sharedComputedAttributes": "සිදු කළ ගුණාංග",
+ "sharedCheckComputedAttribute": "කළ ගුණය පරීක්ෂා කරන්න",
+ "sharedExpression": "ප්රකාශනය",
+ "sharedDevice": "වැනි",
+ "sharedTest": "පරීක්ෂණය",
+ "sharedTestNotification": "අත්හදාබැලීමේ දැනුම්දීම යවන්න",
+ "sharedTestNotificators": "පරීක්ෂණ නාලිකා",
+ "sharedTestExpression": "පරීක්ෂණ ප්රකාශනය",
+ "sharedCalendar": "දින දසුන",
+ "sharedCalendars": "දින දසුන්",
+ "sharedFile": "ගොනුව",
+ "sharedSearchDevices": "සෙවුම් උපාංග",
+ "sharedSortBy": "අනුව තෝරන්න",
+ "sharedFilterMap": "සිතියම මත පෙරහන් කරන්න",
+ "sharedSelectFile": "ගොනුව තෝරන්න",
+ "sharedPhone": "දුරකථනය",
+ "sharedRequired": "අවශයි",
+ "sharedPreferences": "මනාපයන්",
+ "sharedPermissions": "අවසර",
+ "sharedConnections": "සම්බන්ධතා",
+ "sharedExtra": "අමතර",
+ "sharedPrimary": "Primary",
+ "sharedSecondary": "Secondary",
+ "sharedTypeString": "නූල්",
+ "sharedTypeNumber": "අංකය",
+ "sharedTypeBoolean": "බූලියන්",
+ "sharedTimezone": "වේලා කලාපය",
+ "sharedInfoTitle": "තොරතුරු",
+ "sharedSavedCommand": "විධානය සුරකින ලදී",
+ "sharedSavedCommands": "විධාන සුරකින",
+ "sharedNew": "අලුත්…",
+ "sharedShowAddress": "ලිපිනය පෙන්වන්න",
+ "sharedShowDetails": "වැඩිපුර විස්තර",
+ "sharedDisabled": "අබල කර ඇත",
+ "sharedMaintenance": "නඩත්තුව",
+ "sharedDeviceAccumulators": "සමුච්චය කරන්නන්",
+ "sharedAlarms": "අනතුරු ඇඟවීම්",
+ "sharedLocation": "ස්ථානය",
+ "sharedImport": "ආනයන",
+ "sharedColumns": "තීරු",
+ "sharedDropzoneText": "Drag and drop a file here or click",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "Simple",
+ "calendarRecurrence": "Recurrence",
+ "calendarOnce": "Once",
+ "calendarDaily": "Daily",
+ "calendarWeekly": "Weekly",
+ "calendarMonthly": "Monthly",
+ "calendarDays": "Days",
+ "calendarSunday": "Sunday",
+ "calendarMonday": "Monday",
+ "calendarTuesday": "Tuesday",
+ "calendarWednesday": "Wednesday",
+ "calendarThursday": "Thursday",
+ "calendarFriday": "Friday",
+ "calendarSaturday": "Saturday",
+ "attributeShowGeofences": "Show Geofences",
+ "attributeSpeedLimit": "වේග සීමාව",
+ "attributeFuelDropThreshold": "Fuel Drop Threshold",
+ "attributeFuelIncreaseThreshold": "Fuel Increase Threshold",
+ "attributePolylineDistance": "බහු රේඛා දුර",
+ "attributeReportIgnoreOdometer": "වාර්තාව: සැතපුම්මානය නොසලකන්න",
+ "attributeWebReportColor": "වියමන: පාට වාර්තා කරන්න",
+ "attributeDevicePassword": "කැමති මුරපදය",
+ "attributeDeviceImage": "උපාංග රූපය",
+ "attributeDeviceInactivityStart": "උපාංගය අක්රිය වීම ආරම්භය",
+ "attributeDeviceInactivityPeriod": "උපාංග අක්‍රිය කාලය",
+ "attributeProcessingCopyAttributes": "සැකසීම: උපලක්ෂණ පිටපත් කරන්න",
+ "attributeColor": "වර්ණ",
+ "attributeWebLiveRouteLength": "වෙබ්: සජීවී මාර්ග දිග",
+ "attributeWebSelectZoom": "වෙබ්: විශාලනය කරන්න තෝරන්න",
+ "attributeWebMaxZoom": "වියමන: උපරිම විශාලනය",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Pushover User Key",
+ "attributePushoverDeviceNames": "Pushover Device Names",
+ "attributeMailSmtpHost": "තැපෑල: ස.තැ.මා.කේ. සත්කාරකය",
+ "attributeMailSmtpPort": "තැපෑල: ස.තැ.මා.කේ. කෙවෙනිය",
+ "attributeMailSmtpStarttlsEnable": "තැපෑල: SMTP STARTTLS සබල කරන්න",
+ "attributeMailSmtpStarttlsRequired": "තැපෑල: SMTP STARTTLS අවශ්‍යයි",
+ "attributeMailSmtpSslEnable": "තැපෑල: SMTP SSL සක්රිය කරන්න",
+ "attributeMailSmtpSslTrust": "තැපෑල: SMTP SSL භාරය",
+ "attributeMailSmtpSslProtocols": "තැපෑල: SMTP SSL ප්‍රොටෝකෝල",
+ "attributeMailSmtpFrom": "තැපෑල: SMTP වෙතින්",
+ "attributeMailSmtpAuth": "තැපෑල: SMTP Auth සක්රිය කරන්න",
+ "attributeMailSmtpUsername": "තැපෑල: ස.තැ.මා.කේ. පර්යේෂණනාමය",
+ "attributeMailSmtpPassword": "තැපෑල: ස.තැ.මා.කේ. මුරපදය",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: Disable Attributes",
+ "attributeUiDisableGroups": "UI: කණ්ඩායම් අබල කරන්න",
+ "attributeUiDisableEvents": "අ.මු.: සිදුවීම් අබල කරන්න",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "UI: ධාවක අක්‍රීය කරන්න",
+ "attributeUiDisableComputedAttributes": "UI: ගණනය කළ ගුණාංග අක්‍රීය කරන්න",
+ "attributeUiDisableCalendars": "අ.මු.: දින දසුන අබල කරන්න",
+ "attributeUiDisableMaintenance": "UI: නඩත්තු කිරීම අබල කරන්න",
+ "attributeUiHidePositionAttributes": "UI: ස්ථාන ගුණාංග සඟවන්න",
+ "attributeUiDisableLoginLanguage": "UI: පිවිසුම් භාෂාව අක්‍රීය කරන්න",
+ "attributeNotificationTokens": "දැනුම්දීම් ටෝකන",
+ "attributePopupInfo": "Popup Info",
+ "errorTitle": "දෝෂයකි",
+ "errorGeneral": "වලංගු නොවන පරාමිති හෝ සීමාවන් උල්ලංඝනය කිරීම",
+ "errorConnection": "සම්බන්ධතාවේ දෝෂයකි",
+ "errorSocket": "වෙබ් සොකට් සම්බන්ධතා දෝෂයකි",
+ "errorZero": "ශුන්‍ය විය හැකියි",
+ "userEmail": "වි-තැපෑල",
+ "userPassword": "මුරපදය",
+ "userAdmin": "පරිපාලක",
+ "userRemember": "මතක තබාගන්න",
+ "userExpirationTime": "කල් ඉකුත්වීම",
+ "userDeviceLimit": "උපාංග සීමාව",
+ "userUserLimit": "පරිශීලක සීමාව",
+ "userDeviceReadonly": "ඇතුළත් කියවීම පමණි",
+ "userLimitCommands": "සීමිත විධානයන්",
+ "userDisableReports": "වාර්තා අක්රිය කරන්න",
+ "userFixedEmail": "No Email Change",
+ "userToken": "ටෝකනය",
+ "userDeleteAccount": "Delete Account",
+ "userTemporary": "Temporary",
+ "loginTitle": "ඇතුල් වන්න",
+ "loginLanguage": "භාෂාව",
+ "loginReset": "මුරපදය නැවත සකසන්න",
+ "loginRegister": "ලියාපදිංචි වන්න",
+ "loginLogin": "ඇතුල් වන්න",
+ "loginOpenId": "Login with OpenID",
+ "loginFailed": "වි-තැපැල් ලිපිනය හෝ මුරපදය වැරදිය!",
+ "loginCreated": "නව පරිශීලකයෙකු ලියාපදිංචි වී ඇත",
+ "loginResetSuccess": "ඔබගේ විද්යුත් තැපෑල පරීක්ෂා කරන්න",
+ "loginUpdateSuccess": "නව මුරපදය සකසා ඇත",
+ "loginLogout": "නික්මෙන්න",
+ "loginLogo": "ලාංඡනය",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "උපාංග සහ තත්ත්වය",
+ "deviceSelected": "Selected Device",
+ "deviceTitle": "උපාංග",
+ "devicePrimaryInfo": "Device Title",
+ "deviceSecondaryInfo": "Device Detail",
+ "deviceIdentifier": "හඳුන්වනවා",
+ "deviceModel": "ආකෘතිය",
+ "deviceContact": "අමතන්න",
+ "deviceCategory": "ප්‍රවර්ගය",
+ "deviceLastUpdate": "අවසන් යාවත්කාලය",
+ "deviceCommand": "විධානය",
+ "deviceFollow": "ලුහුබඳින්න",
+ "deviceTotalDistance": "මුළු දුර",
+ "deviceStatus": "තත්ත්වය",
+ "deviceStatusOnline": "ඔන්ලයින්",
+ "deviceStatusOffline": "නොබැඳි",
+ "deviceStatusUnknown": "නොදන්නා",
+ "deviceRegisterFirst": "Register your first device",
+ "deviceIdentifierHelp": "IMEI, serial number or other id. It has to match the identifier device reports to the server.",
+ "deviceShare": "Share Device",
+ "groupDialog": "සමූහය",
+ "groupParent": "සමූහය",
+ "groupNoGroup": "සමූහ නැත",
+ "settingsTitle": "සැකසුම්",
+ "settingsUser": "ගැන",
+ "settingsGroups": "සමූහ",
+ "settingsServer": "සේවාදායකයා",
+ "settingsUsers": "පරිශීලකයින්",
+ "settingsDistanceUnit": "දුරස්ථ ඒකකය",
+ "settingsAltitudeUnit": "උන්නතාංශ ඒකකය",
+ "settingsSpeedUnit": "වේග නියමිතක්",
+ "settingsVolumeUnit": "පරිමාව ඒකකය",
+ "settingsTwelveHourFormat": "පැය 12 ආකෘතිය",
+ "settingsCoordinateFormat": "ඛණ්ඩාංක ආකෘතිය",
+ "settingsServerVersion": "Server Version",
+ "settingsAppVersion": "App Version",
+ "settingsConnection": "Connection",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "වාර්තාව",
+ "reportScheduled": "Scheduled Reports",
+ "reportDevice": "වැනි",
+ "reportGroup": "සමුහය",
+ "reportFrom": "සිට",
+ "reportTo": "දක්වා",
+ "reportShow": "පෙන්වන්න",
+ "reportClear": "හිස් කරන්න",
+ "linkGoogleMaps": "ගුගල් සිතියම්",
+ "linkAppleMaps": "ඇපල් සිතියම්",
+ "linkStreetView": "වීථි දසුන",
+ "positionFixTime": "කාලය නිවැරදි කරන්න",
+ "positionDeviceTime": "උපාංග කාලය",
+ "positionServerTime": "සේවාදායක කාලය",
+ "positionValid": "වලංගු",
+ "positionAccuracy": "නිරවද්යතාව",
+ "positionLatitude": "අක්ෂාංශ",
+ "positionLongitude": "දේශාංශ",
+ "positionAltitude": "උන්නතාංශය",
+ "positionSpeed": "වේගවත්",
+ "positionCourse": "දිගංශය",
+ "positionAddress": "ලිපිනය",
+ "positionProtocol": "කෙටුම්පත",
+ "positionDistance": "දුර",
+ "positionRpm": "ආර්පීඑම්",
+ "positionFuel": "ඉන්ධන",
+ "positionPower": "බලය",
+ "positionBattery": "වියළිකෝෂය",
+ "positionRaw": "අමු",
+ "positionIndex": "දර්ශකය",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "චන්ද්‍රිකා",
+ "positionSatVisible": "දෘෂ්‍ය චන්ද්‍රිකා",
+ "positionRssi": "ආර්එස්එස්අයි",
+ "positionGps": "ගි.පි.එස්",
+ "positionRoaming": "රෝමිං",
+ "positionEvent": "සිදුවීම",
+ "positionAlarm": "අනතුරු ඇඟවීම",
+ "positionStatus": "තත්ත්වය",
+ "positionOdometer": "සැතපුම්මානය",
+ "positionServiceOdometer": "සේවා සැතපුම්මානය",
+ "positionTripOdometer": "චාරිකා සැතපුම්මානය",
+ "positionHours": "පැය",
+ "positionSteps": "පියවර",
+ "positionInput": "ආදානය",
+ "positionHeartRate": "Heart Rate",
+ "positionOutput": "ප්‍රතිදානය",
+ "positionBatteryLevel": "විදුලිකෝෂයේ මට්ටමට",
+ "positionFuelConsumption": "ඉන්ධන සඳහා",
+ "positionRfid": "RFID",
+ "positionVersionFw": "ස්ථිරාංගයේ වෙනස්",
+ "positionVersionHw": "දෘඩංගයේ වෙනස්",
+ "positionIgnition": "තාපනය",
+ "positionFlags": "කොඩි",
+ "positionCharge": "ගාස්තුව",
+ "positionIp": "අ.ජා.කේ.",
+ "positionArchive": "සංරක්ෂිතය",
+ "positionVin": "VIN",
+ "positionApproximate": "දළ වශයෙන්",
+ "positionThrottle": "Throttle",
+ "positionMotion": "දක්වා",
+ "positionArmed": "ආයුධ",
+ "positionAcceleration": "ත්වරණය",
+ "positionTemp": "උෂ්ණත්වය",
+ "positionDeviceTemp": "උෂ්ණත්වයේ උෂ්ණත්වය",
+ "positionCoolantTemp": "Coolant Temperature",
+ "positionOperator": "ක්‍රියාකරු",
+ "positionCommand": "විධානය",
+ "positionBlocked": "අවහිර කර ඇත",
+ "positionDtcs": "DTCs",
+ "positionObdSpeed": "OBD වේගය",
+ "positionObdOdometer": "OBD Odometer",
+ "positionDrivingTime": "Driving Time",
+ "positionDriverUniqueId": "රියදුරු අනන්‍ය හැඳුනුම්පත",
+ "positionCard": "Card",
+ "positionImage": "රූප",
+ "positionVideo": "Video",
+ "positionAudio": "ශ්රව්ය උපකරණ",
+ "serverTitle": "සේවාදායකයේ සැකසුම්",
+ "serverZoom": "විශාලනය",
+ "serverRegistration": "ලියාපදිංචි කිරීම",
+ "serverReadonly": "කියවීමට පමණි",
+ "serverForceSettings": "බල සැකසීම්",
+ "serverAnnouncement": "නිවේදනය",
+ "serverName": "Server Name",
+ "serverDescription": "Server Description",
+ "serverColorPrimary": "Color Primary",
+ "serverColorSecondary": "Color Secondary",
+ "serverLogo": "Logo Image",
+ "serverLogoInverted": "Inverted Logo Image",
+ "serverChangeDisable": "Disable Server Change",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "දක්වා",
+ "mapActive": "ක්රියාකාරී සිතියම්",
+ "mapOverlay": "සිතියම උඩ තට්ටුව",
+ "mapOverlayCustom": "අභිරුචි ආවරණයක්",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API යතුර",
+ "mapOpenWeatherClouds": "විවෘත කාලගුණ වලාකුළු",
+ "mapOpenWeatherPrecipitation": "විවෘත කාලගුණ වර්ෂාපතනය",
+ "mapOpenWeatherPressure": "විවෘත කාලගුණ පීඩනය",
+ "mapOpenWeatherWind": "විවෘත කාලගුණ සුළඟ",
+ "mapOpenWeatherTemperature": "විවෘත කාලගුණ උෂ්ණත්වය",
+ "mapLayer": "සිතියම් ස්තරය",
+ "mapCustom": "අභිරුචි (XYZ)",
+ "mapCustomArcgis": "අභිරුචි (ArcGIS)",
+ "mapCustomLabel": "අභිරුචි සිතියම",
+ "mapCarto": "Carto Basemaps",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "බිං මැප්ස් යතුර",
+ "mapBingRoad": "බිං මැප්ස් පාර",
+ "mapBingAerial": "Bing Maps Aerial",
+ "mapBingHybrid": "Bing Maps Hybrid",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "යාන්ඩෙක්ස් සිතියම",
+ "mapYandexSat": "යාන්ඩෙක්ස් චන්ද්‍රිකාව",
+ "mapWikimedia": "විකිමීඩියා",
+ "mapOrdnanceSurvey": "යුධෝපකරණ සමීක්ෂණය",
+ "mapMapboxStreets": "සිතියම් පෙට්ටි වීදි",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox එළිමහන්",
+ "mapMapboxSatellite": "Mapbox චන්ද්‍රිකාව",
+ "mapMapboxKey": "Mapbox ප්රවේශ ටෝකනය",
+ "mapMapTilerBasic": "MapTiler මූලික",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "MapTiler API යතුර",
+ "mapLocationIqStreets": "LocationIQ වීදි",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ ප්‍රවේශ ටෝකනය",
+ "mapTomTomBasic": "TomTom මූලික",
+ "mapTomTomFlow": "TomTom රථවාහන ප්රවාහය",
+ "mapTomTomIncidents": "TomTom රථවාහන සිදුවීම්",
+ "mapTomTomKey": "TomTom API යතුර",
+ "mapHereBasic": "මෙන්න Basic",
+ "mapHereHybrid": "මෙන්න Hybrid",
+ "mapHereSatellite": "මෙන්න සැටලයිට්",
+ "mapHereFlow": "මෙන්න Traffic Flow",
+ "mapHereKey": "මෙන්න API යතුර",
+ "mapShapePolygon": "බහුඅශ්‍රය",
+ "mapShapeCircle": "වෘත්තය",
+ "mapShapePolyline": "බහු රේඛාව",
+ "mapLiveRoutes": "සජීවී මාර්ග",
+ "mapDirection": "Show Direction",
+ "mapCurrentLocation": "වත්මන් ස්ථානය",
+ "mapPoiLayer": "POI ස්තරය",
+ "mapClustering": "සලකුණු පොකුරු කිරීම",
+ "mapOnSelect": "තේරීම මත සිතියම පෙන්වන්න",
+ "mapDefault": "Default Map",
+ "stateTitle": "තත්ත්වය",
+ "stateName": "ගුණාංගය",
+ "stateValue": "අගය",
+ "commandTitle": "විධානය",
+ "commandSend": "යවන්න",
+ "commandSent": "විධානය යවා ඇත",
+ "commandQueued": "විධානය පෝලිමේ",
+ "commandUnit": "සඳහා",
+ "commandCustom": "අභිරුචි විධානය",
+ "commandDeviceIdentification": "හඳුනා ගැනීම",
+ "commandPositionSingle": "තනි වාර්තාව",
+ "commandPositionPeriodic": "ආවර්තිතාව වාර්තා කරන්න",
+ "commandPositionStop": "වාර්තා කිරීම නවත්වන්න",
+ "commandEngineStop": "එන්ජින් නැවතුම",
+ "commandEngineResume": "එන්ජින් Resume",
+ "commandAlarmArm": "ආම් එලාම්",
+ "commandAlarmDisarm": "අනතුරු ඇඟවීම නිරායුධ කරන්න",
+ "commandAlarmDismiss": "එලාමය අස් කරන්න",
+ "commandSetTimezone": "කලාපය සකසන්න",
+ "commandRequestPhoto": "ඡායාරූප ඉල්ලීම",
+ "commandPowerOff": "උපාංගය ක්‍රියා විරහිත කරන්න",
+ "commandRebootDevice": "අපට යළි ආරම්භ කරන්න",
+ "commandFactoryReset": "කර්මාන්තශාලා යළි පිහිටුවීම",
+ "commandSendSms": "කෙටි පණිවිඩ යවන්න",
+ "commandSendUssd": "USSD යවන්න",
+ "commandSosNumber": "SOS අංකය සකසන්න",
+ "commandSilenceTime": "නිශ්ශබ්දතාවයේ වේලාව සකසන්න",
+ "commandSetPhonebook": "දුරකථනපොත සකසන්න",
+ "commandVoiceMessage": "සඳහා පණිවිඩය",
+ "commandOutputControl": "ප්රතිදාන පාලනය",
+ "commandVoiceMonitoring": "හඬ නිරීක්ෂණ",
+ "commandSetAgps": "AGPS සකසන්න",
+ "commandSetIndicator": "දර්ශකය සකසන්න",
+ "commandConfiguration": "වින්‍යාසය",
+ "commandGetVersion": "සහ ගන්න",
+ "commandFirmwareUpdate": "ස්ථිරාංගය යථාවත්කාල කරන්න",
+ "commandSetConnection": "සම්බන්ධතාවය සකසන්න",
+ "commandSetOdometer": "සැතපුම්මානය සකසන්න",
+ "commandGetModemStatus": "මෝඩමය තත්ත්වය ලබා ගන්න",
+ "commandGetDeviceStatus": "ලබා ගැනීමේ තත්ත්වය ගන්න",
+ "commandSetSpeedLimit": "වේග සීමාව සකසන්න",
+ "commandModePowerSaving": "බලශක්ති ඉතිරිකිරීමේ මාදිලිය",
+ "commandModeDeepSleep": "ගැඹුරු නින්ද මාදිලිය",
+ "commandAlarmGeofence": "Geofence එලාම් සකසන්න",
+ "commandAlarmBattery": "බැටරි එලාම් සකසන්න",
+ "commandAlarmSos": "SOS එලාම් සකසන්න",
+ "commandAlarmRemove": "ඉවත් කිරීමේ අනතුරු ඇඟවීම සකසන්න",
+ "commandAlarmClock": "ඔරලෝසු එලාම් සකසන්න",
+ "commandAlarmSpeed": "වේග අනතුරු ඇඟවීම සකසන්න",
+ "commandAlarmFall": "වැටීම අනතුරු ඇඟවීම සකසන්න",
+ "commandAlarmVibration": "කම්පන එලාම් සකසන්න",
+ "commandFrequency": "සංඛ්යාතය",
+ "commandTimezone": "කාල කලාප ඕෆ්සෙට්",
+ "commandMessage": "පණිවිඩය",
+ "commandRadius": "ආරය",
+ "commandEnable": "සබල කරන්න",
+ "commandData": "දත්ත",
+ "commandIndex": "දර්ශකය",
+ "commandPhone": "දුරකථන අංකය",
+ "commandServer": "සේවාදායකයා",
+ "commandPort": "වරාය",
+ "eventAll": "සියලු සිදුවීම්",
+ "eventDeviceOnline": "මාර්ගගත තත්ත්වය",
+ "eventDeviceUnknown": "තත්ත්වය නොදනී",
+ "eventDeviceOffline": "නොබැඳි තත්ත්වය",
+ "eventDeviceInactive": "උපාංගය අක්‍රියයි",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "උපාංගය චලනය",
+ "eventDeviceStopped": "උපාංගය නතර විය",
+ "eventDeviceOverspeed": "වේග සීමාව ඉක්මවා ඇත",
+ "eventDeviceFuelDrop": "ඉන්ධන පහත වැටීම",
+ "eventDeviceFuelIncrease": "Fuel increase",
+ "eventCommandResult": "විධානයේ ප්‍රකාශය",
+ "eventGeofenceEnter": "Geofence ඇතුල් විය",
+ "eventGeofenceExit": "Geofence ඉවත් විය",
+ "eventAlarm": "අනතුරු ඇඟවීම",
+ "eventIgnitionOn": "ගිනි දැල්වීම",
+ "eventIgnitionOff": "ජ්වලනය නිවා දමයි",
+ "eventMaintenance": "නඩත්තු කිරීම අවශ්ය වේ",
+ "eventTextMessage": "කෙටි පණිවිඩයක් ලැබිණි",
+ "eventDriverChanged": "රියදුරු වෙනස් විය",
+ "eventMedia": "Media",
+ "eventsScrollToLast": "අන්තිමට අනුචලනය කරන්න",
+ "eventsSoundEvents": "Sound Events",
+ "eventsSoundAlarms": "Sound Alarms",
+ "alarmGeneral": "ජනරාල්",
+ "alarmSos": "SOS",
+ "alarmVibration": "කම්පනය",
+ "alarmMovement": "චලනය",
+ "alarmLowspeed": "අඩු වේගය",
+ "alarmOverspeed": "අධික වේගය",
+ "alarmFallDown": "වැටෙන්න",
+ "alarmLowPower": "අඩු බලය",
+ "alarmLowBattery": "අඩු බැටරි",
+ "alarmFault": "වරද",
+ "alarmPowerOff": "බලය අක්‍රියයි",
+ "alarmPowerOn": "පවර් ඔන්",
+ "alarmDoor": "දොර",
+ "alarmLock": "අගුළු දමන්න",
+ "alarmUnlock": "අගුළු හරින්න",
+ "alarmGeofence": "භූ වැට",
+ "alarmGeofenceEnter": "Geofence Enter",
+ "alarmGeofenceExit": "Geofence පිටවීම",
+ "alarmGpsAntennaCut": "GPS ඇන්ටෙනා කැපීම",
+ "alarmAccident": "අනතුරක්",
+ "alarmTow": "ඇදගෙන යාම",
+ "alarmIdle": "නිෂ්ක්‍රීයයි",
+ "alarmHighRpm": "ඉහළ ආර්පීඑම්",
+ "alarmHardAcceleration": "දෘඪ ත්වරණය",
+ "alarmHardBraking": "දැඩි තිරිංග",
+ "alarmHardCornering": "Hard Cornering",
+ "alarmLaneChange": "මංතීරු වෙනස් කිරීම",
+ "alarmFatigueDriving": "තෙහෙට්ටුව රිය පැදවීම",
+ "alarmPowerCut": "විදුලි කප්පාදුව",
+ "alarmPowerRestored": "බලය යථා තත්ත්වයට පත් විය",
+ "alarmJamming": "හිරවීම",
+ "alarmTemperature": "උෂ්ණත්වය",
+ "alarmParking": "වාහන නැවැත්වීම",
+ "alarmBonnet": "බොනට්",
+ "alarmFootBrake": "ෆුට් බ්‍රේක්",
+ "alarmFuelLeak": "ඉන්ධන කාන්දු වීම",
+ "alarmTampering": "විකෘති කිරීම",
+ "alarmRemoving": "ඉවත් කිරීම",
+ "notificationType": "දැනුම්දීම් වර්ග",
+ "notificationAlways": "සියලුම උපාංග",
+ "notificationNotificators": "නාලිකා",
+ "notificatorCommand": "Command",
+ "notificatorWeb": "වෙබ්",
+ "notificatorMail": "තැපෑල",
+ "notificatorSms": "කෙටි පණිවිඩ",
+ "notificatorFirebase": "ගිනි පදනම",
+ "notificatorTraccar": "ට්රැකර්",
+ "notificatorTelegram": "විදුලි පණිවුඩ",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "නැවත ධාවනය කරන්න",
+ "reportCombined": "Combined",
+ "reportRoute": "ගමන් කිරීමට",
+ "reportEvents": "සිද්ධි",
+ "reportTrips": "චාරිකා",
+ "reportStops": "නැවතීම්",
+ "reportSummary": "සාරාංශය",
+ "reportDaily": "දෛනික සාරාංශය",
+ "reportChart": "සටහන",
+ "reportConfigure": "වින්‍යාසගත කරන්න",
+ "reportEventTypes": "සිදුවීම් වර්ග",
+ "reportChartType": "ප්‍රස්ථාර වර්ගය",
+ "reportShowMarkers": "සලකුණු පෙන්වන්න",
+ "reportExport": "නිර්යාත",
+ "reportEmail": "ඊමේල් වාර්තාව",
+ "reportSchedule": "Schedule",
+ "reportPeriod": "කාලය",
+ "reportCustom": "අභිරුචි",
+ "reportToday": "අද",
+ "reportYesterday": "කටයුතු",
+ "reportThisWeek": "මෙම සතිය",
+ "reportPreviousWeek": "පසුගිය සතියේ",
+ "reportThisMonth": "මෙම මාසය",
+ "reportPreviousMonth": "පසුගිය මාසය",
+ "reportDeviceName": "ඇතුලත්වේ නම",
+ "reportAverageSpeed": "සාමාන්‍ය වේගය",
+ "reportMaximumSpeed": "උපරිම වේගය",
+ "reportEngineHours": "එන්ජින් පැය",
+ "reportDuration": "කාලසීමාව",
+ "reportStartDate": "ආරම්භක දිනය",
+ "reportStartTime": "ආරම්භක වේලාව",
+ "reportStartAddress": "ආරම්භ ලිපිනය",
+ "reportEndTime": "අවසානය",
+ "reportEndAddress": "අවසාන ලිපිනය",
+ "reportSpentFuel": "වැයවූ ඉන්ධන",
+ "reportStartOdometer": "Odometer ආරම්භය",
+ "reportEndOdometer": "Odometer අවසානය",
+ "statisticsTitle": "සංඛ්යාලේඛන",
+ "statisticsCaptureTime": "අල්ලා ගැනීමේ කාලය",
+ "statisticsActiveUsers": "ක්‍රියාකාරී පරිශීලකයින්",
+ "statisticsActiveDevices": "ක්රියාකාරී උපාංග",
+ "statisticsRequests": "ඉල්ලීම්",
+ "statisticsMessagesReceived": "ලැබුණු පණිවිඩ",
+ "statisticsMessagesStored": "ගබඩා කර ඇති පණිවිඩ",
+ "statisticsGeocoder": "භූ කේත ඉල්ලීම්",
+ "statisticsGeolocation": "භූගෝලීය ඉල්ලීම්",
+ "categoryArrow": "ඊතලය",
+ "categoryDefault": "පෙරනිමි",
+ "categoryAnimal": "සතුන්",
+ "categoryBicycle": "පාපැදිය",
+ "categoryBoat": "බෝට්ටුව",
+ "categoryBus": "බස්",
+ "categoryCar": "මෝටර් රථ",
+ "categoryCamper": "Camper",
+ "categoryCrane": "දොඹකරය",
+ "categoryHelicopter": "හෙලිකොප්ටරය",
+ "categoryMotorcycle": "යතුරුපැදිය",
+ "categoryOffroad": "පාරෙන් පිට",
+ "categoryPerson": "පුද්ගලයා",
+ "categoryPickup": "අවුලා ගන්න",
+ "categoryPlane": "ගුවන්යානය",
+ "categoryShip": "නැව",
+ "categoryTractor": "ට්රැක්ටරය",
+ "categoryTrain": "දුම්රිය",
+ "categoryTram": "ට්‍රෑම් රථය",
+ "categoryTrolleybus": "ට්රොලිබස්",
+ "categoryTruck": "ට්රක් රථය",
+ "categoryVan": "වෑන්",
+ "categoryScooter": "ස්කූටරය",
+ "maintenanceStart": "ආරම්භය",
+ "maintenancePeriod": "කාලය"
+} \ No newline at end of file
diff --git a/src/resources/l10n/sk.json b/src/resources/l10n/sk.json
new file mode 100644
index 00000000..e4aebedc
--- /dev/null
+++ b/src/resources/l10n/sk.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "Načítava...",
+ "sharedHide": "Schovať",
+ "sharedSave": "Uložiť",
+ "sharedUpload": "Upload",
+ "sharedSet": "Nastaviť",
+ "sharedCancel": "Zrušiť",
+ "sharedCopy": "Copy",
+ "sharedAdd": "Pridať",
+ "sharedEdit": "Upraviť",
+ "sharedRemove": "Odstrániť",
+ "sharedRemoveConfirm": "Odstrániť položku?",
+ "sharedNoData": "No data",
+ "sharedSubject": "Subject",
+ "sharedYes": "Yes",
+ "sharedNo": "No",
+ "sharedKm": "Km",
+ "sharedMi": "mi",
+ "sharedNmi": "nmi",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "kn",
+ "sharedKmh": "Km/h",
+ "sharedMph": "mph",
+ "sharedHour": "Hodina",
+ "sharedMinute": "Minúta",
+ "sharedSecond": "Sekunda",
+ "sharedDays": "dni",
+ "sharedHours": "hodiny",
+ "sharedMinutes": "minúty",
+ "sharedDecimalDegrees": "Desatinné stupne",
+ "sharedDegreesDecimalMinutes": "Stupne desatinných minút",
+ "sharedDegreesMinutesSeconds": "Stupne desatinných sekúnd",
+ "sharedName": "Meno",
+ "sharedDescription": "Popis",
+ "sharedSearch": "Hľadať",
+ "sharedIconScale": "Icon Scale",
+ "sharedGeofence": "Geofence",
+ "sharedGeofences": "Geofences",
+ "sharedCreateGeofence": "Create Geofence",
+ "sharedNotifications": "Notifikácie",
+ "sharedNotification": "Notifikácia",
+ "sharedAttributes": "Atribúty",
+ "sharedAttribute": "Atribút",
+ "sharedDrivers": "Vodiči",
+ "sharedDriver": "Vodič",
+ "sharedArea": "Oblasť",
+ "sharedSound": "Notifikačný zvuk",
+ "sharedType": "Typ",
+ "sharedDistance": "Vzdialenosť",
+ "sharedHourAbbreviation": "h",
+ "sharedMinuteAbbreviation": "m",
+ "sharedSecondAbbreviation": "s",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "l",
+ "sharedGallonAbbreviation": "gal",
+ "sharedLiter": "Liter",
+ "sharedImpGallon": "Britský galón",
+ "sharedUsGallon": "Americký galón",
+ "sharedLiterPerHourAbbreviation": "l/h",
+ "sharedGetMapState": "Získať mapu štátu",
+ "sharedComputedAttribute": "Computed Attribute",
+ "sharedComputedAttributes": "Computed Attributes",
+ "sharedCheckComputedAttribute": "Check Computed Attribute",
+ "sharedExpression": "Výraz",
+ "sharedDevice": "Zariadenie",
+ "sharedTest": "Test",
+ "sharedTestNotification": "Pošli skúšobnú notifikáciu",
+ "sharedTestNotificators": "Test Channels",
+ "sharedTestExpression": "Test Expression",
+ "sharedCalendar": "Kalendár",
+ "sharedCalendars": "Kalendáre",
+ "sharedFile": "Súbor",
+ "sharedSearchDevices": "Search Devices",
+ "sharedSortBy": "Sort By",
+ "sharedFilterMap": "Filter on Map",
+ "sharedSelectFile": "Vyberte súbor",
+ "sharedPhone": "Telefón",
+ "sharedRequired": "Požadované",
+ "sharedPreferences": "Preferencie",
+ "sharedPermissions": "Povolenia",
+ "sharedConnections": "Connections",
+ "sharedExtra": "Extra",
+ "sharedPrimary": "Primary",
+ "sharedSecondary": "Secondary",
+ "sharedTypeString": "Reťazec",
+ "sharedTypeNumber": "Číslo",
+ "sharedTypeBoolean": "Binárna hodnota",
+ "sharedTimezone": "Časová zóna",
+ "sharedInfoTitle": "Informácie",
+ "sharedSavedCommand": "Uložený príkaz",
+ "sharedSavedCommands": "Uložené príkazy",
+ "sharedNew": "Nový...",
+ "sharedShowAddress": "Zobraziť adresu",
+ "sharedShowDetails": "More Details",
+ "sharedDisabled": "Vypnuté",
+ "sharedMaintenance": "Maintenance",
+ "sharedDeviceAccumulators": "Accumulators",
+ "sharedAlarms": "Upozornenia",
+ "sharedLocation": "Location",
+ "sharedImport": "Import",
+ "sharedColumns": "Columns",
+ "sharedDropzoneText": "Drag and drop a file here or click",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "Simple",
+ "calendarRecurrence": "Recurrence",
+ "calendarOnce": "Once",
+ "calendarDaily": "Daily",
+ "calendarWeekly": "Weekly",
+ "calendarMonthly": "Monthly",
+ "calendarDays": "Days",
+ "calendarSunday": "Sunday",
+ "calendarMonday": "Monday",
+ "calendarTuesday": "Tuesday",
+ "calendarWednesday": "Wednesday",
+ "calendarThursday": "Thursday",
+ "calendarFriday": "Friday",
+ "calendarSaturday": "Saturday",
+ "attributeShowGeofences": "Show Geofences",
+ "attributeSpeedLimit": "Limit rýchlosti",
+ "attributeFuelDropThreshold": "Fuel Drop Threshold",
+ "attributeFuelIncreaseThreshold": "Fuel Increase Threshold",
+ "attributePolylineDistance": "Polyline Distance",
+ "attributeReportIgnoreOdometer": "Report: Ignoruj kilometrovník",
+ "attributeWebReportColor": "Web: Farba reportu",
+ "attributeDevicePassword": "Heslo zaridenia",
+ "attributeDeviceImage": "Device Image",
+ "attributeDeviceInactivityStart": "Device Inactivity Start",
+ "attributeDeviceInactivityPeriod": "Device Inactivity Period",
+ "attributeProcessingCopyAttributes": "Processing: Copy Attributes",
+ "attributeColor": "Farba",
+ "attributeWebLiveRouteLength": "Web: Live Route Length",
+ "attributeWebSelectZoom": "Web: Zoom On Select",
+ "attributeWebMaxZoom": "Web: Maximálne priblíženie",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Pushover User Key",
+ "attributePushoverDeviceNames": "Pushover Device Names",
+ "attributeMailSmtpHost": "E-Mail: SMTP Host",
+ "attributeMailSmtpPort": "E-Mail: SMTP Port",
+ "attributeMailSmtpStarttlsEnable": "E-Mail: SMTP STARTTLS Zapnuté",
+ "attributeMailSmtpStarttlsRequired": "E-Mail: SMTP STARTTLS Povinná",
+ "attributeMailSmtpSslEnable": "Mail: Povolenie SMTP SSL",
+ "attributeMailSmtpSslTrust": "Mail: SMTP SSL Trust",
+ "attributeMailSmtpSslProtocols": "Mail: SMTP SSL Protokoly",
+ "attributeMailSmtpFrom": "Mail: SMTP Od",
+ "attributeMailSmtpAuth": "Mail: SMTP Auth Enable",
+ "attributeMailSmtpUsername": "Mail: SMTP Používateľské meno",
+ "attributeMailSmtpPassword": "Mail: SMTP Heslo",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: Disable Attributes",
+ "attributeUiDisableGroups": "UI: Disable Groups",
+ "attributeUiDisableEvents": "UI: Zakázať udalosti",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "UI: Zakázať vodičov",
+ "attributeUiDisableComputedAttributes": "UI: Disable Computed Attributes",
+ "attributeUiDisableCalendars": "UI: Zakázať kalendáre",
+ "attributeUiDisableMaintenance": "UI: Disable Maintenance",
+ "attributeUiHidePositionAttributes": "UI: Hide Position Attributes",
+ "attributeUiDisableLoginLanguage": "UI: Disable Login Language",
+ "attributeNotificationTokens": "Notification Tokens",
+ "attributePopupInfo": "Popup Info",
+ "errorTitle": "Chyba",
+ "errorGeneral": "Invalid parameters or constraints violation",
+ "errorConnection": "Chyba pripojenia",
+ "errorSocket": "Chyba pripojenia web konektora",
+ "errorZero": "Can't be zero",
+ "userEmail": "E-mail",
+ "userPassword": "Heslo",
+ "userAdmin": "Admin",
+ "userRemember": "Zapamätať",
+ "userExpirationTime": "Uplynutie platnosti",
+ "userDeviceLimit": "Limit zariadenia",
+ "userUserLimit": "Obmedzenie používateľov",
+ "userDeviceReadonly": "Zariadenie len na čítanie",
+ "userLimitCommands": "Limit Commands",
+ "userDisableReports": "Disable Reports",
+ "userFixedEmail": "No Email Change",
+ "userToken": "Token",
+ "userDeleteAccount": "Delete Account",
+ "userTemporary": "Temporary",
+ "loginTitle": "Prihlásenie",
+ "loginLanguage": "Jazyk",
+ "loginReset": "Reset Password",
+ "loginRegister": "Registrovať",
+ "loginLogin": "Prihlásenie",
+ "loginOpenId": "Login with OpenID",
+ "loginFailed": "Nesprávna e-mailová adresa alebo heslo",
+ "loginCreated": "Nový užívateľ sa zaregistroval",
+ "loginResetSuccess": "Check your email",
+ "loginUpdateSuccess": "New password is set",
+ "loginLogout": "Odhlásiť",
+ "loginLogo": "Logo",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "Zariadenia a Status",
+ "deviceSelected": "Selected Device",
+ "deviceTitle": "Zariadena",
+ "devicePrimaryInfo": "Device Title",
+ "deviceSecondaryInfo": "Device Detail",
+ "deviceIdentifier": "Identifikátor",
+ "deviceModel": "Model",
+ "deviceContact": "Kontakt",
+ "deviceCategory": "Kategória",
+ "deviceLastUpdate": "Posledný update",
+ "deviceCommand": "Príkaz",
+ "deviceFollow": "Nasleduj",
+ "deviceTotalDistance": "Celková vzdialenosť",
+ "deviceStatus": "Status",
+ "deviceStatusOnline": "Online",
+ "deviceStatusOffline": "Offline",
+ "deviceStatusUnknown": "Neznáme",
+ "deviceRegisterFirst": "Register your first device",
+ "deviceIdentifierHelp": "IMEI, serial number or other id. It has to match the identifier device reports to the server.",
+ "deviceShare": "Share Device",
+ "groupDialog": "Skupina",
+ "groupParent": "Skupina",
+ "groupNoGroup": "Žiadna skupina",
+ "settingsTitle": "Nastavenia",
+ "settingsUser": "Účet",
+ "settingsGroups": "Skupiny",
+ "settingsServer": "Server",
+ "settingsUsers": "Používatelia",
+ "settingsDistanceUnit": "Jednotka vzdialenosti",
+ "settingsAltitudeUnit": "Altitude Unit",
+ "settingsSpeedUnit": "Jednotka rýchlosti",
+ "settingsVolumeUnit": "Jednotka objemu",
+ "settingsTwelveHourFormat": "12-hodinový formát",
+ "settingsCoordinateFormat": "Formát koordinátov",
+ "settingsServerVersion": "Server Version",
+ "settingsAppVersion": "App Version",
+ "settingsConnection": "Connection",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "Správy",
+ "reportScheduled": "Scheduled Reports",
+ "reportDevice": "Zariadenie",
+ "reportGroup": "Skupina",
+ "reportFrom": "Od",
+ "reportTo": "Do",
+ "reportShow": "Zobraziť",
+ "reportClear": "Vyčistiť",
+ "linkGoogleMaps": "Google Maps",
+ "linkAppleMaps": "Apple Maps",
+ "linkStreetView": "Street View",
+ "positionFixTime": "Fix Time",
+ "positionDeviceTime": "Device Time",
+ "positionServerTime": "Server Time",
+ "positionValid": "Platný",
+ "positionAccuracy": "Presnosť",
+ "positionLatitude": "Šírka",
+ "positionLongitude": "Dĺžka",
+ "positionAltitude": "Výška",
+ "positionSpeed": "Rýchlosť jazdy",
+ "positionCourse": "Kurz",
+ "positionAddress": "Adresa",
+ "positionProtocol": "Protokol",
+ "positionDistance": "Vzdialenosť",
+ "positionRpm": "OZM",
+ "positionFuel": "Palivo",
+ "positionPower": "Napätie",
+ "positionBattery": "Baterka",
+ "positionRaw": "Raw",
+ "positionIndex": "Index",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "Satelity",
+ "positionSatVisible": "Viditeľné satelity",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "Roaming",
+ "positionEvent": "Udalosť",
+ "positionAlarm": "Upozornenie",
+ "positionStatus": "Stav",
+ "positionOdometer": "Kilometrovník",
+ "positionServiceOdometer": "Service Odometer",
+ "positionTripOdometer": "Trip Odometer",
+ "positionHours": "Hodiny",
+ "positionSteps": "Steps",
+ "positionInput": "Vstup",
+ "positionHeartRate": "Heart Rate",
+ "positionOutput": "Výstup",
+ "positionBatteryLevel": "Stav batérie",
+ "positionFuelConsumption": "Spotreba paliva",
+ "positionRfid": "RFID",
+ "positionVersionFw": "Verzia firmvéru",
+ "positionVersionHw": "Verzia hardvéru",
+ "positionIgnition": "Zapaľovanie",
+ "positionFlags": "Značky",
+ "positionCharge": "Charge",
+ "positionIp": "IP",
+ "positionArchive": "Archív",
+ "positionVin": "VIN",
+ "positionApproximate": "Approximate",
+ "positionThrottle": "Throttle",
+ "positionMotion": "Pohyb",
+ "positionArmed": "Armed",
+ "positionAcceleration": "Zrýchlenie",
+ "positionTemp": "Temperature",
+ "positionDeviceTemp": "Teplota zariadenia",
+ "positionCoolantTemp": "Coolant Temperature",
+ "positionOperator": "Operátor",
+ "positionCommand": "Príkaz",
+ "positionBlocked": "Blokované",
+ "positionDtcs": "DTCs",
+ "positionObdSpeed": "OBD rýchlosť",
+ "positionObdOdometer": "OBD kilometrovník",
+ "positionDrivingTime": "Driving Time",
+ "positionDriverUniqueId": "Unikátne ID vodiča",
+ "positionCard": "Card",
+ "positionImage": "Image",
+ "positionVideo": "Video",
+ "positionAudio": "Audio",
+ "serverTitle": "Nastavenie servera",
+ "serverZoom": "Zoom",
+ "serverRegistration": "Registrácia",
+ "serverReadonly": "Iba na čítanie",
+ "serverForceSettings": "Nastavenie sily",
+ "serverAnnouncement": "Announcement",
+ "serverName": "Server Name",
+ "serverDescription": "Server Description",
+ "serverColorPrimary": "Color Primary",
+ "serverColorSecondary": "Color Secondary",
+ "serverLogo": "Logo Image",
+ "serverLogoInverted": "Inverted Logo Image",
+ "serverChangeDisable": "Disable Server Change",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "Mapa",
+ "mapActive": "Active Maps",
+ "mapOverlay": "Map Overlay",
+ "mapOverlayCustom": "Custom Overlay",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API Key",
+ "mapOpenWeatherClouds": "OpenWeather Clouds",
+ "mapOpenWeatherPrecipitation": "OpenWeather Precipitation",
+ "mapOpenWeatherPressure": "OpenWeather Pressure",
+ "mapOpenWeatherWind": "OpenWeather Wind",
+ "mapOpenWeatherTemperature": "OpenWeather Temperature",
+ "mapLayer": "Mapové vrstvy",
+ "mapCustom": "Custom (XYZ)",
+ "mapCustomArcgis": "Custom (ArcGIS)",
+ "mapCustomLabel": "Custom map",
+ "mapCarto": "Carto Basemaps",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Klúč Bing Maps",
+ "mapBingRoad": "Bing Maps Road",
+ "mapBingAerial": "Bing Maps Arial",
+ "mapBingHybrid": "Bing Maps Hybrid",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Yandex mapa",
+ "mapYandexSat": "Yandex satelit",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox Streets",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox Outdoors",
+ "mapMapboxSatellite": "Mapbox Satellite",
+ "mapMapboxKey": "Mapbox Access Token",
+ "mapMapTilerBasic": "MapTiler Basic",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "MapTiler API Key",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ Access Token",
+ "mapTomTomBasic": "TomTom Basic",
+ "mapTomTomFlow": "TomTom Traffic Flow",
+ "mapTomTomIncidents": "TomTom Traffic Incidents",
+ "mapTomTomKey": "TomTom API Key",
+ "mapHereBasic": "Here Basic",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satellite",
+ "mapHereFlow": "Here Traffic Flow",
+ "mapHereKey": "Here API Key",
+ "mapShapePolygon": "Polygón",
+ "mapShapeCircle": "Kruh",
+ "mapShapePolyline": "Lomená čiara",
+ "mapLiveRoutes": "Live Routes",
+ "mapDirection": "Show Direction",
+ "mapCurrentLocation": "Current Location",
+ "mapPoiLayer": "POI Layer",
+ "mapClustering": "Markers Clustering",
+ "mapOnSelect": "Show Map on Selection",
+ "mapDefault": "Default Map",
+ "stateTitle": "Parametre",
+ "stateName": "Parameter",
+ "stateValue": "Hodnota",
+ "commandTitle": "Príkaz",
+ "commandSend": "Odoslať",
+ "commandSent": "Príkaz odoslaný",
+ "commandQueued": "Command queued",
+ "commandUnit": "Jednotka",
+ "commandCustom": "Vlastný príkaz",
+ "commandDeviceIdentification": "Identifikácia zariadenia",
+ "commandPositionSingle": "Jednoduché podávanie správ",
+ "commandPositionPeriodic": "Pravidelné podávanie správ",
+ "commandPositionStop": "Zastavte podávanie správ",
+ "commandEngineStop": "Zastavenie motora",
+ "commandEngineResume": "Spustenie motora",
+ "commandAlarmArm": "Nastaviť upozornenie",
+ "commandAlarmDisarm": "Zrušiť upozornenie",
+ "commandAlarmDismiss": "Dismiss Alarm",
+ "commandSetTimezone": "Nastaviť časovú zónu",
+ "commandRequestPhoto": "Poslať fotku",
+ "commandPowerOff": "Power Off Device",
+ "commandRebootDevice": "Rebootovať zariadenie",
+ "commandFactoryReset": "Factory Reset",
+ "commandSendSms": "Postať SMS",
+ "commandSendUssd": "Postať USSD",
+ "commandSosNumber": "Nastaviť čislo SOS",
+ "commandSilenceTime": "Nastav tichý čas",
+ "commandSetPhonebook": "Nastav telefónny zoznam",
+ "commandVoiceMessage": "Hlasové správy",
+ "commandOutputControl": "Výstupná kontrola",
+ "commandVoiceMonitoring": "Voice Monitoring",
+ "commandSetAgps": "Nastaviť AGPS",
+ "commandSetIndicator": "Nastavte ukazovateľ",
+ "commandConfiguration": "Konfigurácia",
+ "commandGetVersion": "Získať verziu",
+ "commandFirmwareUpdate": "Aktualizovať firmvér",
+ "commandSetConnection": "Nastaviť spojenie",
+ "commandSetOdometer": "Nastaviť kilometrovník",
+ "commandGetModemStatus": "Získať stav modemu",
+ "commandGetDeviceStatus": "Získať stav zariadenia",
+ "commandSetSpeedLimit": "Set Speed Limit",
+ "commandModePowerSaving": "Power Saving Mode",
+ "commandModeDeepSleep": "Deep Sleep Mode",
+ "commandAlarmGeofence": "Set Geofence Alarm",
+ "commandAlarmBattery": "Set Battery Alarm",
+ "commandAlarmSos": "Set SOS Alarm",
+ "commandAlarmRemove": "Set Remove Alarm",
+ "commandAlarmClock": "Set Clock Alarm",
+ "commandAlarmSpeed": "Set Speed Alarm",
+ "commandAlarmFall": "Set Fall Alarm",
+ "commandAlarmVibration": "Set Vibration Alarm",
+ "commandFrequency": "Frekvencia",
+ "commandTimezone": "Časová zóna Offset",
+ "commandMessage": "Správa",
+ "commandRadius": "Polomer",
+ "commandEnable": "Povoliť",
+ "commandData": "Dáta",
+ "commandIndex": "Index",
+ "commandPhone": "Telefónne číslo",
+ "commandServer": "Server",
+ "commandPort": "Port",
+ "eventAll": "Všetky akcie",
+ "eventDeviceOnline": "Stav online",
+ "eventDeviceUnknown": "Status unknown",
+ "eventDeviceOffline": "Stav offline",
+ "eventDeviceInactive": "Device inactive",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "Zariadenie sa hýbe",
+ "eventDeviceStopped": "Zariadenie zastavilo",
+ "eventDeviceOverspeed": "Speed limit exceeded",
+ "eventDeviceFuelDrop": "Fuel drop",
+ "eventDeviceFuelIncrease": "Fuel increase",
+ "eventCommandResult": "Výsledok príkazu",
+ "eventGeofenceEnter": "Geofence entered",
+ "eventGeofenceExit": "Geofence exited",
+ "eventAlarm": "Upozornenie",
+ "eventIgnitionOn": "Ignition on",
+ "eventIgnitionOff": "Ignition off",
+ "eventMaintenance": "Vyžaduje sa údržba",
+ "eventTextMessage": "Prijatá textová správa",
+ "eventDriverChanged": "Zmena vodiča",
+ "eventMedia": "Media",
+ "eventsScrollToLast": "Scroll To Last",
+ "eventsSoundEvents": "Sound Events",
+ "eventsSoundAlarms": "Sound Alarms",
+ "alarmGeneral": "Všeobecné",
+ "alarmSos": "SOS",
+ "alarmVibration": "Vibrácia",
+ "alarmMovement": "Pobyb",
+ "alarmLowspeed": "Low Speed",
+ "alarmOverspeed": "Prekročenie rýchlosti",
+ "alarmFallDown": "Fall Down",
+ "alarmLowPower": "Slabý prúd",
+ "alarmLowBattery": "Slabá batéria",
+ "alarmFault": "Chyba",
+ "alarmPowerOff": "Power Off",
+ "alarmPowerOn": "Power On",
+ "alarmDoor": "Door",
+ "alarmLock": "Zamknúť",
+ "alarmUnlock": "Odomknúť",
+ "alarmGeofence": "Geofence",
+ "alarmGeofenceEnter": "Geofence Enter",
+ "alarmGeofenceExit": "Geofence Exit",
+ "alarmGpsAntennaCut": "GPS Antenna Cut",
+ "alarmAccident": "Accident",
+ "alarmTow": "Tow",
+ "alarmIdle": "Idle",
+ "alarmHighRpm": "High RPM",
+ "alarmHardAcceleration": "Hard Acceleration",
+ "alarmHardBraking": "Hard Braking",
+ "alarmHardCornering": "Hard Cornering",
+ "alarmLaneChange": "Lane Change",
+ "alarmFatigueDriving": "Fatigue Driving",
+ "alarmPowerCut": "Power Cut",
+ "alarmPowerRestored": "Napájanie obnovené",
+ "alarmJamming": "Jamming",
+ "alarmTemperature": "Teplota",
+ "alarmParking": "Parkovanie",
+ "alarmBonnet": "Bonnet",
+ "alarmFootBrake": "Foot Brake",
+ "alarmFuelLeak": "Fuel Leak",
+ "alarmTampering": "Tampering",
+ "alarmRemoving": "Removing",
+ "notificationType": "Typ notifikácie",
+ "notificationAlways": "Všetky zariadenia",
+ "notificationNotificators": "Channels",
+ "notificatorCommand": "Command",
+ "notificatorWeb": "Web",
+ "notificatorMail": "Mail",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Replay",
+ "reportCombined": "Combined",
+ "reportRoute": "Cesta",
+ "reportEvents": "Udalosti",
+ "reportTrips": "Cesty",
+ "reportStops": "Zastávky",
+ "reportSummary": "Zhrnutie",
+ "reportDaily": "Daily Summary",
+ "reportChart": "Graf",
+ "reportConfigure": "Konfigurácia",
+ "reportEventTypes": "Typy udalstí",
+ "reportChartType": "Typ grafu",
+ "reportShowMarkers": "Zobraziť značky",
+ "reportExport": "Export",
+ "reportEmail": "Email Report",
+ "reportSchedule": "Schedule",
+ "reportPeriod": "Period",
+ "reportCustom": "Vlastné",
+ "reportToday": "Dnes",
+ "reportYesterday": "Včera",
+ "reportThisWeek": "Tento týždeň",
+ "reportPreviousWeek": "Minulý týždeň",
+ "reportThisMonth": "Tento mesiac",
+ "reportPreviousMonth": "Minulý mesiac",
+ "reportDeviceName": "Meno zariadenia",
+ "reportAverageSpeed": "Priemerná rýchlosť",
+ "reportMaximumSpeed": "Maximálna rýchlosť",
+ "reportEngineHours": "Prevádzkové hodiny motora",
+ "reportDuration": "Trvanie",
+ "reportStartDate": "Start Date",
+ "reportStartTime": "Čas spustenia",
+ "reportStartAddress": "Počiatočná adresa",
+ "reportEndTime": "Čas ukončenia",
+ "reportEndAddress": "Koncová adresa",
+ "reportSpentFuel": "Spotrebované palivo",
+ "reportStartOdometer": "Odometer Start",
+ "reportEndOdometer": "Odometer End",
+ "statisticsTitle": "Štatistika",
+ "statisticsCaptureTime": "Zachyť čas",
+ "statisticsActiveUsers": "Aktívny používatelia",
+ "statisticsActiveDevices": "Aktívne zariadenia",
+ "statisticsRequests": "Požiadavka",
+ "statisticsMessagesReceived": "Správa doručená",
+ "statisticsMessagesStored": "Správa uložená",
+ "statisticsGeocoder": "Geocoder Requests",
+ "statisticsGeolocation": "Geolocation Requests",
+ "categoryArrow": "Šípka",
+ "categoryDefault": "Základné",
+ "categoryAnimal": "Zviera",
+ "categoryBicycle": "Bicykel",
+ "categoryBoat": "Loď",
+ "categoryBus": "Autobus",
+ "categoryCar": "Auto",
+ "categoryCamper": "Camper",
+ "categoryCrane": "Žeriav",
+ "categoryHelicopter": "Helikoptéra",
+ "categoryMotorcycle": "Motocykel",
+ "categoryOffroad": "Offroad",
+ "categoryPerson": "Osoba",
+ "categoryPickup": "Pickup",
+ "categoryPlane": "Lietadlo",
+ "categoryShip": "Loď",
+ "categoryTractor": "Traktor",
+ "categoryTrain": "Train",
+ "categoryTram": "Tram",
+ "categoryTrolleybus": "Trolleybus",
+ "categoryTruck": "Nákladné auto",
+ "categoryVan": "Dodávka",
+ "categoryScooter": "Scooter",
+ "maintenanceStart": "Start",
+ "maintenancePeriod": "Period"
+} \ No newline at end of file
diff --git a/src/resources/l10n/sl.json b/src/resources/l10n/sl.json
new file mode 100644
index 00000000..3c55e91b
--- /dev/null
+++ b/src/resources/l10n/sl.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "Nalagam...",
+ "sharedHide": "Skrij",
+ "sharedSave": "Shrani",
+ "sharedUpload": "Naloži",
+ "sharedSet": "Nastavi",
+ "sharedCancel": "Prekini",
+ "sharedCopy": "Copy",
+ "sharedAdd": "Dodaj",
+ "sharedEdit": "Uredi",
+ "sharedRemove": "Odstrani",
+ "sharedRemoveConfirm": "Odstranim zapis?",
+ "sharedNoData": "Ni podatkov",
+ "sharedSubject": "Subject",
+ "sharedYes": "Da",
+ "sharedNo": "Ne",
+ "sharedKm": "km",
+ "sharedMi": "mi",
+ "sharedNmi": "nmi",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "kn",
+ "sharedKmh": "km/h",
+ "sharedMph": "mph",
+ "sharedHour": "Ura",
+ "sharedMinute": "Minuta",
+ "sharedSecond": "Sekunda",
+ "sharedDays": "dni",
+ "sharedHours": "ur",
+ "sharedMinutes": "minut",
+ "sharedDecimalDegrees": "Decimalne stopinje",
+ "sharedDegreesDecimalMinutes": "Stopinje Decimalne Minute",
+ "sharedDegreesMinutesSeconds": "Stopinje Minute Sekunde",
+ "sharedName": "Ime",
+ "sharedDescription": "Opis",
+ "sharedSearch": "Išči",
+ "sharedIconScale": "Icon Scale",
+ "sharedGeofence": "Omejeno območje",
+ "sharedGeofences": "Omejena območja",
+ "sharedCreateGeofence": "Ustvari omejeno območje",
+ "sharedNotifications": "Obvestila",
+ "sharedNotification": "Obvestilo",
+ "sharedAttributes": "Atributi",
+ "sharedAttribute": "Atribut",
+ "sharedDrivers": "Vozniki",
+ "sharedDriver": "Voznik",
+ "sharedArea": "Območje",
+ "sharedSound": "Zvok obvestila",
+ "sharedType": "Tip",
+ "sharedDistance": "Razdalja",
+ "sharedHourAbbreviation": "h",
+ "sharedMinuteAbbreviation": "m",
+ "sharedSecondAbbreviation": "s",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "l",
+ "sharedGallonAbbreviation": "gal",
+ "sharedLiter": "Liter",
+ "sharedImpGallon": "Imp. Gallon",
+ "sharedUsGallon": "U.S. Gallon",
+ "sharedLiterPerHourAbbreviation": "l/h",
+ "sharedGetMapState": "Pridobi stanje zemljevida",
+ "sharedComputedAttribute": "Izračunan atribut",
+ "sharedComputedAttributes": "Izračunani atributi",
+ "sharedCheckComputedAttribute": "Preveri izračunan atribut",
+ "sharedExpression": "Izraz",
+ "sharedDevice": "Naprava",
+ "sharedTest": "Preizkus",
+ "sharedTestNotification": "Pošlji testno obvestilo",
+ "sharedTestNotificators": "Preizkusi kanale",
+ "sharedTestExpression": "Preizkusi izraz",
+ "sharedCalendar": "Koledar",
+ "sharedCalendars": "Koledarji",
+ "sharedFile": "Datoteka",
+ "sharedSearchDevices": "Išči po napravah",
+ "sharedSortBy": "Razvrsti po",
+ "sharedFilterMap": "Filtriraj na zemljevidu",
+ "sharedSelectFile": "Izberi datoteko",
+ "sharedPhone": "Telefon",
+ "sharedRequired": "Obvezno",
+ "sharedPreferences": "Nastavitve",
+ "sharedPermissions": "Dovoljenja",
+ "sharedConnections": "Povezave",
+ "sharedExtra": "Dodatno",
+ "sharedPrimary": "Primarno",
+ "sharedSecondary": "Sekundarno",
+ "sharedTypeString": "Tekst",
+ "sharedTypeNumber": "Številka",
+ "sharedTypeBoolean": "Binarna vrednost",
+ "sharedTimezone": "Časovni pas",
+ "sharedInfoTitle": "Informacije",
+ "sharedSavedCommand": "Shrani ukaz",
+ "sharedSavedCommands": "Shranjeni ukazi",
+ "sharedNew": "Nov...",
+ "sharedShowAddress": "Prikaži naslov",
+ "sharedShowDetails": "Več podrobnosti",
+ "sharedDisabled": "Onemogočen",
+ "sharedMaintenance": "Vzdrževanje",
+ "sharedDeviceAccumulators": "Seštevki",
+ "sharedAlarms": "Alarmi",
+ "sharedLocation": "Lokacija",
+ "sharedImport": "Uvozi",
+ "sharedColumns": "Stolpci",
+ "sharedDropzoneText": "Povlecite in spustite datoteko sem ali kliknite",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "Preprost",
+ "calendarRecurrence": "Ponovitev",
+ "calendarOnce": "Enkrat",
+ "calendarDaily": "Dnevno",
+ "calendarWeekly": "Tedensko",
+ "calendarMonthly": "Mesečno",
+ "calendarDays": "Dnevi",
+ "calendarSunday": "Nedelja",
+ "calendarMonday": "Ponedeljek",
+ "calendarTuesday": "Torek",
+ "calendarWednesday": "Sreda",
+ "calendarThursday": "Četrtek",
+ "calendarFriday": "Petek",
+ "calendarSaturday": "Sobota",
+ "attributeShowGeofences": "Prikaži omejena območja",
+ "attributeSpeedLimit": "Omejitev hitrosti",
+ "attributeFuelDropThreshold": "Mejna vrednost padca goriva",
+ "attributeFuelIncreaseThreshold": "Fuel Increase Threshold",
+ "attributePolylineDistance": "Polyline Distance",
+ "attributeReportIgnoreOdometer": "Poročilo: Prezri stanje števca",
+ "attributeWebReportColor": "Splet: Barva poročila",
+ "attributeDevicePassword": "Geslo naprave",
+ "attributeDeviceImage": "Slika naprave",
+ "attributeDeviceInactivityStart": "Začetek neaktivnosti naprave",
+ "attributeDeviceInactivityPeriod": "Obdobje neaktivnosti naprave",
+ "attributeProcessingCopyAttributes": "Processing: Copy Attributes",
+ "attributeColor": "Barva",
+ "attributeWebLiveRouteLength": "Web: Live Route Length",
+ "attributeWebSelectZoom": "Web: Zoom On Select",
+ "attributeWebMaxZoom": "Web: Maximum Zoom",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Pushover User Key",
+ "attributePushoverDeviceNames": "Pushover Device Names",
+ "attributeMailSmtpHost": "Mail: SMTP Host",
+ "attributeMailSmtpPort": "Mail: SMTP Port",
+ "attributeMailSmtpStarttlsEnable": "Mail: SMTP STARTTLS Enable",
+ "attributeMailSmtpStarttlsRequired": "Mail: SMTP STARTTLS Required",
+ "attributeMailSmtpSslEnable": "Mail: SMTP SSL Enable",
+ "attributeMailSmtpSslTrust": "Mail: SMTP SSL Trust",
+ "attributeMailSmtpSslProtocols": "Mail: SMTP SSL Protocols",
+ "attributeMailSmtpFrom": "Mail: SMTP From",
+ "attributeMailSmtpAuth": "Mail: SMTP Auth Enable",
+ "attributeMailSmtpUsername": "Mail: SMTP Username",
+ "attributeMailSmtpPassword": "Mail: SMTP Password",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: Disable Attributes",
+ "attributeUiDisableGroups": "UI: Disable Groups",
+ "attributeUiDisableEvents": "UI: Disable Events",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "UI: Disable Drivers",
+ "attributeUiDisableComputedAttributes": "UI: Disable Computed Attributes",
+ "attributeUiDisableCalendars": "UI: Disable Calendars",
+ "attributeUiDisableMaintenance": "UI: Disable Maintenance",
+ "attributeUiHidePositionAttributes": "UI: Hide Position Attributes",
+ "attributeUiDisableLoginLanguage": "UI: Disable Login Language",
+ "attributeNotificationTokens": "Notification Tokens",
+ "attributePopupInfo": "Podatki v pojavnem oknu",
+ "errorTitle": "Napaka",
+ "errorGeneral": "Neveljavni parametri ali kršitev omejitve",
+ "errorConnection": "Napaka v povezavi",
+ "errorSocket": "Napaka v povezavi web socket",
+ "errorZero": "Ne sme biti 0",
+ "userEmail": "E-Pošta",
+ "userPassword": "Geslo",
+ "userAdmin": "Admin",
+ "userRemember": "Zapomni si",
+ "userExpirationTime": "Poteče",
+ "userDeviceLimit": "Omejitev naprav",
+ "userUserLimit": "Omejitev uporabnikov",
+ "userDeviceReadonly": "Naprava samo za branje",
+ "userLimitCommands": "Omeji ukaze",
+ "userDisableReports": "Onemogoči poročila",
+ "userFixedEmail": "Prepreči spremembo e-pošte",
+ "userToken": "Žeton",
+ "userDeleteAccount": "Izbriši račun",
+ "userTemporary": "Temporary",
+ "loginTitle": "Prijava",
+ "loginLanguage": "Jezik",
+ "loginReset": "Ponastavi geslo",
+ "loginRegister": "Registracija",
+ "loginLogin": "Prijava",
+ "loginOpenId": "Prijavi se z OpenID",
+ "loginFailed": "Nepravilna e-pošta ali geslo",
+ "loginCreated": "Nov uporabnik je registriran",
+ "loginResetSuccess": "Prosimo preverite svojo e-pošto",
+ "loginUpdateSuccess": "Novo geslo je shranjeno",
+ "loginLogout": "Odjava",
+ "loginLogo": "Logotip",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "Naprava in stanje",
+ "deviceSelected": "Izbrana naprava",
+ "deviceTitle": "Naprave",
+ "devicePrimaryInfo": "Naslov naprave",
+ "deviceSecondaryInfo": "Podrobnosti naprave",
+ "deviceIdentifier": "Identifikacija",
+ "deviceModel": "Model",
+ "deviceContact": "Kontakt",
+ "deviceCategory": "Kategorija",
+ "deviceLastUpdate": "Zadnja posodobitev",
+ "deviceCommand": "Ukaz",
+ "deviceFollow": "Sledi",
+ "deviceTotalDistance": "Skupna razdalja",
+ "deviceStatus": "Stanje",
+ "deviceStatusOnline": "Povezan",
+ "deviceStatusOffline": "Nepovezan",
+ "deviceStatusUnknown": "Neznano",
+ "deviceRegisterFirst": "Vnesite svojo prvo napravo",
+ "deviceIdentifierHelp": "IMEI, serijska številka ali druga identifikacija. Se mora ujemati z identifikacijo ki jo naprava pošilja na strežnik.",
+ "deviceShare": "Share Device",
+ "groupDialog": "Skupina",
+ "groupParent": "Skupina",
+ "groupNoGroup": "Ni skupine",
+ "settingsTitle": "Nastavitve",
+ "settingsUser": "Račun",
+ "settingsGroups": "Skupine",
+ "settingsServer": "Strežnik",
+ "settingsUsers": "Uporabniki",
+ "settingsDistanceUnit": "Enota za razdaljo",
+ "settingsAltitudeUnit": "Enota za nadmorsko višino",
+ "settingsSpeedUnit": "Enota za hitrost",
+ "settingsVolumeUnit": "Enota za volumen",
+ "settingsTwelveHourFormat": "12-urni format",
+ "settingsCoordinateFormat": "Format koordinat",
+ "settingsServerVersion": "Različica strežnika",
+ "settingsAppVersion": "Različica aplikacije",
+ "settingsConnection": "Povezava",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "Na voljo je posodobitev.",
+ "settingsSupport": "Support",
+ "reportTitle": "Poročila",
+ "reportScheduled": "Načrtovana poročila",
+ "reportDevice": "Naprava",
+ "reportGroup": "Skupina",
+ "reportFrom": "Od",
+ "reportTo": "Do",
+ "reportShow": "Prikaži",
+ "reportClear": "Očisti",
+ "linkGoogleMaps": "Google Maps",
+ "linkAppleMaps": "Apple Maps",
+ "linkStreetView": "Street View",
+ "positionFixTime": "Čas določitve",
+ "positionDeviceTime": "Čas naprave",
+ "positionServerTime": "Čas strežnika",
+ "positionValid": "Veljavnost",
+ "positionAccuracy": "Natančnost",
+ "positionLatitude": "Širina",
+ "positionLongitude": "Dolžina",
+ "positionAltitude": "Višina",
+ "positionSpeed": "Hitrost",
+ "positionCourse": "Smer",
+ "positionAddress": "Naslov",
+ "positionProtocol": "Protokol",
+ "positionDistance": "Razdalja",
+ "positionRpm": "Obrati",
+ "positionFuel": "Gorivo",
+ "positionPower": "Napajanje",
+ "positionBattery": "Baterija",
+ "positionRaw": "Raw",
+ "positionIndex": "Index",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "Sateliti",
+ "positionSatVisible": "Vidni sateliti",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "Gostovanje",
+ "positionEvent": "Dogodek",
+ "positionAlarm": "Alarm",
+ "positionStatus": "Stanje",
+ "positionOdometer": "Stanje števca",
+ "positionServiceOdometer": "Servisni števec",
+ "positionTripOdometer": "Trip Odometer",
+ "positionHours": "Ur",
+ "positionSteps": "Koraki",
+ "positionInput": "Vnos",
+ "positionHeartRate": "Heart Rate",
+ "positionOutput": "Izhod",
+ "positionBatteryLevel": "Nivo baterije",
+ "positionFuelConsumption": "Poraba goriva",
+ "positionRfid": "RFID",
+ "positionVersionFw": "Firmware Version",
+ "positionVersionHw": "Hardware Version",
+ "positionIgnition": "Kontakt",
+ "positionFlags": "Zastavice",
+ "positionCharge": "Polnjenje",
+ "positionIp": "IP",
+ "positionArchive": "Arhiv",
+ "positionVin": "VIN",
+ "positionApproximate": "Približno",
+ "positionThrottle": "Throttle",
+ "positionMotion": "Gibanje",
+ "positionArmed": "Armed",
+ "positionAcceleration": "Pospeševanje",
+ "positionTemp": "Temperatura",
+ "positionDeviceTemp": "Temperatura naprave",
+ "positionCoolantTemp": "Temperatura hladilne tekočine",
+ "positionOperator": "Operater",
+ "positionCommand": "Ukaz",
+ "positionBlocked": "Blokirano",
+ "positionDtcs": "DTCs",
+ "positionObdSpeed": "OBD hitrost",
+ "positionObdOdometer": "OBD Stanje števca",
+ "positionDrivingTime": "Čas vožnje",
+ "positionDriverUniqueId": "Unikatna identifikacija voznika",
+ "positionCard": "Card",
+ "positionImage": "Slika",
+ "positionVideo": "Video",
+ "positionAudio": "Zvok",
+ "serverTitle": "Nastavitve strežnika",
+ "serverZoom": "Povečava",
+ "serverRegistration": "Registracija",
+ "serverReadonly": "Samo za branje",
+ "serverForceSettings": "Vsili nastavitve",
+ "serverAnnouncement": "Obvestilo",
+ "serverName": "Server Name",
+ "serverDescription": "Server Description",
+ "serverColorPrimary": "Color Primary",
+ "serverColorSecondary": "Color Secondary",
+ "serverLogo": "Logo Image",
+ "serverLogoInverted": "Inverted Logo Image",
+ "serverChangeDisable": "Disable Server Change",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "Zemljevid",
+ "mapActive": "Aktivni zemljevidi",
+ "mapOverlay": "Podatkovna plast",
+ "mapOverlayCustom": "Podatkovna plast po meri",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API Key",
+ "mapOpenWeatherClouds": "OpenWeather Clouds",
+ "mapOpenWeatherPrecipitation": "OpenWeather Precipitation",
+ "mapOpenWeatherPressure": "OpenWeather Pressure",
+ "mapOpenWeatherWind": "OpenWeather Wind",
+ "mapOpenWeatherTemperature": "OpenWeather Temperature",
+ "mapLayer": "Zemljevidi",
+ "mapCustom": "Po meri (XYZ)",
+ "mapCustomArcgis": "Po meri (ArcGIS)",
+ "mapCustomLabel": "Zemljevid po meri",
+ "mapCarto": "Carto Basemaps",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Bing Maps Ključ",
+ "mapBingRoad": "Bing Maps Ceste",
+ "mapBingAerial": "Bing Maps Satelit",
+ "mapBingHybrid": "Bing zemljevid hibrid",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Yandex zemljevid",
+ "mapYandexSat": "Yandex satelit",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox Streets",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox Outdoors",
+ "mapMapboxSatellite": "Mapbox Satellite",
+ "mapMapboxKey": "Mapbox Access Token",
+ "mapMapTilerBasic": "MapTiler Basic",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "MapTiler API Key",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ Access Token",
+ "mapTomTomBasic": "TomTom Basic",
+ "mapTomTomFlow": "TomTom Traffic Flow",
+ "mapTomTomIncidents": "TomTom Traffic Incidents",
+ "mapTomTomKey": "TomTom API Key",
+ "mapHereBasic": "Here Basic",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satellite",
+ "mapHereFlow": "Here Traffic Flow",
+ "mapHereKey": "Here API Key",
+ "mapShapePolygon": "Poligon",
+ "mapShapeCircle": "Krog",
+ "mapShapePolyline": "Polyline",
+ "mapLiveRoutes": "Žive poti",
+ "mapDirection": "Prikaži smer",
+ "mapCurrentLocation": "Trenutna lokacija",
+ "mapPoiLayer": "POI Layer",
+ "mapClustering": "Združevanje oznak",
+ "mapOnSelect": "Ob izbiri prikaži zemljevid",
+ "mapDefault": "Privzet zemljevid",
+ "stateTitle": "Stanje",
+ "stateName": "Parameter",
+ "stateValue": "Vrednost",
+ "commandTitle": "Ukaz",
+ "commandSend": "Pošlji",
+ "commandSent": "Ukaz poslan",
+ "commandQueued": "Ukaz v čakalni vrsti",
+ "commandUnit": "Naprava",
+ "commandCustom": "Ukaz po meri",
+ "commandDeviceIdentification": "Identifikacija naprave",
+ "commandPositionSingle": "Single Reporting",
+ "commandPositionPeriodic": "Periodično poročanje",
+ "commandPositionStop": "Ustavi poročanje",
+ "commandEngineStop": "Ugasni motor",
+ "commandEngineResume": "Prižgi motor",
+ "commandAlarmArm": "Vklopi alarm",
+ "commandAlarmDisarm": "Izklopi alarm",
+ "commandAlarmDismiss": "Utišaj alarm",
+ "commandSetTimezone": "Nastavi časovni pas",
+ "commandRequestPhoto": "Zahtevaj sliko",
+ "commandPowerOff": "Izklopi napravo",
+ "commandRebootDevice": "Ponovno zaženi napravo",
+ "commandFactoryReset": "Tovarniške nastavitve",
+ "commandSendSms": "Pošlji SMS",
+ "commandSendUssd": "Pošlji USSD",
+ "commandSosNumber": "Nastavi SOS številko",
+ "commandSilenceTime": "Set Silence Time",
+ "commandSetPhonebook": "Set Phonebook",
+ "commandVoiceMessage": "Glasovno sporočilo",
+ "commandOutputControl": "Output Control",
+ "commandVoiceMonitoring": "Voice Monitoring",
+ "commandSetAgps": "Nastavi AGPS",
+ "commandSetIndicator": "Set Indicator",
+ "commandConfiguration": "Nastavitve",
+ "commandGetVersion": "Pridobi različico",
+ "commandFirmwareUpdate": "Update Firmware",
+ "commandSetConnection": "Nastavi povezavo",
+ "commandSetOdometer": "Nastavi stanje števca",
+ "commandGetModemStatus": "Get Modem Status",
+ "commandGetDeviceStatus": "Get Device Status",
+ "commandSetSpeedLimit": "Set Speed Limit",
+ "commandModePowerSaving": "Power Saving Mode",
+ "commandModeDeepSleep": "Deep Sleep Mode",
+ "commandAlarmGeofence": "Set Geofence Alarm",
+ "commandAlarmBattery": "Set Battery Alarm",
+ "commandAlarmSos": "Set SOS Alarm",
+ "commandAlarmRemove": "Set Remove Alarm",
+ "commandAlarmClock": "Set Clock Alarm",
+ "commandAlarmSpeed": "Set Speed Alarm",
+ "commandAlarmFall": "Set Fall Alarm",
+ "commandAlarmVibration": "Set Vibration Alarm",
+ "commandFrequency": "Frekvenca",
+ "commandTimezone": "Timezone Offset",
+ "commandMessage": "Sporočilo",
+ "commandRadius": "Radij",
+ "commandEnable": "Omogoči",
+ "commandData": "Podatki",
+ "commandIndex": "Index",
+ "commandPhone": "Telefonska številka",
+ "commandServer": "Strežnik",
+ "commandPort": "Vrata",
+ "eventAll": "Vsi dogodki",
+ "eventDeviceOnline": "Stanje: povezan",
+ "eventDeviceUnknown": "Neznano stanje",
+ "eventDeviceOffline": "Stanje: nepovezan",
+ "eventDeviceInactive": "Naprava neaktivna",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "Naprava se premika",
+ "eventDeviceStopped": "Naprava se je ustavila",
+ "eventDeviceOverspeed": "Omejitev hitrosti prekoračena",
+ "eventDeviceFuelDrop": "Padec goriva",
+ "eventDeviceFuelIncrease": "Povečanje goriva",
+ "eventCommandResult": "Rezultat ukaza",
+ "eventGeofenceEnter": "Vstopil v omejeno območje",
+ "eventGeofenceExit": "Izšel iz omejenega območja",
+ "eventAlarm": "Alarm",
+ "eventIgnitionOn": "Vžig",
+ "eventIgnitionOff": "Vžig izključen",
+ "eventMaintenance": "Potrebno vzdrževanje",
+ "eventTextMessage": "Sporočilo sprejeto",
+ "eventDriverChanged": "Sprememba voznika",
+ "eventMedia": "Mediji",
+ "eventsScrollToLast": "Pomakni se na zadnje",
+ "eventsSoundEvents": "Zvok dogodka",
+ "eventsSoundAlarms": "Zvok alarma",
+ "alarmGeneral": "Splošno",
+ "alarmSos": "SOS",
+ "alarmVibration": "Vibracije",
+ "alarmMovement": "Premikanje",
+ "alarmLowspeed": "Nizka hitrost",
+ "alarmOverspeed": "Prekoračitev hitrosti",
+ "alarmFallDown": "Padec",
+ "alarmLowPower": "Nizka napetost napajanja",
+ "alarmLowBattery": "Baterija skoraj izpraznjena",
+ "alarmFault": "Napaka",
+ "alarmPowerOff": "Izklop",
+ "alarmPowerOn": "Vklop",
+ "alarmDoor": "Vrata",
+ "alarmLock": "Zakleni",
+ "alarmUnlock": "Odkleni",
+ "alarmGeofence": "Omejeno območje",
+ "alarmGeofenceEnter": "Vstop v omejeno območje",
+ "alarmGeofenceExit": "Izhod iz omejenega območja",
+ "alarmGpsAntennaCut": "GPS Antena prekinjena",
+ "alarmAccident": "Nesreča",
+ "alarmTow": "Vleka",
+ "alarmIdle": "Neaktiven",
+ "alarmHighRpm": "Visoki obrati",
+ "alarmHardAcceleration": "Močno pospeševanje",
+ "alarmHardBraking": "Močno zaviranje",
+ "alarmHardCornering": "Hard Cornering",
+ "alarmLaneChange": "Lane Change",
+ "alarmFatigueDriving": "Fatigue Driving",
+ "alarmPowerCut": "Prekinitev napajanja",
+ "alarmPowerRestored": "Napajanje obnovljeno",
+ "alarmJamming": "Motnja",
+ "alarmTemperature": "Temperatura",
+ "alarmParking": "Parkiranje",
+ "alarmBonnet": "Pokrov motorja",
+ "alarmFootBrake": "Foot Brake",
+ "alarmFuelLeak": "Fuel Leak",
+ "alarmTampering": "Tampering",
+ "alarmRemoving": "Odstranjujem",
+ "notificationType": "Tip obvestil",
+ "notificationAlways": "Vse naprave",
+ "notificationNotificators": "Kanali",
+ "notificatorCommand": "Ukaz",
+ "notificatorWeb": "Splet",
+ "notificatorMail": "Elektronska pošta",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Ponovi",
+ "reportCombined": "Kombinirano",
+ "reportRoute": "Pot",
+ "reportEvents": "Dogodki",
+ "reportTrips": "Poti",
+ "reportStops": "Postanki",
+ "reportSummary": "Povzetek",
+ "reportDaily": "Dnevni povzetek",
+ "reportChart": "Graf",
+ "reportConfigure": "Nastavi",
+ "reportEventTypes": "Tipi dogodkov",
+ "reportChartType": "Tip grafa",
+ "reportShowMarkers": "Prikaži oznake",
+ "reportExport": "Izvozi",
+ "reportEmail": "Pošlji po e-pošti",
+ "reportSchedule": "Urnik",
+ "reportPeriod": "Obdobje",
+ "reportCustom": "Poljubno",
+ "reportToday": "Danes",
+ "reportYesterday": "Včeraj",
+ "reportThisWeek": "Ta teden",
+ "reportPreviousWeek": "Prejšnji teden",
+ "reportThisMonth": "Ta mesec",
+ "reportPreviousMonth": "Prejšnji mesec",
+ "reportDeviceName": "Ime naprave",
+ "reportAverageSpeed": "Povprečna hitrost",
+ "reportMaximumSpeed": "Najvišja hitrost",
+ "reportEngineHours": "Motorinh ur",
+ "reportDuration": "Trajanje",
+ "reportStartDate": "Datum začetka",
+ "reportStartTime": "Začetni čas",
+ "reportStartAddress": "Začetni naslov",
+ "reportEndTime": "Končni čas",
+ "reportEndAddress": "Končni naslov",
+ "reportSpentFuel": "Porabljeno gorivo",
+ "reportStartOdometer": "Začetno stanje števca",
+ "reportEndOdometer": "Končno stanje števca",
+ "statisticsTitle": "Statistika",
+ "statisticsCaptureTime": "Začetni čas",
+ "statisticsActiveUsers": "Aktivni uporabniki",
+ "statisticsActiveDevices": "Aktivne naprave",
+ "statisticsRequests": "Zahtevkov",
+ "statisticsMessagesReceived": "Prejetih sporočil",
+ "statisticsMessagesStored": "Shranjenih sporočil",
+ "statisticsGeocoder": "Geocoder Requests",
+ "statisticsGeolocation": "Geolocation Requests",
+ "categoryArrow": "Puščica",
+ "categoryDefault": "Privzeto",
+ "categoryAnimal": "Žival",
+ "categoryBicycle": "Kolo",
+ "categoryBoat": "Čoln",
+ "categoryBus": "Avtobus",
+ "categoryCar": "Avto",
+ "categoryCamper": "Camper",
+ "categoryCrane": "Žerjav",
+ "categoryHelicopter": "Helikopter",
+ "categoryMotorcycle": "Motor",
+ "categoryOffroad": "Terenec",
+ "categoryPerson": "Oseba",
+ "categoryPickup": "Pickup",
+ "categoryPlane": "Letalo",
+ "categoryShip": "Ladja",
+ "categoryTractor": "Traktor",
+ "categoryTrain": "Vlak",
+ "categoryTram": "Tramvaj",
+ "categoryTrolleybus": "Trolejbus",
+ "categoryTruck": "Tovornjak",
+ "categoryVan": "Kombi",
+ "categoryScooter": "Skuter",
+ "maintenanceStart": "Začetek",
+ "maintenancePeriod": "Obdobje"
+} \ No newline at end of file
diff --git a/src/resources/l10n/sq.json b/src/resources/l10n/sq.json
new file mode 100644
index 00000000..8b77004a
--- /dev/null
+++ b/src/resources/l10n/sq.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "Ngarkim…",
+ "sharedHide": "Hide",
+ "sharedSave": "Ruaj",
+ "sharedUpload": "Upload",
+ "sharedSet": "Set",
+ "sharedCancel": "Anullim",
+ "sharedCopy": "Copy",
+ "sharedAdd": "Shto",
+ "sharedEdit": "Ndrysho",
+ "sharedRemove": "Hiq",
+ "sharedRemoveConfirm": "Hiq skedarin",
+ "sharedNoData": "No data",
+ "sharedSubject": "Subject",
+ "sharedYes": "Yes",
+ "sharedNo": "No",
+ "sharedKm": "km",
+ "sharedMi": "Milje",
+ "sharedNmi": "nmi",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "Nyje",
+ "sharedKmh": "km/h",
+ "sharedMph": "mph",
+ "sharedHour": "Orë",
+ "sharedMinute": "Minuta",
+ "sharedSecond": "Sekonda",
+ "sharedDays": "days",
+ "sharedHours": "hours",
+ "sharedMinutes": "minutes",
+ "sharedDecimalDegrees": "Decimal Degrees",
+ "sharedDegreesDecimalMinutes": "Degrees Decimal Minutes",
+ "sharedDegreesMinutesSeconds": "Degrees Minutes Seconds",
+ "sharedName": "Emri",
+ "sharedDescription": "Description",
+ "sharedSearch": "Kërkim",
+ "sharedIconScale": "Icon Scale",
+ "sharedGeofence": "Geofence",
+ "sharedGeofences": "Geofences",
+ "sharedCreateGeofence": "Create Geofence",
+ "sharedNotifications": "Notifications",
+ "sharedNotification": "Notification",
+ "sharedAttributes": "Attributes",
+ "sharedAttribute": "Attribute",
+ "sharedDrivers": "Drivers",
+ "sharedDriver": "Driver",
+ "sharedArea": "Area",
+ "sharedSound": "Notification Sound",
+ "sharedType": "Type",
+ "sharedDistance": "Distance",
+ "sharedHourAbbreviation": "h",
+ "sharedMinuteAbbreviation": "m",
+ "sharedSecondAbbreviation": "s",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "l",
+ "sharedGallonAbbreviation": "gal",
+ "sharedLiter": "Liter",
+ "sharedImpGallon": "Imp. Gallon",
+ "sharedUsGallon": "U.S. Gallon",
+ "sharedLiterPerHourAbbreviation": "l/h",
+ "sharedGetMapState": "Get Map State",
+ "sharedComputedAttribute": "Computed Attribute",
+ "sharedComputedAttributes": "Computed Attributes",
+ "sharedCheckComputedAttribute": "Check Computed Attribute",
+ "sharedExpression": "Expression",
+ "sharedDevice": "Device",
+ "sharedTest": "Test",
+ "sharedTestNotification": "Send Test Notification",
+ "sharedTestNotificators": "Test Channels",
+ "sharedTestExpression": "Test Expression",
+ "sharedCalendar": "Calendar",
+ "sharedCalendars": "Calendars",
+ "sharedFile": "File",
+ "sharedSearchDevices": "Search Devices",
+ "sharedSortBy": "Sort By",
+ "sharedFilterMap": "Filter on Map",
+ "sharedSelectFile": "Select File",
+ "sharedPhone": "Phone",
+ "sharedRequired": "Required",
+ "sharedPreferences": "Preferences",
+ "sharedPermissions": "Permissions",
+ "sharedConnections": "Connections",
+ "sharedExtra": "Extra",
+ "sharedPrimary": "Primary",
+ "sharedSecondary": "Secondary",
+ "sharedTypeString": "String",
+ "sharedTypeNumber": "Number",
+ "sharedTypeBoolean": "Boolean",
+ "sharedTimezone": "Timezone",
+ "sharedInfoTitle": "Info",
+ "sharedSavedCommand": "Saved Command",
+ "sharedSavedCommands": "Saved Commands",
+ "sharedNew": "New…",
+ "sharedShowAddress": "Show Address",
+ "sharedShowDetails": "More Details",
+ "sharedDisabled": "Disabled",
+ "sharedMaintenance": "Maintenance",
+ "sharedDeviceAccumulators": "Accumulators",
+ "sharedAlarms": "Alarms",
+ "sharedLocation": "Location",
+ "sharedImport": "Import",
+ "sharedColumns": "Columns",
+ "sharedDropzoneText": "Drag and drop a file here or click",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "Simple",
+ "calendarRecurrence": "Recurrence",
+ "calendarOnce": "Once",
+ "calendarDaily": "Daily",
+ "calendarWeekly": "Weekly",
+ "calendarMonthly": "Monthly",
+ "calendarDays": "Days",
+ "calendarSunday": "Sunday",
+ "calendarMonday": "Monday",
+ "calendarTuesday": "Tuesday",
+ "calendarWednesday": "Wednesday",
+ "calendarThursday": "Thursday",
+ "calendarFriday": "Friday",
+ "calendarSaturday": "Saturday",
+ "attributeShowGeofences": "Show Geofences",
+ "attributeSpeedLimit": "Speed Limit",
+ "attributeFuelDropThreshold": "Fuel Drop Threshold",
+ "attributeFuelIncreaseThreshold": "Fuel Increase Threshold",
+ "attributePolylineDistance": "Polyline Distance",
+ "attributeReportIgnoreOdometer": "Report: Ignore Odometer",
+ "attributeWebReportColor": "Web: Report Color",
+ "attributeDevicePassword": "Device Password",
+ "attributeDeviceImage": "Device Image",
+ "attributeDeviceInactivityStart": "Device Inactivity Start",
+ "attributeDeviceInactivityPeriod": "Device Inactivity Period",
+ "attributeProcessingCopyAttributes": "Processing: Copy Attributes",
+ "attributeColor": "Color",
+ "attributeWebLiveRouteLength": "Web: Live Route Length",
+ "attributeWebSelectZoom": "Web: Zoom On Select",
+ "attributeWebMaxZoom": "Web: Maximum Zoom",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Pushover User Key",
+ "attributePushoverDeviceNames": "Pushover Device Names",
+ "attributeMailSmtpHost": "Mail: SMTP Host",
+ "attributeMailSmtpPort": "Mail: SMTP Port",
+ "attributeMailSmtpStarttlsEnable": "Mail: SMTP STARTTLS Enable",
+ "attributeMailSmtpStarttlsRequired": "Mail: SMTP STARTTLS Required",
+ "attributeMailSmtpSslEnable": "Mail: SMTP SSL Enable",
+ "attributeMailSmtpSslTrust": "Mail: SMTP SSL Trust",
+ "attributeMailSmtpSslProtocols": "Mail: SMTP SSL Protocols",
+ "attributeMailSmtpFrom": "Mail: SMTP From",
+ "attributeMailSmtpAuth": "Mail: SMTP Auth Enable",
+ "attributeMailSmtpUsername": "Mail: SMTP Username",
+ "attributeMailSmtpPassword": "Mail: SMTP Password",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: Disable Attributes",
+ "attributeUiDisableGroups": "UI: Disable Groups",
+ "attributeUiDisableEvents": "UI: Disable Events",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "UI: Disable Drivers",
+ "attributeUiDisableComputedAttributes": "UI: Disable Computed Attributes",
+ "attributeUiDisableCalendars": "UI: Disable Calendars",
+ "attributeUiDisableMaintenance": "UI: Disable Maintenance",
+ "attributeUiHidePositionAttributes": "UI: Hide Position Attributes",
+ "attributeUiDisableLoginLanguage": "UI: Disable Login Language",
+ "attributeNotificationTokens": "Notification Tokens",
+ "attributePopupInfo": "Popup Info",
+ "errorTitle": "Gabim",
+ "errorGeneral": "Invalid parameters or constraints violation",
+ "errorConnection": "Gabim lidhjeje",
+ "errorSocket": "Web socket connection error",
+ "errorZero": "Can't be zero",
+ "userEmail": "Email",
+ "userPassword": "Fjalëkalimi",
+ "userAdmin": "Administratori",
+ "userRemember": "Remember",
+ "userExpirationTime": "Expiration",
+ "userDeviceLimit": "Device Limit",
+ "userUserLimit": "User Limit",
+ "userDeviceReadonly": "Device Readonly",
+ "userLimitCommands": "Limit Commands",
+ "userDisableReports": "Disable Reports",
+ "userFixedEmail": "No Email Change",
+ "userToken": "Token",
+ "userDeleteAccount": "Delete Account",
+ "userTemporary": "Temporary",
+ "loginTitle": "Hyrje",
+ "loginLanguage": "Gjuha",
+ "loginReset": "Reset Password",
+ "loginRegister": "Regjistrim",
+ "loginLogin": "Lidhu",
+ "loginOpenId": "Login with OpenID",
+ "loginFailed": "Adresë Email-i ose fjalëkalim i gabuar",
+ "loginCreated": "Përdoruesi i ri u regjistrua",
+ "loginResetSuccess": "Check your email",
+ "loginUpdateSuccess": "New password is set",
+ "loginLogout": "Shkëputu",
+ "loginLogo": "Logo",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "Gjendja e pajisjeve",
+ "deviceSelected": "Selected Device",
+ "deviceTitle": "Pajisjet",
+ "devicePrimaryInfo": "Device Title",
+ "deviceSecondaryInfo": "Device Detail",
+ "deviceIdentifier": "Identifikues",
+ "deviceModel": "Model",
+ "deviceContact": "Contact",
+ "deviceCategory": "Category",
+ "deviceLastUpdate": "Përditësimi i fundit",
+ "deviceCommand": "Komandë",
+ "deviceFollow": "Ndjek",
+ "deviceTotalDistance": "Total Distance",
+ "deviceStatus": "Status",
+ "deviceStatusOnline": "Online",
+ "deviceStatusOffline": "Offline",
+ "deviceStatusUnknown": "Unknown",
+ "deviceRegisterFirst": "Register your first device",
+ "deviceIdentifierHelp": "IMEI, serial number or other id. It has to match the identifier device reports to the server.",
+ "deviceShare": "Share Device",
+ "groupDialog": "Grup",
+ "groupParent": "Grup",
+ "groupNoGroup": "Pa Grup",
+ "settingsTitle": "Parametra",
+ "settingsUser": "Llogari",
+ "settingsGroups": "Grupe",
+ "settingsServer": "Rrjeti",
+ "settingsUsers": "Përdorues",
+ "settingsDistanceUnit": "Distance Unit",
+ "settingsAltitudeUnit": "Altitude Unit",
+ "settingsSpeedUnit": "Speed Unit",
+ "settingsVolumeUnit": "Volume Unit",
+ "settingsTwelveHourFormat": "Formë 12-orëshe",
+ "settingsCoordinateFormat": "Coordinates Format",
+ "settingsServerVersion": "Server Version",
+ "settingsAppVersion": "App Version",
+ "settingsConnection": "Connection",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "Raporte",
+ "reportScheduled": "Scheduled Reports",
+ "reportDevice": "Pajisje",
+ "reportGroup": "Group",
+ "reportFrom": "Nga",
+ "reportTo": "Tek",
+ "reportShow": "Shfaqje",
+ "reportClear": "Pastrim",
+ "linkGoogleMaps": "Google Maps",
+ "linkAppleMaps": "Apple Maps",
+ "linkStreetView": "Street View",
+ "positionFixTime": "Fix Time",
+ "positionDeviceTime": "Device Time",
+ "positionServerTime": "Server Time",
+ "positionValid": "I vlefshëm",
+ "positionAccuracy": "Accuracy",
+ "positionLatitude": "Gjerësi Gjeografike",
+ "positionLongitude": "Gjatësi Gjeografike",
+ "positionAltitude": "Lartësia",
+ "positionSpeed": "Shpejtësia",
+ "positionCourse": "Itinerari (Rruga)",
+ "positionAddress": "Adresa",
+ "positionProtocol": "Protokolli",
+ "positionDistance": "Distance",
+ "positionRpm": "RPM",
+ "positionFuel": "Fuel",
+ "positionPower": "Power",
+ "positionBattery": "Battery",
+ "positionRaw": "Raw",
+ "positionIndex": "Index",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "Satellites",
+ "positionSatVisible": "Visible Satellites",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "Roaming",
+ "positionEvent": "Event",
+ "positionAlarm": "Alarm",
+ "positionStatus": "Status",
+ "positionOdometer": "Odometer",
+ "positionServiceOdometer": "Service Odometer",
+ "positionTripOdometer": "Trip Odometer",
+ "positionHours": "Hours",
+ "positionSteps": "Steps",
+ "positionInput": "Input",
+ "positionHeartRate": "Heart Rate",
+ "positionOutput": "Output",
+ "positionBatteryLevel": "Battery Level",
+ "positionFuelConsumption": "Fuel Consumption",
+ "positionRfid": "RFID",
+ "positionVersionFw": "Firmware Version",
+ "positionVersionHw": "Hardware Version",
+ "positionIgnition": "Ignition",
+ "positionFlags": "Flags",
+ "positionCharge": "Charge",
+ "positionIp": "IP",
+ "positionArchive": "Archive",
+ "positionVin": "VIN",
+ "positionApproximate": "Approximate",
+ "positionThrottle": "Throttle",
+ "positionMotion": "Motion",
+ "positionArmed": "Armed",
+ "positionAcceleration": "Acceleration",
+ "positionTemp": "Temperature",
+ "positionDeviceTemp": "Device Temperature",
+ "positionCoolantTemp": "Coolant Temperature",
+ "positionOperator": "Operator",
+ "positionCommand": "Command",
+ "positionBlocked": "Blocked",
+ "positionDtcs": "DTCs",
+ "positionObdSpeed": "OBD Speed",
+ "positionObdOdometer": "OBD Odometer",
+ "positionDrivingTime": "Driving Time",
+ "positionDriverUniqueId": "Driver Unique Id",
+ "positionCard": "Card",
+ "positionImage": "Image",
+ "positionVideo": "Video",
+ "positionAudio": "Audio",
+ "serverTitle": "Server-Parametrat",
+ "serverZoom": "Fokus",
+ "serverRegistration": "Regjistrim",
+ "serverReadonly": "Vetëm për lexim",
+ "serverForceSettings": "Force Settings",
+ "serverAnnouncement": "Announcement",
+ "serverName": "Server Name",
+ "serverDescription": "Server Description",
+ "serverColorPrimary": "Color Primary",
+ "serverColorSecondary": "Color Secondary",
+ "serverLogo": "Logo Image",
+ "serverLogoInverted": "Inverted Logo Image",
+ "serverChangeDisable": "Disable Server Change",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "Harta",
+ "mapActive": "Active Maps",
+ "mapOverlay": "Map Overlay",
+ "mapOverlayCustom": "Custom Overlay",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API Key",
+ "mapOpenWeatherClouds": "OpenWeather Clouds",
+ "mapOpenWeatherPrecipitation": "OpenWeather Precipitation",
+ "mapOpenWeatherPressure": "OpenWeather Pressure",
+ "mapOpenWeatherWind": "OpenWeather Wind",
+ "mapOpenWeatherTemperature": "OpenWeather Temperature",
+ "mapLayer": "Zgjedhje harte",
+ "mapCustom": "Custom (XYZ)",
+ "mapCustomArcgis": "Custom (ArcGIS)",
+ "mapCustomLabel": "Custom map",
+ "mapCarto": "Carto Basemaps",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Bing Maps Key",
+ "mapBingRoad": "Bing Maps Road",
+ "mapBingAerial": "Bing Maps Aerial",
+ "mapBingHybrid": "Bing Maps Hybrid",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Yandex Map",
+ "mapYandexSat": "Yandex Satellite",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox Streets",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox Outdoors",
+ "mapMapboxSatellite": "Mapbox Satellite",
+ "mapMapboxKey": "Mapbox Access Token",
+ "mapMapTilerBasic": "MapTiler Basic",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "MapTiler API Key",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ Access Token",
+ "mapTomTomBasic": "TomTom Basic",
+ "mapTomTomFlow": "TomTom Traffic Flow",
+ "mapTomTomIncidents": "TomTom Traffic Incidents",
+ "mapTomTomKey": "TomTom API Key",
+ "mapHereBasic": "Here Basic",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satellite",
+ "mapHereFlow": "Here Traffic Flow",
+ "mapHereKey": "Here API Key",
+ "mapShapePolygon": "Polygon",
+ "mapShapeCircle": "Circle",
+ "mapShapePolyline": "Polyline",
+ "mapLiveRoutes": "Live Routes",
+ "mapDirection": "Show Direction",
+ "mapCurrentLocation": "Current Location",
+ "mapPoiLayer": "POI Layer",
+ "mapClustering": "Markers Clustering",
+ "mapOnSelect": "Show Map on Selection",
+ "mapDefault": "Default Map",
+ "stateTitle": "Gjëndja",
+ "stateName": "Atribut",
+ "stateValue": "Vlera",
+ "commandTitle": "Komandë",
+ "commandSend": "Dërgo",
+ "commandSent": "Command sent",
+ "commandQueued": "Command queued",
+ "commandUnit": "Njësi",
+ "commandCustom": "Custom command",
+ "commandDeviceIdentification": "Device Identification",
+ "commandPositionSingle": "Single Reporting",
+ "commandPositionPeriodic": "Raporte periodike",
+ "commandPositionStop": "Ndalo raportin",
+ "commandEngineStop": "Ndalo punën",
+ "commandEngineResume": "Rinis",
+ "commandAlarmArm": "Arm Alarm",
+ "commandAlarmDisarm": "Disarm Alarm",
+ "commandAlarmDismiss": "Dismiss Alarm",
+ "commandSetTimezone": "Set Timezone",
+ "commandRequestPhoto": "Request Photo",
+ "commandPowerOff": "Power Off Device",
+ "commandRebootDevice": "Reboot Device",
+ "commandFactoryReset": "Factory Reset",
+ "commandSendSms": "Send SMS",
+ "commandSendUssd": "Send USSD",
+ "commandSosNumber": "Set SOS Number",
+ "commandSilenceTime": "Set Silence Time",
+ "commandSetPhonebook": "Set Phonebook",
+ "commandVoiceMessage": "Voice Message",
+ "commandOutputControl": "Output Control",
+ "commandVoiceMonitoring": "Voice Monitoring",
+ "commandSetAgps": "Set AGPS",
+ "commandSetIndicator": "Set Indicator",
+ "commandConfiguration": "Configuration",
+ "commandGetVersion": "Get Version",
+ "commandFirmwareUpdate": "Update Firmware",
+ "commandSetConnection": "Set Connection",
+ "commandSetOdometer": "Set Odometer",
+ "commandGetModemStatus": "Get Modem Status",
+ "commandGetDeviceStatus": "Get Device Status",
+ "commandSetSpeedLimit": "Set Speed Limit",
+ "commandModePowerSaving": "Power Saving Mode",
+ "commandModeDeepSleep": "Deep Sleep Mode",
+ "commandAlarmGeofence": "Set Geofence Alarm",
+ "commandAlarmBattery": "Set Battery Alarm",
+ "commandAlarmSos": "Set SOS Alarm",
+ "commandAlarmRemove": "Set Remove Alarm",
+ "commandAlarmClock": "Set Clock Alarm",
+ "commandAlarmSpeed": "Set Speed Alarm",
+ "commandAlarmFall": "Set Fall Alarm",
+ "commandAlarmVibration": "Set Vibration Alarm",
+ "commandFrequency": "Frekuenca",
+ "commandTimezone": "Timezone Offset",
+ "commandMessage": "Message",
+ "commandRadius": "Radius",
+ "commandEnable": "Enable",
+ "commandData": "Data",
+ "commandIndex": "Index",
+ "commandPhone": "Phone Number",
+ "commandServer": "Server",
+ "commandPort": "Port",
+ "eventAll": "All Events",
+ "eventDeviceOnline": "Status online",
+ "eventDeviceUnknown": "Status unknown",
+ "eventDeviceOffline": "Status offline",
+ "eventDeviceInactive": "Device inactive",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "Device moving",
+ "eventDeviceStopped": "Device stopped",
+ "eventDeviceOverspeed": "Speed limit exceeded",
+ "eventDeviceFuelDrop": "Fuel drop",
+ "eventDeviceFuelIncrease": "Fuel increase",
+ "eventCommandResult": "Command result",
+ "eventGeofenceEnter": "Geofence entered",
+ "eventGeofenceExit": "Geofence exited",
+ "eventAlarm": "Alarm",
+ "eventIgnitionOn": "Ignition on",
+ "eventIgnitionOff": "Ignition off",
+ "eventMaintenance": "Maintenance required",
+ "eventTextMessage": "Text message received",
+ "eventDriverChanged": "Driver changed",
+ "eventMedia": "Media",
+ "eventsScrollToLast": "Scroll To Last",
+ "eventsSoundEvents": "Sound Events",
+ "eventsSoundAlarms": "Sound Alarms",
+ "alarmGeneral": "General",
+ "alarmSos": "SOS",
+ "alarmVibration": "Vibration",
+ "alarmMovement": "Movement",
+ "alarmLowspeed": "Low Speed",
+ "alarmOverspeed": "Overspeed",
+ "alarmFallDown": "Fall Down",
+ "alarmLowPower": "Low Power",
+ "alarmLowBattery": "Low Battery",
+ "alarmFault": "Fault",
+ "alarmPowerOff": "Power Off",
+ "alarmPowerOn": "Power On",
+ "alarmDoor": "Door",
+ "alarmLock": "Lock",
+ "alarmUnlock": "Unlock",
+ "alarmGeofence": "Geofence",
+ "alarmGeofenceEnter": "Geofence Enter",
+ "alarmGeofenceExit": "Geofence Exit",
+ "alarmGpsAntennaCut": "GPS Antenna Cut",
+ "alarmAccident": "Accident",
+ "alarmTow": "Tow",
+ "alarmIdle": "Idle",
+ "alarmHighRpm": "High RPM",
+ "alarmHardAcceleration": "Hard Acceleration",
+ "alarmHardBraking": "Hard Braking",
+ "alarmHardCornering": "Hard Cornering",
+ "alarmLaneChange": "Lane Change",
+ "alarmFatigueDriving": "Fatigue Driving",
+ "alarmPowerCut": "Power Cut",
+ "alarmPowerRestored": "Power Restored",
+ "alarmJamming": "Jamming",
+ "alarmTemperature": "Temperature",
+ "alarmParking": "Parking",
+ "alarmBonnet": "Bonnet",
+ "alarmFootBrake": "Foot Brake",
+ "alarmFuelLeak": "Fuel Leak",
+ "alarmTampering": "Tampering",
+ "alarmRemoving": "Removing",
+ "notificationType": "Type of Notification",
+ "notificationAlways": "All Devices",
+ "notificationNotificators": "Channels",
+ "notificatorCommand": "Command",
+ "notificatorWeb": "Web",
+ "notificatorMail": "Mail",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Replay",
+ "reportCombined": "Combined",
+ "reportRoute": "Route",
+ "reportEvents": "Events",
+ "reportTrips": "Trips",
+ "reportStops": "Stops",
+ "reportSummary": "Summary",
+ "reportDaily": "Daily Summary",
+ "reportChart": "Chart",
+ "reportConfigure": "Configure",
+ "reportEventTypes": "Event Types",
+ "reportChartType": "Chart Type",
+ "reportShowMarkers": "Show Markers",
+ "reportExport": "Export",
+ "reportEmail": "Email Report",
+ "reportSchedule": "Schedule",
+ "reportPeriod": "Period",
+ "reportCustom": "Custom",
+ "reportToday": "Today",
+ "reportYesterday": "Yesterday",
+ "reportThisWeek": "This Week",
+ "reportPreviousWeek": "Previous Week",
+ "reportThisMonth": "This Month",
+ "reportPreviousMonth": "Previous Month",
+ "reportDeviceName": "Device Name",
+ "reportAverageSpeed": "Average Speed",
+ "reportMaximumSpeed": "Maximum Speed",
+ "reportEngineHours": "Engine Hours",
+ "reportDuration": "Duration",
+ "reportStartDate": "Start Date",
+ "reportStartTime": "Start Time",
+ "reportStartAddress": "Start Address",
+ "reportEndTime": "End Time",
+ "reportEndAddress": "End Address",
+ "reportSpentFuel": "Spent Fuel",
+ "reportStartOdometer": "Odometer Start",
+ "reportEndOdometer": "Odometer End",
+ "statisticsTitle": "Statistics",
+ "statisticsCaptureTime": "Capture Time",
+ "statisticsActiveUsers": "Active Users",
+ "statisticsActiveDevices": "Active Devices",
+ "statisticsRequests": "Requests",
+ "statisticsMessagesReceived": "Messages Received",
+ "statisticsMessagesStored": "Messages Stored",
+ "statisticsGeocoder": "Geocoder Requests",
+ "statisticsGeolocation": "Geolocation Requests",
+ "categoryArrow": "Arrow",
+ "categoryDefault": "Default",
+ "categoryAnimal": "Animal",
+ "categoryBicycle": "Bicycle",
+ "categoryBoat": "Boat",
+ "categoryBus": "Bus",
+ "categoryCar": "Car",
+ "categoryCamper": "Camper",
+ "categoryCrane": "Crane",
+ "categoryHelicopter": "Helicopter",
+ "categoryMotorcycle": "Motorcycle",
+ "categoryOffroad": "Offroad",
+ "categoryPerson": "Person",
+ "categoryPickup": "Pickup",
+ "categoryPlane": "Plane",
+ "categoryShip": "Ship",
+ "categoryTractor": "Tractor",
+ "categoryTrain": "Train",
+ "categoryTram": "Tram",
+ "categoryTrolleybus": "Trolleybus",
+ "categoryTruck": "Truck",
+ "categoryVan": "Van",
+ "categoryScooter": "Scooter",
+ "maintenanceStart": "Start",
+ "maintenancePeriod": "Period"
+} \ No newline at end of file
diff --git a/src/resources/l10n/sr.json b/src/resources/l10n/sr.json
new file mode 100644
index 00000000..a23fc3ee
--- /dev/null
+++ b/src/resources/l10n/sr.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "Učitavanje...",
+ "sharedHide": "Sakrij",
+ "sharedSave": "Sačuvaj",
+ "sharedUpload": "Učitaj",
+ "sharedSet": "Podesi",
+ "sharedCancel": "Odustani",
+ "sharedCopy": "Kopiraj",
+ "sharedAdd": "Dodaj",
+ "sharedEdit": "Podesi",
+ "sharedRemove": "Ukloni",
+ "sharedRemoveConfirm": "Ukloniti jedinicu?",
+ "sharedNoData": "Nema podataka",
+ "sharedSubject": "Tema",
+ "sharedYes": "Da",
+ "sharedNo": "Ne",
+ "sharedKm": "km",
+ "sharedMi": "mi",
+ "sharedNmi": "nmi",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "kn",
+ "sharedKmh": "km/h",
+ "sharedMph": "mph",
+ "sharedHour": "Čas",
+ "sharedMinute": "Minut",
+ "sharedSecond": "Sekunda",
+ "sharedDays": "dana",
+ "sharedHours": "sata/i",
+ "sharedMinutes": "minuta",
+ "sharedDecimalDegrees": "Decimalni Stepeni",
+ "sharedDegreesDecimalMinutes": "Minuti Decimalnih stepeni",
+ "sharedDegreesMinutesSeconds": "Sekunde decimalnih stepeni",
+ "sharedName": "Ime",
+ "sharedDescription": "Opis",
+ "sharedSearch": "Traži",
+ "sharedIconScale": "Razmera Ikonice",
+ "sharedGeofence": "Geoograda",
+ "sharedGeofences": "Geoograde",
+ "sharedCreateGeofence": "Napravi Geoogradu",
+ "sharedNotifications": "Obaveštenja",
+ "sharedNotification": "Obaveštenja",
+ "sharedAttributes": "Osobine",
+ "sharedAttribute": "Osobina",
+ "sharedDrivers": "Vozači",
+ "sharedDriver": "Vozač",
+ "sharedArea": "Oblast",
+ "sharedSound": "Zvuk Notifikacije",
+ "sharedType": "Tip",
+ "sharedDistance": "Razdaljina",
+ "sharedHourAbbreviation": "č",
+ "sharedMinuteAbbreviation": "m",
+ "sharedSecondAbbreviation": "s",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "L",
+ "sharedGallonAbbreviation": "gal",
+ "sharedLiter": "Litar",
+ "sharedImpGallon": "Imp. Gallon",
+ "sharedUsGallon": "U.S. Gallon",
+ "sharedLiterPerHourAbbreviation": "L/h",
+ "sharedGetMapState": "Stanje mape",
+ "sharedComputedAttribute": "Izračunati atribut",
+ "sharedComputedAttributes": "Izračunati atributi",
+ "sharedCheckComputedAttribute": "Proveri Izračunati atribut",
+ "sharedExpression": "Izraz",
+ "sharedDevice": "Uređaj",
+ "sharedTest": "Test",
+ "sharedTestNotification": "Pošalji Test Poruku",
+ "sharedTestNotificators": "Test Kanali",
+ "sharedTestExpression": "Test izraz",
+ "sharedCalendar": "Kalendar",
+ "sharedCalendars": "Kalendari",
+ "sharedFile": "Fajl",
+ "sharedSearchDevices": "Pretraži uređaje",
+ "sharedSortBy": "Poređaj po",
+ "sharedFilterMap": "Filter na Mapi",
+ "sharedSelectFile": "Odaberi fajl",
+ "sharedPhone": "Telefon",
+ "sharedRequired": "Obavezno",
+ "sharedPreferences": "Podešavanja",
+ "sharedPermissions": "Dozvole",
+ "sharedConnections": "Konekcije",
+ "sharedExtra": "Dodatno",
+ "sharedPrimary": "Primarno",
+ "sharedSecondary": "Sekundarni",
+ "sharedTypeString": "String",
+ "sharedTypeNumber": "Broj",
+ "sharedTypeBoolean": "Boolean",
+ "sharedTimezone": "Vremenska zona",
+ "sharedInfoTitle": "Informacija",
+ "sharedSavedCommand": "Sačuvana Komanda ",
+ "sharedSavedCommands": "Sačuvane komande ",
+ "sharedNew": "Nov... ",
+ "sharedShowAddress": "Prikaži adresu",
+ "sharedShowDetails": "Više detalja",
+ "sharedDisabled": "Onemogućeno",
+ "sharedMaintenance": "Održavanje",
+ "sharedDeviceAccumulators": "Akumulatori",
+ "sharedAlarms": "Alarmi",
+ "sharedLocation": "Lokacija",
+ "sharedImport": "Uvoz",
+ "sharedColumns": "Kolone",
+ "sharedDropzoneText": "Prevuci file i pusti ili klikni",
+ "sharedLogs": "Dnevnici",
+ "sharedLink": "Link",
+ "calendarSimple": "Jednostavno",
+ "calendarRecurrence": "Ponavljanje",
+ "calendarOnce": "Jednom",
+ "calendarDaily": "Dnevno",
+ "calendarWeekly": "Nedeljno",
+ "calendarMonthly": "Mesečno",
+ "calendarDays": "Dani",
+ "calendarSunday": "Nedelja",
+ "calendarMonday": "Ponedeljak",
+ "calendarTuesday": "Utorak",
+ "calendarWednesday": "Sreda",
+ "calendarThursday": "Četvrtak",
+ "calendarFriday": "Petak",
+ "calendarSaturday": "Subota",
+ "attributeShowGeofences": "Prikaži Geoograde",
+ "attributeSpeedLimit": "Ograničenje brzine",
+ "attributeFuelDropThreshold": "Granica opadanja goriva",
+ "attributeFuelIncreaseThreshold": "Granica dodavanja goriva",
+ "attributePolylineDistance": "Višelinijska udaljenost",
+ "attributeReportIgnoreOdometer": "Izveštaj: Ignoriši odometar",
+ "attributeWebReportColor": "Web: Boja izveštaja",
+ "attributeDevicePassword": "Lozinka uređaja",
+ "attributeDeviceImage": "Slika uređaja",
+ "attributeDeviceInactivityStart": "Početak neaktivnosti uređaja",
+ "attributeDeviceInactivityPeriod": "Period neaktivnosti uređaja",
+ "attributeProcessingCopyAttributes": "Proces: Kopiraj atribute",
+ "attributeColor": "Boja",
+ "attributeWebLiveRouteLength": "Web: Dužina rute",
+ "attributeWebSelectZoom": "Web:Zumiraj izabrano",
+ "attributeWebMaxZoom": "Web: Maksimalno uvećanje",
+ "attributeTelegramChatId": "Telegram čet ID",
+ "attributePushoverUserKey": "Pritisnite korisnički ključ",
+ "attributePushoverDeviceNames": "Pritisni ime uređaja",
+ "attributeMailSmtpHost": "Mail: SMTP host",
+ "attributeMailSmtpPort": "Mail: SMTP port",
+ "attributeMailSmtpStarttlsEnable": "Mail: SMTP STARTTLS Enable",
+ "attributeMailSmtpStarttlsRequired": "Mail: SMTP STARTTLS Required",
+ "attributeMailSmtpSslEnable": "Mail: SMTP SSL omogući",
+ "attributeMailSmtpSslTrust": "Mail: SMTP SSL Trust",
+ "attributeMailSmtpSslProtocols": "Mail: SMTP SSL Protokoli",
+ "attributeMailSmtpFrom": "Mail: SMTP Od",
+ "attributeMailSmtpAuth": "Mail: SMTP Auten Omogući",
+ "attributeMailSmtpUsername": "Mail: SMTP Korisničko ime",
+ "attributeMailSmtpPassword": "Mail: SMTP Lozinka",
+ "attributeUiDisableSavedCommands": "Komande",
+ "attributeUiDisableAttributes": "Kor.Int.: Onemogući atribute",
+ "attributeUiDisableGroups": "UI: Onemogući grupe",
+ "attributeUiDisableEvents": "UI:Onemogući Dogadjaje",
+ "attributeUiDisableVehicleFeatures": "KI: Onemogući Karakteristike vozila",
+ "attributeUiDisableDrivers": "UI: Onemogući Vozače",
+ "attributeUiDisableComputedAttributes": "Kor.Int. : Onemogući Izračunate atribute",
+ "attributeUiDisableCalendars": "UI: Onemogući Kalendare",
+ "attributeUiDisableMaintenance": "Korisnički interfejs: Onemogući Održavanje",
+ "attributeUiHidePositionAttributes": "KI:Sakrij poziciju atributa ",
+ "attributeUiDisableLoginLanguage": "UI: Onemogući jezik za prijavu",
+ "attributeNotificationTokens": "Tokeni obaveštenja",
+ "attributePopupInfo": "Popup Info",
+ "errorTitle": "Greška",
+ "errorGeneral": "Nevažeći parametri ili kršenje ograničenja",
+ "errorConnection": "Greška u konekciji",
+ "errorSocket": "Greška u konekciji",
+ "errorZero": "Ne može biti nula",
+ "userEmail": "Email",
+ "userPassword": "Lozinka",
+ "userAdmin": "Admin",
+ "userRemember": "Zapamti",
+ "userExpirationTime": "Ističe",
+ "userDeviceLimit": "Limit uređaja",
+ "userUserLimit": "Limit korisnika",
+ "userDeviceReadonly": "Uređaj readonly",
+ "userLimitCommands": "Limitiraj Komande",
+ "userDisableReports": "Onemogući izveštaje",
+ "userFixedEmail": "Nema Email promena",
+ "userToken": "Znak",
+ "userDeleteAccount": "Izbriši nalog",
+ "userTemporary": "Privremeno",
+ "loginTitle": "Prijava",
+ "loginLanguage": "Jezik",
+ "loginReset": "Resetuj Lozinku",
+ "loginRegister": "Registruj se",
+ "loginLogin": "Prijava",
+ "loginOpenId": "Prijava sa OpenID",
+ "loginFailed": "Neispravna email adresa ili lozinka",
+ "loginCreated": "Novi korisnik je registrovan",
+ "loginResetSuccess": "Proverite Vaš email",
+ "loginUpdateSuccess": "Nova lozinka je postavljena",
+ "loginLogout": "Odjava",
+ "loginLogo": "Logo",
+ "loginTotpCode": "Jednokratni kod lozinke",
+ "loginTotpKey": "Jednokratni ključ lozinke",
+ "devicesAndState": "Uređaji i Stanje ",
+ "deviceSelected": "Izabrani uređaj",
+ "deviceTitle": "Uređaji",
+ "devicePrimaryInfo": "Naziv uređaja",
+ "deviceSecondaryInfo": "Detalji uređaj",
+ "deviceIdentifier": "Identifikator",
+ "deviceModel": "Model",
+ "deviceContact": "Kontakt",
+ "deviceCategory": "Kategorija",
+ "deviceLastUpdate": "Poslednji kontakt",
+ "deviceCommand": "Komanda",
+ "deviceFollow": "Prati",
+ "deviceTotalDistance": "Ukupno rastojanje",
+ "deviceStatus": "Status",
+ "deviceStatusOnline": "Dostupan",
+ "deviceStatusOffline": "Nedostupan",
+ "deviceStatusUnknown": "Nepoznato",
+ "deviceRegisterFirst": "Registruj svoj prvi uređaj",
+ "deviceIdentifierHelp": "IMEI, serijski broj, ili neki drugi ID. Mora da bude isti kao identifikator koji uređaj koristi na serveru.",
+ "deviceShare": "Podeli Uređaj",
+ "groupDialog": "Grupa",
+ "groupParent": "Grupa",
+ "groupNoGroup": "Nema grupe",
+ "settingsTitle": "Podešavanja",
+ "settingsUser": "Nalog",
+ "settingsGroups": "Grupe",
+ "settingsServer": "Server",
+ "settingsUsers": "Korisnici",
+ "settingsDistanceUnit": "Jedinica razdaljine",
+ "settingsAltitudeUnit": "Jedinica visine",
+ "settingsSpeedUnit": "Jedinica brzine",
+ "settingsVolumeUnit": "Jedinica zapremine",
+ "settingsTwelveHourFormat": "12-časovni format",
+ "settingsCoordinateFormat": "Koordinatni Format",
+ "settingsServerVersion": "Verzija servera",
+ "settingsAppVersion": "Verzija aplikacije",
+ "settingsConnection": "Konekcija",
+ "settingsDarkMode": "Taman mod",
+ "settingsTotpEnable": "Ommogući jednokratnu lozinku",
+ "settingsTotpForce": "Forsiraj jednokratnu lozinku",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Interval Ažuriranja",
+ "settingsUpdateAvailable": "Postoji novo ažuriranje",
+ "settingsSupport": "Podrška",
+ "reportTitle": "Izveštaji",
+ "reportScheduled": "Zakazani izveštaji",
+ "reportDevice": "Uređaj",
+ "reportGroup": "Grupa",
+ "reportFrom": "Od",
+ "reportTo": "Do",
+ "reportShow": "Prikaži",
+ "reportClear": "Izbriši",
+ "linkGoogleMaps": "Google Mape",
+ "linkAppleMaps": "Apple Mape",
+ "linkStreetView": "Pogled na ulicu",
+ "positionFixTime": "Vreme javljanja",
+ "positionDeviceTime": "Vreme na uređaju",
+ "positionServerTime": "Vreme na serveru",
+ "positionValid": "Ispravno",
+ "positionAccuracy": "Tačnost",
+ "positionLatitude": "Geografska širina",
+ "positionLongitude": "Geografska dužina",
+ "positionAltitude": "Visina",
+ "positionSpeed": "Brzina",
+ "positionCourse": "Pravac",
+ "positionAddress": "Adresa",
+ "positionProtocol": "Protokol",
+ "positionDistance": "Razdaljina",
+ "positionRpm": "Obrtaji",
+ "positionFuel": "Gorivo",
+ "positionPower": "Napon",
+ "positionBattery": "Baterija",
+ "positionRaw": "Neobrađen",
+ "positionIndex": "Index",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "Sateliti",
+ "positionSatVisible": "Vidiljivi sateliti",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "Roming",
+ "positionEvent": "Događaj",
+ "positionAlarm": "Alarm",
+ "positionStatus": "Status",
+ "positionOdometer": "Kilometar sat",
+ "positionServiceOdometer": "Servisni odometar",
+ "positionTripOdometer": "Putni odometar",
+ "positionHours": "Sati",
+ "positionSteps": "Koraci",
+ "positionInput": "Ulaz",
+ "positionHeartRate": "Otkucaj srca",
+ "positionOutput": "Izlaz",
+ "positionBatteryLevel": "Jačina baterije",
+ "positionFuelConsumption": "Ukupna potrošnja",
+ "positionRfid": "RFID",
+ "positionVersionFw": "Verzija firmvera",
+ "positionVersionHw": "Verzija hardvera",
+ "positionIgnition": "Kontakt",
+ "positionFlags": "Oznake",
+ "positionCharge": "Punjenje",
+ "positionIp": "IP adresa",
+ "positionArchive": "Arhiva",
+ "positionVin": "Broj šasije",
+ "positionApproximate": "Približno",
+ "positionThrottle": "Gas",
+ "positionMotion": "Kretanje",
+ "positionArmed": "Naoružan",
+ "positionAcceleration": "Ubrzanje",
+ "positionTemp": "Temperatura",
+ "positionDeviceTemp": "Temperatura uređaja",
+ "positionCoolantTemp": "Temperatura antifriza",
+ "positionOperator": "Operator",
+ "positionCommand": "Komanda",
+ "positionBlocked": "Blokirano",
+ "positionDtcs": "DTCs",
+ "positionObdSpeed": "OBD brzina",
+ "positionObdOdometer": "OBD odometar",
+ "positionDrivingTime": "Vreme vožnje",
+ "positionDriverUniqueId": "Jedinstveni ID vozača",
+ "positionCard": "Kartica",
+ "positionImage": "Slika",
+ "positionVideo": "Video",
+ "positionAudio": "Zvuk",
+ "serverTitle": "Podešavanja Servera",
+ "serverZoom": "Zumiranje",
+ "serverRegistration": "Registracija",
+ "serverReadonly": "Readonly verzija",
+ "serverForceSettings": "Obavezna podešavanja",
+ "serverAnnouncement": "Objava",
+ "serverName": "Ime Servera",
+ "serverDescription": "Opis Servera",
+ "serverColorPrimary": "Osnovna Boja",
+ "serverColorSecondary": "Sekundarna Boja",
+ "serverLogo": "Logo slika",
+ "serverLogoInverted": "Obrnuta logo slika",
+ "serverChangeDisable": "Onemogući Server promene",
+ "serverDisableShare": "Onemogući deljenje vozila",
+ "mapTitle": "Mapa",
+ "mapActive": "Aktivne mape",
+ "mapOverlay": "Preklapanje mape",
+ "mapOverlayCustom": "Prilagođen prikaz",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API Key",
+ "mapOpenWeatherClouds": "OpenWeather Clouds",
+ "mapOpenWeatherPrecipitation": "OpenWeather Padavine",
+ "mapOpenWeatherPressure": "OpenWeather Pritisak",
+ "mapOpenWeatherWind": "OpenWeather Vetar",
+ "mapOpenWeatherTemperature": "OpenWeather Temperatura",
+ "mapLayer": "Vrsta Mape",
+ "mapCustom": "Prilagođeno (XYZ)",
+ "mapCustomArcgis": "Prilagođeno (ArcGIS)",
+ "mapCustomLabel": "Prilagođena mapa",
+ "mapCarto": "Carto Basemaps",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hibrid",
+ "mapGoogleSatellite": "Google Satelit",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Bing Maps Key",
+ "mapBingRoad": "Bing Maps Road",
+ "mapBingAerial": "Bing Maps Aerial",
+ "mapBingHybrid": "Bing Hibrid Mapa",
+ "mapBaidu": "Badu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Yandex Mapa",
+ "mapYandexSat": "Yandex Satelit",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox Streets",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox Outdoors",
+ "mapMapboxSatellite": "Mapbox Satellite",
+ "mapMapboxKey": "Mapbox Access Token",
+ "mapMapTilerBasic": "MapTiler Basic",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "MapTiler API Key",
+ "mapLocationIqStreets": "LocationIQ Ulice",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ Access Token",
+ "mapTomTomBasic": "TomTom Basic",
+ "mapTomTomFlow": "TomTom Saobraćaj",
+ "mapTomTomIncidents": "TomTom Saobraćajni incidenti",
+ "mapTomTomKey": "TomTom API Key",
+ "mapHereBasic": "Here Basic",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satelit",
+ "mapHereFlow": "Here Saobraćaj",
+ "mapHereKey": "Here API Key",
+ "mapShapePolygon": "Višeugao",
+ "mapShapeCircle": "Krug",
+ "mapShapePolyline": "Višelinijski",
+ "mapLiveRoutes": "Validne rute",
+ "mapDirection": "Pokaži pravac",
+ "mapCurrentLocation": "Trenutna lokacija",
+ "mapPoiLayer": "POI sloj",
+ "mapClustering": "Klasterovanje markera",
+ "mapOnSelect": "Prikaži Mapu pri selekciji",
+ "mapDefault": "Osnovna mapa",
+ "stateTitle": "Stanje",
+ "stateName": "Parametar",
+ "stateValue": "Vrednost",
+ "commandTitle": "Komanda",
+ "commandSend": "Pošalji",
+ "commandSent": "Komanda poslata",
+ "commandQueued": "Komanda na čekanju",
+ "commandUnit": "Jedinica",
+ "commandCustom": "Prilagođena komanda",
+ "commandDeviceIdentification": "Identifikacija uređaja",
+ "commandPositionSingle": "Izveštaj za jednog",
+ "commandPositionPeriodic": "Periodično izveštavanje",
+ "commandPositionStop": "Prekini izveštavanja",
+ "commandEngineStop": "Zaustavi motor",
+ "commandEngineResume": "Pokreni motor",
+ "commandAlarmArm": "Omogući alarm",
+ "commandAlarmDisarm": "Onemogući alarm",
+ "commandAlarmDismiss": "Onemogući alarm",
+ "commandSetTimezone": "Podesi vremensku zonu",
+ "commandRequestPhoto": "Zahtevaj fotografiju",
+ "commandPowerOff": "Isključi uređjaj",
+ "commandRebootDevice": "Ponovo pokreni uređaj",
+ "commandFactoryReset": "Fabrički reset",
+ "commandSendSms": "Pošalji SMS",
+ "commandSendUssd": "Pošalji USSD",
+ "commandSosNumber": "Podesi SOS broj",
+ "commandSilenceTime": "Podesi nečujno vreme ",
+ "commandSetPhonebook": "Podesi kontakte",
+ "commandVoiceMessage": "Glasovna poruka",
+ "commandOutputControl": "Kontrola izlaza",
+ "commandVoiceMonitoring": "Monitoring glasa",
+ "commandSetAgps": "Podesi AGPS",
+ "commandSetIndicator": "Postavi indikator",
+ "commandConfiguration": "Konfiguracija",
+ "commandGetVersion": "Proveri verziju",
+ "commandFirmwareUpdate": "Ažuriraj firmver",
+ "commandSetConnection": "Podesi konekciju",
+ "commandSetOdometer": "Podesi odometar",
+ "commandGetModemStatus": "Zatraži status modema",
+ "commandGetDeviceStatus": "Zatraži status uređaja",
+ "commandSetSpeedLimit": "Podesi ograničenje brzine",
+ "commandModePowerSaving": "Mod čuvanja energije",
+ "commandModeDeepSleep": "Mod dubokog spavanja",
+ "commandAlarmGeofence": "Postavite alarm geoograde",
+ "commandAlarmBattery": "Podesite alarm baterije",
+ "commandAlarmSos": "Postavite SOS alarm",
+ "commandAlarmRemove": "Postavite alarm uklanjanja",
+ "commandAlarmClock": "Postavite alarm sata",
+ "commandAlarmSpeed": "Postavite alarm brzine",
+ "commandAlarmFall": "Postavite alarm padanja",
+ "commandAlarmVibration": "Postavite alarm vibracije",
+ "commandFrequency": "Frekvencija",
+ "commandTimezone": "Vremenska zona ",
+ "commandMessage": "Poruka",
+ "commandRadius": "Radius",
+ "commandEnable": "Omogući",
+ "commandData": "Podaci",
+ "commandIndex": "Lista",
+ "commandPhone": "Broj telefona",
+ "commandServer": "Server",
+ "commandPort": "Port",
+ "eventAll": "Svi događaji",
+ "eventDeviceOnline": "Status online",
+ "eventDeviceUnknown": "Status nepoznat",
+ "eventDeviceOffline": "Status offline",
+ "eventDeviceInactive": "Uređaj je neaktivan",
+ "eventQueuedCommandSent": "Komanda na čekanju",
+ "eventDeviceMoving": "Uređaj u pokretu",
+ "eventDeviceStopped": "Uređaj zaustavljen",
+ "eventDeviceOverspeed": "Granica brzine je prekoračena",
+ "eventDeviceFuelDrop": "Pad nivoa goriva",
+ "eventDeviceFuelIncrease": "Dolivanje goriva",
+ "eventCommandResult": "Stanje komande",
+ "eventGeofenceEnter": "Ulazak u geoogradu",
+ "eventGeofenceExit": "Izlazak iz geoograde",
+ "eventAlarm": "Alarm",
+ "eventIgnitionOn": "Kontakt uključen",
+ "eventIgnitionOff": "Kontakt isključen",
+ "eventMaintenance": "Potrebno održavanje",
+ "eventTextMessage": "Tekstualma poruka primljena",
+ "eventDriverChanged": "Vozač promenjen",
+ "eventMedia": "Medija",
+ "eventsScrollToLast": "Skroluj do zadnjeg",
+ "eventsSoundEvents": "Zvuci događaja",
+ "eventsSoundAlarms": "Zvuci alarma",
+ "alarmGeneral": "Opšte",
+ "alarmSos": "SOS",
+ "alarmVibration": "Vibracija",
+ "alarmMovement": "Kretanje",
+ "alarmLowspeed": "Mala brzina",
+ "alarmOverspeed": "Prekoračenje brzine",
+ "alarmFallDown": "Pad",
+ "alarmLowPower": "Nizak napon",
+ "alarmLowBattery": "Slaba baterija",
+ "alarmFault": "Greška",
+ "alarmPowerOff": "Alarm isključen",
+ "alarmPowerOn": "Alarm uključen",
+ "alarmDoor": "Vrata",
+ "alarmLock": "Zaključan",
+ "alarmUnlock": "Otključan",
+ "alarmGeofence": "Geoograda",
+ "alarmGeofenceEnter": "Ulazak u geeogradu",
+ "alarmGeofenceExit": "Izlaz iz geoograde",
+ "alarmGpsAntennaCut": "GPS antena u prekidu",
+ "alarmAccident": "Nesreća",
+ "alarmTow": "Odvlačenje",
+ "alarmIdle": "Na čekanju",
+ "alarmHighRpm": "Visoki obrtaji",
+ "alarmHardAcceleration": "Naglo ubrzanje",
+ "alarmHardBraking": "Naglo kočenje",
+ "alarmHardCornering": "Naglo skretanje",
+ "alarmLaneChange": "Promena trake",
+ "alarmFatigueDriving": "Fatigue Driving",
+ "alarmPowerCut": "Nema napajanja",
+ "alarmPowerRestored": "Napajanje obnovljeno",
+ "alarmJamming": "Ometanje signala",
+ "alarmTemperature": "Temperatura",
+ "alarmParking": "Parking",
+ "alarmBonnet": "Hauba",
+ "alarmFootBrake": "Nožna kočnica",
+ "alarmFuelLeak": "Curenje goriva",
+ "alarmTampering": "Tampering",
+ "alarmRemoving": "Uklanjanje",
+ "notificationType": "Tip obaveštenja",
+ "notificationAlways": "Svi uređaji",
+ "notificationNotificators": "Kanali",
+ "notificatorCommand": "Komanda",
+ "notificatorWeb": "Internet",
+ "notificatorMail": "Pošta",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Istorija kretanja",
+ "reportCombined": "Kombinovano",
+ "reportRoute": "Ruta",
+ "reportEvents": "Događaji",
+ "reportTrips": "Vožnje",
+ "reportStops": "Zaustavljanja",
+ "reportSummary": "Izveštaj",
+ "reportDaily": "Dnevni pregled",
+ "reportChart": "Grafikon",
+ "reportConfigure": "Konfiguracija",
+ "reportEventTypes": "Tip događaja",
+ "reportChartType": "Tip grafikona",
+ "reportShowMarkers": "Pokaži markere",
+ "reportExport": "Izvezi",
+ "reportEmail": "Email Izveštaj",
+ "reportSchedule": "Zakazivanje",
+ "reportPeriod": "Period",
+ "reportCustom": "Prilagodjeno",
+ "reportToday": "Danas",
+ "reportYesterday": "Juče",
+ "reportThisWeek": "Ove nedelje",
+ "reportPreviousWeek": "Prethodna nedelja",
+ "reportThisMonth": "Ovaj mesec",
+ "reportPreviousMonth": "Prethodni mesec",
+ "reportDeviceName": "Ime uređaja",
+ "reportAverageSpeed": "Prosečna brzina",
+ "reportMaximumSpeed": "Maksimalna brzina",
+ "reportEngineHours": "Radni sati",
+ "reportDuration": "Trajanje",
+ "reportStartDate": "Početni datum",
+ "reportStartTime": "Startno vreme",
+ "reportStartAddress": "Početna adresa",
+ "reportEndTime": "Završno vreme",
+ "reportEndAddress": "Krajnja adresa",
+ "reportSpentFuel": "Potrošeno goriva",
+ "reportStartOdometer": "Odometar početak",
+ "reportEndOdometer": "Odometar kraj",
+ "statisticsTitle": "Statistika",
+ "statisticsCaptureTime": "Vreme slikanja",
+ "statisticsActiveUsers": "Aktivni korisnici",
+ "statisticsActiveDevices": "Aktivni uređaji",
+ "statisticsRequests": "Zahtevi",
+ "statisticsMessagesReceived": "Primljene poruke",
+ "statisticsMessagesStored": "Sačuvane poruke",
+ "statisticsGeocoder": "Geokoder upiti",
+ "statisticsGeolocation": "Geolokacijski upiti",
+ "categoryArrow": "Strelica",
+ "categoryDefault": "Osnovni",
+ "categoryAnimal": "Životinja",
+ "categoryBicycle": "Bicikl",
+ "categoryBoat": "Brod",
+ "categoryBus": "Autobus",
+ "categoryCar": "Auto",
+ "categoryCamper": "Kamper",
+ "categoryCrane": "Kran",
+ "categoryHelicopter": "Helikopter",
+ "categoryMotorcycle": "Motor",
+ "categoryOffroad": "Terensko",
+ "categoryPerson": "Osoba",
+ "categoryPickup": "Pikap",
+ "categoryPlane": "Avion",
+ "categoryShip": "Brod",
+ "categoryTractor": "Traktor",
+ "categoryTrain": "Voz",
+ "categoryTram": "Tramvaj",
+ "categoryTrolleybus": "Trolejbus",
+ "categoryTruck": "Kamion",
+ "categoryVan": "Kombi",
+ "categoryScooter": "Skuter",
+ "maintenanceStart": "Početak",
+ "maintenancePeriod": "Period"
+} \ No newline at end of file
diff --git a/src/resources/l10n/sv.json b/src/resources/l10n/sv.json
new file mode 100644
index 00000000..e6ee943b
--- /dev/null
+++ b/src/resources/l10n/sv.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "Laddar...",
+ "sharedHide": "Göm",
+ "sharedSave": "Spara",
+ "sharedUpload": "Upload",
+ "sharedSet": "Set",
+ "sharedCancel": "Avbryt",
+ "sharedCopy": "Copy",
+ "sharedAdd": "Lägg till",
+ "sharedEdit": "Redigera",
+ "sharedRemove": "Radera",
+ "sharedRemoveConfirm": "Radera objekt?",
+ "sharedNoData": "Data saknas",
+ "sharedSubject": "Subject",
+ "sharedYes": "Ja",
+ "sharedNo": "Nej",
+ "sharedKm": "km",
+ "sharedMi": "mi",
+ "sharedNmi": "nmi",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "kn",
+ "sharedKmh": "km/h",
+ "sharedMph": "mph",
+ "sharedHour": "tim",
+ "sharedMinute": "min",
+ "sharedSecond": "sek",
+ "sharedDays": "d",
+ "sharedHours": "tim",
+ "sharedMinutes": "min",
+ "sharedDecimalDegrees": "Decimalgrader",
+ "sharedDegreesDecimalMinutes": "Decimalgrader Minuter",
+ "sharedDegreesMinutesSeconds": "Decimalgrader Sekunder",
+ "sharedName": "Namn",
+ "sharedDescription": "Beskrivning",
+ "sharedSearch": "Sök",
+ "sharedIconScale": "Ikonskala",
+ "sharedGeofence": "Geofence",
+ "sharedGeofences": "Geofences",
+ "sharedCreateGeofence": "Skapa Geofence",
+ "sharedNotifications": "Notiser",
+ "sharedNotification": "Notis",
+ "sharedAttributes": "Attributer",
+ "sharedAttribute": "Attribut",
+ "sharedDrivers": "Förare",
+ "sharedDriver": "Förare",
+ "sharedArea": "Område",
+ "sharedSound": "Ljudnotis",
+ "sharedType": "Typ",
+ "sharedDistance": "Distans",
+ "sharedHourAbbreviation": "h",
+ "sharedMinuteAbbreviation": "m",
+ "sharedSecondAbbreviation": "s",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "l",
+ "sharedGallonAbbreviation": "gallon",
+ "sharedLiter": "Liter",
+ "sharedImpGallon": "Imp.Gallon",
+ "sharedUsGallon": "U.S. Gallon",
+ "sharedLiterPerHourAbbreviation": "l/h",
+ "sharedGetMapState": "Få karttillstånd",
+ "sharedComputedAttribute": "Computed Attribute",
+ "sharedComputedAttributes": "Computed Attributes",
+ "sharedCheckComputedAttribute": "Check Computed Attribute",
+ "sharedExpression": "Uttryck",
+ "sharedDevice": "Enhet",
+ "sharedTest": "Test",
+ "sharedTestNotification": "Skicka testnortis",
+ "sharedTestNotificators": "Testkanaler",
+ "sharedTestExpression": "Testuttryck",
+ "sharedCalendar": "Kalender",
+ "sharedCalendars": "Kalendrar",
+ "sharedFile": "Fil",
+ "sharedSearchDevices": "Sök Enheter",
+ "sharedSortBy": "Sortera efter",
+ "sharedFilterMap": "Filtrera på kartan",
+ "sharedSelectFile": "Välj fil",
+ "sharedPhone": "Telefon",
+ "sharedRequired": "Nödvändig",
+ "sharedPreferences": "Inställningar",
+ "sharedPermissions": "Rättigheter",
+ "sharedConnections": "Anslutningar",
+ "sharedExtra": "Extra",
+ "sharedPrimary": "Primär",
+ "sharedSecondary": "Sekundär",
+ "sharedTypeString": "String",
+ "sharedTypeNumber": "Number",
+ "sharedTypeBoolean": "Boolean",
+ "sharedTimezone": "Tidszon",
+ "sharedInfoTitle": "Info",
+ "sharedSavedCommand": "Sparade kommando",
+ "sharedSavedCommands": "Sparade kommandon",
+ "sharedNew": "Ny",
+ "sharedShowAddress": "Visa Adress",
+ "sharedShowDetails": "Mer Detaljer",
+ "sharedDisabled": "Inaktiverad",
+ "sharedMaintenance": "Underhåll",
+ "sharedDeviceAccumulators": "Ackumulatorer",
+ "sharedAlarms": "Alarm",
+ "sharedLocation": "Plats",
+ "sharedImport": "Import",
+ "sharedColumns": "Kolumn",
+ "sharedDropzoneText": "Dra och släpp en fil här eller klicka",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "Enkel",
+ "calendarRecurrence": "Upprepning",
+ "calendarOnce": "En gång",
+ "calendarDaily": "Dagligen",
+ "calendarWeekly": "Varje vecka",
+ "calendarMonthly": "Varje Månad",
+ "calendarDays": "Dagar",
+ "calendarSunday": "Söndag",
+ "calendarMonday": "Måndag",
+ "calendarTuesday": "Tisdag",
+ "calendarWednesday": "Onsdag",
+ "calendarThursday": "Torsdag",
+ "calendarFriday": "Fredag",
+ "calendarSaturday": "Lördag",
+ "attributeShowGeofences": "Show Geofences",
+ "attributeSpeedLimit": "Hastighetsbegränsning",
+ "attributeFuelDropThreshold": "Bränsleminskningströskel",
+ "attributeFuelIncreaseThreshold": "Bränsleökningsgräns",
+ "attributePolylineDistance": "Polylinedistans",
+ "attributeReportIgnoreOdometer": "Rapport: Ignorera Odometer",
+ "attributeWebReportColor": "Web: Rapport färg",
+ "attributeDevicePassword": "Enhetslösenord",
+ "attributeDeviceImage": "Enhetsbild",
+ "attributeDeviceInactivityStart": "Enhetens inaktivitetsstart",
+ "attributeDeviceInactivityPeriod": "Enhetens inaktivitetslängd",
+ "attributeProcessingCopyAttributes": "Processing:",
+ "attributeColor": "Färg",
+ "attributeWebLiveRouteLength": "Web: Live Route Length",
+ "attributeWebSelectZoom": "Web: Zoom On Select",
+ "attributeWebMaxZoom": "Webb: Maximal Zoom",
+ "attributeTelegramChatId": "Telegram Chat-ID",
+ "attributePushoverUserKey": "Pushover användarnyckel",
+ "attributePushoverDeviceNames": "Pushover Enhetsnamn",
+ "attributeMailSmtpHost": "Mail: SMTP Host",
+ "attributeMailSmtpPort": "Mail: SMTP Port",
+ "attributeMailSmtpStarttlsEnable": "Mail: SMTP STARTTLS Enable",
+ "attributeMailSmtpStarttlsRequired": "Mail: SMTP STARTTLS Required",
+ "attributeMailSmtpSslEnable": "Mail: SMTP SSL Enable",
+ "attributeMailSmtpSslTrust": "Mail: SMTP SSL Trust",
+ "attributeMailSmtpSslProtocols": "Mail: SMTP SSL Protocols",
+ "attributeMailSmtpFrom": "Mail: SMTP Från",
+ "attributeMailSmtpAuth": "Mail: SMTP Auth Enable",
+ "attributeMailSmtpUsername": "Mail: SMTP Användarnamn",
+ "attributeMailSmtpPassword": "Mail: SMTP Lösenord",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: Inaktivera attribut",
+ "attributeUiDisableGroups": "UI: Inaktivera Grupper",
+ "attributeUiDisableEvents": "UI: Inaktivera Händelser",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "UI: Inaktivera Förare",
+ "attributeUiDisableComputedAttributes": "UI: Inaktivera Beräknade Attribut",
+ "attributeUiDisableCalendars": "UI: Inaktivera Kalendrar",
+ "attributeUiDisableMaintenance": "UI: Inaktivera Underhåll",
+ "attributeUiHidePositionAttributes": "UI: Göm Positionsattribut",
+ "attributeUiDisableLoginLanguage": "UI: Inaktivera Loginspråk",
+ "attributeNotificationTokens": "Notistoken",
+ "attributePopupInfo": "Popup Info",
+ "errorTitle": "Fel",
+ "errorGeneral": "Invalid parameters or constraints violation",
+ "errorConnection": "Anslutningsfel",
+ "errorSocket": "Anslutningsfel till Web socket",
+ "errorZero": "Kan inte vara noll",
+ "userEmail": "Epost",
+ "userPassword": "Lösenord",
+ "userAdmin": "Admin",
+ "userRemember": "Kom ihåg",
+ "userExpirationTime": "Utgång",
+ "userDeviceLimit": "Enhetsbegränsning",
+ "userUserLimit": "Användarbegränsning",
+ "userDeviceReadonly": "Enhet endast läsbar",
+ "userLimitCommands": "Begränsa kommandon",
+ "userDisableReports": "Inaktivera Rappporter",
+ "userFixedEmail": "Ingen förändring av Email",
+ "userToken": "Token",
+ "userDeleteAccount": "Ta bor Konto",
+ "userTemporary": "Temporary",
+ "loginTitle": "Logga in",
+ "loginLanguage": "Språk",
+ "loginReset": "Återställ Lösenord",
+ "loginRegister": "Registrera",
+ "loginLogin": "Logga in",
+ "loginOpenId": "Login with OpenID",
+ "loginFailed": "Felaktig epostadress eller lösenord",
+ "loginCreated": "Ny användare har blivit registrerad",
+ "loginResetSuccess": "Kontrollera din Email",
+ "loginUpdateSuccess": "Nytt lösenord är sparat",
+ "loginLogout": "Logga ut",
+ "loginLogo": "Logo",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "Enhet och tillstånd",
+ "deviceSelected": "Selected Device",
+ "deviceTitle": "Enhet",
+ "devicePrimaryInfo": "Device Title",
+ "deviceSecondaryInfo": "Device Detail",
+ "deviceIdentifier": "Identifierare",
+ "deviceModel": "Model",
+ "deviceContact": "Kontakt",
+ "deviceCategory": "Kategori",
+ "deviceLastUpdate": "Senast uppdaterad",
+ "deviceCommand": "Kommando",
+ "deviceFollow": "Följ",
+ "deviceTotalDistance": "Total distans",
+ "deviceStatus": "Status",
+ "deviceStatusOnline": "Online",
+ "deviceStatusOffline": "Offline",
+ "deviceStatusUnknown": "Okänd",
+ "deviceRegisterFirst": "Register your first device",
+ "deviceIdentifierHelp": "IMEI, serial number or other id. It has to match the identifier device reports to the server.",
+ "deviceShare": "Share Device",
+ "groupDialog": "Grupp",
+ "groupParent": "Grupp",
+ "groupNoGroup": "Ingen grupp",
+ "settingsTitle": "Inställningar",
+ "settingsUser": "Konto",
+ "settingsGroups": "Grupper",
+ "settingsServer": "Server",
+ "settingsUsers": "Användare",
+ "settingsDistanceUnit": "Distansenhet",
+ "settingsAltitudeUnit": "Höjdenhet",
+ "settingsSpeedUnit": "Hastighetsenhet",
+ "settingsVolumeUnit": "Volymenhet",
+ "settingsTwelveHourFormat": "12-timmars format",
+ "settingsCoordinateFormat": "Kordinatformat",
+ "settingsServerVersion": "Server Version",
+ "settingsAppVersion": "App Version",
+ "settingsConnection": "Connection",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "Rapport",
+ "reportScheduled": "Scheduled Reports",
+ "reportDevice": "Enhet",
+ "reportGroup": "Grupp",
+ "reportFrom": "Från",
+ "reportTo": "Till",
+ "reportShow": "Visa",
+ "reportClear": "Rensa",
+ "linkGoogleMaps": "Google Maps",
+ "linkAppleMaps": "Apple Maps",
+ "linkStreetView": "Gatuvy",
+ "positionFixTime": "Låst tid",
+ "positionDeviceTime": "Enhetstid",
+ "positionServerTime": "Servertid",
+ "positionValid": "Giltig",
+ "positionAccuracy": "Noggrannhet",
+ "positionLatitude": "Latitud",
+ "positionLongitude": "Longitud",
+ "positionAltitude": "Höjdnivå",
+ "positionSpeed": "Hastighet",
+ "positionCourse": "Kurs",
+ "positionAddress": "Adress",
+ "positionProtocol": "Protokoll",
+ "positionDistance": "Distans",
+ "positionRpm": "RPM",
+ "positionFuel": "Bränsle",
+ "positionPower": "Power",
+ "positionBattery": "Batteri",
+ "positionRaw": "Rå",
+ "positionIndex": "Index",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "Sateliter",
+ "positionSatVisible": "Synliga sateliter",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "Roaming",
+ "positionEvent": "Händelser",
+ "positionAlarm": "Larm",
+ "positionStatus": "Status",
+ "positionOdometer": "Odometer",
+ "positionServiceOdometer": "Service Odometer",
+ "positionTripOdometer": "Trip",
+ "positionHours": "tim",
+ "positionSteps": "Steg",
+ "positionInput": "Input",
+ "positionHeartRate": "Heart Rate",
+ "positionOutput": "Output",
+ "positionBatteryLevel": "Batteri nivå",
+ "positionFuelConsumption": "Bränsle åtgång",
+ "positionRfid": "RFID",
+ "positionVersionFw": "Firmware version",
+ "positionVersionHw": "Hardware version",
+ "positionIgnition": "Tändning",
+ "positionFlags": "Flaggor",
+ "positionCharge": "Laddar",
+ "positionIp": "IP",
+ "positionArchive": "Arkivera",
+ "positionVin": "VIN",
+ "positionApproximate": "Uppskattningsvis",
+ "positionThrottle": "Gasreglage",
+ "positionMotion": "Rörelse",
+ "positionArmed": "Larmad",
+ "positionAcceleration": "Acceleration",
+ "positionTemp": "Temperatur",
+ "positionDeviceTemp": "Enhetstemperatur",
+ "positionCoolantTemp": "Coolant Temperature",
+ "positionOperator": "Operatör",
+ "positionCommand": "Kommando",
+ "positionBlocked": "Blockerad ",
+ "positionDtcs": "DTCs",
+ "positionObdSpeed": "ODB Hastighet",
+ "positionObdOdometer": "ODB Odometer",
+ "positionDrivingTime": "Driving Time",
+ "positionDriverUniqueId": "Unikt Förar-id",
+ "positionCard": "Card",
+ "positionImage": "Bild",
+ "positionVideo": "Video",
+ "positionAudio": "Ljud",
+ "serverTitle": "Serverinställningar",
+ "serverZoom": "Zoom",
+ "serverRegistration": "Registrera",
+ "serverReadonly": "Endast läsbar",
+ "serverForceSettings": "Tvinga inställning",
+ "serverAnnouncement": "Tillkännagivande",
+ "serverName": "Server Name",
+ "serverDescription": "Server Description",
+ "serverColorPrimary": "Color Primary",
+ "serverColorSecondary": "Color Secondary",
+ "serverLogo": "Logo Image",
+ "serverLogoInverted": "Inverted Logo Image",
+ "serverChangeDisable": "Disable Server Change",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "Karta",
+ "mapActive": "Aktiva Kartor",
+ "mapOverlay": "Kartöverlägg",
+ "mapOverlayCustom": "Specialöverlägg",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API-nyckel",
+ "mapOpenWeatherClouds": "OpenWeather Moln",
+ "mapOpenWeatherPrecipitation": "OpenWeather Nederbörd",
+ "mapOpenWeatherPressure": "OpenWeather Lufttryck",
+ "mapOpenWeatherWind": "OpenWeather Vind",
+ "mapOpenWeatherTemperature": "OpenWeather Temperatur",
+ "mapLayer": "Kartlager",
+ "mapCustom": "Special (XYZ)",
+ "mapCustomArcgis": "Special (ArcGIS)",
+ "mapCustomLabel": "Specialkarta",
+ "mapCarto": "Carto grundkarta",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Bing Maps Nyckel",
+ "mapBingRoad": "Bing Maps Väg",
+ "mapBingAerial": "Bing Maps Flygfoto",
+ "mapBingHybrid": "Bing kartor hybrid",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Yandex kartor",
+ "mapYandexSat": "Yandex satellit",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox Streets",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox Outdoors",
+ "mapMapboxSatellite": "Mapbox Satellite",
+ "mapMapboxKey": "Mapbox Access Token",
+ "mapMapTilerBasic": "MapTiler Bas",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "MapTiler API Key",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ Access Token",
+ "mapTomTomBasic": "TomTom Bas",
+ "mapTomTomFlow": "TomTom Traffic Flow",
+ "mapTomTomIncidents": "TomTom Traffic Incidents",
+ "mapTomTomKey": "TomTom API Key",
+ "mapHereBasic": "Here Bas",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satellite",
+ "mapHereFlow": "Here Traffic Flow",
+ "mapHereKey": "Here API Key",
+ "mapShapePolygon": "Polygon",
+ "mapShapeCircle": "Cirkel",
+ "mapShapePolyline": "Polyline",
+ "mapLiveRoutes": "Liverutter",
+ "mapDirection": "Show Direction",
+ "mapCurrentLocation": "Nuvarande position",
+ "mapPoiLayer": "POI Lager",
+ "mapClustering": "Markörssamling",
+ "mapOnSelect": "Visa karta på urval",
+ "mapDefault": "Default Map",
+ "stateTitle": "Status",
+ "stateName": "Attribut",
+ "stateValue": "Värde",
+ "commandTitle": "Kommando",
+ "commandSend": "Skicka",
+ "commandSent": "Kommando sänt",
+ "commandQueued": "Kommando i kö",
+ "commandUnit": "Enhet",
+ "commandCustom": "Anpassat kommandot",
+ "commandDeviceIdentification": "Enhetsidentifikation",
+ "commandPositionSingle": "Enkel rapportering",
+ "commandPositionPeriodic": "Periodisk rapport",
+ "commandPositionStop": "Stoppa rapportering",
+ "commandEngineStop": "Motor Stopp",
+ "commandEngineResume": "Motor Återuppta",
+ "commandAlarmArm": "Slå på larm",
+ "commandAlarmDisarm": "Slå av larm",
+ "commandAlarmDismiss": "Avvisa larm",
+ "commandSetTimezone": "Ställ in tidszon",
+ "commandRequestPhoto": "Begär fotografi",
+ "commandPowerOff": "Stäng av enheten",
+ "commandRebootDevice": "Starta om enhet",
+ "commandFactoryReset": "Fabriksåterställning",
+ "commandSendSms": "Skicka SMS",
+ "commandSendUssd": "Skicka USSD",
+ "commandSosNumber": "Ställ in SOS nummer",
+ "commandSilenceTime": "Ställ in stilleståndstid",
+ "commandSetPhonebook": "Ställ in telefonbok",
+ "commandVoiceMessage": "Typ av notis",
+ "commandOutputControl": "Utgångskontroll",
+ "commandVoiceMonitoring": "Röstövervakning",
+ "commandSetAgps": "Ställ in AGPS",
+ "commandSetIndicator": "Ställ in indikator",
+ "commandConfiguration": "Konfigurering",
+ "commandGetVersion": "Hämta version",
+ "commandFirmwareUpdate": "Uppdatera programvara",
+ "commandSetConnection": "Ställ in anslutning",
+ "commandSetOdometer": "Ställ in Odometer",
+ "commandGetModemStatus": "Hämta modemstatus",
+ "commandGetDeviceStatus": "Hämta enhetsstatus",
+ "commandSetSpeedLimit": "Ställ in hastighetsgräns",
+ "commandModePowerSaving": "Strömsparläge",
+ "commandModeDeepSleep": "Ställ in djupviloläge",
+ "commandAlarmGeofence": "Ställ in Geofencealarm",
+ "commandAlarmBattery": "Ställ in batterinivåalarm",
+ "commandAlarmSos": "Stöll in SOS-larm",
+ "commandAlarmRemove": "Ställ in Ta bort larm",
+ "commandAlarmClock": "Ställ in klockalarm",
+ "commandAlarmSpeed": "Ställ in hastighetslarm",
+ "commandAlarmFall": "Ställ in fallarm",
+ "commandAlarmVibration": "Ställ in vibrationslarm",
+ "commandFrequency": "Frekvens",
+ "commandTimezone": "Tidszon Offset",
+ "commandMessage": "Meddelande",
+ "commandRadius": "Radie",
+ "commandEnable": "Aktivera",
+ "commandData": "Data",
+ "commandIndex": "Register",
+ "commandPhone": "Telefonnummer",
+ "commandServer": "Server",
+ "commandPort": "Port",
+ "eventAll": "Alla event",
+ "eventDeviceOnline": "Status online",
+ "eventDeviceUnknown": "Status okänd",
+ "eventDeviceOffline": "Status offline",
+ "eventDeviceInactive": "Enhet inaktiverad",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "Enheten i rörelse",
+ "eventDeviceStopped": "Enheten stannade",
+ "eventDeviceOverspeed": "Hastighetsgränsen överskreds",
+ "eventDeviceFuelDrop": "Bränslenivåminskning",
+ "eventDeviceFuelIncrease": "Bränslenivåökning",
+ "eventCommandResult": "Kommando resultat",
+ "eventGeofenceEnter": "Ankommit geofence",
+ "eventGeofenceExit": "Lämnat geofence",
+ "eventAlarm": "Larm",
+ "eventIgnitionOn": "Tändning påslagen",
+ "eventIgnitionOff": "Tändning avslagen",
+ "eventMaintenance": "Underhåll krävs",
+ "eventTextMessage": "Textmeddelande mottaget",
+ "eventDriverChanged": "Förarbyte",
+ "eventMedia": "Media",
+ "eventsScrollToLast": "Scrolla till sista",
+ "eventsSoundEvents": "Sound Events",
+ "eventsSoundAlarms": "Sound Alarms",
+ "alarmGeneral": "Generellt",
+ "alarmSos": "SOS",
+ "alarmVibration": "Vibration",
+ "alarmMovement": "Rörelse",
+ "alarmLowspeed": "Låg hastighet",
+ "alarmOverspeed": "För hög hastighet",
+ "alarmFallDown": "Fall",
+ "alarmLowPower": "Låg effekt",
+ "alarmLowBattery": "Låg laddnivå",
+ "alarmFault": "Fel",
+ "alarmPowerOff": "Avstängd",
+ "alarmPowerOn": "Påslagen",
+ "alarmDoor": "Dörr",
+ "alarmLock": "Lås",
+ "alarmUnlock": "Lås upp",
+ "alarmGeofence": "Geofence",
+ "alarmGeofenceEnter": "Ankom geofence",
+ "alarmGeofenceExit": "Lämnade geofence",
+ "alarmGpsAntennaCut": "Avklippt GPS-antenn",
+ "alarmAccident": "Olycka",
+ "alarmTow": "Bogsera",
+ "alarmIdle": "På tomgång",
+ "alarmHighRpm": "Högt motorvarv",
+ "alarmHardAcceleration": "Hård acceleration",
+ "alarmHardBraking": "Hård inbromsning",
+ "alarmHardCornering": "Hård kurvtagning",
+ "alarmLaneChange": "Körfältsbyte",
+ "alarmFatigueDriving": "Trötthetskörning",
+ "alarmPowerCut": "Strömavbrott",
+ "alarmPowerRestored": "Strömmen återställd",
+ "alarmJamming": "Störning",
+ "alarmTemperature": "Temperatur",
+ "alarmParking": "Parkering",
+ "alarmBonnet": "Motorhuv",
+ "alarmFootBrake": "Fotbroms",
+ "alarmFuelLeak": "Bränsleläcka",
+ "alarmTampering": "Manupulering",
+ "alarmRemoving": "Borttagning",
+ "notificationType": "Typ av notis",
+ "notificationAlways": "Alla enheter",
+ "notificationNotificators": "Kanaler",
+ "notificatorCommand": "Command",
+ "notificatorWeb": "Webb",
+ "notificatorMail": "Emejl",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Svara",
+ "reportCombined": "Combined",
+ "reportRoute": "Rutt",
+ "reportEvents": "Händelser",
+ "reportTrips": "Resor",
+ "reportStops": "Stop",
+ "reportSummary": "Sammanfattning",
+ "reportDaily": "Daglig summering",
+ "reportChart": "Diagram",
+ "reportConfigure": "Konfigurera",
+ "reportEventTypes": "Händelsetyp",
+ "reportChartType": "Diagramtyp",
+ "reportShowMarkers": "Visa markörer",
+ "reportExport": "Export",
+ "reportEmail": "Emailrepport",
+ "reportSchedule": "Schedule",
+ "reportPeriod": "Period",
+ "reportCustom": "Anpassad",
+ "reportToday": "Idag",
+ "reportYesterday": "Igår",
+ "reportThisWeek": "Denna veckan",
+ "reportPreviousWeek": "Förra veckan",
+ "reportThisMonth": "Denna månaden",
+ "reportPreviousMonth": "Förra månaden",
+ "reportDeviceName": "Enhetsnamn",
+ "reportAverageSpeed": "Genomsnittshastighet",
+ "reportMaximumSpeed": "Maxhastighet",
+ "reportEngineHours": "Motortimmar",
+ "reportDuration": "Varaktighet",
+ "reportStartDate": "Startdatum",
+ "reportStartTime": "Starttid",
+ "reportStartAddress": "Startadress",
+ "reportEndTime": "Sluttid",
+ "reportEndAddress": "Slutadress",
+ "reportSpentFuel": "Förbrukat bränsle",
+ "reportStartOdometer": "Odometer Start",
+ "reportEndOdometer": "Odometer Slut",
+ "statisticsTitle": "Statistik",
+ "statisticsCaptureTime": "Inspelningstid",
+ "statisticsActiveUsers": "Aktiva användare",
+ "statisticsActiveDevices": "Aktiva enheter",
+ "statisticsRequests": "Förfrågningar",
+ "statisticsMessagesReceived": "Meddelande mottaget",
+ "statisticsMessagesStored": "Meddelande lagrat",
+ "statisticsGeocoder": "Geocoder förfrågan",
+ "statisticsGeolocation": "Geolocation förfrågan",
+ "categoryArrow": "Pil",
+ "categoryDefault": "Standart",
+ "categoryAnimal": "Djur",
+ "categoryBicycle": "Cykel",
+ "categoryBoat": "Båt",
+ "categoryBus": "Buss",
+ "categoryCar": "Bil",
+ "categoryCamper": "Camper",
+ "categoryCrane": "Kran",
+ "categoryHelicopter": "Helikopter",
+ "categoryMotorcycle": "Motorcykel",
+ "categoryOffroad": "Offroad",
+ "categoryPerson": "Person",
+ "categoryPickup": "Pickup",
+ "categoryPlane": "Flygplan",
+ "categoryShip": "Fartyg",
+ "categoryTractor": "Traktor",
+ "categoryTrain": "Täg",
+ "categoryTram": "Spårvagn",
+ "categoryTrolleybus": "Trådbuss",
+ "categoryTruck": "Lastbil",
+ "categoryVan": "Van",
+ "categoryScooter": "Scooter",
+ "maintenanceStart": "Start",
+ "maintenancePeriod": "Period"
+} \ No newline at end of file
diff --git a/src/resources/l10n/ta.json b/src/resources/l10n/ta.json
new file mode 100644
index 00000000..8cdbdc62
--- /dev/null
+++ b/src/resources/l10n/ta.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "பதிவு செய்",
+ "sharedHide": "மறை",
+ "sharedSave": "சேமி",
+ "sharedUpload": "Upload",
+ "sharedSet": "அமை",
+ "sharedCancel": "ரத்து செய்",
+ "sharedCopy": "Copy",
+ "sharedAdd": "சேர்க்க",
+ "sharedEdit": "தொகுக்க",
+ "sharedRemove": "நீக்குக",
+ "sharedRemoveConfirm": "நீக்கம் உறுதி செய்?",
+ "sharedNoData": "No data",
+ "sharedSubject": "Subject",
+ "sharedYes": "Yes",
+ "sharedNo": "No",
+ "sharedKm": "கிமீ",
+ "sharedMi": "மைல்",
+ "sharedNmi": "nmi",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "கடல் மைல்",
+ "sharedKmh": "கிமீ/மணிக்கு",
+ "sharedMph": "மைல்/மணிக்கு",
+ "sharedHour": "மணி நேரம்",
+ "sharedMinute": "நிமிடம்",
+ "sharedSecond": "விநாடி",
+ "sharedDays": "நாட்கள் ",
+ "sharedHours": "மணி நேரம் ",
+ "sharedMinutes": "நிமிடங்கள்",
+ "sharedDecimalDegrees": "தசம டிகிரி",
+ "sharedDegreesDecimalMinutes": "தசம டிகிரி நிமிடங்கள்",
+ "sharedDegreesMinutesSeconds": "தசம டிகிரி நொடிகள்",
+ "sharedName": "பெயர்",
+ "sharedDescription": "விளக்கம்",
+ "sharedSearch": "தேடுக",
+ "sharedIconScale": "Icon Scale",
+ "sharedGeofence": "பூகோள வேலி",
+ "sharedGeofences": "பூகோள வேலிகள்",
+ "sharedCreateGeofence": "Create Geofence",
+ "sharedNotifications": "அறிவிப்புகள்",
+ "sharedNotification": "அறிவிப்பு",
+ "sharedAttributes": "பண்புகள்",
+ "sharedAttribute": "பண்பு",
+ "sharedDrivers": "வாகன ஓட்டிகள் ",
+ "sharedDriver": "ஓட்டுநர்கள்",
+ "sharedArea": "பகுதி",
+ "sharedSound": "அறிவிப்பு ஒலி",
+ "sharedType": "வகை",
+ "sharedDistance": "தொலைவு",
+ "sharedHourAbbreviation": "ம",
+ "sharedMinuteAbbreviation": "நி",
+ "sharedSecondAbbreviation": "எஸ் ",
+ "sharedVoltAbbreviation": "வி ",
+ "sharedLiterAbbreviation": "எல் ",
+ "sharedGallonAbbreviation": "கேலன்கள்",
+ "sharedLiter": "லிட்டர்",
+ "sharedImpGallon": "கேலன்கள்",
+ "sharedUsGallon": "அமெரிக்கா கேலன்கள்",
+ "sharedLiterPerHourAbbreviation": "லிட்டர்/மணி ",
+ "sharedGetMapState": "வரைபட நிலையை பெறு",
+ "sharedComputedAttribute": "கணக்கிடப்பட்ட பண்புக்கூறு",
+ "sharedComputedAttributes": "கணக்கிடப்பட்ட பண்புக்கூறுகள்",
+ "sharedCheckComputedAttribute": "கணக்கிடப்பட்ட பண்புகளை சரிபார்க்கவும்",
+ "sharedExpression": "வெளிப்பாடு",
+ "sharedDevice": "கருவி ",
+ "sharedTest": "Test",
+ "sharedTestNotification": "சோதனை அறிவிப்பை அனுப்பவும்",
+ "sharedTestNotificators": "Test Channels",
+ "sharedTestExpression": "Test Expression",
+ "sharedCalendar": "நாட்காட்டி",
+ "sharedCalendars": "நாட்காட்டிகள் ",
+ "sharedFile": "கோப்பு",
+ "sharedSearchDevices": "Search Devices",
+ "sharedSortBy": "Sort By",
+ "sharedFilterMap": "Filter on Map",
+ "sharedSelectFile": "கோப்பைத் தேர்ந்தெடு",
+ "sharedPhone": "பேசி ",
+ "sharedRequired": "தேவையான",
+ "sharedPreferences": "விரும்பிய தேவைகள் ",
+ "sharedPermissions": "அனுமதிகள்",
+ "sharedConnections": "Connections",
+ "sharedExtra": "கூடுதல்",
+ "sharedPrimary": "Primary",
+ "sharedSecondary": "Secondary",
+ "sharedTypeString": "சரம்",
+ "sharedTypeNumber": "எண் ",
+ "sharedTypeBoolean": "பூலியன்",
+ "sharedTimezone": "நேர மண்டலம்",
+ "sharedInfoTitle": "தகவல் ",
+ "sharedSavedCommand": "சேமித்த கட்டளை",
+ "sharedSavedCommands": "சேமித்த கட்டளைகள்",
+ "sharedNew": "புதிய",
+ "sharedShowAddress": "முகவரி காட்டு",
+ "sharedShowDetails": "More Details",
+ "sharedDisabled": "முடக்கப்பட்ட ",
+ "sharedMaintenance": "பராமரிப்பு",
+ "sharedDeviceAccumulators": "திரட்டி",
+ "sharedAlarms": "முக்கிய அறிவிப்பு ",
+ "sharedLocation": "Location",
+ "sharedImport": "Import",
+ "sharedColumns": "Columns",
+ "sharedDropzoneText": "Drag and drop a file here or click",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "Simple",
+ "calendarRecurrence": "Recurrence",
+ "calendarOnce": "Once",
+ "calendarDaily": "Daily",
+ "calendarWeekly": "Weekly",
+ "calendarMonthly": "Monthly",
+ "calendarDays": "Days",
+ "calendarSunday": "Sunday",
+ "calendarMonday": "Monday",
+ "calendarTuesday": "Tuesday",
+ "calendarWednesday": "Wednesday",
+ "calendarThursday": "Thursday",
+ "calendarFriday": "Friday",
+ "calendarSaturday": "Saturday",
+ "attributeShowGeofences": "Show Geofences",
+ "attributeSpeedLimit": "வேக வரம்பு",
+ "attributeFuelDropThreshold": "Fuel Drop Threshold",
+ "attributeFuelIncreaseThreshold": "Fuel Increase Threshold",
+ "attributePolylineDistance": "பாலிலைன் தொலைவு",
+ "attributeReportIgnoreOdometer": "அறிக்கை: புறக்கணிக்க வாகன பயண தூர எண்ணி ",
+ "attributeWebReportColor": "வலை: அறிக்கை வண்ணம்",
+ "attributeDevicePassword": "சாதன கடவுச்சொல்",
+ "attributeDeviceImage": "Device Image",
+ "attributeDeviceInactivityStart": "Device Inactivity Start",
+ "attributeDeviceInactivityPeriod": "Device Inactivity Period",
+ "attributeProcessingCopyAttributes": "செயல்முறை: நகல் காரணிகள்",
+ "attributeColor": "நிறம்",
+ "attributeWebLiveRouteLength": "வலை : வாகன துரித பயண விபரம் ",
+ "attributeWebSelectZoom": "வலை: பெரிதாக்கு தேர்வு",
+ "attributeWebMaxZoom": "வலை: அதிகபட்ச பெரிதாக்கு",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Pushover User Key",
+ "attributePushoverDeviceNames": "Pushover Device Names",
+ "attributeMailSmtpHost": "அஞ்சல்: SMTP புரவலன்",
+ "attributeMailSmtpPort": "அஞ்சல்: SMTP போர்ட்",
+ "attributeMailSmtpStarttlsEnable": "அஞ்சல்: SMTP STARTTLS இயக்கு",
+ "attributeMailSmtpStarttlsRequired": "அஞ்சல்: SMTP STARTTLS தேவை",
+ "attributeMailSmtpSslEnable": "அஞ்சல்: SMTP SSL இயக்கு",
+ "attributeMailSmtpSslTrust": "அஞ்சல்: SMTP SSL அறக்கட்டளை",
+ "attributeMailSmtpSslProtocols": "அஞ்சல்: SMTP SSL வழிமுறைகள் ",
+ "attributeMailSmtpFrom": "அஞ்சல்: SMTP துவக்கம் ",
+ "attributeMailSmtpAuth": "அஞ்சல்: SMTP Auth இயக்கு",
+ "attributeMailSmtpUsername": "அஞ்சல்: SMTP பயனர்பெயர்",
+ "attributeMailSmtpPassword": "அஞ்சல்: SMTP கடவுச்சொல்",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: Disable Attributes",
+ "attributeUiDisableGroups": "UI: Disable Groups",
+ "attributeUiDisableEvents": "UI: முடக்கு நிகழ்வுகள்",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "UI: முடக்கு இயக்கிகள்",
+ "attributeUiDisableComputedAttributes": "UI: கணக்கிடப்பட்ட பண்புக்கூறுகளை முடக்கு",
+ "attributeUiDisableCalendars": "UI: நாட்காட்டியை முடக்கு ",
+ "attributeUiDisableMaintenance": "UI: பராமரிப்பு முடக்கவும்",
+ "attributeUiHidePositionAttributes": "UI: நிலை காரணிகள் மறை",
+ "attributeUiDisableLoginLanguage": "UI: Disable Login Language",
+ "attributeNotificationTokens": "Notification Tokens",
+ "attributePopupInfo": "Popup Info",
+ "errorTitle": "பிழை",
+ "errorGeneral": "தவறான அளவுருக்கள் அல்லது தடைகள் மீறல்",
+ "errorConnection": "இணைப்புப் பிழை",
+ "errorSocket": "வலை இணைப்பு பிழை",
+ "errorZero": "பூஜ்யமாக இருக்க முடியாது",
+ "userEmail": "மின்னஞ்சல்",
+ "userPassword": "கடவுச்சொல்",
+ "userAdmin": "நிர்வாகி",
+ "userRemember": "நினைவில் கொள்",
+ "userExpirationTime": "காலாவதி",
+ "userDeviceLimit": "சாதன வரம்பு",
+ "userUserLimit": "பயனர் வரம்பு",
+ "userDeviceReadonly": "சாதன வாசிப்பு",
+ "userLimitCommands": "வரம்பு கட்டளைகள்",
+ "userDisableReports": "Disable Reports",
+ "userFixedEmail": "No Email Change",
+ "userToken": "டோக்கன் ",
+ "userDeleteAccount": "Delete Account",
+ "userTemporary": "Temporary",
+ "loginTitle": "உள் நுழை",
+ "loginLanguage": "மொழி",
+ "loginReset": "Reset Password",
+ "loginRegister": "பதிவு செய்ய",
+ "loginLogin": "உள்நுழைய",
+ "loginOpenId": "Login with OpenID",
+ "loginFailed": "தவறான மின்னஞ்சல் முகவரி அல்லது கடவுச்சொல்",
+ "loginCreated": "புதிய பயனர் பதிவு செய்யப்பட்டுள்ளது",
+ "loginResetSuccess": "Check your email",
+ "loginUpdateSuccess": "New password is set",
+ "loginLogout": "வெளியேறு",
+ "loginLogo": "இலட்சினை ",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "கருவிகள் மற்றும் அதன் நிலை",
+ "deviceSelected": "Selected Device",
+ "deviceTitle": "சாதனம்",
+ "devicePrimaryInfo": "Device Title",
+ "deviceSecondaryInfo": "Device Detail",
+ "deviceIdentifier": "அடையாளங்காட்டி",
+ "deviceModel": "மாதிரி",
+ "deviceContact": "தொடர்பு",
+ "deviceCategory": "வகை",
+ "deviceLastUpdate": "கடைசியாக புதுப்பிக்கப்பட்டது",
+ "deviceCommand": "கட்டளை",
+ "deviceFollow": "பின்தொடர்",
+ "deviceTotalDistance": "மொத்த தூரம்",
+ "deviceStatus": "நிலைமை",
+ "deviceStatusOnline": "வலை தொடர்பில் ",
+ "deviceStatusOffline": "வலை தொடர்பில்லாமை ",
+ "deviceStatusUnknown": "தெரியாத",
+ "deviceRegisterFirst": "Register your first device",
+ "deviceIdentifierHelp": "IMEI, serial number or other id. It has to match the identifier device reports to the server.",
+ "deviceShare": "Share Device",
+ "groupDialog": "குழு",
+ "groupParent": "குழு",
+ "groupNoGroup": "குழு இல்லை",
+ "settingsTitle": "அமைப்பு",
+ "settingsUser": "கணக்கு",
+ "settingsGroups": "குழுக்கள்",
+ "settingsServer": "சர்வர்",
+ "settingsUsers": "உறுப்பினர்கள்",
+ "settingsDistanceUnit": "தூரம் அலகு",
+ "settingsAltitudeUnit": "Altitude Unit",
+ "settingsSpeedUnit": "வேக அலகு",
+ "settingsVolumeUnit": "தொகுதி அலகு",
+ "settingsTwelveHourFormat": "12 மணி நேர வடிவம்",
+ "settingsCoordinateFormat": "வடிவமைப்பை ஒருங்கிணைக்கிறது",
+ "settingsServerVersion": "Server Version",
+ "settingsAppVersion": "App Version",
+ "settingsConnection": "Connection",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "அறிக்கை",
+ "reportScheduled": "Scheduled Reports",
+ "reportDevice": "சாதனம்",
+ "reportGroup": "குழு",
+ "reportFrom": "இருந்து",
+ "reportTo": "வரை",
+ "reportShow": "காண்பி",
+ "reportClear": "அழி",
+ "linkGoogleMaps": "Google Maps",
+ "linkAppleMaps": "Apple Maps",
+ "linkStreetView": "Street View",
+ "positionFixTime": "Fix Time",
+ "positionDeviceTime": "Device Time",
+ "positionServerTime": "Server Time",
+ "positionValid": "செல்லுபடியான",
+ "positionAccuracy": "துல்லியம்",
+ "positionLatitude": "அட்சரேகை",
+ "positionLongitude": "தீர்க்கரேகை",
+ "positionAltitude": "உயரம்",
+ "positionSpeed": "வேகம்",
+ "positionCourse": "பாடநெறி",
+ "positionAddress": "முகவரி",
+ "positionProtocol": "புரோட்டோகால்",
+ "positionDistance": "தூரம்",
+ "positionRpm": "ஆர்பிஎம்",
+ "positionFuel": "எரிபொருள்",
+ "positionPower": "சக்தி",
+ "positionBattery": "மின்கலன் ",
+ "positionRaw": "மூல",
+ "positionIndex": "குறியீட்டு",
+ "positionHdop": "துல்லியமான கிடைமட்ட நீக்கம்",
+ "positionVdop": "துல்லியத்தின் செங்குத்து நீக்கம்",
+ "positionPdop": "துல்லியத்தின் நிலை நீக்கம்",
+ "positionSat": "செயற்கைக்கோள்கள்",
+ "positionSatVisible": "தெரியும் செயற்கைக்கோள்கள்",
+ "positionRssi": "சிக்னல் வலிமை காட்டி",
+ "positionGps": "உலகளாவிய நிலைப்படுத்தல் அமைப்பு",
+ "positionRoaming": "சுற்றி கொண்டு",
+ "positionEvent": "நிகழ்வு",
+ "positionAlarm": "அலாரம்",
+ "positionStatus": "நிலைமை ",
+ "positionOdometer": "பயண தூர விபரக்காட்டி ",
+ "positionServiceOdometer": "வாகன பயண தூர விபரக்காட்டி சேவைகள் ",
+ "positionTripOdometer": "வாகன பயண தூர விபரக்காட்டி ",
+ "positionHours": "மணி நேரம் ",
+ "positionSteps": "படிகள்",
+ "positionInput": "உள்ளீடு",
+ "positionHeartRate": "Heart Rate",
+ "positionOutput": "வெளியீடு",
+ "positionBatteryLevel": "மின்கலன் நிலை",
+ "positionFuelConsumption": "எரிபொருள் பயன்பாடு",
+ "positionRfid": "ரேடியோ அதிர்வெண் அடையாளம்",
+ "positionVersionFw": "நிலையான மென்பொருள் பதிப்பு",
+ "positionVersionHw": "வன்பொருள் பதிப்பு",
+ "positionIgnition": "வாகன துவக்கி ",
+ "positionFlags": "கொடிகள்",
+ "positionCharge": "மின் ஆற்றல் சேமிக்க",
+ "positionIp": "இணைய நெறிமுறை",
+ "positionArchive": "காப்பகத்தை",
+ "positionVin": "வாகனம் அடையாள எண் ",
+ "positionApproximate": "தோராயமான",
+ "positionThrottle": "கழுத்துப்பகுதி",
+ "positionMotion": "இயக்கம்",
+ "positionArmed": "ஆயுத",
+ "positionAcceleration": "முடுக்கம்",
+ "positionTemp": "Temperature",
+ "positionDeviceTemp": "சாதன வெப்பநிலை",
+ "positionCoolantTemp": "Coolant Temperature",
+ "positionOperator": "இயக்குனர் ",
+ "positionCommand": "கட்டளை",
+ "positionBlocked": "தடுக்கப்பட்டது",
+ "positionDtcs": "DTCs",
+ "positionObdSpeed": "ஓ பி டியின் வேகம் ",
+ "positionObdOdometer": "ஓ பி டி ஓடோமீட்டர் ",
+ "positionDrivingTime": "Driving Time",
+ "positionDriverUniqueId": "இயக்கி தனிப்பட்ட ஐடி",
+ "positionCard": "Card",
+ "positionImage": "படம் ",
+ "positionVideo": "Video",
+ "positionAudio": "ஒலி ",
+ "serverTitle": "சர்வர் அமைப்பு",
+ "serverZoom": "பெரிதாக்கு",
+ "serverRegistration": "பதிவுசெய்ய",
+ "serverReadonly": "படிக்கமட்டும்",
+ "serverForceSettings": "சக்தி அமைப்புகள்",
+ "serverAnnouncement": "Announcement",
+ "serverName": "Server Name",
+ "serverDescription": "Server Description",
+ "serverColorPrimary": "Color Primary",
+ "serverColorSecondary": "Color Secondary",
+ "serverLogo": "Logo Image",
+ "serverLogoInverted": "Inverted Logo Image",
+ "serverChangeDisable": "Disable Server Change",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "வரைபடம்",
+ "mapActive": "Active Maps",
+ "mapOverlay": "Map Overlay",
+ "mapOverlayCustom": "Custom Overlay",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API Key",
+ "mapOpenWeatherClouds": "OpenWeather Clouds",
+ "mapOpenWeatherPrecipitation": "OpenWeather Precipitation",
+ "mapOpenWeatherPressure": "OpenWeather Pressure",
+ "mapOpenWeatherWind": "OpenWeather Wind",
+ "mapOpenWeatherTemperature": "OpenWeather Temperature",
+ "mapLayer": "வரைபடம் அடுக்கு",
+ "mapCustom": "Custom (XYZ)",
+ "mapCustomArcgis": "Custom (ArcGIS)",
+ "mapCustomLabel": "Custom map",
+ "mapCarto": "கார்ட்டோ வரைபடங்கள்",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "பிங் வரைபட கீ",
+ "mapBingRoad": "பிங் சாலை வரைபடம்",
+ "mapBingAerial": "பிங் வான்வழி வரைபடம்",
+ "mapBingHybrid": "பிங் வரைபடங்கள்",
+ "mapBaidu": "பைடு வரைபடங்கள்",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "யான் டெக்ஸ் வரைபடங்கள்",
+ "mapYandexSat": "யான் டெக்ஸ் செயற்கைக்கோள்",
+ "mapWikimedia": "விக்கிப்பீடியா",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox Streets",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox Outdoors",
+ "mapMapboxSatellite": "Mapbox Satellite",
+ "mapMapboxKey": "Mapbox Access Token",
+ "mapMapTilerBasic": "MapTiler Basic",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "MapTiler API Key",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ Access Token",
+ "mapTomTomBasic": "TomTom Basic",
+ "mapTomTomFlow": "TomTom Traffic Flow",
+ "mapTomTomIncidents": "TomTom Traffic Incidents",
+ "mapTomTomKey": "TomTom API Key",
+ "mapHereBasic": "Here Basic",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satellite",
+ "mapHereFlow": "Here Traffic Flow",
+ "mapHereKey": "Here API Key",
+ "mapShapePolygon": "பலகோணம்",
+ "mapShapeCircle": "வட்டம்",
+ "mapShapePolyline": "பாலிலைன்",
+ "mapLiveRoutes": "நேரடி வழிகள்",
+ "mapDirection": "Show Direction",
+ "mapCurrentLocation": "Current Location",
+ "mapPoiLayer": "POI லேயர்",
+ "mapClustering": "Markers Clustering",
+ "mapOnSelect": "Show Map on Selection",
+ "mapDefault": "Default Map",
+ "stateTitle": "நிலை",
+ "stateName": "சாட்டு",
+ "stateValue": "மதிப்பு",
+ "commandTitle": "கட்டளை",
+ "commandSend": "அனுப்பு",
+ "commandSent": "கட்டளை அனுப்பப்பட்டது",
+ "commandQueued": "கட்டளை வரிசைப்படுத்தப்பட்டது",
+ "commandUnit": "அலகு",
+ "commandCustom": "விருப்பமான கட்டளை",
+ "commandDeviceIdentification": "\nசாதன அடையாளம்",
+ "commandPositionSingle": "ஒற்றை அறிக்கை",
+ "commandPositionPeriodic": "காலமுறை அறிக்கையிடல்",
+ "commandPositionStop": "அறிக்கையிடுதல் நிறுத்து ",
+ "commandEngineStop": "எஞ்சின் நிறுத்து",
+ "commandEngineResume": "எஞ்சின் தொடங்க",
+ "commandAlarmArm": "அலறிமணி துவக்கம்",
+ "commandAlarmDisarm": "அலறிமணி நிறுத்தம்",
+ "commandAlarmDismiss": "Dismiss Alarm",
+ "commandSetTimezone": "நேர மண்டலம்",
+ "commandRequestPhoto": "புகைப்படம் வேண்டு",
+ "commandPowerOff": "சாதனத்தை முடக்கு ",
+ "commandRebootDevice": "சாதன மறுதுவக்கம்",
+ "commandFactoryReset": "Factory Reset",
+ "commandSendSms": "குருஞ்செய்தி அனுப்பு",
+ "commandSendUssd": "ussd ஐ அனுப்பவும்",
+ "commandSosNumber": "அவசர அழைப்பு எண்(SOS)",
+ "commandSilenceTime": "அமைதி நேரம் அமைக்க",
+ "commandSetPhonebook": "தொலைபேசிப்புத்தகம் அமை",
+ "commandVoiceMessage": "குரல் செய்தி",
+ "commandOutputControl": "வெளியீட்டு கட்டுப்பாடு",
+ "commandVoiceMonitoring": "குரல் கண்காணிப்பு",
+ "commandSetAgps": "அடுக்களை அமைக்கவும்",
+ "commandSetIndicator": "காட்டி அமைக்க",
+ "commandConfiguration": "கட்டமைப்பு",
+ "commandGetVersion": "பதிப்பை பெரு",
+ "commandFirmwareUpdate": "பிர்மவாறு அமைத்தல் ",
+ "commandSetConnection": "இணைப்பு அமைக்க",
+ "commandSetOdometer": "ஓடோமீட்டர் அமைக்க ",
+ "commandGetModemStatus": "மோடம் நிலையைப் பெறுக",
+ "commandGetDeviceStatus": "சாதன விவரங்கள் பெறுக ",
+ "commandSetSpeedLimit": "Set Speed Limit",
+ "commandModePowerSaving": "Power Saving Mode",
+ "commandModeDeepSleep": "Deep Sleep Mode",
+ "commandAlarmGeofence": "Set Geofence Alarm",
+ "commandAlarmBattery": "Set Battery Alarm",
+ "commandAlarmSos": "Set SOS Alarm",
+ "commandAlarmRemove": "Set Remove Alarm",
+ "commandAlarmClock": "Set Clock Alarm",
+ "commandAlarmSpeed": "Set Speed Alarm",
+ "commandAlarmFall": "Set Fall Alarm",
+ "commandAlarmVibration": "Set Vibration Alarm",
+ "commandFrequency": "காலஇடைவெளி",
+ "commandTimezone": "நேர மண்டலத்தை முடக்கு",
+ "commandMessage": "குறுஞ்செய்தி",
+ "commandRadius": "ஆரம்",
+ "commandEnable": "செயல்படுத்த",
+ "commandData": "தரவு",
+ "commandIndex": "குறியீட்டு",
+ "commandPhone": "தொலைபேசி எண்",
+ "commandServer": "சர்வர்",
+ "commandPort": "தகவல் வழி ",
+ "eventAll": "அனைத்து நிகழ்வுகள்",
+ "eventDeviceOnline": "நிலை ஆன்லைன்",
+ "eventDeviceUnknown": "தெரியாத நிலை",
+ "eventDeviceOffline": "நிலை ஆஃப்லைன்",
+ "eventDeviceInactive": "Device inactive",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "சாதனம் நகரும்",
+ "eventDeviceStopped": "சாதனம் நிறுத்தப்பட்டது",
+ "eventDeviceOverspeed": "வேகம் வரம்பை மீறிவிட்டது",
+ "eventDeviceFuelDrop": "எரிபொருள் குறைதல் ",
+ "eventDeviceFuelIncrease": "Fuel increase",
+ "eventCommandResult": "கட்டளை விளைவு",
+ "eventGeofenceEnter": "புவி வேலிக்குள் நுழைந்தது",
+ "eventGeofenceExit": "புவி வேலி வெளியேறின",
+ "eventAlarm": "அலாரம்",
+ "eventIgnitionOn": "பற்றவைப்பு",
+ "eventIgnitionOff": "பற்றவைத்தல்",
+ "eventMaintenance": "பராமரிப்பு தேவை",
+ "eventTextMessage": "உரை செய்தி பெற்றது",
+ "eventDriverChanged": "இயக்குணர் மாற்றப்பட்டது",
+ "eventMedia": "Media",
+ "eventsScrollToLast": "நீடிக்கும்",
+ "eventsSoundEvents": "Sound Events",
+ "eventsSoundAlarms": "Sound Alarms",
+ "alarmGeneral": "பொது",
+ "alarmSos": "அவசர உதவி ",
+ "alarmVibration": "அதிர்வு",
+ "alarmMovement": "இயக்கம்",
+ "alarmLowspeed": "குறைவான வேகம்",
+ "alarmOverspeed": "அதி வேகம் ",
+ "alarmFallDown": "கீழே விழுதல்",
+ "alarmLowPower": "குறைந்த சக்தி",
+ "alarmLowBattery": "குறைவான மின்கலம் ",
+ "alarmFault": "குற்றம்",
+ "alarmPowerOff": "சக்தி அணை",
+ "alarmPowerOn": "துவக்கு ",
+ "alarmDoor": "கதவு ",
+ "alarmLock": "பூட்டு ",
+ "alarmUnlock": "திறக்க",
+ "alarmGeofence": "புவி வேலி",
+ "alarmGeofenceEnter": "புவி வேலி நுழைவு ",
+ "alarmGeofenceExit": "குறைந்த சக்தி",
+ "alarmGpsAntennaCut": "gps ஆண்டெனா",
+ "alarmAccident": "விபத்து",
+ "alarmTow": "கயிறு",
+ "alarmIdle": "சும்மா",
+ "alarmHighRpm": "உயர் rpm",
+ "alarmHardAcceleration": "கடின முடுக்கம்",
+ "alarmHardBraking": "கடின முறிப்பு",
+ "alarmHardCornering": "கடின முனை",
+ "alarmLaneChange": "பாதை மாற்றம் ",
+ "alarmFatigueDriving": "சோர்வு ஓட்டுநர்",
+ "alarmPowerCut": "மின் வெட்டு",
+ "alarmPowerRestored": "சக்தி மீட்டெடுப்பு ",
+ "alarmJamming": "நெருக்குதல்",
+ "alarmTemperature": "வெப்ப நிலை",
+ "alarmParking": "பார்க்கிங்",
+ "alarmBonnet": "கார் குட்நெட்",
+ "alarmFootBrake": "கால் பிரேக்",
+ "alarmFuelLeak": "எரிபொருள் கசிவு",
+ "alarmTampering": "சேதப்படுத்திய",
+ "alarmRemoving": "நீக்கி",
+ "notificationType": "type of notification",
+ "notificationAlways": "எல்லா சாதனங்களும்",
+ "notificationNotificators": "சேனல்கள்",
+ "notificatorCommand": "Command",
+ "notificatorWeb": "வலைத்தளம் ",
+ "notificatorMail": "மின்னஞ்சல்",
+ "notificatorSms": "குறுஞ்சய்தி ",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Replay",
+ "reportCombined": "Combined",
+ "reportRoute": "பாதை",
+ "reportEvents": "நிகழ்வுகள்",
+ "reportTrips": "பயணங்கள்",
+ "reportStops": "நிறுத்தங்கள்",
+ "reportSummary": "சுருக்கம்",
+ "reportDaily": "Daily Summary",
+ "reportChart": "விளக்கப்படம்",
+ "reportConfigure": "கட்டமைக்கவும்",
+ "reportEventTypes": "நிகழ்வு வகைகள்",
+ "reportChartType": "விளக்கப்படம் வகை",
+ "reportShowMarkers": "குறிப்பான்கள் காட்டு",
+ "reportExport": "ஏற்றுமதி",
+ "reportEmail": "மின்னஞ்சல் அறிக்கை",
+ "reportSchedule": "Schedule",
+ "reportPeriod": "காலம்",
+ "reportCustom": "விருப்ப",
+ "reportToday": "இன்று",
+ "reportYesterday": "நேற்று",
+ "reportThisWeek": "இந்த வாரம்",
+ "reportPreviousWeek": "முந்தைய வாரம்",
+ "reportThisMonth": "இந்த மாதம்",
+ "reportPreviousMonth": "சென்ற மாதம்",
+ "reportDeviceName": "சாதன பெயர்",
+ "reportAverageSpeed": "சராசரி வேகம்",
+ "reportMaximumSpeed": "அதிகபட்ச வேகம்",
+ "reportEngineHours": "இயந்திர மணி",
+ "reportDuration": "கால அளவு ",
+ "reportStartDate": "Start Date",
+ "reportStartTime": "துவக்க நேரம் ",
+ "reportStartAddress": "தொடக்க முகவரி ",
+ "reportEndTime": "முடிவு நேரம்",
+ "reportEndAddress": "இறுதி முகவரி",
+ "reportSpentFuel": "எரிபொருள் செலவு",
+ "reportStartOdometer": "ஓடோமீட்டர் தொடக்கம்",
+ "reportEndOdometer": "ஓடோமீட்டர் முடிவு  ",
+ "statisticsTitle": "புள்ளியியல்",
+ "statisticsCaptureTime": "நேரம் பிடி",
+ "statisticsActiveUsers": "செயலில் பயனர்கள்",
+ "statisticsActiveDevices": "செயல்படும் சாதனங்கள் ",
+ "statisticsRequests": "கோரிக்கைகள் ",
+ "statisticsMessagesReceived": "செய்திகள் பெறப்பட்டன",
+ "statisticsMessagesStored": "சேமித்த செய்திகள்",
+ "statisticsGeocoder": "பூகோள கோரிக்கை",
+ "statisticsGeolocation": "பூகோள கோரிக்கைகள் ",
+ "categoryArrow": "அம்பு",
+ "categoryDefault": "இயல்புநிலை",
+ "categoryAnimal": "கால்நடை",
+ "categoryBicycle": "மிதிவண்டி",
+ "categoryBoat": "படகு",
+ "categoryBus": "பேருந்து",
+ "categoryCar": "கார்",
+ "categoryCamper": "Camper",
+ "categoryCrane": "கிரேன்",
+ "categoryHelicopter": "ஹெலிகாப்டர்",
+ "categoryMotorcycle": "மோட்டார் சைக்கிள்",
+ "categoryOffroad": "இனிய சாலை",
+ "categoryPerson": "நபர்",
+ "categoryPickup": "இடும்",
+ "categoryPlane": "விமானம்",
+ "categoryShip": "கப்பல்",
+ "categoryTractor": "டிராக்டர்",
+ "categoryTrain": "ரயில்",
+ "categoryTram": "டிராம்",
+ "categoryTrolleybus": "தள்ளுவண்டி",
+ "categoryTruck": "கண ரக வாகனம்",
+ "categoryVan": "வேன் வாகனம் ",
+ "categoryScooter": "Scooter",
+ "maintenanceStart": "துவக்கு ",
+ "maintenancePeriod": "காலம்"
+} \ No newline at end of file
diff --git a/src/resources/l10n/th.json b/src/resources/l10n/th.json
new file mode 100644
index 00000000..95f783c4
--- /dev/null
+++ b/src/resources/l10n/th.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "กำลังโหลด...",
+ "sharedHide": "ซ่อน",
+ "sharedSave": "จัดเก็บแฟ้มข้อมูล",
+ "sharedUpload": "อัพโหลด",
+ "sharedSet": "ตั้งค่า",
+ "sharedCancel": "ยกเลิก",
+ "sharedCopy": "คัดลอก",
+ "sharedAdd": "เพิ่ม",
+ "sharedEdit": "แก้ไข",
+ "sharedRemove": "ลบรายการ",
+ "sharedRemoveConfirm": "ยืนยันลบรายการ",
+ "sharedNoData": "ไม่มีข้อมูล",
+ "sharedSubject": "หัวข้อ",
+ "sharedYes": "ใช่",
+ "sharedNo": "ไม่",
+ "sharedKm": "กม.",
+ "sharedMi": "ไมล์",
+ "sharedNmi": "nml",
+ "sharedMeters": "ม.",
+ "sharedFeet": "ฟุต",
+ "sharedKn": "น๊อต",
+ "sharedKmh": "กม./ชม.",
+ "sharedMph": "ไมล์ต่อชั่วโมง",
+ "sharedHour": "ชั่วโมง",
+ "sharedMinute": "นาที",
+ "sharedSecond": "วินาที",
+ "sharedDays": "วัน",
+ "sharedHours": "ชั่วโมง",
+ "sharedMinutes": "นาที",
+ "sharedDecimalDegrees": "องศา ทศนิยม",
+ "sharedDegreesDecimalMinutes": "องศา นาที",
+ "sharedDegreesMinutesSeconds": "องศา นาที วินาที",
+ "sharedName": "ชื่อ",
+ "sharedDescription": "คำอธิบาย",
+ "sharedSearch": "ค้นหา",
+ "sharedIconScale": "ขนาดไอคอน",
+ "sharedGeofence": "เขตพื้นที่",
+ "sharedGeofences": "เขตพื้นที่",
+ "sharedCreateGeofence": "สร้างขอบเขตภูมิศาสตร์",
+ "sharedNotifications": "การแจ้งเตือน",
+ "sharedNotification": "แจ้งเตือน",
+ "sharedAttributes": "คุณลักษณะ",
+ "sharedAttribute": "คุณลักษณะ",
+ "sharedDrivers": "ไดร์เวอร์",
+ "sharedDriver": "คนขับรถ",
+ "sharedArea": "พื้นที่",
+ "sharedSound": "การแจ้งเตือนเสียง",
+ "sharedType": "ชนิด",
+ "sharedDistance": "ระยะทาง",
+ "sharedHourAbbreviation": "ชม.",
+ "sharedMinuteAbbreviation": "นาที",
+ "sharedSecondAbbreviation": "วินาที",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "ลิตร",
+ "sharedGallonAbbreviation": "แกลลอน",
+ "sharedLiter": "ลิตร",
+ "sharedImpGallon": "Imp แกลลอน",
+ "sharedUsGallon": "U.S. แกลลอน",
+ "sharedLiterPerHourAbbreviation": "ลิตรต่อชั่วโมง",
+ "sharedGetMapState": "ได้รับสถานะแผนที่",
+ "sharedComputedAttribute": "คำนวณแอตทริบิวต์",
+ "sharedComputedAttributes": "คำนวณแอตทริบิวต์",
+ "sharedCheckComputedAttribute": "ตรวจสอบคอมพิวเตอร์แอตทริบิวต์",
+ "sharedExpression": "การแสดงออก",
+ "sharedDevice": "อุปกรณ์",
+ "sharedTest": "ทดสอบ",
+ "sharedTestNotification": "ส่งการแจ้งเตือนการทดสอบ",
+ "sharedTestNotificators": "ทดสอบแชนแนล",
+ "sharedTestExpression": "ทดสอบนิพจน์",
+ "sharedCalendar": "ปฏิทิน",
+ "sharedCalendars": "ปฏิทิน",
+ "sharedFile": "ไฟล์",
+ "sharedSearchDevices": "ค้นหาอุปกรณ์",
+ "sharedSortBy": "เรียงตาม",
+ "sharedFilterMap": "กรองบนแผนที่",
+ "sharedSelectFile": "เลือกไฟล์",
+ "sharedPhone": "โทรศัพท์",
+ "sharedRequired": "ต้องระบุ",
+ "sharedPreferences": "การตั้งค่า",
+ "sharedPermissions": "สิทธิ์",
+ "sharedConnections": "การเชื่อมต่อ",
+ "sharedExtra": "พิเศษ",
+ "sharedPrimary": "หลัก",
+ "sharedSecondary": "รอง",
+ "sharedTypeString": "ตัวอักษร",
+ "sharedTypeNumber": "จำนวน",
+ "sharedTypeBoolean": "ตัวเลข",
+ "sharedTimezone": "เขตเวลา",
+ "sharedInfoTitle": "ข้อมูล",
+ "sharedSavedCommand": "บันทึกคำสั่ง",
+ "sharedSavedCommands": "บันทึกคำสั่ง",
+ "sharedNew": "ใหม่...",
+ "sharedShowAddress": "แสดงที่อยู่",
+ "sharedShowDetails": "รายละเอียดเพิ่มเติม",
+ "sharedDisabled": "ปิดการใช้งาน",
+ "sharedMaintenance": "ซ่อมบำรุง",
+ "sharedDeviceAccumulators": "รวม",
+ "sharedAlarms": "เตือน",
+ "sharedLocation": "สถานที่",
+ "sharedImport": "นำเข้า",
+ "sharedColumns": "คอลัมน์",
+ "sharedDropzoneText": "ลากและวางไฟล์ที่นี่หรือคลิก",
+ "sharedLogs": "Logs",
+ "sharedLink": "ลิงก์",
+ "calendarSimple": "เรียบง่าย",
+ "calendarRecurrence": "การเกิดซ้ำ",
+ "calendarOnce": "ครั้งเดียว",
+ "calendarDaily": "รายวัน",
+ "calendarWeekly": "รายสัปดาห์",
+ "calendarMonthly": "รายเดือน",
+ "calendarDays": "วัน",
+ "calendarSunday": "อาทิตย์",
+ "calendarMonday": "วันจันทร์",
+ "calendarTuesday": "วันอังคาร",
+ "calendarWednesday": "วันพุธ",
+ "calendarThursday": "วันพฤหัสบดี",
+ "calendarFriday": "วันศุกร์",
+ "calendarSaturday": "วันเสาร์",
+ "attributeShowGeofences": "แสดงขอบเขตภูมิศาสตร์",
+ "attributeSpeedLimit": "จำกัดความเร็ว",
+ "attributeFuelDropThreshold": "เกณฑ์การลดของเชื้อเพลิง",
+ "attributeFuelIncreaseThreshold": "เกณฑ์การเพิ่มของเชื้อเพลิง",
+ "attributePolylineDistance": "เส้นระยะทาง",
+ "attributeReportIgnoreOdometer": "รายงาน: ละเว้นวัดระยะ",
+ "attributeWebReportColor": "เว็บ: สีรายงาน",
+ "attributeDevicePassword": "รหัสผ่านอุปกรณ์",
+ "attributeDeviceImage": "ภาพอุปกรณ์",
+ "attributeDeviceInactivityStart": "อุปกรณ์ยังไม่เริ่มทำงาน",
+ "attributeDeviceInactivityPeriod": "ระยะเวลาที่อุปกรณ์ไม่ทำงาน",
+ "attributeProcessingCopyAttributes": "การประมวลผล: คัดลอกคุณสมบัติ",
+ "attributeColor": "สี",
+ "attributeWebLiveRouteLength": "เว็บ: สดเส้นทางความยาว",
+ "attributeWebSelectZoom": "เว็บ: ขยายเมื่อเลือก",
+ "attributeWebMaxZoom": "เว็บ: ซูมสูงสุด",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "รหัสผู้ใช้ Pushover",
+ "attributePushoverDeviceNames": "ชื่ออุปกรณ์ Pushover",
+ "attributeMailSmtpHost": "Mail: SMTP Host",
+ "attributeMailSmtpPort": "Mail: SMTP Host",
+ "attributeMailSmtpStarttlsEnable": "Mail: SMTP STARTTLS Enable",
+ "attributeMailSmtpStarttlsRequired": "Mail: SMTP STARTTLS Required",
+ "attributeMailSmtpSslEnable": "Mail: SMTP SSL Enable",
+ "attributeMailSmtpSslTrust": "Mail: SMTP SSL Trust",
+ "attributeMailSmtpSslProtocols": "Mail: SMTP SSL Protocols",
+ "attributeMailSmtpFrom": "Mail: SMTP From",
+ "attributeMailSmtpAuth": "Mail: SMTP Auth Enable",
+ "attributeMailSmtpUsername": "Mail: SMTP Username",
+ "attributeMailSmtpPassword": "Mail: SMTP Password",
+ "attributeUiDisableSavedCommands": "UI: ปิดใช้งานคำสั่งที่บันทึกไว้",
+ "attributeUiDisableAttributes": "UI: ปิดใช้งานแอตทริบิวต์",
+ "attributeUiDisableGroups": "UI: ปิดการใช้งานกลุ่ม",
+ "attributeUiDisableEvents": "UI: ปิดการใช้งานเหตุการณ์",
+ "attributeUiDisableVehicleFeatures": "UI : ปิดการใช้งานคุณสมบัติของยานพาหนะ",
+ "attributeUiDisableDrivers": "UI: ปิดการใช้งานไดร์เวอร์",
+ "attributeUiDisableComputedAttributes": "UI: ปิดใช้งานแอ็ตทริบิวต์ที่คำนวณ",
+ "attributeUiDisableCalendars": "UI: ปิดปฏิทิน",
+ "attributeUiDisableMaintenance": "UI: ปิด-ซ่อมบำรุง",
+ "attributeUiHidePositionAttributes": "UI: ซ่อนตำแหน่งลักษณะ",
+ "attributeUiDisableLoginLanguage": "UI: ปิดการใช้งานภาษาเข้าสู่ระบบ",
+ "attributeNotificationTokens": "โทเค็นการแจ้งเตือน",
+ "attributePopupInfo": "ป๊อปอัปข้อมูล",
+ "errorTitle": "ผิดพลาด",
+ "errorGeneral": "ไม่ถูกต้องพารามิเตอร์หรือ จำกัด การละเมิด",
+ "errorConnection": "การเชื่อมต่อผิดพลาด",
+ "errorSocket": "ข้อผิดพลาดการเชื่อมต่อซ็อกเก็ตเว็บ",
+ "errorZero": "ไม่สามารถเป็นศูนย์",
+ "userEmail": "อีเมล์",
+ "userPassword": "รหัสผ่าน",
+ "userAdmin": "ผู้ดูแลระบบ",
+ "userRemember": "จำไว้",
+ "userExpirationTime": "วันหมดอายุ",
+ "userDeviceLimit": "จำนวน อุปกรณ์",
+ "userUserLimit": "ขีดจำกัดผู้ใช้",
+ "userDeviceReadonly": "อุปกรณ์ อ่านได้อย่างเดียว",
+ "userLimitCommands": "จำกัดคำสั่ง",
+ "userDisableReports": "ปิดการใช้งานรายงาน",
+ "userFixedEmail": "ไม่มีการเปลี่ยนแปลงอีเมล",
+ "userToken": "Token",
+ "userDeleteAccount": "ลบบัญชี",
+ "userTemporary": "ชั่วคราว",
+ "loginTitle": "เข้าสู่ระบบ",
+ "loginLanguage": "ภาษา",
+ "loginReset": "รีเซ็ตรหัสผ่าน",
+ "loginRegister": "ลงทะเบียน",
+ "loginLogin": "เข้าสู่ระบบ",
+ "loginOpenId": "เข้าสู่ระบบด้วย OpenID",
+ "loginFailed": "ที่อยู่อีเมลหรือรหัสผ่านไม่ถูกต้อง",
+ "loginCreated": "ผู้ใช้ใหม่ ได้รับการลงทะเบียน",
+ "loginResetSuccess": "ตรวจสอบอีเมลของคุณ",
+ "loginUpdateSuccess": "ตั้งรหัสผ่านใหม่แล้ว",
+ "loginLogout": "ออกจากระบบ",
+ "loginLogo": "Logo",
+ "loginTotpCode": "รหัสผ่านแบบใช้ครั้งเดียว",
+ "loginTotpKey": "คีย์ รหัสผ่านครั้งเดียว",
+ "devicesAndState": "อุปกรณ์และสถานะ",
+ "deviceSelected": "อุปกรณ์ที่เลือก",
+ "deviceTitle": "เครื่อง/อุปกรณ์",
+ "devicePrimaryInfo": "ชื่ออุปกรณ์",
+ "deviceSecondaryInfo": "รายละเอียดอุปกรณ์",
+ "deviceIdentifier": "ระบุเลขอุปกรณ์",
+ "deviceModel": "รุ่น",
+ "deviceContact": "เบอร์ติดต่อ",
+ "deviceCategory": "หมวดหมู่",
+ "deviceLastUpdate": "แก้ไขล่าสุด",
+ "deviceCommand": "คำสั่ง",
+ "deviceFollow": "ติดตาม",
+ "deviceTotalDistance": "รวม ระยะทาง",
+ "deviceStatus": "สถานะ",
+ "deviceStatusOnline": "ออนไลน์",
+ "deviceStatusOffline": "ออฟไลน์",
+ "deviceStatusUnknown": "ไม่รู้จัก",
+ "deviceRegisterFirst": "ลงทะเบียนอุปกรณ์เครื่องแรกของคุณ",
+ "deviceIdentifierHelp": "IMEI หมายเลขซีเรียลหรือรหัสอื่นๆ จะต้องตรงกับรายงานอุปกรณ์ระบุไปยังเซิร์ฟเวอร์",
+ "deviceShare": "แบ่งปันอุปกรณ์",
+ "groupDialog": "กลุ่ม",
+ "groupParent": "กลุ่ม",
+ "groupNoGroup": "ไม่จัดกลุ่ม",
+ "settingsTitle": "การตั้งค่า",
+ "settingsUser": "บัญชีผู้ใช้",
+ "settingsGroups": "ตั้งค่ากลุ่ม",
+ "settingsServer": "เซิร์ฟเวอร์",
+ "settingsUsers": "ตั้งค่าผู้ใช้งาน",
+ "settingsDistanceUnit": "หน่วยระยะทาง",
+ "settingsAltitudeUnit": "หน่วยระดับความสูง",
+ "settingsSpeedUnit": "หน่วยความเร็ว",
+ "settingsVolumeUnit": "หน่วยจำนวน",
+ "settingsTwelveHourFormat": "รูปแบบเวลา 12 ชั่วโมง",
+ "settingsCoordinateFormat": "รูปแบบพิกัด",
+ "settingsServerVersion": "เวอร์ชั่นเซิร์ฟเวอร์",
+ "settingsAppVersion": "เวอร์ชั่นแอพ",
+ "settingsConnection": "การเชื่อมต่อ",
+ "settingsDarkMode": "โหมดมืด",
+ "settingsTotpEnable": "เปิดใช้งานรหัสผ่านครั้งเดียว",
+ "settingsTotpForce": "บังคับใช้รหัสผ่านครั้งเดียว",
+ "settingsServiceWorkerUpdateInterval": "รอบเวลาการอัปเดต ServiceWorker",
+ "settingsUpdateAvailable": "มีการอัพเดต",
+ "settingsSupport": "การสนับสนุน",
+ "reportTitle": "รายงาน",
+ "reportScheduled": "รายงานตามกำหนดเวลา",
+ "reportDevice": "รายงานเครื่อง/อุปกรณ์",
+ "reportGroup": "กลุ่ม",
+ "reportFrom": "จาก",
+ "reportTo": "ไปถึง",
+ "reportShow": "แสดง",
+ "reportClear": "ล้างรายงาน",
+ "linkGoogleMaps": "Google Maps",
+ "linkAppleMaps": "Apple Maps",
+ "linkStreetView": "Street View",
+ "positionFixTime": "แก้ไขเวลา",
+ "positionDeviceTime": "เวลาอุปกรณ์",
+ "positionServerTime": "เวลาเซิร์ฟเวอร์",
+ "positionValid": "ถูกต้อง",
+ "positionAccuracy": "ความถูกต้อง",
+ "positionLatitude": "ละติจูด",
+ "positionLongitude": "ลองจิจูด",
+ "positionAltitude": "แอตติจูด",
+ "positionSpeed": "ความเร็ว",
+ "positionCourse": "ทิศทาง",
+ "positionAddress": "ที่อยู่",
+ "positionProtocol": "โปรโตคอล",
+ "positionDistance": "ระยะทาง",
+ "positionRpm": "RPM",
+ "positionFuel": "เชื้อเพลิง",
+ "positionPower": "พลังงาน",
+ "positionBattery": "แบตเตอรี่",
+ "positionRaw": "ดิบ",
+ "positionIndex": "ดัชนี",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "ดาวเทียม",
+ "positionSatVisible": "ดาวเทียมที่มองเห็นได้",
+ "positionRssi": "RSSI",
+ "positionGps": "จีพีเอส",
+ "positionRoaming": "โรมมิ่ง",
+ "positionEvent": "กิจกรรม",
+ "positionAlarm": "แจ้งเตือน",
+ "positionStatus": "สถานะ",
+ "positionOdometer": "วัดระยะทาง",
+ "positionServiceOdometer": "บริการวัดระยะทาง",
+ "positionTripOdometer": "มาตรวัดการเดินทาง",
+ "positionHours": "ชั่วโมง",
+ "positionSteps": "Steps",
+ "positionInput": "อินพุต",
+ "positionHeartRate": "อัตราการเต้นของหัวใจ",
+ "positionOutput": "เอาต์พุต",
+ "positionBatteryLevel": "ระดับแบตเตอรี่",
+ "positionFuelConsumption": "การใช้น้ำมันเชื้อเพลิง",
+ "positionRfid": "RFID",
+ "positionVersionFw": "Firmware Version",
+ "positionVersionHw": "Hardware Version",
+ "positionIgnition": "ระบบจุดระเบิด",
+ "positionFlags": "สัญลักษณ์",
+ "positionCharge": "ชาร์จ",
+ "positionIp": "IP",
+ "positionArchive": "เก็บ",
+ "positionVin": "VIN",
+ "positionApproximate": "ประมาณ",
+ "positionThrottle": "คันเร่ง",
+ "positionMotion": "เคลื่อนไหว",
+ "positionArmed": "Armed",
+ "positionAcceleration": "การเร่งความเร็ว",
+ "positionTemp": "อุณหภูมิ",
+ "positionDeviceTemp": "อุณหภูมิของอุปกรณ์",
+ "positionCoolantTemp": "อุณหภูมิน้ำหล่อเย็น",
+ "positionOperator": "ผู้ประกอบการ",
+ "positionCommand": "คำสั่ง",
+ "positionBlocked": "Blocked",
+ "positionDtcs": "DTCs",
+ "positionObdSpeed": "ความเร็ว OBD",
+ "positionObdOdometer": "มาตรวัดระยะ OBD",
+ "positionDrivingTime": "เวลาในการขับขี่",
+ "positionDriverUniqueId": "หมายเลขเฉพาะไดร์เวอร์",
+ "positionCard": "การ์ด",
+ "positionImage": "รูปภาพ",
+ "positionVideo": "วิดีโอ",
+ "positionAudio": "เสียง",
+ "serverTitle": "การตั้งค่าเซิร์ฟเวอร์",
+ "serverZoom": "ชยาย +/-",
+ "serverRegistration": "ลงทะเบียน",
+ "serverReadonly": "อ่านได้อย่างเดียว",
+ "serverForceSettings": "บังคับ การตั้งค่า",
+ "serverAnnouncement": "ประกาศ",
+ "serverName": "ชื่อเซิร์ฟเวอร์",
+ "serverDescription": "รายละเอียดเซิร์ฟเวอร์",
+ "serverColorPrimary": "สีหลัก",
+ "serverColorSecondary": "สีรอง",
+ "serverLogo": "รูปภาพโลโก้",
+ "serverLogoInverted": "กลับด้านภาพโลโก้แล้ว",
+ "serverChangeDisable": "ปิดใช้งานการเปลี่ยนแปลงเซิร์ฟเวอร์",
+ "serverDisableShare": "ปิดใช้งานการแชร์อุปกรณ์",
+ "mapTitle": "แผนที่",
+ "mapActive": "แผนที่ที่ใช้งานอยู่",
+ "mapOverlay": "การวางซ้อนแผนที่",
+ "mapOverlayCustom": "การวางซ้อนแบบกำหนดเอง",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API Key",
+ "mapOpenWeatherClouds": "OpenWeather Clouds",
+ "mapOpenWeatherPrecipitation": "ปริมาณน้ำฝน OpenWeather",
+ "mapOpenWeatherPressure": "ความดัน OpenWeather",
+ "mapOpenWeatherWind": "ลม OpenWeather",
+ "mapOpenWeatherTemperature": "อุณหภูมิ OpenWeather",
+ "mapLayer": "ชั้นแผนที่",
+ "mapCustom": "กำหนดเอง (XYZ)",
+ "mapCustomArcgis": "กำหนดเอง (ArcGIS)",
+ "mapCustomLabel": "แผนที่ที่กำหนดเอง",
+ "mapCarto": "Carto Basemaps",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Bing Maps สำคัญ",
+ "mapBingRoad": "Bing Maps ถนน",
+ "mapBingAerial": "Bing Maps ทางอากาศ",
+ "mapBingHybrid": "Bing Maps Hybrid",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Yandex Map",
+ "mapYandexSat": "Yandex Satellite",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "การสำรวจยุทโธปกรณ์",
+ "mapMapboxStreets": "Mapbox Streets",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox Outdoors",
+ "mapMapboxSatellite": "Mapbox Satellite",
+ "mapMapboxKey": "Mapbox Access Token",
+ "mapMapTilerBasic": "MapTiler Basic",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "MapTiler API Key",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ Access Token",
+ "mapTomTomBasic": "TomTom Basic",
+ "mapTomTomFlow": "TomTom Traffic Flow",
+ "mapTomTomIncidents": "TomTom Traffic Incidents",
+ "mapTomTomKey": "TomTom API Key",
+ "mapHereBasic": "Here Basic",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satellite",
+ "mapHereFlow": "Here Traffic Flow",
+ "mapHereKey": "Here API Key",
+ "mapShapePolygon": "รูปหลายมุม",
+ "mapShapeCircle": "วงกลม",
+ "mapShapePolyline": "โพลีไลน์",
+ "mapLiveRoutes": "เส้นทาง Live",
+ "mapDirection": "แสดงทิศทาง",
+ "mapCurrentLocation": "สถานที่ปัจจุบัน",
+ "mapPoiLayer": "POI Layer",
+ "mapClustering": "การจัดกลุ่มเครื่องหมาย",
+ "mapOnSelect": "แสดงแผนที่บริเวณที่เลือก",
+ "mapDefault": "แผนที่เริ่มต้น",
+ "stateTitle": "สถานะ",
+ "stateName": "พารามิเตอร์",
+ "stateValue": "มูลค่า",
+ "commandTitle": "คำสั่ง",
+ "commandSend": "ส่ง",
+ "commandSent": "คำสั่ง-ส่งแล้ว",
+ "commandQueued": "คำสั่ง-คิว",
+ "commandUnit": "หน่วย",
+ "commandCustom": "คำสั่งกำหนดเอง",
+ "commandDeviceIdentification": "หมายเลขอุปกรณ์",
+ "commandPositionSingle": "รายงานตำแหน่งเดียว",
+ "commandPositionPeriodic": "แก้ไขตำแหน่ง",
+ "commandPositionStop": "ตำแหน่ง หยุด",
+ "commandEngineStop": "ดับเครื่องยนต์",
+ "commandEngineResume": "ติดครื่องยนต์ใหม่",
+ "commandAlarmArm": "แจ้งเตือนติดต่อสาขา",
+ "commandAlarmDisarm": "แจ้งเตือนยกเลิกติดต่อสาขา",
+ "commandAlarmDismiss": "ปิดการเตือน",
+ "commandSetTimezone": "ตั้งค่าเขตเวลา",
+ "commandRequestPhoto": "สั่งถ่ายภาพ",
+ "commandPowerOff": "ปิดอุปกรณ์",
+ "commandRebootDevice": "รีบูต",
+ "commandFactoryReset": "รีเซ็ตเป็นค่าจากโรงงาน",
+ "commandSendSms": "ส่ง SMS",
+ "commandSendUssd": "ส่ง USSD",
+ "commandSosNumber": "ตั้งค่าเลขหมายโทรฉุกเฉิน SOS",
+ "commandSilenceTime": "ตั้งค่าช่วงเวลาหยุดนิ่ง",
+ "commandSetPhonebook": "ตั้งค่าสมุดโทรศัพท์",
+ "commandVoiceMessage": "ข้อความเสียง",
+ "commandOutputControl": "ควบคุมข้อมูลที่ส่งออก",
+ "commandVoiceMonitoring": "การตรวจสอบด้วยเสียง",
+ "commandSetAgps": "ตั้งค่า AGPS",
+ "commandSetIndicator": "ตั้งค่าตัวบ่งชี้",
+ "commandConfiguration": "องค์ประกอบ",
+ "commandGetVersion": "รับเวอร์ชัน",
+ "commandFirmwareUpdate": "อัพเดตเฟิร์มแวร์",
+ "commandSetConnection": "ตั้งค่าการเชื่อมต่อ",
+ "commandSetOdometer": "ตั้งค่ามาตรวัดระยะทาง",
+ "commandGetModemStatus": "ดูสถานะโมเด็ม",
+ "commandGetDeviceStatus": "รับสถานะอุปกรณ์",
+ "commandSetSpeedLimit": "ตั้งขีดจำกัดความเร็ว",
+ "commandModePowerSaving": "โหมดประหยัดพลังงาน",
+ "commandModeDeepSleep": "โหมดหลับลึก",
+ "commandAlarmGeofence": "ตั้งเตือน Geofence",
+ "commandAlarmBattery": "ตั้งการเตือนแบตเตอรี่",
+ "commandAlarmSos": "ตั้งการเตือน SOS",
+ "commandAlarmRemove": "ตั้งลบการเตือน",
+ "commandAlarmClock": "ตั้งนาฬิการปลุก",
+ "commandAlarmSpeed": "ตั้งการเตือนความเร็ว",
+ "commandAlarmFall": "ตั้งการเตือนการตก",
+ "commandAlarmVibration": "ตั้งการเตือนการสั่น",
+ "commandFrequency": "ความถี่",
+ "commandTimezone": "เขตเวลา ตรงข้าม",
+ "commandMessage": "ข้อความ",
+ "commandRadius": "รัศมี",
+ "commandEnable": "เปิดใช้งาน",
+ "commandData": "ข้อมูล",
+ "commandIndex": "ดัชนี",
+ "commandPhone": "หมายเลขโทรศัพท์",
+ "commandServer": "Server",
+ "commandPort": "Port",
+ "eventAll": "เหตุการณ์ทั้งหมด",
+ "eventDeviceOnline": "ออนไลน์",
+ "eventDeviceUnknown": "ไม่ระบุ",
+ "eventDeviceOffline": "ออฟไลน์",
+ "eventDeviceInactive": "อุปกรณ์ไม่ทำงาน",
+ "eventQueuedCommandSent": "ส่งคำสั่งเข้าคิวแล้ว",
+ "eventDeviceMoving": "เคลื่อนที่",
+ "eventDeviceStopped": "หยุด/จอด",
+ "eventDeviceOverspeed": "ความเร็วเกิน",
+ "eventDeviceFuelDrop": "เชื้อเพลิงลด",
+ "eventDeviceFuelIncrease": "น้ำมันเชื้อเพลิงเพิ่มขึ้น",
+ "eventCommandResult": "ผลลัพธ์จากคำสั่ง",
+ "eventGeofenceEnter": "เข้าเขต",
+ "eventGeofenceExit": "ออกนอกเขต",
+ "eventAlarm": "เตือน",
+ "eventIgnitionOn": "เครื่องยนต์ติด",
+ "eventIgnitionOff": "เครื่องยนต์ดับ",
+ "eventMaintenance": "จำเป็นต้อง บำรุงรักษา",
+ "eventTextMessage": "ข้อความที่ได้รับ",
+ "eventDriverChanged": "เปลี่ยนคนขับ",
+ "eventMedia": "สื่อ",
+ "eventsScrollToLast": "เลื่อนไปที่ล่าสุด",
+ "eventsSoundEvents": "เสียงเหตุการณ์",
+ "eventsSoundAlarms": "เสียงปลุก",
+ "alarmGeneral": "ทั่วไป",
+ "alarmSos": "SOS",
+ "alarmVibration": "สั่นสะเทือน",
+ "alarmMovement": "เคลื่อนที่",
+ "alarmLowspeed": "เคลื่อนช้าๆ",
+ "alarmOverspeed": "ความเร็วเกิน",
+ "alarmFallDown": "ร่วงหล่น",
+ "alarmLowPower": "กำลัง-อ่อน",
+ "alarmLowBattery": "แบต-อ่อน",
+ "alarmFault": "ผิดพลาด",
+ "alarmPowerOff": "ปิดเครื่อง",
+ "alarmPowerOn": "เปิดเครื่อง",
+ "alarmDoor": "ประตู",
+ "alarmLock": "ล็อค",
+ "alarmUnlock": "ปลดล็อค",
+ "alarmGeofence": "ขอบเขต",
+ "alarmGeofenceEnter": "เข้าขอบเขต",
+ "alarmGeofenceExit": "ออกขอบเขต",
+ "alarmGpsAntennaCut": "เสา GPS ตัด",
+ "alarmAccident": "อุบัติเหตุ",
+ "alarmTow": "ลาก",
+ "alarmIdle": "นิ่ง",
+ "alarmHighRpm": "รอบเครื่องสูง",
+ "alarmHardAcceleration": "ออกตัวแรง",
+ "alarmHardBraking": "เบรคแรง",
+ "alarmHardCornering": "เลี้ยวกระทันหัน",
+ "alarmLaneChange": "เปลี่ยนเลน",
+ "alarmFatigueDriving": "ขับรถ-ล้า",
+ "alarmPowerCut": "ตัดสตาร์ท",
+ "alarmPowerRestored": "ไฟฟ้า-ต่อ",
+ "alarmJamming": "สัญญาณรบกวน",
+ "alarmTemperature": "อุณหภูมิ",
+ "alarmParking": "จอด",
+ "alarmBonnet": "ฝาสูบ",
+ "alarmFootBrake": "เบรคเท้า",
+ "alarmFuelLeak": "เชื้อเพลิงรั่ว",
+ "alarmTampering": "บุกรุกเครื่อง",
+ "alarmRemoving": "ถอดออก",
+ "notificationType": "ชนิดการแจ้งเตือน",
+ "notificationAlways": "อุปกรณ์ทั้งหมด",
+ "notificationNotificators": "ช่อง",
+ "notificatorCommand": "คำสั่ง",
+ "notificatorWeb": "เว็ป",
+ "notificatorMail": "เมลล์",
+ "notificatorSms": "ส่งข้อความ",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "เล่นซ้ำ",
+ "reportCombined": "รวมเรียบร้อยแล้ว",
+ "reportRoute": "เส้นทาง",
+ "reportEvents": "เหตุการณ์",
+ "reportTrips": "การเดินทาง",
+ "reportStops": "จุดจอด",
+ "reportSummary": "ผลรวม",
+ "reportDaily": "สรุปรายวัน",
+ "reportChart": "แผนภูมิ",
+ "reportConfigure": "ตั้งค่า",
+ "reportEventTypes": "ประเภทเหตุการณ์",
+ "reportChartType": "ชนิดของแผนภูมิ",
+ "reportShowMarkers": "แสดงเครื่องหมาย",
+ "reportExport": "ส่งออก",
+ "reportEmail": "รายงาน-Email",
+ "reportSchedule": "กำหนดการ",
+ "reportPeriod": "ช่วงเวลา",
+ "reportCustom": "กำหนดเอง",
+ "reportToday": "วันนี้",
+ "reportYesterday": "เมื่อวาน",
+ "reportThisWeek": "สัปดาห์นี้",
+ "reportPreviousWeek": "สัปดาห์ก่อน",
+ "reportThisMonth": "เดือนนี้",
+ "reportPreviousMonth": "เดือนก่อน",
+ "reportDeviceName": "ชื่ออุปกรณ์",
+ "reportAverageSpeed": "ความเร็วเฉลี่ย",
+ "reportMaximumSpeed": "ความเร็วสูงสุด",
+ "reportEngineHours": "เวลาการทำงานเครื่องยนต์",
+ "reportDuration": "ช่วงเวลา",
+ "reportStartDate": "วันที่เริ่ม",
+ "reportStartTime": "เวลาเริ่มต้น",
+ "reportStartAddress": "จุดเริ่มต้น",
+ "reportEndTime": "เวลาสิ้นสุด",
+ "reportEndAddress": "จุดสิ้นสุด",
+ "reportSpentFuel": "เชื้อเพลิงที่ใช้",
+ "reportStartOdometer": "เดินทาง-เริ่ม",
+ "reportEndOdometer": "เดินทาง-สิ้นสุด",
+ "statisticsTitle": "ข้อมูลสถิติ",
+ "statisticsCaptureTime": "จับเวลา",
+ "statisticsActiveUsers": "ผู้ใช้ที่ใช้งานอยู่",
+ "statisticsActiveDevices": "อุปกรณ์ที่ใช้งานอยู่",
+ "statisticsRequests": "การร้องขอ",
+ "statisticsMessagesReceived": "ข้อความที่ได้รับ",
+ "statisticsMessagesStored": "ข้อความที่เก็บไว้",
+ "statisticsGeocoder": "คำขอรหัสทางภูมิศาสตร์",
+ "statisticsGeolocation": "คำขอตำแหน่งทางภูมิศาสตร์",
+ "categoryArrow": "ลูกศร",
+ "categoryDefault": "ค่าเริ่มต้น",
+ "categoryAnimal": "สัตว์",
+ "categoryBicycle": "จักรยาน",
+ "categoryBoat": "เรือ",
+ "categoryBus": "รถบัส",
+ "categoryCar": "รถยนต์",
+ "categoryCamper": "คนพักแรม",
+ "categoryCrane": "เครน",
+ "categoryHelicopter": "เฮลิคอปเตอร์",
+ "categoryMotorcycle": "รถจักรยานยนต์",
+ "categoryOffroad": "ออฟโร้ด",
+ "categoryPerson": "บุคคล",
+ "categoryPickup": "รถกระบะ",
+ "categoryPlane": "เครื่องบิน",
+ "categoryShip": "เรือ",
+ "categoryTractor": "รถแทรกเตอร์",
+ "categoryTrain": "รถไฟ",
+ "categoryTram": "รถราง",
+ "categoryTrolleybus": "รถยก",
+ "categoryTruck": "รถบรรทุก",
+ "categoryVan": "รถตู้",
+ "categoryScooter": "สกู๊ตเตอร์",
+ "maintenanceStart": "เริ่มต้น",
+ "maintenancePeriod": "ระยะเวลา"
+} \ No newline at end of file
diff --git a/src/resources/l10n/tr.json b/src/resources/l10n/tr.json
new file mode 100644
index 00000000..ff534af7
--- /dev/null
+++ b/src/resources/l10n/tr.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "Yükleniyor...",
+ "sharedHide": "Gizle",
+ "sharedSave": "Kaydet",
+ "sharedUpload": "Upload",
+ "sharedSet": "Belirle",
+ "sharedCancel": "İptal",
+ "sharedCopy": "Copy",
+ "sharedAdd": "Ekle",
+ "sharedEdit": "Düzenle",
+ "sharedRemove": "Kaldır",
+ "sharedRemoveConfirm": "Öğeyi kaldır",
+ "sharedNoData": "Veri yok",
+ "sharedSubject": "Subject",
+ "sharedYes": "Evet",
+ "sharedNo": "Hayır",
+ "sharedKm": "km",
+ "sharedMi": "mil",
+ "sharedNmi": "nmi",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "knot",
+ "sharedKmh": "km/s",
+ "sharedMph": "mil/s",
+ "sharedHour": "Saat",
+ "sharedMinute": "Dakika",
+ "sharedSecond": "Saniye",
+ "sharedDays": "gün",
+ "sharedHours": "saat",
+ "sharedMinutes": "dakika",
+ "sharedDecimalDegrees": "Onluk Derece",
+ "sharedDegreesDecimalMinutes": "Onluk Dakika Derecesi",
+ "sharedDegreesMinutesSeconds": "Dakika Saniye Derecesi",
+ "sharedName": "İsim",
+ "sharedDescription": "Açıklama",
+ "sharedSearch": "Arama",
+ "sharedIconScale": "Simge Ölçeği",
+ "sharedGeofence": "Güvenli Bölge",
+ "sharedGeofences": "Güvenli Bölgeler",
+ "sharedCreateGeofence": "Coğrafi Sınır oluştur",
+ "sharedNotifications": "Bildirimler",
+ "sharedNotification": "Bildirim",
+ "sharedAttributes": "Nitelikler",
+ "sharedAttribute": "Nitelik",
+ "sharedDrivers": "Sürücüler",
+ "sharedDriver": "Sürücü",
+ "sharedArea": "Bölge",
+ "sharedSound": "Bildirim sesi",
+ "sharedType": "Tip",
+ "sharedDistance": "Mesafe",
+ "sharedHourAbbreviation": "s",
+ "sharedMinuteAbbreviation": "d",
+ "sharedSecondAbbreviation": "s",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "l",
+ "sharedGallonAbbreviation": "galon",
+ "sharedLiter": "Litre",
+ "sharedImpGallon": "İngiliz Galonu",
+ "sharedUsGallon": "Amerikan Galonu",
+ "sharedLiterPerHourAbbreviation": "l/h",
+ "sharedGetMapState": "Harita Durumunu Getir",
+ "sharedComputedAttribute": "Hesaplanmış Nitelik",
+ "sharedComputedAttributes": "Hesaplanmış Nitelikler",
+ "sharedCheckComputedAttribute": "Hesaplanmış Nitelikleri Kontrol Et",
+ "sharedExpression": "İfade",
+ "sharedDevice": "Cihaz",
+ "sharedTest": "Test",
+ "sharedTestNotification": "Deneme bildirimi gönder",
+ "sharedTestNotificators": "Test Kanalları",
+ "sharedTestExpression": "Test İfadesi",
+ "sharedCalendar": "Takvim",
+ "sharedCalendars": "Takvimler",
+ "sharedFile": "Dosya",
+ "sharedSearchDevices": "Cihaz Arama",
+ "sharedSortBy": "Göre sırala",
+ "sharedFilterMap": "Haritada Filtrele",
+ "sharedSelectFile": "Dosya Seçin",
+ "sharedPhone": "Telefon",
+ "sharedRequired": "Gerekli",
+ "sharedPreferences": "Özellikler",
+ "sharedPermissions": "Yetkiler",
+ "sharedConnections": "Bağlantılar",
+ "sharedExtra": "Ekstra",
+ "sharedPrimary": "Birincil",
+ "sharedSecondary": "İkincil",
+ "sharedTypeString": "Akış",
+ "sharedTypeNumber": "Sayı",
+ "sharedTypeBoolean": "Evet - Hayır",
+ "sharedTimezone": "Saat dilimi",
+ "sharedInfoTitle": "Info",
+ "sharedSavedCommand": "Komut Kaydet",
+ "sharedSavedCommands": "Komutları Kaydet",
+ "sharedNew": "Yeni",
+ "sharedShowAddress": "Adresi Göster",
+ "sharedShowDetails": "Daha fazla detay",
+ "sharedDisabled": "Devre Dışı",
+ "sharedMaintenance": "Bakım",
+ "sharedDeviceAccumulators": "Akümülatörler",
+ "sharedAlarms": "Alarmlar",
+ "sharedLocation": "Konum",
+ "sharedImport": "İçe aktarmak",
+ "sharedColumns": "Sütunlar",
+ "sharedDropzoneText": "Bir dosyayı buraya sürükleyip bırakın veya tıklayın",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "Basit",
+ "calendarRecurrence": "Recurrence",
+ "calendarOnce": "Bir kere",
+ "calendarDaily": "Günlük",
+ "calendarWeekly": "Haftalık",
+ "calendarMonthly": "Aylık",
+ "calendarDays": "Gün",
+ "calendarSunday": "Pazar",
+ "calendarMonday": "Pazartesi",
+ "calendarTuesday": "Salı",
+ "calendarWednesday": "Çarşamba",
+ "calendarThursday": "Perşembe",
+ "calendarFriday": "Cuma",
+ "calendarSaturday": "Cumartesi",
+ "attributeShowGeofences": "Coğrafi Sınırları Göster",
+ "attributeSpeedLimit": "Hız Limiti",
+ "attributeFuelDropThreshold": "Fuel Drop Threshold",
+ "attributeFuelIncreaseThreshold": "Fuel Increase Threshold",
+ "attributePolylineDistance": "Çizgi uzaklığı",
+ "attributeReportIgnoreOdometer": "Rapor: Odometerı yoksay",
+ "attributeWebReportColor": "Web: Rapor Rengi",
+ "attributeDevicePassword": "Araç şifresi",
+ "attributeDeviceImage": "Cihaz Resmi",
+ "attributeDeviceInactivityStart": "Device Inactivity Start",
+ "attributeDeviceInactivityPeriod": "Device Inactivity Period",
+ "attributeProcessingCopyAttributes": "İşleniyor: Nitelikler Kopyalanıyor",
+ "attributeColor": "Renk",
+ "attributeWebLiveRouteLength": "Web: Rota Uzunluğu",
+ "attributeWebSelectZoom": "Web: Seçiliye Yaklaş",
+ "attributeWebMaxZoom": "Web: Maksimum Yakınlaştırma",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Pushover User Key",
+ "attributePushoverDeviceNames": "Pushover Device Names",
+ "attributeMailSmtpHost": "Eposta: SMTP Sunucu",
+ "attributeMailSmtpPort": "Eposta: SMTP Port",
+ "attributeMailSmtpStarttlsEnable": "Eposta: SMTP STARTTLS Kullan",
+ "attributeMailSmtpStarttlsRequired": "Eposta: SMTP STARTTLS Gerekli",
+ "attributeMailSmtpSslEnable": "Eposta: SMTP SSL Kullan",
+ "attributeMailSmtpSslTrust": "Eposta: SMTP SSL Güvenli",
+ "attributeMailSmtpSslProtocols": "Eposta: SMTP SSL Protokol",
+ "attributeMailSmtpFrom": "Eposta: SMTP Kimden",
+ "attributeMailSmtpAuth": "Eposta: SMTP Parola Etkin",
+ "attributeMailSmtpUsername": "Eposta: SMTP Kullanıcı Adı",
+ "attributeMailSmtpPassword": "Eposta: SMTP Şifre",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: Disable Attributes",
+ "attributeUiDisableGroups": "UI: Disable Groups",
+ "attributeUiDisableEvents": "Etkinlikler Devredışı",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "Görünüm: Sürücüler Devredışı",
+ "attributeUiDisableComputedAttributes": "Görünüm: Hesaplanmış Öznitelikler Devredışı",
+ "attributeUiDisableCalendars": "Görünüm: Takvim Devredışı",
+ "attributeUiDisableMaintenance": "UI: Disable Maintenance",
+ "attributeUiHidePositionAttributes": "Konum Özniteliklerini Gizle",
+ "attributeUiDisableLoginLanguage": "UI: Disable Login Language",
+ "attributeNotificationTokens": "Notification Tokens",
+ "attributePopupInfo": "Popup Info",
+ "errorTitle": "Hata",
+ "errorGeneral": "Geçersiz parametre veya kısıtlama ihlali",
+ "errorConnection": "Bağlantı Hatası",
+ "errorSocket": "Web Soketine bağlanırken hata",
+ "errorZero": "Sıfır olamaz",
+ "userEmail": "Eposta",
+ "userPassword": "Şifre",
+ "userAdmin": "Yönetici",
+ "userRemember": "Hatırla",
+ "userExpirationTime": "Geçersizlik Tarihi",
+ "userDeviceLimit": "Cihaz Limiti",
+ "userUserLimit": "Kullanıcı Limiti",
+ "userDeviceReadonly": "Cihaz Salt Okunur",
+ "userLimitCommands": "Komutları Kısıtla",
+ "userDisableReports": "Raporları Devre Dışı Bırak",
+ "userFixedEmail": "No Email Change",
+ "userToken": "Kullanıcı Anahtarı",
+ "userDeleteAccount": "Hesabı sil",
+ "userTemporary": "Temporary",
+ "loginTitle": "Oturum aç",
+ "loginLanguage": "Lisan",
+ "loginReset": "Şifreyi yenile",
+ "loginRegister": "Kayıt",
+ "loginLogin": "Oturumu aç",
+ "loginOpenId": "Login with OpenID",
+ "loginFailed": "Geçersiz eposta veya şifre",
+ "loginCreated": "Yeni kullanıcı kaydedildi",
+ "loginResetSuccess": "E-postanı kontrol et",
+ "loginUpdateSuccess": "Yeni şifre belirlendi",
+ "loginLogout": "Oturumu sonlandır",
+ "loginLogo": "Logo",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "Cihazlar ve Bölge",
+ "deviceSelected": "Seçilen Cihaz",
+ "deviceTitle": "Cihazlar",
+ "devicePrimaryInfo": "Cihaz Başlığı",
+ "deviceSecondaryInfo": "Cihaz Detayı",
+ "deviceIdentifier": "Kimlik",
+ "deviceModel": "Model",
+ "deviceContact": "İletişim",
+ "deviceCategory": "Kategori",
+ "deviceLastUpdate": "Son Güncelleme",
+ "deviceCommand": "Komut",
+ "deviceFollow": "Takip",
+ "deviceTotalDistance": "Toplam Mesafe",
+ "deviceStatus": "Durum",
+ "deviceStatusOnline": "Çevrimiçi",
+ "deviceStatusOffline": "Çevrimdışı",
+ "deviceStatusUnknown": "Bilinmeyen",
+ "deviceRegisterFirst": "İlk cihazınızı kaydedin",
+ "deviceIdentifierHelp": "IMEI, seri numarası veya diğer kimlik. Tanımlayıcı cihaz raporlarını sunucuyla eşleştirmesi gerekir.",
+ "deviceShare": "Share Device",
+ "groupDialog": "Grup",
+ "groupParent": "Grup",
+ "groupNoGroup": "Grupsuz",
+ "settingsTitle": "Ayarlar",
+ "settingsUser": "Hesap",
+ "settingsGroups": "Gruplar",
+ "settingsServer": "Sunucu",
+ "settingsUsers": "Kullanıcı",
+ "settingsDistanceUnit": "Uzaklık Birimi",
+ "settingsAltitudeUnit": "Rakım Birimi",
+ "settingsSpeedUnit": "Hız Birimi",
+ "settingsVolumeUnit": "Hacim Birimi",
+ "settingsTwelveHourFormat": "12 saat formatı",
+ "settingsCoordinateFormat": "Koordinat Formatı",
+ "settingsServerVersion": "Sunucu Sürümü",
+ "settingsAppVersion": "Uygulama sürümü",
+ "settingsConnection": "Bağlantı",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "Raporlar",
+ "reportScheduled": "Planlanmış Raporlar",
+ "reportDevice": "Aygıt",
+ "reportGroup": "Grup",
+ "reportFrom": "Başlangıç",
+ "reportTo": "Varış",
+ "reportShow": "Göster",
+ "reportClear": "Temizle",
+ "linkGoogleMaps": "Google Maps",
+ "linkAppleMaps": "Apple Maps",
+ "linkStreetView": "Street View",
+ "positionFixTime": "Fix Time",
+ "positionDeviceTime": "Cihaz Saati",
+ "positionServerTime": "Sunucu zamanı",
+ "positionValid": "Geçerli",
+ "positionAccuracy": "Hata Payı",
+ "positionLatitude": "Enlem",
+ "positionLongitude": "Boylam",
+ "positionAltitude": "Rakım",
+ "positionSpeed": "Sürat",
+ "positionCourse": "Yön",
+ "positionAddress": "Adres",
+ "positionProtocol": "Protokol",
+ "positionDistance": "Mesafe",
+ "positionRpm": "RPM",
+ "positionFuel": "Yakıt",
+ "positionPower": "Güç",
+ "positionBattery": "Batarya",
+ "positionRaw": "Ham",
+ "positionIndex": "Indeks",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "Uydu",
+ "positionSatVisible": "Görünür Uydu",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "Dolaşım",
+ "positionEvent": "Etkinlik",
+ "positionAlarm": "Alarm",
+ "positionStatus": "Durum",
+ "positionOdometer": "Kilometre Sayacı",
+ "positionServiceOdometer": "Kilometre Sayacı Hizmeti",
+ "positionTripOdometer": "Kilometre Sayacı Turu",
+ "positionHours": "Saat",
+ "positionSteps": "Adım",
+ "positionInput": "Giren",
+ "positionHeartRate": "Heart Rate",
+ "positionOutput": "Çıkan",
+ "positionBatteryLevel": "Pil Seviyesi",
+ "positionFuelConsumption": "Yakıt Tüketimi",
+ "positionRfid": "RFID",
+ "positionVersionFw": "Firmware Versiyonu",
+ "positionVersionHw": "Hardware Versiyonu",
+ "positionIgnition": "Kontak",
+ "positionFlags": "Etiketler",
+ "positionCharge": "Şarz",
+ "positionIp": "IP",
+ "positionArchive": "Arşiv",
+ "positionVin": "VIN",
+ "positionApproximate": "Yaklaşık",
+ "positionThrottle": "Kısıtlama",
+ "positionMotion": "Hareket",
+ "positionArmed": "Hazır",
+ "positionAcceleration": "Hızlanma",
+ "positionTemp": "Sıcaklık",
+ "positionDeviceTemp": "Cihaz Sıcaklığı",
+ "positionCoolantTemp": "Coolant Temperature",
+ "positionOperator": "Operatör",
+ "positionCommand": "Komut",
+ "positionBlocked": "Bloke Edildi",
+ "positionDtcs": "DTCs",
+ "positionObdSpeed": "OBD Hız",
+ "positionObdOdometer": "OBD Kilometre Sayacı",
+ "positionDrivingTime": "Driving Time",
+ "positionDriverUniqueId": "Benzersiz Sürücü Kimliği",
+ "positionCard": "Card",
+ "positionImage": "Görüntü",
+ "positionVideo": "Video",
+ "positionAudio": "Ses",
+ "serverTitle": "Sunucu Ayarları",
+ "serverZoom": "Yakınlaştırma",
+ "serverRegistration": "Kayıt",
+ "serverReadonly": "Sadece Okunur",
+ "serverForceSettings": "Zorunlu Ayarlar",
+ "serverAnnouncement": "Duyuru",
+ "serverName": "Server Name",
+ "serverDescription": "Server Description",
+ "serverColorPrimary": "Color Primary",
+ "serverColorSecondary": "Color Secondary",
+ "serverLogo": "Logo Image",
+ "serverLogoInverted": "Inverted Logo Image",
+ "serverChangeDisable": "Disable Server Change",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "Harita",
+ "mapActive": "Aktif Haritalar",
+ "mapOverlay": "Map Overlay",
+ "mapOverlayCustom": "Custom Overlay",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API Key",
+ "mapOpenWeatherClouds": "OpenWeather Clouds",
+ "mapOpenWeatherPrecipitation": "OpenWeather Precipitation",
+ "mapOpenWeatherPressure": "OpenWeather Pressure",
+ "mapOpenWeatherWind": "OpenWeather Wind",
+ "mapOpenWeatherTemperature": "OpenWeather Temperature",
+ "mapLayer": "Harita Katmanı",
+ "mapCustom": "Özel (XYZ)",
+ "mapCustomArcgis": "Özel (ArcGIS)",
+ "mapCustomLabel": "Özel harita",
+ "mapCarto": "Carto Basemaps",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Bing Maps Key",
+ "mapBingRoad": "Bing Maps Road",
+ "mapBingAerial": "Bing Maps Aerial",
+ "mapBingHybrid": "Bing Maps Hybrid",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Yandex Map",
+ "mapYandexSat": "Yandex Satellite",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox Streets",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox Outdoors",
+ "mapMapboxSatellite": "Mapbox Satellite",
+ "mapMapboxKey": "Mapbox Access Token",
+ "mapMapTilerBasic": "MapTiler Basic",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "MapTiler API Key",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ Access Token",
+ "mapTomTomBasic": "TomTom Basic",
+ "mapTomTomFlow": "TomTom Traffic Flow",
+ "mapTomTomIncidents": "TomTom Traffic Incidents",
+ "mapTomTomKey": "TomTom API Key",
+ "mapHereBasic": "Here Basic",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satellite",
+ "mapHereFlow": "Here Traffic Flow",
+ "mapHereKey": "Here API Key",
+ "mapShapePolygon": "Çokgen",
+ "mapShapeCircle": "Çember",
+ "mapShapePolyline": "Çizim",
+ "mapLiveRoutes": "Canlı Takip",
+ "mapDirection": "Yönü Göster",
+ "mapCurrentLocation": "Mevcut konum",
+ "mapPoiLayer": "POI Katmanı",
+ "mapClustering": "Markers Clustering",
+ "mapOnSelect": "Seçimde Haritayı Göster",
+ "mapDefault": "Varsayılan Harita",
+ "stateTitle": "Bölge",
+ "stateName": "Özellik",
+ "stateValue": "Değer",
+ "commandTitle": "Komut",
+ "commandSend": "Gönder",
+ "commandSent": "Komut gönderildi",
+ "commandQueued": "Komut sıraya alındı",
+ "commandUnit": "Ünite",
+ "commandCustom": "Özel komut",
+ "commandDeviceIdentification": "Cihaz Tanımı",
+ "commandPositionSingle": "Tekil Raporlama",
+ "commandPositionPeriodic": "Periyodik Rapor",
+ "commandPositionStop": "Raporlamayı Durdur",
+ "commandEngineStop": "Motoru Durdur",
+ "commandEngineResume": "Motoru Çalıştır",
+ "commandAlarmArm": "Alarm Kur",
+ "commandAlarmDisarm": "Alarmı Kapat",
+ "commandAlarmDismiss": "Alarmı Kapat",
+ "commandSetTimezone": "Zaman Dilimini Belirle",
+ "commandRequestPhoto": "Fotoğraf İste",
+ "commandPowerOff": "Cihazı Kapatın",
+ "commandRebootDevice": "Aygıtı Yeniden Başlat",
+ "commandFactoryReset": "Fabrika Ayarları",
+ "commandSendSms": "SMS Gönder",
+ "commandSendUssd": "USSD Gönder",
+ "commandSosNumber": "Acil Durum Numarasını Belirle",
+ "commandSilenceTime": "Sessiz Zamanı Belirle",
+ "commandSetPhonebook": "Telefon Defterini Belirle",
+ "commandVoiceMessage": "Ses Mesajı",
+ "commandOutputControl": "Çıkış Kontrolü",
+ "commandVoiceMonitoring": "Ses İzleme",
+ "commandSetAgps": "AGPS Ayarla",
+ "commandSetIndicator": "Belirteci Ayarla",
+ "commandConfiguration": "Konfigürasyon",
+ "commandGetVersion": "Sürüm Al",
+ "commandFirmwareUpdate": "Aygıt Yazılımı Güncelle",
+ "commandSetConnection": "Bağlantı Ayarla",
+ "commandSetOdometer": "Kilometre Sayacı Ayarla",
+ "commandGetModemStatus": "Modem Durumunu Al",
+ "commandGetDeviceStatus": "Cihaz Durumunu Al",
+ "commandSetSpeedLimit": "Hız Sınırını Ayarla",
+ "commandModePowerSaving": "Enerji Tasarrufu Modu",
+ "commandModeDeepSleep": "Derin Uyku Modu",
+ "commandAlarmGeofence": "Coğrafi Sınır Alarmını Ayarla",
+ "commandAlarmBattery": "Pil Alarmını Ayarla",
+ "commandAlarmSos": "SOS Alarmını Ayarla",
+ "commandAlarmRemove": "Set Remove Alarm",
+ "commandAlarmClock": "Saat Alarmını Ayarla",
+ "commandAlarmSpeed": "Hız Alarmını Ayarla",
+ "commandAlarmFall": "Düşme Alarmını Ayarla",
+ "commandAlarmVibration": "Titreşim Alarmını Ayarla",
+ "commandFrequency": "Frekans",
+ "commandTimezone": "Saat Dilimi Dışında",
+ "commandMessage": "Mesaj",
+ "commandRadius": "Yarıçap",
+ "commandEnable": "Etkinleştir",
+ "commandData": "Veri",
+ "commandIndex": "Fihrist",
+ "commandPhone": "Telefon Numarası",
+ "commandServer": "Sunucu",
+ "commandPort": "Port",
+ "eventAll": "Tüm Olaylar",
+ "eventDeviceOnline": "Durumu çevrimiçi",
+ "eventDeviceUnknown": "Durumu bilinmiyor",
+ "eventDeviceOffline": "Durum çevrimdışı",
+ "eventDeviceInactive": "Cihaz etkin değil",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "Cihaz hareket halinde",
+ "eventDeviceStopped": "Cihaz hareket etmiyor",
+ "eventDeviceOverspeed": "Hız limiti aşıldı",
+ "eventDeviceFuelDrop": "Yakıt düştü",
+ "eventDeviceFuelIncrease": "Yakıt artışı",
+ "eventCommandResult": "Komut sonucu",
+ "eventGeofenceEnter": "Coğrafik çite girildi",
+ "eventGeofenceExit": "Coğrafik çitden çıkıldı",
+ "eventAlarm": "Alarm",
+ "eventIgnitionOn": "Kontak açık",
+ "eventIgnitionOff": "Kontak kapalı",
+ "eventMaintenance": "Bakım Gerekli",
+ "eventTextMessage": "Kısa mesaj alındı",
+ "eventDriverChanged": "Sürücü değişti",
+ "eventMedia": "Media",
+ "eventsScrollToLast": "Sona Git",
+ "eventsSoundEvents": "Sound Events",
+ "eventsSoundAlarms": "Sound Alarms",
+ "alarmGeneral": "Genel",
+ "alarmSos": "Acil Yardım",
+ "alarmVibration": "Sarsıntı",
+ "alarmMovement": "Hareket",
+ "alarmLowspeed": "Düşük Hız",
+ "alarmOverspeed": "Aşırı Hız",
+ "alarmFallDown": "Düşmek",
+ "alarmLowPower": "Düşük Güç",
+ "alarmLowBattery": "Düşük pil",
+ "alarmFault": "Arıza",
+ "alarmPowerOff": "Kapalı",
+ "alarmPowerOn": "Çalışıyor",
+ "alarmDoor": "Kapı",
+ "alarmLock": "Kilit acık",
+ "alarmUnlock": "Kilit kapalı",
+ "alarmGeofence": "Coğrafik çit",
+ "alarmGeofenceEnter": "Coğrafik çite girildi",
+ "alarmGeofenceExit": "Coğrafik çitden çıkıldı",
+ "alarmGpsAntennaCut": "GPS Anteni Kes",
+ "alarmAccident": "Kaza",
+ "alarmTow": "Çekme",
+ "alarmIdle": "Çalışmıyor",
+ "alarmHighRpm": "Yüksek devir",
+ "alarmHardAcceleration": "Ani Hızlanma",
+ "alarmHardBraking": "Ani Yavaşlama",
+ "alarmHardCornering": "Sert Viraj",
+ "alarmLaneChange": "Şerit Değiştirme",
+ "alarmFatigueDriving": "Yorgun Sürücü",
+ "alarmPowerCut": "Enerji kesildi",
+ "alarmPowerRestored": "Enerji geldi",
+ "alarmJamming": "Sıkışıklık",
+ "alarmTemperature": "Hararet",
+ "alarmParking": "Park halinde",
+ "alarmBonnet": "Kaput",
+ "alarmFootBrake": "Ayak freni",
+ "alarmFuelLeak": "Yakıt sızıntısı",
+ "alarmTampering": "Kurcalama",
+ "alarmRemoving": "Çıkarma",
+ "notificationType": "Bildirim tipi",
+ "notificationAlways": "Tüm Cihazlar",
+ "notificationNotificators": "Kanallar",
+ "notificatorCommand": "Emir",
+ "notificatorWeb": "Web",
+ "notificatorMail": "Eposta",
+ "notificatorSms": "SMS Mesajı",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Tekrar oynat",
+ "reportCombined": "Kombine",
+ "reportRoute": "Rota",
+ "reportEvents": "Olaylar",
+ "reportTrips": "Turlar",
+ "reportStops": "Stops",
+ "reportSummary": "Özet",
+ "reportDaily": "Günlük Özet",
+ "reportChart": "Grafik",
+ "reportConfigure": "Ayarlar",
+ "reportEventTypes": "Olay Tipleri",
+ "reportChartType": "Grafik Tipi",
+ "reportShowMarkers": "İşaretleri Göster",
+ "reportExport": "Çıktı Al",
+ "reportEmail": "Eposta Rapor",
+ "reportSchedule": "Takvim",
+ "reportPeriod": "Dönem",
+ "reportCustom": "Özel",
+ "reportToday": "Bugün",
+ "reportYesterday": "Dün",
+ "reportThisWeek": "Bu Hafta",
+ "reportPreviousWeek": "Geçen Hafta",
+ "reportThisMonth": "Bu Ay",
+ "reportPreviousMonth": "Geçen Ay",
+ "reportDeviceName": "Cihaz İsmi",
+ "reportAverageSpeed": "Ortalama Hız",
+ "reportMaximumSpeed": "En Fazla Hız",
+ "reportEngineHours": "Motor Saatleri",
+ "reportDuration": "Süre",
+ "reportStartDate": "Başlangıç ​​Tarihi",
+ "reportStartTime": "Başlama Zamanı",
+ "reportStartAddress": "Başlama Adresi",
+ "reportEndTime": "Bittiği Zaman",
+ "reportEndAddress": "Bittiği Adres",
+ "reportSpentFuel": "Tüketilen Yakıt",
+ "reportStartOdometer": "Kilometre Sayacı Başlangıçı",
+ "reportEndOdometer": "Kilometre Sayacı Sonu",
+ "statisticsTitle": "Statistics",
+ "statisticsCaptureTime": "Yakalama Zamanı",
+ "statisticsActiveUsers": "Aktif Kullanıcılar",
+ "statisticsActiveDevices": "Aktif Cihazlar",
+ "statisticsRequests": "İstenilenler",
+ "statisticsMessagesReceived": "Mesajlar Alındı",
+ "statisticsMessagesStored": "Mesajlar Kaydedildi",
+ "statisticsGeocoder": "Geocoder Requests",
+ "statisticsGeolocation": "Geolocation Requests",
+ "categoryArrow": "Yön",
+ "categoryDefault": "Varsayılan",
+ "categoryAnimal": "Hayvan",
+ "categoryBicycle": "Bisiklet",
+ "categoryBoat": "Tekne",
+ "categoryBus": "Otobüs",
+ "categoryCar": "Araba",
+ "categoryCamper": "Camper",
+ "categoryCrane": "Vinç",
+ "categoryHelicopter": "Helikopter",
+ "categoryMotorcycle": "Motorsiklet",
+ "categoryOffroad": "Offroad",
+ "categoryPerson": "İnsan",
+ "categoryPickup": "Kamyonet",
+ "categoryPlane": "Uçak",
+ "categoryShip": "Gemi",
+ "categoryTractor": "Tractor",
+ "categoryTrain": "Tren",
+ "categoryTram": "Tramvay",
+ "categoryTrolleybus": "Troleybüs",
+ "categoryTruck": "Kamyon",
+ "categoryVan": "Van",
+ "categoryScooter": "Mobilet",
+ "maintenanceStart": "Başlat",
+ "maintenancePeriod": "Dönem"
+} \ No newline at end of file
diff --git a/src/resources/l10n/uk.json b/src/resources/l10n/uk.json
new file mode 100644
index 00000000..5e013dc6
--- /dev/null
+++ b/src/resources/l10n/uk.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "Завантаження... ",
+ "sharedHide": "Приховувати",
+ "sharedSave": "Зберегти",
+ "sharedUpload": "Upload",
+ "sharedSet": "Встановити",
+ "sharedCancel": "Відміна",
+ "sharedCopy": "Copy",
+ "sharedAdd": "Додати",
+ "sharedEdit": "Редагувати",
+ "sharedRemove": "Видалити",
+ "sharedRemoveConfirm": "Видалити пункт?",
+ "sharedNoData": "No data",
+ "sharedSubject": "Subject",
+ "sharedYes": "Yes",
+ "sharedNo": "No",
+ "sharedKm": "км",
+ "sharedMi": "Милi",
+ "sharedNmi": "морська миля",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "Вузли",
+ "sharedKmh": "км/год",
+ "sharedMph": "Миль/год",
+ "sharedHour": "Години",
+ "sharedMinute": "Хвилини",
+ "sharedSecond": "Секунди",
+ "sharedDays": "днів",
+ "sharedHours": "годин",
+ "sharedMinutes": "хвилин",
+ "sharedDecimalDegrees": "Градуси°",
+ "sharedDegreesDecimalMinutes": "Градуси° хвилини′",
+ "sharedDegreesMinutesSeconds": "Градуси° хвилини′ секунди″",
+ "sharedName": "Назва пристрою",
+ "sharedDescription": "Опис",
+ "sharedSearch": "Пошук",
+ "sharedIconScale": "Icon Scale",
+ "sharedGeofence": "Геозон",
+ "sharedGeofences": "Геозони",
+ "sharedCreateGeofence": "Create Geofence",
+ "sharedNotifications": "Повідомлення",
+ "sharedNotification": "Повідомлення",
+ "sharedAttributes": "Атрибути",
+ "sharedAttribute": "Атрибут",
+ "sharedDrivers": "Водії",
+ "sharedDriver": "Водій",
+ "sharedArea": "Площа",
+ "sharedSound": "Звукове повідомлення",
+ "sharedType": "Тип",
+ "sharedDistance": "Відстань",
+ "sharedHourAbbreviation": "г",
+ "sharedMinuteAbbreviation": "хв",
+ "sharedSecondAbbreviation": "c",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "l",
+ "sharedGallonAbbreviation": "gal",
+ "sharedLiter": "Літр",
+ "sharedImpGallon": "Імп. галон",
+ "sharedUsGallon": "Галон США",
+ "sharedLiterPerHourAbbreviation": "л/ч",
+ "sharedGetMapState": "Отримати стан карти",
+ "sharedComputedAttribute": "Обчислюваний атрибут",
+ "sharedComputedAttributes": "Обчислювані атрибути",
+ "sharedCheckComputedAttribute": "Перевірити обчислюваний атрибут",
+ "sharedExpression": "Вираз",
+ "sharedDevice": "Пристрій",
+ "sharedTest": "Test",
+ "sharedTestNotification": "Відправити тестове повідомлення",
+ "sharedTestNotificators": "Test Channels",
+ "sharedTestExpression": "Test Expression",
+ "sharedCalendar": "Календар",
+ "sharedCalendars": "Календарi",
+ "sharedFile": "Файл",
+ "sharedSearchDevices": "Search Devices",
+ "sharedSortBy": "Sort By",
+ "sharedFilterMap": "Filter on Map",
+ "sharedSelectFile": "Вибрати файл",
+ "sharedPhone": "Телефон",
+ "sharedRequired": "Обов'язкові",
+ "sharedPreferences": "Налаштування",
+ "sharedPermissions": "Дозволи",
+ "sharedConnections": "Connections",
+ "sharedExtra": "Екстра",
+ "sharedPrimary": "Primary",
+ "sharedSecondary": "Secondary",
+ "sharedTypeString": "Строка",
+ "sharedTypeNumber": "Число",
+ "sharedTypeBoolean": "Логічне значення",
+ "sharedTimezone": "Часовий пояс",
+ "sharedInfoTitle": "Інфо",
+ "sharedSavedCommand": "Збережена команда",
+ "sharedSavedCommands": "Збережені команди",
+ "sharedNew": "Новий…",
+ "sharedShowAddress": "Показати адрес",
+ "sharedShowDetails": "More Details",
+ "sharedDisabled": "Вимкнутий",
+ "sharedMaintenance": "Обслуговування",
+ "sharedDeviceAccumulators": "Акумулятори",
+ "sharedAlarms": "Тривоги",
+ "sharedLocation": "Location",
+ "sharedImport": "Import",
+ "sharedColumns": "Columns",
+ "sharedDropzoneText": "Drag and drop a file here or click",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "Simple",
+ "calendarRecurrence": "Recurrence",
+ "calendarOnce": "Once",
+ "calendarDaily": "Daily",
+ "calendarWeekly": "Weekly",
+ "calendarMonthly": "Monthly",
+ "calendarDays": "Days",
+ "calendarSunday": "Sunday",
+ "calendarMonday": "Monday",
+ "calendarTuesday": "Tuesday",
+ "calendarWednesday": "Wednesday",
+ "calendarThursday": "Thursday",
+ "calendarFriday": "Friday",
+ "calendarSaturday": "Saturday",
+ "attributeShowGeofences": "Show Geofences",
+ "attributeSpeedLimit": "Обмеження швидкості",
+ "attributeFuelDropThreshold": "Fuel Drop Threshold",
+ "attributeFuelIncreaseThreshold": "Fuel Increase Threshold",
+ "attributePolylineDistance": "Відстань від лінії",
+ "attributeReportIgnoreOdometer": "Звіт: Ігнорувати одометер",
+ "attributeWebReportColor": "Веб: Колір звіту",
+ "attributeDevicePassword": "Пароль пристрою",
+ "attributeDeviceImage": "Device Image",
+ "attributeDeviceInactivityStart": "Device Inactivity Start",
+ "attributeDeviceInactivityPeriod": "Device Inactivity Period",
+ "attributeProcessingCopyAttributes": "Обробка: Копіювання атрибутів",
+ "attributeColor": "Колір",
+ "attributeWebLiveRouteLength": "Веб: Довжина онлайн маршруту",
+ "attributeWebSelectZoom": "Веб: Збільшення при виборі",
+ "attributeWebMaxZoom": "Веб: Максимальне збільшення",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Pushover User Key",
+ "attributePushoverDeviceNames": "Pushover Device Names",
+ "attributeMailSmtpHost": "Пошта: SMTP хост",
+ "attributeMailSmtpPort": "Пошта: SMTP порт",
+ "attributeMailSmtpStarttlsEnable": "Пошта: увімкнути SMTP STARTTLS",
+ "attributeMailSmtpStarttlsRequired": "Пошта: потрібен SMTP STARTTLS",
+ "attributeMailSmtpSslEnable": "Пошта: увімкнути SMTP SSL",
+ "attributeMailSmtpSslTrust": "Пошта: довіра SMTP SSL",
+ "attributeMailSmtpSslProtocols": "Пошта: Протоколи SMTP SSL",
+ "attributeMailSmtpFrom": "Пошта: SMTP відправник",
+ "attributeMailSmtpAuth": "Пошта: Увімкнути SMTP Auth",
+ "attributeMailSmtpUsername": "Пошта: SMTP ім'я користувача",
+ "attributeMailSmtpPassword": "Пошта: SMTP пароль",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: Disable Attributes",
+ "attributeUiDisableGroups": "UI: Disable Groups",
+ "attributeUiDisableEvents": "UI: Вимкнути події",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "UI: Вимкнути водіїв",
+ "attributeUiDisableComputedAttributes": "UI: Вимкнути обчислювані атрибути",
+ "attributeUiDisableCalendars": "UI: Вимкнути календарі",
+ "attributeUiDisableMaintenance": "UI: Вимкнути обслуговування",
+ "attributeUiHidePositionAttributes": "UI: Приховувати атрибути",
+ "attributeUiDisableLoginLanguage": "UI: Disable Login Language",
+ "attributeNotificationTokens": "Notification Tokens",
+ "attributePopupInfo": "Popup Info",
+ "errorTitle": "Помилка",
+ "errorGeneral": "Неправильні параметри або порушення обмежень",
+ "errorConnection": "Помилка з'єднання",
+ "errorSocket": "Помилка web socket з'єднання",
+ "errorZero": "Не може бути нульовим",
+ "userEmail": "E-mail",
+ "userPassword": "Пароль",
+ "userAdmin": "Адмiнiстратор",
+ "userRemember": "Запам'ятати",
+ "userExpirationTime": "Термін дії",
+ "userDeviceLimit": "Обмеження пристроїв",
+ "userUserLimit": "Обмеження користувачів",
+ "userDeviceReadonly": "Тільки перегляд пристроїв",
+ "userLimitCommands": "Обмеження команд",
+ "userDisableReports": "Disable Reports",
+ "userFixedEmail": "No Email Change",
+ "userToken": "Ключ",
+ "userDeleteAccount": "Delete Account",
+ "userTemporary": "Temporary",
+ "loginTitle": "Логiн",
+ "loginLanguage": "Мова",
+ "loginReset": "Reset Password",
+ "loginRegister": "Реєстрація",
+ "loginLogin": "Ввійти",
+ "loginOpenId": "Login with OpenID",
+ "loginFailed": "Неправильне адреса електронної пошти або пароль",
+ "loginCreated": "Новий користувач був зареєстрований",
+ "loginResetSuccess": "Check your email",
+ "loginUpdateSuccess": "New password is set",
+ "loginLogout": "Вийти",
+ "loginLogo": "Логотип",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "Пристрої та стан",
+ "deviceSelected": "Selected Device",
+ "deviceTitle": " Прилади",
+ "devicePrimaryInfo": "Device Title",
+ "deviceSecondaryInfo": "Device Detail",
+ "deviceIdentifier": "Iдентифікатор",
+ "deviceModel": "Модель",
+ "deviceContact": "Контакт",
+ "deviceCategory": "Категорія",
+ "deviceLastUpdate": "Останнє оновлення",
+ "deviceCommand": "Команда",
+ "deviceFollow": "Слідувати",
+ "deviceTotalDistance": "Загальна відстань",
+ "deviceStatus": "Статус",
+ "deviceStatusOnline": "Онлайн",
+ "deviceStatusOffline": "Офлайн",
+ "deviceStatusUnknown": "Невідомий",
+ "deviceRegisterFirst": "Register your first device",
+ "deviceIdentifierHelp": "IMEI, serial number or other id. It has to match the identifier device reports to the server.",
+ "deviceShare": "Share Device",
+ "groupDialog": "Група",
+ "groupParent": "Група",
+ "groupNoGroup": "Група відсутня",
+ "settingsTitle": "Налаштування",
+ "settingsUser": "Аккаунт",
+ "settingsGroups": "Групи",
+ "settingsServer": "Сервер",
+ "settingsUsers": "Користувачі",
+ "settingsDistanceUnit": "Одиниця відстані",
+ "settingsAltitudeUnit": "Altitude Unit",
+ "settingsSpeedUnit": "Одиниця швидкості",
+ "settingsVolumeUnit": "Одиниця об'єму",
+ "settingsTwelveHourFormat": "12-годинний формат",
+ "settingsCoordinateFormat": "Формат координат",
+ "settingsServerVersion": "Server Version",
+ "settingsAppVersion": "App Version",
+ "settingsConnection": "Connection",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "Звіти",
+ "reportScheduled": "Scheduled Reports",
+ "reportDevice": "Пристрій ",
+ "reportGroup": "Група",
+ "reportFrom": "З",
+ "reportTo": "До",
+ "reportShow": "Показати",
+ "reportClear": "Очистити",
+ "linkGoogleMaps": "Google Maps",
+ "linkAppleMaps": "Apple Maps",
+ "linkStreetView": "Street View",
+ "positionFixTime": "Fix Time",
+ "positionDeviceTime": "Device Time",
+ "positionServerTime": "Server Time",
+ "positionValid": "Дійсний",
+ "positionAccuracy": "Точність",
+ "positionLatitude": "Широта",
+ "positionLongitude": "Довгота ",
+ "positionAltitude": "Висота",
+ "positionSpeed": "Швидкість ",
+ "positionCourse": "Напрямок",
+ "positionAddress": "Адреса",
+ "positionProtocol": "Протокол",
+ "positionDistance": "Відстань",
+ "positionRpm": "Обороти",
+ "positionFuel": "Паливо",
+ "positionPower": "Живлення",
+ "positionBattery": "Батарея",
+ "positionRaw": "Cирі дані",
+ "positionIndex": "Індекс",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "Супутники",
+ "positionSatVisible": "Видимі супутники",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "Роумінг",
+ "positionEvent": "Подія",
+ "positionAlarm": "Тривога",
+ "positionStatus": "Статус",
+ "positionOdometer": "Одометр",
+ "positionServiceOdometer": "Одометр обслуговування",
+ "positionTripOdometer": "Одометр поїздки",
+ "positionHours": "Години",
+ "positionSteps": "Кроки",
+ "positionInput": "Входи",
+ "positionHeartRate": "Heart Rate",
+ "positionOutput": "Виходи",
+ "positionBatteryLevel": "Рівень заряду батареї",
+ "positionFuelConsumption": "Витрати палива",
+ "positionRfid": "RFID",
+ "positionVersionFw": "Версія прошивки",
+ "positionVersionHw": "Версія пристрою",
+ "positionIgnition": "Запалення",
+ "positionFlags": "Флаги",
+ "positionCharge": "Заряд",
+ "positionIp": "IP",
+ "positionArchive": "Архів",
+ "positionVin": "VIN",
+ "positionApproximate": "Приблизний",
+ "positionThrottle": "Дросель",
+ "positionMotion": "Рух",
+ "positionArmed": "Охорона",
+ "positionAcceleration": "Прискорення",
+ "positionTemp": "Temperature",
+ "positionDeviceTemp": "Температура пристрою",
+ "positionCoolantTemp": "Coolant Temperature",
+ "positionOperator": "Оператор",
+ "positionCommand": "Команда",
+ "positionBlocked": "Блокування",
+ "positionDtcs": "Помилки",
+ "positionObdSpeed": "OBD швидкість",
+ "positionObdOdometer": "OBD одометр",
+ "positionDrivingTime": "Driving Time",
+ "positionDriverUniqueId": "ID водія",
+ "positionCard": "Card",
+ "positionImage": "Зображення",
+ "positionVideo": "Video",
+ "positionAudio": "Аудіо",
+ "serverTitle": "Налаштування сервера",
+ "serverZoom": "Наближення",
+ "serverRegistration": "Реєстрація",
+ "serverReadonly": "Лише для читання",
+ "serverForceSettings": "Налаштування Force",
+ "serverAnnouncement": "Announcement",
+ "serverName": "Server Name",
+ "serverDescription": "Server Description",
+ "serverColorPrimary": "Color Primary",
+ "serverColorSecondary": "Color Secondary",
+ "serverLogo": "Logo Image",
+ "serverLogoInverted": "Inverted Logo Image",
+ "serverChangeDisable": "Disable Server Change",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "Карта",
+ "mapActive": "Active Maps",
+ "mapOverlay": "Map Overlay",
+ "mapOverlayCustom": "Custom Overlay",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API Key",
+ "mapOpenWeatherClouds": "OpenWeather Clouds",
+ "mapOpenWeatherPrecipitation": "OpenWeather Precipitation",
+ "mapOpenWeatherPressure": "OpenWeather Pressure",
+ "mapOpenWeatherWind": "OpenWeather Wind",
+ "mapOpenWeatherTemperature": "OpenWeather Temperature",
+ "mapLayer": "Використання мап",
+ "mapCustom": "Custom (XYZ)",
+ "mapCustomArcgis": "Custom (ArcGIS)",
+ "mapCustomLabel": "Custom map",
+ "mapCarto": "Carto Basemaps",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Ключ Bing Maps ",
+ "mapBingRoad": "Bing Maps Дороги",
+ "mapBingAerial": "Bing Maps Супутник",
+ "mapBingHybrid": "Bing Maps Hybrid",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Yandex Map",
+ "mapYandexSat": "Yandex Satellite",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox Streets",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox Outdoors",
+ "mapMapboxSatellite": "Mapbox Satellite",
+ "mapMapboxKey": "Mapbox Access Token",
+ "mapMapTilerBasic": "MapTiler Basic",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "MapTiler API Key",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ Access Token",
+ "mapTomTomBasic": "TomTom Basic",
+ "mapTomTomFlow": "TomTom Traffic Flow",
+ "mapTomTomIncidents": "TomTom Traffic Incidents",
+ "mapTomTomKey": "TomTom API Key",
+ "mapHereBasic": "Here Basic",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satellite",
+ "mapHereFlow": "Here Traffic Flow",
+ "mapHereKey": "Here API Key",
+ "mapShapePolygon": "Багатокутник",
+ "mapShapeCircle": "Коло",
+ "mapShapePolyline": "Лінія",
+ "mapLiveRoutes": "Поточні маршрути",
+ "mapDirection": "Show Direction",
+ "mapCurrentLocation": "Current Location",
+ "mapPoiLayer": "Шар POI",
+ "mapClustering": "Markers Clustering",
+ "mapOnSelect": "Show Map on Selection",
+ "mapDefault": "Default Map",
+ "stateTitle": "Стан",
+ "stateName": "Атрибут",
+ "stateValue": "Значення ",
+ "commandTitle": "Команда ",
+ "commandSend": "Послати. ",
+ "commandSent": "Команда відправлена",
+ "commandQueued": "Команда додана в чергу",
+ "commandUnit": "Одиниці",
+ "commandCustom": "Користувацька команда",
+ "commandDeviceIdentification": "Ідентифікація пристрою",
+ "commandPositionSingle": "Разове відстеження",
+ "commandPositionPeriodic": "Періодична звітність",
+ "commandPositionStop": "Скасувати відстеження. ",
+ "commandEngineStop": "Заблокувати двигун ",
+ "commandEngineResume": "Розблокувати двигун",
+ "commandAlarmArm": "Активувати сигналізацію",
+ "commandAlarmDisarm": "Вимкнути сигналізацію",
+ "commandAlarmDismiss": "Dismiss Alarm",
+ "commandSetTimezone": "Часовий пояс",
+ "commandRequestPhoto": "Запит фото",
+ "commandPowerOff": "Вимкнути пристрій",
+ "commandRebootDevice": "Перезавантаження пристрою",
+ "commandFactoryReset": "Factory Reset",
+ "commandSendSms": "Надсилання SMS",
+ "commandSendUssd": "Надсилання USSD",
+ "commandSosNumber": "Номер SOS",
+ "commandSilenceTime": "Встановити час пиші",
+ "commandSetPhonebook": "Телефонна книга",
+ "commandVoiceMessage": "Голосове повідомлення",
+ "commandOutputControl": "Контроль виходу",
+ "commandVoiceMonitoring": "Голосове повідомлення",
+ "commandSetAgps": "Налаштувати AGPS",
+ "commandSetIndicator": "Налаштувати ітдикатор",
+ "commandConfiguration": "Конфігурація",
+ "commandGetVersion": "Отримати версію",
+ "commandFirmwareUpdate": "Оновити прошивку",
+ "commandSetConnection": "Налаштувати з'єднання",
+ "commandSetOdometer": "Налаштувати одометр",
+ "commandGetModemStatus": "Отримати стан модема",
+ "commandGetDeviceStatus": "Отримати статус пристрою",
+ "commandSetSpeedLimit": "Set Speed Limit",
+ "commandModePowerSaving": "Power Saving Mode",
+ "commandModeDeepSleep": "Deep Sleep Mode",
+ "commandAlarmGeofence": "Set Geofence Alarm",
+ "commandAlarmBattery": "Set Battery Alarm",
+ "commandAlarmSos": "Set SOS Alarm",
+ "commandAlarmRemove": "Set Remove Alarm",
+ "commandAlarmClock": "Set Clock Alarm",
+ "commandAlarmSpeed": "Set Speed Alarm",
+ "commandAlarmFall": "Set Fall Alarm",
+ "commandAlarmVibration": "Set Vibration Alarm",
+ "commandFrequency": "Частота",
+ "commandTimezone": "Зсув часової зони",
+ "commandMessage": "Повідомлення",
+ "commandRadius": "Радіус",
+ "commandEnable": "Увімкнути",
+ "commandData": "Дані",
+ "commandIndex": "Індекс",
+ "commandPhone": "Номер телефону",
+ "commandServer": "Сервер",
+ "commandPort": "Порт",
+ "eventAll": "Всі події",
+ "eventDeviceOnline": "Статус онлайн",
+ "eventDeviceUnknown": "Статус невідомий",
+ "eventDeviceOffline": "Статус офлайн",
+ "eventDeviceInactive": "Device inactive",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "Пристрій рухається",
+ "eventDeviceStopped": "Пристрій зупинився",
+ "eventDeviceOverspeed": "Перевищено обмеження швидкості",
+ "eventDeviceFuelDrop": "Злив палива",
+ "eventDeviceFuelIncrease": "Fuel increase",
+ "eventCommandResult": "Результат команди",
+ "eventGeofenceEnter": "Вхід в геозон",
+ "eventGeofenceExit": "Вихід з геозон",
+ "eventAlarm": "Тривога",
+ "eventIgnitionOn": "Запалення увімкнено",
+ "eventIgnitionOff": "Запалення вимкнено",
+ "eventMaintenance": "Потрібне обслуговування",
+ "eventTextMessage": "Текстове повідомлення отримано",
+ "eventDriverChanged": "Водій змінений",
+ "eventMedia": "Media",
+ "eventsScrollToLast": "Прокрутка до кінця",
+ "eventsSoundEvents": "Sound Events",
+ "eventsSoundAlarms": "Sound Alarms",
+ "alarmGeneral": "Загальне",
+ "alarmSos": "SOS",
+ "alarmVibration": "Вібрація",
+ "alarmMovement": "Рух",
+ "alarmLowspeed": "Низька швидкість",
+ "alarmOverspeed": "Перевищення швидкості",
+ "alarmFallDown": "Падіння",
+ "alarmLowPower": "Низький рівень живлення",
+ "alarmLowBattery": "Батарея розряджена",
+ "alarmFault": "Несправність",
+ "alarmPowerOff": "Power Off",
+ "alarmPowerOn": "Power On",
+ "alarmDoor": "Двері",
+ "alarmLock": "Блокування",
+ "alarmUnlock": "Разблокування",
+ "alarmGeofence": "Геозон",
+ "alarmGeofenceEnter": "Вхід в геозон",
+ "alarmGeofenceExit": "Вихід з геозон",
+ "alarmGpsAntennaCut": "GPS Antenna Cut",
+ "alarmAccident": "Accident",
+ "alarmTow": "Tow",
+ "alarmIdle": "Idle",
+ "alarmHighRpm": "High RPM",
+ "alarmHardAcceleration": "Hard Acceleration",
+ "alarmHardBraking": "Hard Braking",
+ "alarmHardCornering": "Hard Cornering",
+ "alarmLaneChange": "Lane Change",
+ "alarmFatigueDriving": "Fatigue Driving",
+ "alarmPowerCut": "Power Cut",
+ "alarmPowerRestored": "Power Restored",
+ "alarmJamming": "Jamming",
+ "alarmTemperature": "Temperature",
+ "alarmParking": "Parking",
+ "alarmBonnet": "Bonnet",
+ "alarmFootBrake": "Foot Brake",
+ "alarmFuelLeak": "Fuel Leak",
+ "alarmTampering": "Tampering",
+ "alarmRemoving": "Removing",
+ "notificationType": "Тип повідомлення",
+ "notificationAlways": "Всi пристрої",
+ "notificationNotificators": "Channels",
+ "notificatorCommand": "Command",
+ "notificatorWeb": "Web",
+ "notificatorMail": "Mail",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Replay",
+ "reportCombined": "Combined",
+ "reportRoute": "Маршрут",
+ "reportEvents": "Події",
+ "reportTrips": "Подорожі",
+ "reportStops": "Зупинки",
+ "reportSummary": "Звіт",
+ "reportDaily": "Daily Summary",
+ "reportChart": "Діаграма",
+ "reportConfigure": "Конфігурувати",
+ "reportEventTypes": "Тип події",
+ "reportChartType": "Тип діаграми",
+ "reportShowMarkers": "Показати маркери",
+ "reportExport": "Експорт",
+ "reportEmail": "Звіт по пошті",
+ "reportSchedule": "Schedule",
+ "reportPeriod": "Період",
+ "reportCustom": "Користувальницький",
+ "reportToday": "Сьогодні",
+ "reportYesterday": "Вчора",
+ "reportThisWeek": "Поточний тиждень",
+ "reportPreviousWeek": "Попередній тиждень",
+ "reportThisMonth": "Поточний місяць",
+ "reportPreviousMonth": "Попередній місяць",
+ "reportDeviceName": "Ім'я пристрою",
+ "reportAverageSpeed": "Середня швидкість",
+ "reportMaximumSpeed": "Максимальна швидкість",
+ "reportEngineHours": "Мотогодинник",
+ "reportDuration": "Тривалість",
+ "reportStartDate": "Start Date",
+ "reportStartTime": "Початковий час",
+ "reportStartAddress": "Початкова адреса",
+ "reportEndTime": "Кінцевий час",
+ "reportEndAddress": "Кінцева адреса",
+ "reportSpentFuel": "Використано палива",
+ "reportStartOdometer": "Одометр, початок",
+ "reportEndOdometer": "Одометр, закінчення",
+ "statisticsTitle": "Статистика",
+ "statisticsCaptureTime": "Час збору",
+ "statisticsActiveUsers": "Активні користувачі",
+ "statisticsActiveDevices": "Активні пристрої",
+ "statisticsRequests": "Запити",
+ "statisticsMessagesReceived": "Отримано повідомлень",
+ "statisticsMessagesStored": "Збережено повідомлень",
+ "statisticsGeocoder": "Запити геокодера",
+ "statisticsGeolocation": "Запити геолокації",
+ "categoryArrow": "Стрілка",
+ "categoryDefault": "За замовчуванням",
+ "categoryAnimal": "Тварина",
+ "categoryBicycle": "Велосипед",
+ "categoryBoat": "Човен",
+ "categoryBus": "Автобус",
+ "categoryCar": "Автомобіль",
+ "categoryCamper": "Camper",
+ "categoryCrane": "Кран",
+ "categoryHelicopter": "Гвинтокрил",
+ "categoryMotorcycle": "Мотоцикл",
+ "categoryOffroad": "Позашляховик",
+ "categoryPerson": "Людина",
+ "categoryPickup": "Пікап",
+ "categoryPlane": "Літак",
+ "categoryShip": "Корабель",
+ "categoryTractor": "Трактор",
+ "categoryTrain": "Поїзд",
+ "categoryTram": "Трамвай",
+ "categoryTrolleybus": "Тролейбус",
+ "categoryTruck": "Вантажний автомобіль",
+ "categoryVan": "Фургон",
+ "categoryScooter": "Скутер",
+ "maintenanceStart": "Початок",
+ "maintenancePeriod": "Період"
+} \ No newline at end of file
diff --git a/src/resources/l10n/uz.json b/src/resources/l10n/uz.json
new file mode 100644
index 00000000..51b9364d
--- /dev/null
+++ b/src/resources/l10n/uz.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "Юкланмоқда...",
+ "sharedHide": "Беркитиш",
+ "sharedSave": "Сақлаш",
+ "sharedUpload": "Upload",
+ "sharedSet": "Ўрнатиш",
+ "sharedCancel": "Бекор қилиш",
+ "sharedCopy": "Copy",
+ "sharedAdd": "Қўшиш",
+ "sharedEdit": "Таҳрирлаш",
+ "sharedRemove": "Ўчириш",
+ "sharedRemoveConfirm": "Элементни ўчирайми?",
+ "sharedNoData": "No data",
+ "sharedSubject": "Subject",
+ "sharedYes": "Yes",
+ "sharedNo": "No",
+ "sharedKm": "км",
+ "sharedMi": "миллар",
+ "sharedNmi": "м.миллар",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "уз",
+ "sharedKmh": "км/с",
+ "sharedMph": "миль/с",
+ "sharedHour": "Соат",
+ "sharedMinute": "Дақиқалар",
+ "sharedSecond": "Сониялар",
+ "sharedDays": "days",
+ "sharedHours": "hours",
+ "sharedMinutes": "minutes",
+ "sharedDecimalDegrees": "Ўнлик даража",
+ "sharedDegreesDecimalMinutes": "Градуслар Ўнлик Дақиқалар",
+ "sharedDegreesMinutesSeconds": "Градуслар Дақиқалар Сониялар",
+ "sharedName": "Исм",
+ "sharedDescription": "Тавсиф",
+ "sharedSearch": "Қидирув",
+ "sharedIconScale": "Icon Scale",
+ "sharedGeofence": "Геозона",
+ "sharedGeofences": "Геозоналар",
+ "sharedCreateGeofence": "Create Geofence",
+ "sharedNotifications": "Хабарнома",
+ "sharedNotification": "Notification",
+ "sharedAttributes": "Атрибутлар",
+ "sharedAttribute": "Атрибут",
+ "sharedDrivers": "Drivers",
+ "sharedDriver": "Driver",
+ "sharedArea": "Соҳа",
+ "sharedSound": "Notification Sound",
+ "sharedType": "Тур",
+ "sharedDistance": "Масофа",
+ "sharedHourAbbreviation": "с",
+ "sharedMinuteAbbreviation": "д",
+ "sharedSecondAbbreviation": "s",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "l",
+ "sharedGallonAbbreviation": "gal",
+ "sharedLiter": "Liter",
+ "sharedImpGallon": "Imp. Gallon",
+ "sharedUsGallon": "U.S. Gallon",
+ "sharedLiterPerHourAbbreviation": "l/h",
+ "sharedGetMapState": "Харита ҳолатини билиш",
+ "sharedComputedAttribute": "Computed Attribute",
+ "sharedComputedAttributes": "Computed Attributes",
+ "sharedCheckComputedAttribute": "Check Computed Attribute",
+ "sharedExpression": "Expression",
+ "sharedDevice": "Қурилма",
+ "sharedTest": "Test",
+ "sharedTestNotification": "Send Test Notification",
+ "sharedTestNotificators": "Test Channels",
+ "sharedTestExpression": "Test Expression",
+ "sharedCalendar": "Тақвим",
+ "sharedCalendars": "Тақвимлар",
+ "sharedFile": "Файл",
+ "sharedSearchDevices": "Search Devices",
+ "sharedSortBy": "Sort By",
+ "sharedFilterMap": "Filter on Map",
+ "sharedSelectFile": "Файлни танлаш",
+ "sharedPhone": "Phone",
+ "sharedRequired": "Required",
+ "sharedPreferences": "Preferences",
+ "sharedPermissions": "Permissions",
+ "sharedConnections": "Connections",
+ "sharedExtra": "Extra",
+ "sharedPrimary": "Primary",
+ "sharedSecondary": "Secondary",
+ "sharedTypeString": "String",
+ "sharedTypeNumber": "Number",
+ "sharedTypeBoolean": "Boolean",
+ "sharedTimezone": "Timezone",
+ "sharedInfoTitle": "Info",
+ "sharedSavedCommand": "Saved Command",
+ "sharedSavedCommands": "Saved Commands",
+ "sharedNew": "New…",
+ "sharedShowAddress": "Show Address",
+ "sharedShowDetails": "More Details",
+ "sharedDisabled": "Disabled",
+ "sharedMaintenance": "Maintenance",
+ "sharedDeviceAccumulators": "Accumulators",
+ "sharedAlarms": "Alarms",
+ "sharedLocation": "Location",
+ "sharedImport": "Import",
+ "sharedColumns": "Columns",
+ "sharedDropzoneText": "Drag and drop a file here or click",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "Simple",
+ "calendarRecurrence": "Recurrence",
+ "calendarOnce": "Once",
+ "calendarDaily": "Daily",
+ "calendarWeekly": "Weekly",
+ "calendarMonthly": "Monthly",
+ "calendarDays": "Days",
+ "calendarSunday": "Sunday",
+ "calendarMonday": "Monday",
+ "calendarTuesday": "Tuesday",
+ "calendarWednesday": "Wednesday",
+ "calendarThursday": "Thursday",
+ "calendarFriday": "Friday",
+ "calendarSaturday": "Saturday",
+ "attributeShowGeofences": "Show Geofences",
+ "attributeSpeedLimit": "Speed Limit",
+ "attributeFuelDropThreshold": "Fuel Drop Threshold",
+ "attributeFuelIncreaseThreshold": "Fuel Increase Threshold",
+ "attributePolylineDistance": "Polyline Distance",
+ "attributeReportIgnoreOdometer": "Report: Ignore Odometer",
+ "attributeWebReportColor": "Web: Report Color",
+ "attributeDevicePassword": "Device Password",
+ "attributeDeviceImage": "Device Image",
+ "attributeDeviceInactivityStart": "Device Inactivity Start",
+ "attributeDeviceInactivityPeriod": "Device Inactivity Period",
+ "attributeProcessingCopyAttributes": "Processing: Copy Attributes",
+ "attributeColor": "Color",
+ "attributeWebLiveRouteLength": "Web: Live Route Length",
+ "attributeWebSelectZoom": "Web: Zoom On Select",
+ "attributeWebMaxZoom": "Web: Maximum Zoom",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Pushover User Key",
+ "attributePushoverDeviceNames": "Pushover Device Names",
+ "attributeMailSmtpHost": "Mail: SMTP Host",
+ "attributeMailSmtpPort": "Mail: SMTP Port",
+ "attributeMailSmtpStarttlsEnable": "Mail: SMTP STARTTLS Enable",
+ "attributeMailSmtpStarttlsRequired": "Mail: SMTP STARTTLS Required",
+ "attributeMailSmtpSslEnable": "Mail: SMTP SSL Enable",
+ "attributeMailSmtpSslTrust": "Mail: SMTP SSL Trust",
+ "attributeMailSmtpSslProtocols": "Mail: SMTP SSL Protocols",
+ "attributeMailSmtpFrom": "Mail: SMTP From",
+ "attributeMailSmtpAuth": "Mail: SMTP Auth Enable",
+ "attributeMailSmtpUsername": "Mail: SMTP Username",
+ "attributeMailSmtpPassword": "Mail: SMTP Password",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: Disable Attributes",
+ "attributeUiDisableGroups": "UI: Disable Groups",
+ "attributeUiDisableEvents": "UI: Disable Events",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "UI: Disable Drivers",
+ "attributeUiDisableComputedAttributes": "UI: Disable Computed Attributes",
+ "attributeUiDisableCalendars": "UI: Disable Calendars",
+ "attributeUiDisableMaintenance": "UI: Disable Maintenance",
+ "attributeUiHidePositionAttributes": "UI: Hide Position Attributes",
+ "attributeUiDisableLoginLanguage": "UI: Disable Login Language",
+ "attributeNotificationTokens": "Notification Tokens",
+ "attributePopupInfo": "Popup Info",
+ "errorTitle": "Хато",
+ "errorGeneral": "Invalid parameters or constraints violation",
+ "errorConnection": "Уланишда хато",
+ "errorSocket": "Web socket connection error",
+ "errorZero": "Can't be zero",
+ "userEmail": "Email",
+ "userPassword": "Парол",
+ "userAdmin": "Маъмурият",
+ "userRemember": "Эслаб қолиш",
+ "userExpirationTime": "Хизмат муддати",
+ "userDeviceLimit": "Қурилманинг чекловлари",
+ "userUserLimit": "Фойдаланувчини чегаралаш",
+ "userDeviceReadonly": "Фақат кўриш",
+ "userLimitCommands": "Limit Commands",
+ "userDisableReports": "Disable Reports",
+ "userFixedEmail": "No Email Change",
+ "userToken": "Калит",
+ "userDeleteAccount": "Delete Account",
+ "userTemporary": "Temporary",
+ "loginTitle": "Кириш",
+ "loginLanguage": "Тил",
+ "loginReset": "Reset Password",
+ "loginRegister": "Рўйхатдан ўтиш",
+ "loginLogin": "Кириш",
+ "loginOpenId": "Login with OpenID",
+ "loginFailed": "Нотўғри email ёки пароль",
+ "loginCreated": "Янги фойдаланувчи рўйхатдан ўтди",
+ "loginResetSuccess": "Check your email",
+ "loginUpdateSuccess": "New password is set",
+ "loginLogout": "Чиқиш",
+ "loginLogo": "Логотип",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "Қурилмалар ва Ҳолатлар",
+ "deviceSelected": "Selected Device",
+ "deviceTitle": "Қурилмалар",
+ "devicePrimaryInfo": "Device Title",
+ "deviceSecondaryInfo": "Device Detail",
+ "deviceIdentifier": "Идентификатор",
+ "deviceModel": "Модел",
+ "deviceContact": "Алоқа воситаси",
+ "deviceCategory": "Тоифа",
+ "deviceLastUpdate": "Сўнгги янгиланиш",
+ "deviceCommand": "Гуруҳ",
+ "deviceFollow": "Амал қилиш",
+ "deviceTotalDistance": "Умумий масофа",
+ "deviceStatus": "Status",
+ "deviceStatusOnline": "Online",
+ "deviceStatusOffline": "Offline",
+ "deviceStatusUnknown": "Unknown",
+ "deviceRegisterFirst": "Register your first device",
+ "deviceIdentifierHelp": "IMEI, serial number or other id. It has to match the identifier device reports to the server.",
+ "deviceShare": "Share Device",
+ "groupDialog": "Гуруҳ",
+ "groupParent": "Гуруҳ",
+ "groupNoGroup": "Гуруҳсиз",
+ "settingsTitle": "Созламалар",
+ "settingsUser": "Аккаунт",
+ "settingsGroups": "Гуруҳлар",
+ "settingsServer": "Сервер",
+ "settingsUsers": "Фойдаланувчилар",
+ "settingsDistanceUnit": "Distance Unit",
+ "settingsAltitudeUnit": "Altitude Unit",
+ "settingsSpeedUnit": "Speed Unit",
+ "settingsVolumeUnit": "Volume Unit",
+ "settingsTwelveHourFormat": "12-соатли формат",
+ "settingsCoordinateFormat": "Координаталар формати",
+ "settingsServerVersion": "Server Version",
+ "settingsAppVersion": "App Version",
+ "settingsConnection": "Connection",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "Ҳисоботлар",
+ "reportScheduled": "Scheduled Reports",
+ "reportDevice": "Қурилма",
+ "reportGroup": "Гуруҳ",
+ "reportFrom": "дан",
+ "reportTo": "гача",
+ "reportShow": "Кўрсатиш",
+ "reportClear": "Тозалаш",
+ "linkGoogleMaps": "Google Maps",
+ "linkAppleMaps": "Apple Maps",
+ "linkStreetView": "Street View",
+ "positionFixTime": "Fix Time",
+ "positionDeviceTime": "Device Time",
+ "positionServerTime": "Server Time",
+ "positionValid": "Аниқлик",
+ "positionAccuracy": "Аниқлик",
+ "positionLatitude": "Кенглик",
+ "positionLongitude": "Узунлик",
+ "positionAltitude": "Баландлик",
+ "positionSpeed": "Тезлик",
+ "positionCourse": "Йўналиш",
+ "positionAddress": "Манзил",
+ "positionProtocol": "Баённома",
+ "positionDistance": "Оралиқ масофа",
+ "positionRpm": "RPM",
+ "positionFuel": "Fuel",
+ "positionPower": "Power",
+ "positionBattery": "Battery",
+ "positionRaw": "Raw",
+ "positionIndex": "Index",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "Satellites",
+ "positionSatVisible": "Visible Satellites",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "Roaming",
+ "positionEvent": "Event",
+ "positionAlarm": "Alarm",
+ "positionStatus": "Status",
+ "positionOdometer": "Odometer",
+ "positionServiceOdometer": "Service Odometer",
+ "positionTripOdometer": "Trip Odometer",
+ "positionHours": "Hours",
+ "positionSteps": "Steps",
+ "positionInput": "Input",
+ "positionHeartRate": "Heart Rate",
+ "positionOutput": "Output",
+ "positionBatteryLevel": "Battery Level",
+ "positionFuelConsumption": "Fuel Consumption",
+ "positionRfid": "RFID",
+ "positionVersionFw": "Firmware Version",
+ "positionVersionHw": "Hardware Version",
+ "positionIgnition": "Ignition",
+ "positionFlags": "Flags",
+ "positionCharge": "Charge",
+ "positionIp": "IP",
+ "positionArchive": "Archive",
+ "positionVin": "VIN",
+ "positionApproximate": "Approximate",
+ "positionThrottle": "Throttle",
+ "positionMotion": "Motion",
+ "positionArmed": "Armed",
+ "positionAcceleration": "Acceleration",
+ "positionTemp": "Temperature",
+ "positionDeviceTemp": "Device Temperature",
+ "positionCoolantTemp": "Coolant Temperature",
+ "positionOperator": "Operator",
+ "positionCommand": "Command",
+ "positionBlocked": "Blocked",
+ "positionDtcs": "DTCs",
+ "positionObdSpeed": "OBD Speed",
+ "positionObdOdometer": "OBD Odometer",
+ "positionDrivingTime": "Driving Time",
+ "positionDriverUniqueId": "Driver Unique Id",
+ "positionCard": "Card",
+ "positionImage": "Image",
+ "positionVideo": "Video",
+ "positionAudio": "Audio",
+ "serverTitle": "Сервер созламалари",
+ "serverZoom": "Яқинлаштириш",
+ "serverRegistration": "Рўйхатдан ўтиш",
+ "serverReadonly": "Фақат кўриш",
+ "serverForceSettings": "Созламаларни кучайтириш",
+ "serverAnnouncement": "Announcement",
+ "serverName": "Server Name",
+ "serverDescription": "Server Description",
+ "serverColorPrimary": "Color Primary",
+ "serverColorSecondary": "Color Secondary",
+ "serverLogo": "Logo Image",
+ "serverLogoInverted": "Inverted Logo Image",
+ "serverChangeDisable": "Disable Server Change",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "Харита",
+ "mapActive": "Active Maps",
+ "mapOverlay": "Map Overlay",
+ "mapOverlayCustom": "Custom Overlay",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API Key",
+ "mapOpenWeatherClouds": "OpenWeather Clouds",
+ "mapOpenWeatherPrecipitation": "OpenWeather Precipitation",
+ "mapOpenWeatherPressure": "OpenWeather Pressure",
+ "mapOpenWeatherWind": "OpenWeather Wind",
+ "mapOpenWeatherTemperature": "OpenWeather Temperature",
+ "mapLayer": "Харита қавати",
+ "mapCustom": "Custom (XYZ)",
+ "mapCustomArcgis": "Custom (ArcGIS)",
+ "mapCustomLabel": "Custom map",
+ "mapCarto": "Carto Basemaps",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Bing Maps калити",
+ "mapBingRoad": "Bing Maps йўллар",
+ "mapBingAerial": "Bing Maps Спутник",
+ "mapBingHybrid": "Bing Maps Hybrid",
+ "mapBaidu": "Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Yandex Map",
+ "mapYandexSat": "Yandex Satellite",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox Streets",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox Outdoors",
+ "mapMapboxSatellite": "Mapbox Satellite",
+ "mapMapboxKey": "Mapbox Access Token",
+ "mapMapTilerBasic": "MapTiler Basic",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "MapTiler API Key",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ Access Token",
+ "mapTomTomBasic": "TomTom Basic",
+ "mapTomTomFlow": "TomTom Traffic Flow",
+ "mapTomTomIncidents": "TomTom Traffic Incidents",
+ "mapTomTomKey": "TomTom API Key",
+ "mapHereBasic": "Here Basic",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satellite",
+ "mapHereFlow": "Here Traffic Flow",
+ "mapHereKey": "Here API Key",
+ "mapShapePolygon": "Кўпбурчак",
+ "mapShapeCircle": "Айлана",
+ "mapShapePolyline": "Чизиқ",
+ "mapLiveRoutes": "Амалдаги маршрутлар",
+ "mapDirection": "Show Direction",
+ "mapCurrentLocation": "Current Location",
+ "mapPoiLayer": "POI Layer",
+ "mapClustering": "Markers Clustering",
+ "mapOnSelect": "Show Map on Selection",
+ "mapDefault": "Default Map",
+ "stateTitle": "Ҳолат",
+ "stateName": "Параметр",
+ "stateValue": "Маъноси",
+ "commandTitle": "Буйруқ",
+ "commandSend": "Жўнатиш",
+ "commandSent": "Command sent",
+ "commandQueued": "Command queued",
+ "commandUnit": "Бирликлар",
+ "commandCustom": "Фойдаланувчи буйруғи",
+ "commandDeviceIdentification": "Қурилма идентификацияси",
+ "commandPositionSingle": "Бир марта кузатиш",
+ "commandPositionPeriodic": "Кузатувни бошлаш",
+ "commandPositionStop": "Кузатувни бекор қилиш",
+ "commandEngineStop": "Двигателни блокировка қилиш",
+ "commandEngineResume": "Двигателдан блокировкани ечиш",
+ "commandAlarmArm": "Сигнализацияни активлаштириш",
+ "commandAlarmDisarm": "Сигнализация активлигини бекор қилиш",
+ "commandAlarmDismiss": "Dismiss Alarm",
+ "commandSetTimezone": "Соат қутбини созлаш",
+ "commandRequestPhoto": "Фото сўраш",
+ "commandPowerOff": "Power Off Device",
+ "commandRebootDevice": "Қурилмани қайта юклаш",
+ "commandFactoryReset": "Factory Reset",
+ "commandSendSms": "СМС жўнатиш",
+ "commandSendUssd": "USSD жўнатиш",
+ "commandSosNumber": "Шошилинч рақамни созлаш",
+ "commandSilenceTime": "Тинчлик вақтини созлаш",
+ "commandSetPhonebook": "Телефон дафтарини созлаш",
+ "commandVoiceMessage": "Товушли хабар",
+ "commandOutputControl": "Чиқиш назорати",
+ "commandVoiceMonitoring": "Voice Monitoring",
+ "commandSetAgps": "Set AGPS",
+ "commandSetIndicator": "Set Indicator",
+ "commandConfiguration": "Configuration",
+ "commandGetVersion": "Get Version",
+ "commandFirmwareUpdate": "Update Firmware",
+ "commandSetConnection": "Set Connection",
+ "commandSetOdometer": "Set Odometer",
+ "commandGetModemStatus": "Get Modem Status",
+ "commandGetDeviceStatus": "Get Device Status",
+ "commandSetSpeedLimit": "Set Speed Limit",
+ "commandModePowerSaving": "Power Saving Mode",
+ "commandModeDeepSleep": "Deep Sleep Mode",
+ "commandAlarmGeofence": "Set Geofence Alarm",
+ "commandAlarmBattery": "Set Battery Alarm",
+ "commandAlarmSos": "Set SOS Alarm",
+ "commandAlarmRemove": "Set Remove Alarm",
+ "commandAlarmClock": "Set Clock Alarm",
+ "commandAlarmSpeed": "Set Speed Alarm",
+ "commandAlarmFall": "Set Fall Alarm",
+ "commandAlarmVibration": "Set Vibration Alarm",
+ "commandFrequency": "Частота",
+ "commandTimezone": "Соат қутбининг силжиши",
+ "commandMessage": "Хабар",
+ "commandRadius": "Radius",
+ "commandEnable": "Enable",
+ "commandData": "Маълумотлар",
+ "commandIndex": "Индекс",
+ "commandPhone": "Телефон рақами",
+ "commandServer": "Server",
+ "commandPort": "Port",
+ "eventAll": "Барча воқеалар",
+ "eventDeviceOnline": "Status online",
+ "eventDeviceUnknown": "Status unknown",
+ "eventDeviceOffline": "Status offline",
+ "eventDeviceInactive": "Device inactive",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "Device moving",
+ "eventDeviceStopped": "Device stopped",
+ "eventDeviceOverspeed": "Speed limit exceeded",
+ "eventDeviceFuelDrop": "Fuel drop",
+ "eventDeviceFuelIncrease": "Fuel increase",
+ "eventCommandResult": "Буйруқ натижаси",
+ "eventGeofenceEnter": "Geofence entered",
+ "eventGeofenceExit": "Geofence exited",
+ "eventAlarm": "Alarm",
+ "eventIgnitionOn": "Ignition on",
+ "eventIgnitionOff": "Ignition off",
+ "eventMaintenance": "Хизмат кўрсатиш талаб этилади",
+ "eventTextMessage": "Text message received",
+ "eventDriverChanged": "Driver changed",
+ "eventMedia": "Media",
+ "eventsScrollToLast": "Scroll To Last",
+ "eventsSoundEvents": "Sound Events",
+ "eventsSoundAlarms": "Sound Alarms",
+ "alarmGeneral": "General",
+ "alarmSos": "SOS",
+ "alarmVibration": "Vibration",
+ "alarmMovement": "Movement",
+ "alarmLowspeed": "Low Speed",
+ "alarmOverspeed": "Overspeed",
+ "alarmFallDown": "Fall Down",
+ "alarmLowPower": "Low Power",
+ "alarmLowBattery": "Low Battery",
+ "alarmFault": "Fault",
+ "alarmPowerOff": "Power Off",
+ "alarmPowerOn": "Power On",
+ "alarmDoor": "Door",
+ "alarmLock": "Lock",
+ "alarmUnlock": "Unlock",
+ "alarmGeofence": "Geofence",
+ "alarmGeofenceEnter": "Geofence Enter",
+ "alarmGeofenceExit": "Geofence Exit",
+ "alarmGpsAntennaCut": "GPS Antenna Cut",
+ "alarmAccident": "Accident",
+ "alarmTow": "Tow",
+ "alarmIdle": "Idle",
+ "alarmHighRpm": "High RPM",
+ "alarmHardAcceleration": "Hard Acceleration",
+ "alarmHardBraking": "Hard Braking",
+ "alarmHardCornering": "Hard Cornering",
+ "alarmLaneChange": "Lane Change",
+ "alarmFatigueDriving": "Fatigue Driving",
+ "alarmPowerCut": "Power Cut",
+ "alarmPowerRestored": "Power Restored",
+ "alarmJamming": "Jamming",
+ "alarmTemperature": "Temperature",
+ "alarmParking": "Parking",
+ "alarmBonnet": "Bonnet",
+ "alarmFootBrake": "Foot Brake",
+ "alarmFuelLeak": "Fuel Leak",
+ "alarmTampering": "Tampering",
+ "alarmRemoving": "Removing",
+ "notificationType": "Хабарнома тури",
+ "notificationAlways": "All Devices",
+ "notificationNotificators": "Channels",
+ "notificatorCommand": "Command",
+ "notificatorWeb": "Web",
+ "notificatorMail": "Mail",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Replay",
+ "reportCombined": "Combined",
+ "reportRoute": "Маршрут",
+ "reportEvents": "Ҳодисалар",
+ "reportTrips": "Сафарлар",
+ "reportStops": "Stops",
+ "reportSummary": "Маълумот",
+ "reportDaily": "Daily Summary",
+ "reportChart": "График",
+ "reportConfigure": "Конфигурациялаш",
+ "reportEventTypes": "Ҳодиса тури",
+ "reportChartType": "График тури",
+ "reportShowMarkers": "Маркетларни кўрсатиш",
+ "reportExport": "Экспорт",
+ "reportEmail": "Email Report",
+ "reportSchedule": "Schedule",
+ "reportPeriod": "Period",
+ "reportCustom": "Custom",
+ "reportToday": "Today",
+ "reportYesterday": "Yesterday",
+ "reportThisWeek": "This Week",
+ "reportPreviousWeek": "Previous Week",
+ "reportThisMonth": "This Month",
+ "reportPreviousMonth": "Previous Month",
+ "reportDeviceName": "Қурилма номи",
+ "reportAverageSpeed": "Ўртача тезлик",
+ "reportMaximumSpeed": "Максимал тезлик",
+ "reportEngineHours": "Мотосоат",
+ "reportDuration": "Узунлиги",
+ "reportStartDate": "Start Date",
+ "reportStartTime": "Бошланиш вақти",
+ "reportStartAddress": "Бошланиш манзили",
+ "reportEndTime": "Тугаш вақти",
+ "reportEndAddress": "Сўнгги манзил",
+ "reportSpentFuel": "Ёқилғи сарфи",
+ "reportStartOdometer": "Odometer Start",
+ "reportEndOdometer": "Odometer End",
+ "statisticsTitle": "Статистика",
+ "statisticsCaptureTime": "Йиғилиш вақти",
+ "statisticsActiveUsers": "Актив фойдаланувчилар",
+ "statisticsActiveDevices": "Актив қурилмалар",
+ "statisticsRequests": "Сўровлар",
+ "statisticsMessagesReceived": "Хабарлар қабул қилинган",
+ "statisticsMessagesStored": "Хабарлар сақланган",
+ "statisticsGeocoder": "Geocoder Requests",
+ "statisticsGeolocation": "Geolocation Requests",
+ "categoryArrow": "Стрелка",
+ "categoryDefault": "Сўзсиз",
+ "categoryAnimal": "Жонивор",
+ "categoryBicycle": "Велосипед",
+ "categoryBoat": "Boat",
+ "categoryBus": "Автобус",
+ "categoryCar": "Автомобиль",
+ "categoryCamper": "Camper",
+ "categoryCrane": "Crane",
+ "categoryHelicopter": "Helicopter",
+ "categoryMotorcycle": "Мотоцикл",
+ "categoryOffroad": "Offroad",
+ "categoryPerson": "Одам",
+ "categoryPickup": "Pickup",
+ "categoryPlane": "Самолёт",
+ "categoryShip": "Кема",
+ "categoryTractor": "Tractor",
+ "categoryTrain": "Train",
+ "categoryTram": "Tram",
+ "categoryTrolleybus": "Trolleybus",
+ "categoryTruck": "Юк автомобили",
+ "categoryVan": "Van",
+ "categoryScooter": "Scooter",
+ "maintenanceStart": "Start",
+ "maintenancePeriod": "Period"
+} \ No newline at end of file
diff --git a/src/resources/l10n/vi.json b/src/resources/l10n/vi.json
new file mode 100644
index 00000000..87f0e236
--- /dev/null
+++ b/src/resources/l10n/vi.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "Đang tải...",
+ "sharedHide": "Ẩn",
+ "sharedSave": "Lưu",
+ "sharedUpload": "Upload",
+ "sharedSet": "Thiết lập",
+ "sharedCancel": "Hủy",
+ "sharedCopy": "Copy",
+ "sharedAdd": "Thêm mới",
+ "sharedEdit": "Chỉnh sửa",
+ "sharedRemove": "Xóa",
+ "sharedRemoveConfirm": "Xóa lựa chọn?",
+ "sharedNoData": "No data",
+ "sharedSubject": "Subject",
+ "sharedYes": "Yes",
+ "sharedNo": "No",
+ "sharedKm": "km",
+ "sharedMi": "dặm",
+ "sharedNmi": "nmi",
+ "sharedMeters": "m",
+ "sharedFeet": "ft",
+ "sharedKn": "kn",
+ "sharedKmh": "km/h",
+ "sharedMph": "mph",
+ "sharedHour": "Giờ",
+ "sharedMinute": "Phút",
+ "sharedSecond": "Giây",
+ "sharedDays": "Ngày",
+ "sharedHours": "Giờ",
+ "sharedMinutes": "Phút",
+ "sharedDecimalDegrees": "Độ thập phân",
+ "sharedDegreesDecimalMinutes": "Độ thập phân phút",
+ "sharedDegreesMinutesSeconds": "Độ thập phân giây",
+ "sharedName": "Tên",
+ "sharedDescription": "Mô tả",
+ "sharedSearch": "Tìm kiếm",
+ "sharedIconScale": "Icon Scale",
+ "sharedGeofence": "Giới hạn địa lý",
+ "sharedGeofences": "Giới hạn địa lý",
+ "sharedCreateGeofence": "Create Geofence",
+ "sharedNotifications": "Thông báo",
+ "sharedNotification": "Notification",
+ "sharedAttributes": "Thuộc tính",
+ "sharedAttribute": "Thuộc tính",
+ "sharedDrivers": "Drivers",
+ "sharedDriver": "Driver",
+ "sharedArea": "Khu vực",
+ "sharedSound": "Tiếng thông báo",
+ "sharedType": "Loại",
+ "sharedDistance": "Khoảng cách",
+ "sharedHourAbbreviation": "h",
+ "sharedMinuteAbbreviation": "m",
+ "sharedSecondAbbreviation": "s",
+ "sharedVoltAbbreviation": "V",
+ "sharedLiterAbbreviation": "l",
+ "sharedGallonAbbreviation": "gal",
+ "sharedLiter": "Liter",
+ "sharedImpGallon": "Imp. Gallon",
+ "sharedUsGallon": "U.S. Gallon",
+ "sharedLiterPerHourAbbreviation": "l/h",
+ "sharedGetMapState": "Lấy trạng thái bản đồ",
+ "sharedComputedAttribute": "Tính toán thuộc tính",
+ "sharedComputedAttributes": "Tính toán thuộc tính",
+ "sharedCheckComputedAttribute": "Kiểm tra tính toán thuộc tính",
+ "sharedExpression": "Truyền",
+ "sharedDevice": "Thiết bị",
+ "sharedTest": "Test",
+ "sharedTestNotification": "Gửi thông báo thử",
+ "sharedTestNotificators": "Test Channels",
+ "sharedTestExpression": "Test Expression",
+ "sharedCalendar": "Lịch",
+ "sharedCalendars": "Lịch",
+ "sharedFile": "Tập tin",
+ "sharedSearchDevices": "Search Devices",
+ "sharedSortBy": "Sort By",
+ "sharedFilterMap": "Filter on Map",
+ "sharedSelectFile": "Lựa chọn tập tin",
+ "sharedPhone": "Điện thoại",
+ "sharedRequired": "Bắt buộc",
+ "sharedPreferences": "Ưu tiên",
+ "sharedPermissions": "Cấp phép",
+ "sharedConnections": "Connections",
+ "sharedExtra": "Thêm vào",
+ "sharedPrimary": "Primary",
+ "sharedSecondary": "Secondary",
+ "sharedTypeString": "Chuỗi",
+ "sharedTypeNumber": "Số",
+ "sharedTypeBoolean": "Toán tử",
+ "sharedTimezone": "Múi giờ",
+ "sharedInfoTitle": "Thông tin",
+ "sharedSavedCommand": "Saved Command",
+ "sharedSavedCommands": "Saved Commands",
+ "sharedNew": "New…",
+ "sharedShowAddress": "Show Address",
+ "sharedShowDetails": "More Details",
+ "sharedDisabled": "Disabled",
+ "sharedMaintenance": "Maintenance",
+ "sharedDeviceAccumulators": "Accumulators",
+ "sharedAlarms": "Alarms",
+ "sharedLocation": "Location",
+ "sharedImport": "Import",
+ "sharedColumns": "Columns",
+ "sharedDropzoneText": "Drag and drop a file here or click",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "Simple",
+ "calendarRecurrence": "Recurrence",
+ "calendarOnce": "Once",
+ "calendarDaily": "Daily",
+ "calendarWeekly": "Weekly",
+ "calendarMonthly": "Monthly",
+ "calendarDays": "Days",
+ "calendarSunday": "Sunday",
+ "calendarMonday": "Monday",
+ "calendarTuesday": "Tuesday",
+ "calendarWednesday": "Wednesday",
+ "calendarThursday": "Thursday",
+ "calendarFriday": "Friday",
+ "calendarSaturday": "Saturday",
+ "attributeShowGeofences": "Show Geofences",
+ "attributeSpeedLimit": "Giới hạn tốc độ",
+ "attributeFuelDropThreshold": "Fuel Drop Threshold",
+ "attributeFuelIncreaseThreshold": "Fuel Increase Threshold",
+ "attributePolylineDistance": "Polyline Distance",
+ "attributeReportIgnoreOdometer": "Báo cáo: Bỏ qua đồng hồ đo",
+ "attributeWebReportColor": "Web: Màu báo cáo",
+ "attributeDevicePassword": "Mật khẩu thiết bị",
+ "attributeDeviceImage": "Device Image",
+ "attributeDeviceInactivityStart": "Device Inactivity Start",
+ "attributeDeviceInactivityPeriod": "Device Inactivity Period",
+ "attributeProcessingCopyAttributes": "Thực thi: sao chép thuộc tính",
+ "attributeColor": "Màu",
+ "attributeWebLiveRouteLength": "Web: Độ dài tuyến",
+ "attributeWebSelectZoom": "Web: phóng to lựa chọn",
+ "attributeWebMaxZoom": "Web: Maximum Zoom",
+ "attributeTelegramChatId": "Telegram Chat ID",
+ "attributePushoverUserKey": "Pushover User Key",
+ "attributePushoverDeviceNames": "Pushover Device Names",
+ "attributeMailSmtpHost": "Email: SMTP Host",
+ "attributeMailSmtpPort": "Email: Cổng SMTP",
+ "attributeMailSmtpStarttlsEnable": "Mail: SMTP STARTTLS Enable",
+ "attributeMailSmtpStarttlsRequired": "Mail: SMTP STARTTLS Required",
+ "attributeMailSmtpSslEnable": "Mail: SMTP SSL Enable",
+ "attributeMailSmtpSslTrust": "Mail: SMTP SSL Trust",
+ "attributeMailSmtpSslProtocols": "Mail: SMTP SSL Protocols",
+ "attributeMailSmtpFrom": "Mail: SMTP From",
+ "attributeMailSmtpAuth": "Mail: SMTP Auth Enable",
+ "attributeMailSmtpUsername": "Mail: SMTP Username",
+ "attributeMailSmtpPassword": "Mail: SMTP Password",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "UI: Disable Attributes",
+ "attributeUiDisableGroups": "UI: Disable Groups",
+ "attributeUiDisableEvents": "UI: Disable Events",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "UI: Disable Drivers",
+ "attributeUiDisableComputedAttributes": "UI: Disable Computed Attributes",
+ "attributeUiDisableCalendars": "UI: Disable Calendars",
+ "attributeUiDisableMaintenance": "UI: Disable Maintenance",
+ "attributeUiHidePositionAttributes": "UI: Hide Position Attributes",
+ "attributeUiDisableLoginLanguage": "UI: Disable Login Language",
+ "attributeNotificationTokens": "Notification Tokens",
+ "attributePopupInfo": "Popup Info",
+ "errorTitle": "Lỗi",
+ "errorGeneral": "Tham số không hợp lệ hoặc hạn chế vi phạm",
+ "errorConnection": "Lỗi kết nối",
+ "errorSocket": "Lỗi kết nối trang",
+ "errorZero": "Can't be zero",
+ "userEmail": "Email",
+ "userPassword": "Mật khẩu",
+ "userAdmin": "Quản trị",
+ "userRemember": "Nhớ",
+ "userExpirationTime": "Hết hạn",
+ "userDeviceLimit": "Hạn chế thiết bị",
+ "userUserLimit": "Hạn chế người dùng",
+ "userDeviceReadonly": "Chỉ xem thiết bị",
+ "userLimitCommands": "Limit Commands",
+ "userDisableReports": "Disable Reports",
+ "userFixedEmail": "No Email Change",
+ "userToken": "Token",
+ "userDeleteAccount": "Delete Account",
+ "userTemporary": "Temporary",
+ "loginTitle": "Đăng nhập",
+ "loginLanguage": "Ngôn ngữ",
+ "loginReset": "Reset Password",
+ "loginRegister": "Đăng ký",
+ "loginLogin": "Đăng nhập",
+ "loginOpenId": "Login with OpenID",
+ "loginFailed": "Sai mật khẩu hoặc địa chỉ email",
+ "loginCreated": "Người dùng mới đã được đăng ký",
+ "loginResetSuccess": "Check your email",
+ "loginUpdateSuccess": "New password is set",
+ "loginLogout": "Đăng xuất",
+ "loginLogo": "Biểu tượng",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "Các thiết bị và trạng thái",
+ "deviceSelected": "Selected Device",
+ "deviceTitle": "Các thiết bị",
+ "devicePrimaryInfo": "Device Title",
+ "deviceSecondaryInfo": "Device Detail",
+ "deviceIdentifier": "Định danh",
+ "deviceModel": "Mẫu",
+ "deviceContact": "Liên hệ",
+ "deviceCategory": "Phân loại",
+ "deviceLastUpdate": "Cập nhật lần cuối",
+ "deviceCommand": "Lệnh",
+ "deviceFollow": "Theo dõi",
+ "deviceTotalDistance": "Tổng quãng đường",
+ "deviceStatus": "Trạng thái",
+ "deviceStatusOnline": "Trực tuyến",
+ "deviceStatusOffline": "Ngoại tuyến",
+ "deviceStatusUnknown": "Không xác định",
+ "deviceRegisterFirst": "Register your first device",
+ "deviceIdentifierHelp": "IMEI, serial number or other id. It has to match the identifier device reports to the server.",
+ "deviceShare": "Share Device",
+ "groupDialog": "Nhóm",
+ "groupParent": "Nhóm",
+ "groupNoGroup": "Không có nhóm",
+ "settingsTitle": "Cài đặt",
+ "settingsUser": "Tài khoản",
+ "settingsGroups": "Nhóm",
+ "settingsServer": "Máy chủ",
+ "settingsUsers": "Người dùng",
+ "settingsDistanceUnit": "Distance Unit",
+ "settingsAltitudeUnit": "Altitude Unit",
+ "settingsSpeedUnit": "Speed Unit",
+ "settingsVolumeUnit": "Volume Unit",
+ "settingsTwelveHourFormat": "Định dạng 12h",
+ "settingsCoordinateFormat": "Cấu chúc tọa độ",
+ "settingsServerVersion": "Server Version",
+ "settingsAppVersion": "App Version",
+ "settingsConnection": "Connection",
+ "settingsDarkMode": "Dark Mode",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "Báo cáo",
+ "reportScheduled": "Scheduled Reports",
+ "reportDevice": "Thiết bị",
+ "reportGroup": "Nhóm",
+ "reportFrom": "Từ",
+ "reportTo": "Đến",
+ "reportShow": "Hiển thị",
+ "reportClear": "Xóa",
+ "linkGoogleMaps": "Google Maps",
+ "linkAppleMaps": "Apple Maps",
+ "linkStreetView": "Street View",
+ "positionFixTime": "Fix Time",
+ "positionDeviceTime": "Device Time",
+ "positionServerTime": "Server Time",
+ "positionValid": "Có hiệu lực",
+ "positionAccuracy": "Chính xác",
+ "positionLatitude": "Vĩ độ",
+ "positionLongitude": "Kinh độ",
+ "positionAltitude": "Độ cao",
+ "positionSpeed": "Tốc độ",
+ "positionCourse": "Hướng",
+ "positionAddress": "Địa chỉ",
+ "positionProtocol": "Giao thức",
+ "positionDistance": "Khoảng cách",
+ "positionRpm": "RPM",
+ "positionFuel": "Nhiên liệu",
+ "positionPower": "Nguồn",
+ "positionBattery": "Pin",
+ "positionRaw": "Thô",
+ "positionIndex": "Danh mục",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "Satellites",
+ "positionSatVisible": "Visible Satellites",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "Roaming",
+ "positionEvent": "Sự kiện",
+ "positionAlarm": "Cảnh báo",
+ "positionStatus": "Trạng thái",
+ "positionOdometer": "Đồng hồ đo",
+ "positionServiceOdometer": "Đồng hồ đo",
+ "positionTripOdometer": "Quãng đường",
+ "positionHours": "Giờ",
+ "positionSteps": "Steps",
+ "positionInput": "Nhập",
+ "positionHeartRate": "Heart Rate",
+ "positionOutput": "Xuất",
+ "positionBatteryLevel": "Mức pin",
+ "positionFuelConsumption": "Tiêu thụ nhiên liệu",
+ "positionRfid": "RFID",
+ "positionVersionFw": "Phiên bản",
+ "positionVersionHw": "Phiên bản phần cứng",
+ "positionIgnition": "Máy",
+ "positionFlags": "Cờ",
+ "positionCharge": "Nạp tiền",
+ "positionIp": "IP",
+ "positionArchive": "Lưu trữ",
+ "positionVin": "VIN",
+ "positionApproximate": "Độ chính xác",
+ "positionThrottle": "Cổ hút",
+ "positionMotion": "Chuyển động",
+ "positionArmed": "Bật báo động",
+ "positionAcceleration": "Tăng tốc",
+ "positionTemp": "Temperature",
+ "positionDeviceTemp": "Nhiệt độ thiết bị",
+ "positionCoolantTemp": "Coolant Temperature",
+ "positionOperator": "Operator",
+ "positionCommand": "Command",
+ "positionBlocked": "Chặn",
+ "positionDtcs": "DTCs",
+ "positionObdSpeed": "Tốc độ OBD",
+ "positionObdOdometer": "Quãng đường ODB",
+ "positionDrivingTime": "Driving Time",
+ "positionDriverUniqueId": "Driver Unique Id",
+ "positionCard": "Card",
+ "positionImage": "Image",
+ "positionVideo": "Video",
+ "positionAudio": "Audio",
+ "serverTitle": "Cài đặt máy chủ",
+ "serverZoom": "Phóng to",
+ "serverRegistration": "Đăng ký",
+ "serverReadonly": "Chỉ đọc",
+ "serverForceSettings": "Buộc thiết lập",
+ "serverAnnouncement": "Announcement",
+ "serverName": "Server Name",
+ "serverDescription": "Server Description",
+ "serverColorPrimary": "Color Primary",
+ "serverColorSecondary": "Color Secondary",
+ "serverLogo": "Logo Image",
+ "serverLogoInverted": "Inverted Logo Image",
+ "serverChangeDisable": "Disable Server Change",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "Bản đồ",
+ "mapActive": "Active Maps",
+ "mapOverlay": "Map Overlay",
+ "mapOverlayCustom": "Custom Overlay",
+ "mapOpenSeaMap": "OpenSeaMap",
+ "mapOpenRailwayMap": "OpenRailwayMap",
+ "mapOpenWeatherKey": "OpenWeather API Key",
+ "mapOpenWeatherClouds": "OpenWeather Clouds",
+ "mapOpenWeatherPrecipitation": "OpenWeather Precipitation",
+ "mapOpenWeatherPressure": "OpenWeather Pressure",
+ "mapOpenWeatherWind": "OpenWeather Wind",
+ "mapOpenWeatherTemperature": "OpenWeather Temperature",
+ "mapLayer": "Lớp bản đồ",
+ "mapCustom": "Custom (XYZ)",
+ "mapCustomArcgis": "Custom (ArcGIS)",
+ "mapCustomLabel": "Custom map",
+ "mapCarto": "Carto Basemaps",
+ "mapOsm": "OpenStreetMap",
+ "mapGoogleRoad": "Google Road",
+ "mapGoogleHybrid": "Google Hybrid",
+ "mapGoogleSatellite": "Google Satellite",
+ "mapOpenTopoMap": "OpenTopoMap",
+ "mapBingKey": "Bing Maps Key",
+ "mapBingRoad": "Bing Maps Road",
+ "mapBingAerial": "Bing Maps Aerial",
+ "mapBingHybrid": "Bản đồ Bing Hybird",
+ "mapBaidu": "Bản đồ Baidu",
+ "mapAutoNavi": "AutoNavi",
+ "mapYandexMap": "Bản đồ Yandex",
+ "mapYandexSat": "Bản đồ Yandex Vệ tinh ",
+ "mapWikimedia": "Wikimedia",
+ "mapOrdnanceSurvey": "Ordnance Survey",
+ "mapMapboxStreets": "Mapbox Streets",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox Outdoors",
+ "mapMapboxSatellite": "Mapbox Satellite",
+ "mapMapboxKey": "Mapbox Access Token",
+ "mapMapTilerBasic": "MapTiler Basic",
+ "mapMapTilerHybrid": "MapTiler Hybrid",
+ "mapMapTilerKey": "MapTiler API Key",
+ "mapLocationIqStreets": "LocationIQ Streets",
+ "mapLocationIqDark": "LocationIQ Dark",
+ "mapLocationIqKey": "LocationIQ Access Token",
+ "mapTomTomBasic": "TomTom Basic",
+ "mapTomTomFlow": "TomTom Traffic Flow",
+ "mapTomTomIncidents": "TomTom Traffic Incidents",
+ "mapTomTomKey": "TomTom API Key",
+ "mapHereBasic": "Here Basic",
+ "mapHereHybrid": "Here Hybrid",
+ "mapHereSatellite": "Here Satellite",
+ "mapHereFlow": "Here Traffic Flow",
+ "mapHereKey": "Here API Key",
+ "mapShapePolygon": "Đa giác",
+ "mapShapeCircle": "Vòng tròn",
+ "mapShapePolyline": "Đường kẻ đa giác",
+ "mapLiveRoutes": "Lộ trình trực tuyến",
+ "mapDirection": "Show Direction",
+ "mapCurrentLocation": "Current Location",
+ "mapPoiLayer": "POI Layer",
+ "mapClustering": "Markers Clustering",
+ "mapOnSelect": "Show Map on Selection",
+ "mapDefault": "Default Map",
+ "stateTitle": "Trạng thái",
+ "stateName": "Thuộc tính",
+ "stateValue": "Giá trị",
+ "commandTitle": "Lệnh",
+ "commandSend": "Gửi",
+ "commandSent": "Command sent",
+ "commandQueued": "Command queued",
+ "commandUnit": "Đơn vị",
+ "commandCustom": "Lệnh tùy chỉnh",
+ "commandDeviceIdentification": "Định danh thiết bị",
+ "commandPositionSingle": "Báo cáo đơn",
+ "commandPositionPeriodic": "Báo cáo định kỳ",
+ "commandPositionStop": "Dừng báo cáo",
+ "commandEngineStop": "Tắt máy",
+ "commandEngineResume": "Bật máy",
+ "commandAlarmArm": "Báo động cho phép",
+ "commandAlarmDisarm": "Báo động không cho phép",
+ "commandAlarmDismiss": "Dismiss Alarm",
+ "commandSetTimezone": "Thiết lập múi giờ",
+ "commandRequestPhoto": "Yêu cầu ảnh",
+ "commandPowerOff": "Power Off Device",
+ "commandRebootDevice": "Khởi động lại thiết bị",
+ "commandFactoryReset": "Factory Reset",
+ "commandSendSms": "Gửi tin nhắn",
+ "commandSendUssd": "Gửi mã USSD",
+ "commandSosNumber": "Thiết lập số khẩn cấp",
+ "commandSilenceTime": "Thiêt lập giờ im lặng",
+ "commandSetPhonebook": "Thiết lập danh bạ điện thoại",
+ "commandVoiceMessage": "Tin nhắn thoại",
+ "commandOutputControl": "Điều khiển đầu ra",
+ "commandVoiceMonitoring": "Voice Monitoring",
+ "commandSetAgps": "Set AGPS",
+ "commandSetIndicator": "Thiết lập hiển thị",
+ "commandConfiguration": "Configuration",
+ "commandGetVersion": "Get Version",
+ "commandFirmwareUpdate": "Update Firmware",
+ "commandSetConnection": "Set Connection",
+ "commandSetOdometer": "Set Odometer",
+ "commandGetModemStatus": "Get Modem Status",
+ "commandGetDeviceStatus": "Get Device Status",
+ "commandSetSpeedLimit": "Set Speed Limit",
+ "commandModePowerSaving": "Power Saving Mode",
+ "commandModeDeepSleep": "Deep Sleep Mode",
+ "commandAlarmGeofence": "Set Geofence Alarm",
+ "commandAlarmBattery": "Set Battery Alarm",
+ "commandAlarmSos": "Set SOS Alarm",
+ "commandAlarmRemove": "Set Remove Alarm",
+ "commandAlarmClock": "Set Clock Alarm",
+ "commandAlarmSpeed": "Set Speed Alarm",
+ "commandAlarmFall": "Set Fall Alarm",
+ "commandAlarmVibration": "Set Vibration Alarm",
+ "commandFrequency": "Tần suất",
+ "commandTimezone": "Múi giờ",
+ "commandMessage": "Thông báo",
+ "commandRadius": "Radius",
+ "commandEnable": "Enable",
+ "commandData": "Dữ liệu",
+ "commandIndex": "Danh mục",
+ "commandPhone": "Số điện thoại",
+ "commandServer": "Server",
+ "commandPort": "Port",
+ "eventAll": "Tất cả sự kiện",
+ "eventDeviceOnline": "Status online",
+ "eventDeviceUnknown": "Status unknown",
+ "eventDeviceOffline": "Status offline",
+ "eventDeviceInactive": "Device inactive",
+ "eventQueuedCommandSent": "Queued command sent",
+ "eventDeviceMoving": "Device moving",
+ "eventDeviceStopped": "Device stopped",
+ "eventDeviceOverspeed": "Speed limit exceeded",
+ "eventDeviceFuelDrop": "Fuel drop",
+ "eventDeviceFuelIncrease": "Fuel increase",
+ "eventCommandResult": "Kết quả lệnh",
+ "eventGeofenceEnter": "Geofence entered",
+ "eventGeofenceExit": "Geofence exited",
+ "eventAlarm": "Alarm",
+ "eventIgnitionOn": "Ignition on",
+ "eventIgnitionOff": "Ignition off",
+ "eventMaintenance": "Yêu cầu bảo dưỡng",
+ "eventTextMessage": "Đã nhận tin nhắn",
+ "eventDriverChanged": "Driver changed",
+ "eventMedia": "Media",
+ "eventsScrollToLast": "Kéo xuống cuối",
+ "eventsSoundEvents": "Sound Events",
+ "eventsSoundAlarms": "Sound Alarms",
+ "alarmGeneral": "General",
+ "alarmSos": "SOS",
+ "alarmVibration": "Vibration",
+ "alarmMovement": "Movement",
+ "alarmLowspeed": "Low Speed",
+ "alarmOverspeed": "Overspeed",
+ "alarmFallDown": "Fall Down",
+ "alarmLowPower": "Low Power",
+ "alarmLowBattery": "Low Battery",
+ "alarmFault": "Fault",
+ "alarmPowerOff": "Power Off",
+ "alarmPowerOn": "Power On",
+ "alarmDoor": "Door",
+ "alarmLock": "Lock",
+ "alarmUnlock": "Unlock",
+ "alarmGeofence": "Geofence",
+ "alarmGeofenceEnter": "Geofence Enter",
+ "alarmGeofenceExit": "Geofence Exit",
+ "alarmGpsAntennaCut": "GPS Antenna Cut",
+ "alarmAccident": "Accident",
+ "alarmTow": "Tow",
+ "alarmIdle": "Idle",
+ "alarmHighRpm": "High RPM",
+ "alarmHardAcceleration": "Hard Acceleration",
+ "alarmHardBraking": "Hard Braking",
+ "alarmHardCornering": "Hard Cornering",
+ "alarmLaneChange": "Lane Change",
+ "alarmFatigueDriving": "Fatigue Driving",
+ "alarmPowerCut": "Power Cut",
+ "alarmPowerRestored": "Power Restored",
+ "alarmJamming": "Jamming",
+ "alarmTemperature": "Temperature",
+ "alarmParking": "Parking",
+ "alarmBonnet": "Bonnet",
+ "alarmFootBrake": "Foot Brake",
+ "alarmFuelLeak": "Fuel Leak",
+ "alarmTampering": "Tampering",
+ "alarmRemoving": "Removing",
+ "notificationType": "Loại thông báo",
+ "notificationAlways": "All Devices",
+ "notificationNotificators": "Channels",
+ "notificatorCommand": "Command",
+ "notificatorWeb": "Web",
+ "notificatorMail": "Mail",
+ "notificatorSms": "SMS",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "Replay",
+ "reportCombined": "Combined",
+ "reportRoute": "Lộ trình",
+ "reportEvents": "Sự kiện",
+ "reportTrips": "Hành trình",
+ "reportStops": "Dừng đỗ",
+ "reportSummary": "Sơ lược",
+ "reportDaily": "Daily Summary",
+ "reportChart": "Biểu đồ",
+ "reportConfigure": "Cấu hình",
+ "reportEventTypes": "Loại sự kiện",
+ "reportChartType": "Kiểu biều đồ",
+ "reportShowMarkers": "Hiển thị dấu",
+ "reportExport": "Xuất",
+ "reportEmail": "Email Report",
+ "reportSchedule": "Schedule",
+ "reportPeriod": "Period",
+ "reportCustom": "Custom",
+ "reportToday": "Today",
+ "reportYesterday": "Yesterday",
+ "reportThisWeek": "This Week",
+ "reportPreviousWeek": "Previous Week",
+ "reportThisMonth": "This Month",
+ "reportPreviousMonth": "Previous Month",
+ "reportDeviceName": "Tên thiết bị",
+ "reportAverageSpeed": "Tốc độ trung bình",
+ "reportMaximumSpeed": "Tốc độ cao nhất",
+ "reportEngineHours": "Thời gian nổ máy",
+ "reportDuration": "Khoảng thời gian",
+ "reportStartDate": "Start Date",
+ "reportStartTime": "Thời gian bắt đầu",
+ "reportStartAddress": "Địa chỉ bắt đầu",
+ "reportEndTime": "Thời gian kết thúc",
+ "reportEndAddress": "Địa chỉ kết thúc",
+ "reportSpentFuel": "Tiêu thụ nguyên liệu",
+ "reportStartOdometer": "Odometer Start",
+ "reportEndOdometer": "Odometer End",
+ "statisticsTitle": "Thống kê",
+ "statisticsCaptureTime": "Thời gian",
+ "statisticsActiveUsers": "Người dùng được kích hoạt",
+ "statisticsActiveDevices": "Thiết bị được kích hoạt",
+ "statisticsRequests": "Yêu cầu",
+ "statisticsMessagesReceived": "Thông báo đã nhận",
+ "statisticsMessagesStored": "Thông báo đã nhận",
+ "statisticsGeocoder": "Yêu cầu địa chỉ",
+ "statisticsGeolocation": "Yêu cầu địa chỉ",
+ "categoryArrow": "Mũi tên",
+ "categoryDefault": "Mặc định",
+ "categoryAnimal": "Vật nuôi",
+ "categoryBicycle": "Xe đạp",
+ "categoryBoat": "Tầu thuyền",
+ "categoryBus": "Xe buýt",
+ "categoryCar": "Xe con",
+ "categoryCamper": "Camper",
+ "categoryCrane": "Cần cẩu",
+ "categoryHelicopter": "Trực thăng",
+ "categoryMotorcycle": "Xe máy",
+ "categoryOffroad": "Offroad",
+ "categoryPerson": "Cá nhân",
+ "categoryPickup": "Pickup",
+ "categoryPlane": "Máy bay",
+ "categoryShip": "Tàu thủy",
+ "categoryTractor": "Máy kéo",
+ "categoryTrain": "Train",
+ "categoryTram": "Tram",
+ "categoryTrolleybus": "Trolleybus",
+ "categoryTruck": "Xe tải",
+ "categoryVan": "Xe thùng",
+ "categoryScooter": "Scooter",
+ "maintenanceStart": "Start",
+ "maintenancePeriod": "Period"
+} \ No newline at end of file
diff --git a/src/resources/l10n/zh.json b/src/resources/l10n/zh.json
new file mode 100644
index 00000000..65d9e723
--- /dev/null
+++ b/src/resources/l10n/zh.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "加载",
+ "sharedHide": "隐藏",
+ "sharedSave": "保存",
+ "sharedUpload": "上传",
+ "sharedSet": "设置",
+ "sharedCancel": "取消",
+ "sharedCopy": "复制",
+ "sharedAdd": "添加",
+ "sharedEdit": "编辑",
+ "sharedRemove": "删除",
+ "sharedRemoveConfirm": "删除选中项?",
+ "sharedNoData": "没有数据",
+ "sharedSubject": "主题",
+ "sharedYes": "是",
+ "sharedNo": "否",
+ "sharedKm": "千米",
+ "sharedMi": "英里",
+ "sharedNmi": "海里",
+ "sharedMeters": "米",
+ "sharedFeet": "英尺",
+ "sharedKn": "节",
+ "sharedKmh": "千米/小时",
+ "sharedMph": "英里/小时",
+ "sharedHour": "小时",
+ "sharedMinute": "分钟",
+ "sharedSecond": "秒",
+ "sharedDays": "天",
+ "sharedHours": "小时",
+ "sharedMinutes": "分钟",
+ "sharedDecimalDegrees": "度",
+ "sharedDegreesDecimalMinutes": "度分",
+ "sharedDegreesMinutesSeconds": "度分秒",
+ "sharedName": "名称",
+ "sharedDescription": "描述",
+ "sharedSearch": "搜索",
+ "sharedIconScale": "图标比例",
+ "sharedGeofence": "围栏",
+ "sharedGeofences": "围栏",
+ "sharedCreateGeofence": "创建围栏",
+ "sharedNotifications": "通知",
+ "sharedNotification": "通知",
+ "sharedAttributes": "属性",
+ "sharedAttribute": "属性",
+ "sharedDrivers": "驾驶员",
+ "sharedDriver": "驾驶员",
+ "sharedArea": "区域",
+ "sharedSound": "通知铃声",
+ "sharedType": "类型",
+ "sharedDistance": "里程",
+ "sharedHourAbbreviation": "小时",
+ "sharedMinuteAbbreviation": "分",
+ "sharedSecondAbbreviation": "秒",
+ "sharedVoltAbbreviation": "伏",
+ "sharedLiterAbbreviation": "升",
+ "sharedGallonAbbreviation": "加仑",
+ "sharedLiter": "升",
+ "sharedImpGallon": "英制加仑",
+ "sharedUsGallon": "美制加仑",
+ "sharedLiterPerHourAbbreviation": "升/小时",
+ "sharedGetMapState": "获取地图状态",
+ "sharedComputedAttribute": "计算属性",
+ "sharedComputedAttributes": "计算属性",
+ "sharedCheckComputedAttribute": "确认计算属性",
+ "sharedExpression": "表达式",
+ "sharedDevice": "设备",
+ "sharedTest": "测试",
+ "sharedTestNotification": "发送测试通知",
+ "sharedTestNotificators": "测试通道",
+ "sharedTestExpression": "测试表达式",
+ "sharedCalendar": "日历",
+ "sharedCalendars": "日历",
+ "sharedFile": "文件",
+ "sharedSearchDevices": "搜索设备",
+ "sharedSortBy": "排序方式",
+ "sharedFilterMap": "在地图上过滤",
+ "sharedSelectFile": "选择文件",
+ "sharedPhone": "电话",
+ "sharedRequired": "必填",
+ "sharedPreferences": "首选项",
+ "sharedPermissions": "权限",
+ "sharedConnections": "关联",
+ "sharedExtra": "附加",
+ "sharedPrimary": "设备信息首行",
+ "sharedSecondary": "设备信息次行",
+ "sharedTypeString": "字符串",
+ "sharedTypeNumber": "数字",
+ "sharedTypeBoolean": "布尔",
+ "sharedTimezone": "时区",
+ "sharedInfoTitle": "信息",
+ "sharedSavedCommand": "保存指令",
+ "sharedSavedCommands": "指令表",
+ "sharedNew": "新建",
+ "sharedShowAddress": "显示地址",
+ "sharedShowDetails": "详情请见",
+ "sharedDisabled": "禁用",
+ "sharedMaintenance": "维护",
+ "sharedDeviceAccumulators": "蓄电池",
+ "sharedAlarms": "警报",
+ "sharedLocation": "位置",
+ "sharedImport": "导入",
+ "sharedColumns": "显示列",
+ "sharedDropzoneText": "将文件拖放到此处或者单击",
+ "sharedLogs": "日志",
+ "sharedLink": "关联",
+ "calendarSimple": "简单",
+ "calendarRecurrence": "复现",
+ "calendarOnce": "单次",
+ "calendarDaily": "每日",
+ "calendarWeekly": "每周",
+ "calendarMonthly": "每月",
+ "calendarDays": "天",
+ "calendarSunday": "周日",
+ "calendarMonday": "周一",
+ "calendarTuesday": "周二",
+ "calendarWednesday": "周三",
+ "calendarThursday": "周四",
+ "calendarFriday": "周五",
+ "calendarSaturday": "周六",
+ "attributeShowGeofences": "显示围栏",
+ "attributeSpeedLimit": "速度限制",
+ "attributeFuelDropThreshold": "燃油下降阈值",
+ "attributeFuelIncreaseThreshold": "燃油增加阈值",
+ "attributePolylineDistance": "折线里程",
+ "attributeReportIgnoreOdometer": "报告:忽略里程表",
+ "attributeWebReportColor": "网页:报告颜色",
+ "attributeDevicePassword": "设备密码",
+ "attributeDeviceImage": "设备图片",
+ "attributeDeviceInactivityStart": "设备无响应起始",
+ "attributeDeviceInactivityPeriod": "设备无响应时段",
+ "attributeProcessingCopyAttributes": "处理中:复制属性",
+ "attributeColor": "颜色",
+ "attributeWebLiveRouteLength": "网页:活动路径长度",
+ "attributeWebSelectZoom": "网页:缩放选择项",
+ "attributeWebMaxZoom": "网页:最大的缩放",
+ "attributeTelegramChatId": "Telegram 账号",
+ "attributePushoverUserKey": "Pushover 用户密钥",
+ "attributePushoverDeviceNames": "Pushover 设备名称",
+ "attributeMailSmtpHost": "邮件:SMTP 主机",
+ "attributeMailSmtpPort": "邮件:SMTP 端口",
+ "attributeMailSmtpStarttlsEnable": "邮件:SMTP STARTTLS 开启",
+ "attributeMailSmtpStarttlsRequired": "邮件:SMTP STARTTLS 必填",
+ "attributeMailSmtpSslEnable": "邮件:SMTP SSL 开启",
+ "attributeMailSmtpSslTrust": "邮件:SMTP SSL 信任",
+ "attributeMailSmtpSslProtocols": "邮件:SMTP SSL 协议",
+ "attributeMailSmtpFrom": "邮件:SMTP 来源",
+ "attributeMailSmtpAuth": "邮件:SMTP 认证开启",
+ "attributeMailSmtpUsername": "邮件:SMTP 用户名",
+ "attributeMailSmtpPassword": "邮件:SMTP 密码",
+ "attributeUiDisableSavedCommands": "用户界面:禁用已保存的命令",
+ "attributeUiDisableAttributes": "用户界面:禁用属性",
+ "attributeUiDisableGroups": "用户界面:禁用分组",
+ "attributeUiDisableEvents": "用户界面:禁用事件",
+ "attributeUiDisableVehicleFeatures": "用户界面:禁用车辆功能",
+ "attributeUiDisableDrivers": "用户界面:禁用驾驶员",
+ "attributeUiDisableComputedAttributes": "用户界面:禁用计算属性",
+ "attributeUiDisableCalendars": "用户界面:禁用日历",
+ "attributeUiDisableMaintenance": "用户界面:禁用维护",
+ "attributeUiHidePositionAttributes": "用户界面:隐藏位置属性",
+ "attributeUiDisableLoginLanguage": "用户界面:禁用登录语言",
+ "attributeNotificationTokens": "通知令牌",
+ "attributePopupInfo": "卡片信息",
+ "errorTitle": "错误",
+ "errorGeneral": "无效参数",
+ "errorConnection": "连接错误",
+ "errorSocket": "网络通讯错误",
+ "errorZero": "不能为零",
+ "userEmail": "邮箱",
+ "userPassword": "密码",
+ "userAdmin": "管理员",
+ "userRemember": "记住密码",
+ "userExpirationTime": "失效日期",
+ "userDeviceLimit": "设备限制",
+ "userUserLimit": "用户限制",
+ "userDeviceReadonly": "设备只读",
+ "userLimitCommands": "禁用指令",
+ "userDisableReports": "禁用报表",
+ "userFixedEmail": "禁止更改邮箱",
+ "userToken": "令牌",
+ "userDeleteAccount": "删除账户",
+ "userTemporary": "暂时",
+ "loginTitle": "登录",
+ "loginLanguage": "语言",
+ "loginReset": "密码重设",
+ "loginRegister": "注册",
+ "loginLogin": "登录",
+ "loginOpenId": "以开放 ID 登录",
+ "loginFailed": "邮箱或密码错误",
+ "loginCreated": "新用户已注册成功",
+ "loginResetSuccess": "查看您的电子邮件",
+ "loginUpdateSuccess": "密码已重置",
+ "loginLogout": "注销",
+ "loginLogo": "图标",
+ "loginTotpCode": "一次性密码",
+ "loginTotpKey": "一次性密码密钥",
+ "devicesAndState": "设备和状态",
+ "deviceSelected": "已选设备",
+ "deviceTitle": "设备",
+ "devicePrimaryInfo": "设备名称",
+ "deviceSecondaryInfo": "设备详情",
+ "deviceIdentifier": "标识符",
+ "deviceModel": "型号",
+ "deviceContact": "联系人",
+ "deviceCategory": "类别",
+ "deviceLastUpdate": "最后更新时间",
+ "deviceCommand": "指令",
+ "deviceFollow": "跟随设备",
+ "deviceTotalDistance": "总里程",
+ "deviceStatus": "状态",
+ "deviceStatusOnline": "在线",
+ "deviceStatusOffline": "离线",
+ "deviceStatusUnknown": "未知",
+ "deviceRegisterFirst": "注册您的第一台设备",
+ "deviceIdentifierHelp": "IMEI、序列号或其他 ID。它必须匹配报告给服务器的设备标识符。",
+ "deviceShare": "共享设备",
+ "groupDialog": "分组",
+ "groupParent": "分组",
+ "groupNoGroup": "未分组",
+ "settingsTitle": "设置",
+ "settingsUser": "账户",
+ "settingsGroups": "分组",
+ "settingsServer": "服务器",
+ "settingsUsers": "用户",
+ "settingsDistanceUnit": "里程单位",
+ "settingsAltitudeUnit": "海拔单位",
+ "settingsSpeedUnit": "速度单位",
+ "settingsVolumeUnit": "体积单位",
+ "settingsTwelveHourFormat": "12 小时制",
+ "settingsCoordinateFormat": "坐标格式",
+ "settingsServerVersion": "服务器版本",
+ "settingsAppVersion": "应用版本",
+ "settingsConnection": "连接",
+ "settingsDarkMode": "深色模式",
+ "settingsTotpEnable": "启用一次性密码",
+ "settingsTotpForce": "强制使用一次性密码",
+ "settingsServiceWorkerUpdateInterval": "服务线程更新间隔",
+ "settingsUpdateAvailable": "有可用更新。",
+ "settingsSupport": "支持",
+ "reportTitle": "报表",
+ "reportScheduled": "预定报告",
+ "reportDevice": "设备",
+ "reportGroup": "分组",
+ "reportFrom": "起始",
+ "reportTo": "结束",
+ "reportShow": "查询",
+ "reportClear": "清空",
+ "linkGoogleMaps": "谷歌地图",
+ "linkAppleMaps": "苹果地图",
+ "linkStreetView": "街景",
+ "positionFixTime": "修正时间",
+ "positionDeviceTime": "设备时间",
+ "positionServerTime": "服务器时间",
+ "positionValid": "有效",
+ "positionAccuracy": "精度",
+ "positionLatitude": "纬度",
+ "positionLongitude": "经度",
+ "positionAltitude": "海拔",
+ "positionSpeed": "速度",
+ "positionCourse": "行驶方向",
+ "positionAddress": "地址",
+ "positionProtocol": "协议",
+ "positionDistance": "里程",
+ "positionRpm": "转速",
+ "positionFuel": "燃油",
+ "positionPower": "电源",
+ "positionBattery": "电池",
+ "positionRaw": "原生数据",
+ "positionIndex": "索引",
+ "positionHdop": "水平精度",
+ "positionVdop": "垂直精度",
+ "positionPdop": "位置精度",
+ "positionSat": "卫星",
+ "positionSatVisible": "可见卫星",
+ "positionRssi": "接收信号强度",
+ "positionGps": "全球定位系统",
+ "positionRoaming": "漫游",
+ "positionEvent": "事件",
+ "positionAlarm": "警报",
+ "positionStatus": "状态",
+ "positionOdometer": "里程表",
+ "positionServiceOdometer": "服务里程表",
+ "positionTripOdometer": "行程里程表",
+ "positionHours": "小时",
+ "positionSteps": "步骤",
+ "positionInput": "输入",
+ "positionHeartRate": "心率",
+ "positionOutput": "输出",
+ "positionBatteryLevel": "电池电量",
+ "positionFuelConsumption": "油耗",
+ "positionRfid": "射频识别",
+ "positionVersionFw": "固件版本",
+ "positionVersionHw": "硬件版本",
+ "positionIgnition": "点火",
+ "positionFlags": "标记",
+ "positionCharge": "充电",
+ "positionIp": "IP 地址",
+ "positionArchive": "档案",
+ "positionVin": "车辆识别码",
+ "positionApproximate": "近似值",
+ "positionThrottle": "油门",
+ "positionMotion": "移动",
+ "positionArmed": "Armed",
+ "positionAcceleration": "加速度",
+ "positionTemp": "温度",
+ "positionDeviceTemp": "设备温度",
+ "positionCoolantTemp": "冷却液温度",
+ "positionOperator": "操作员",
+ "positionCommand": "指令",
+ "positionBlocked": "屏蔽",
+ "positionDtcs": "双离合变速箱",
+ "positionObdSpeed": "OBD 速度",
+ "positionObdOdometer": "OBD 里程表",
+ "positionDrivingTime": "行驶时间",
+ "positionDriverUniqueId": "驾驶员独有 ID",
+ "positionCard": "卡片",
+ "positionImage": "图片",
+ "positionVideo": "视频",
+ "positionAudio": "音频",
+ "serverTitle": "服务器设置",
+ "serverZoom": "缩放",
+ "serverRegistration": "注册",
+ "serverReadonly": "只读",
+ "serverForceSettings": "强制设置",
+ "serverAnnouncement": "公告",
+ "serverName": "服务器名称",
+ "serverDescription": "服务器描述",
+ "serverColorPrimary": "首选颜色",
+ "serverColorSecondary": "次选颜色",
+ "serverLogo": "标识图片",
+ "serverLogoInverted": "倒置标识图片",
+ "serverChangeDisable": "禁用服务器更改",
+ "serverDisableShare": "禁用设备共享",
+ "mapTitle": "地图",
+ "mapActive": "已选地图",
+ "mapOverlay": "地图叠层",
+ "mapOverlayCustom": "自定义叠层",
+ "mapOpenSeaMap": "开放海洋地图",
+ "mapOpenRailwayMap": "开放铁路地图",
+ "mapOpenWeatherKey": "开放气候API密钥",
+ "mapOpenWeatherClouds": "开放气候云层图",
+ "mapOpenWeatherPrecipitation": "开放气候降水图",
+ "mapOpenWeatherPressure": "开放气候气压图",
+ "mapOpenWeatherWind": "开放气候风力图",
+ "mapOpenWeatherTemperature": "开放气候气温图",
+ "mapLayer": "地图图层",
+ "mapCustom": "自定义 (XYZ)",
+ "mapCustomArcgis": "自定义 (ArcGIS)",
+ "mapCustomLabel": "自定义地图",
+ "mapCarto": "Carto 基础地图",
+ "mapOsm": "开放街景地图",
+ "mapGoogleRoad": "谷歌道路地图",
+ "mapGoogleHybrid": "谷歌混合地图",
+ "mapGoogleSatellite": "谷歌卫星地图",
+ "mapOpenTopoMap": "OpenTopo 地图",
+ "mapBingKey": "必应地图密钥",
+ "mapBingRoad": "必应公路地图",
+ "mapBingAerial": "必应航测地图",
+ "mapBingHybrid": "必应混合地图",
+ "mapBaidu": "百度地图",
+ "mapAutoNavi": " 高德地图",
+ "mapYandexMap": "Yandex 地图",
+ "mapYandexSat": "Yandex 卫星地图",
+ "mapWikimedia": "维基媒体",
+ "mapOrdnanceSurvey": " 全国地形测量",
+ "mapMapboxStreets": "Mapbox 街道",
+ "mapMapboxStreetsDark": "Mapbox 深色街图",
+ "mapMapboxOutdoors": "Mapbox 外景",
+ "mapMapboxSatellite": "Mapbox 卫星地图",
+ "mapMapboxKey": "Mapbox 访问令牌",
+ "mapMapTilerBasic": "MapTiler 基础地图",
+ "mapMapTilerHybrid": "MapTiler 混合地图",
+ "mapMapTilerKey": "MapTiler API 密钥",
+ "mapLocationIqStreets": "LocationIQ 街道地图",
+ "mapLocationIqDark": "LocationIQ 深色地图",
+ "mapLocationIqKey": "LocationIQ 访问令牌",
+ "mapTomTomBasic": "TomTom 基础地图",
+ "mapTomTomFlow": "TomTom 交通路况",
+ "mapTomTomIncidents": "TomTom 交通事故",
+ "mapTomTomKey": "TomTom API 密钥",
+ "mapHereBasic": "Here 基础地图",
+ "mapHereHybrid": "Here 混合地图",
+ "mapHereSatellite": "Here 卫星地图",
+ "mapHereFlow": "Here 交通路况",
+ "mapHereKey": "Here API 密钥",
+ "mapShapePolygon": "多边形",
+ "mapShapeCircle": "圆形",
+ "mapShapePolyline": "多段线",
+ "mapLiveRoutes": "移动轨迹",
+ "mapDirection": "显示方向",
+ "mapCurrentLocation": "当前位置",
+ "mapPoiLayer": "兴趣点图层",
+ "mapClustering": "合并车辆标记",
+ "mapOnSelect": "显示地图选项",
+ "mapDefault": "默认地图",
+ "stateTitle": "状态",
+ "stateName": "参数",
+ "stateValue": "值",
+ "commandTitle": "指令",
+ "commandSend": "发送",
+ "commandSent": "指令已发送",
+ "commandQueued": "指令已排队",
+ "commandUnit": "单位",
+ "commandCustom": "自定义指令",
+ "commandDeviceIdentification": "设备标识",
+ "commandPositionSingle": "单个报告",
+ "commandPositionPeriodic": "定期报告",
+ "commandPositionStop": "停止报告",
+ "commandEngineStop": "引擎熄火",
+ "commandEngineResume": "引擎重启",
+ "commandAlarmArm": "部署警报",
+ "commandAlarmDisarm": "解除警报",
+ "commandAlarmDismiss": "消除警报",
+ "commandSetTimezone": "设置时区",
+ "commandRequestPhoto": "请求图片",
+ "commandPowerOff": "关闭设备",
+ "commandRebootDevice": "重启设备",
+ "commandFactoryReset": "恢复出厂设置",
+ "commandSendSms": "发送短信",
+ "commandSendUssd": "发送 USSD",
+ "commandSosNumber": "设置 SOS 号码",
+ "commandSilenceTime": "设置静音时间",
+ "commandSetPhonebook": "设置通讯录",
+ "commandVoiceMessage": "语音信息",
+ "commandOutputControl": "输出控制",
+ "commandVoiceMonitoring": "语音监控",
+ "commandSetAgps": "设置 AGPS",
+ "commandSetIndicator": "设置指示器",
+ "commandConfiguration": "配置",
+ "commandGetVersion": "获取版本",
+ "commandFirmwareUpdate": "更新固件",
+ "commandSetConnection": "设置连接",
+ "commandSetOdometer": "设置里程表",
+ "commandGetModemStatus": "获取调制解调器状态",
+ "commandGetDeviceStatus": "获取设备状态",
+ "commandSetSpeedLimit": "设置速度限制",
+ "commandModePowerSaving": "节能模式",
+ "commandModeDeepSleep": "睡眠模式",
+ "commandAlarmGeofence": "设置围栏警报",
+ "commandAlarmBattery": "设置电池警报",
+ "commandAlarmSos": "设置 SOS 警报",
+ "commandAlarmRemove": "设置删除警报",
+ "commandAlarmClock": "设置时钟警报",
+ "commandAlarmSpeed": "设置速度警报",
+ "commandAlarmFall": "设置跌落警报",
+ "commandAlarmVibration": "设置振动警报",
+ "commandFrequency": "频率",
+ "commandTimezone": "时区偏移",
+ "commandMessage": "消息",
+ "commandRadius": "半径",
+ "commandEnable": "开启",
+ "commandData": "日期",
+ "commandIndex": "索引",
+ "commandPhone": "电话号码",
+ "commandServer": "服务器",
+ "commandPort": "端口",
+ "eventAll": "所有事件",
+ "eventDeviceOnline": "在线状态",
+ "eventDeviceUnknown": "未知状态",
+ "eventDeviceOffline": "离线状态",
+ "eventDeviceInactive": "设备无响应",
+ "eventQueuedCommandSent": "已发送命令队列",
+ "eventDeviceMoving": "设备移动中",
+ "eventDeviceStopped": "设备已停止",
+ "eventDeviceOverspeed": "超速限制",
+ "eventDeviceFuelDrop": "燃油下降",
+ "eventDeviceFuelIncrease": "燃油增加",
+ "eventCommandResult": "指令结果",
+ "eventGeofenceEnter": "进入围栏",
+ "eventGeofenceExit": "离开围栏",
+ "eventAlarm": "警报",
+ "eventIgnitionOn": "点火",
+ "eventIgnitionOff": "熄火",
+ "eventMaintenance": "需要维护",
+ "eventTextMessage": "已接收文本信息",
+ "eventDriverChanged": "驾驶员变更",
+ "eventMedia": "媒体",
+ "eventsScrollToLast": "移至最后",
+ "eventsSoundEvents": "事件铃声",
+ "eventsSoundAlarms": "报警铃声",
+ "alarmGeneral": "通常",
+ "alarmSos": "求救",
+ "alarmVibration": "振动",
+ "alarmMovement": "移动",
+ "alarmLowspeed": "低速",
+ "alarmOverspeed": "超速",
+ "alarmFallDown": "跌落",
+ "alarmLowPower": "低功率",
+ "alarmLowBattery": "低电量",
+ "alarmFault": "故障",
+ "alarmPowerOff": "关闭电源",
+ "alarmPowerOn": "开启电源",
+ "alarmDoor": "门",
+ "alarmLock": "锁定",
+ "alarmUnlock": "解锁",
+ "alarmGeofence": "围栏",
+ "alarmGeofenceEnter": "进入围栏",
+ "alarmGeofenceExit": "离开围栏",
+ "alarmGpsAntennaCut": "GPS 天线断开",
+ "alarmAccident": "事故",
+ "alarmTow": "拖",
+ "alarmIdle": "空转",
+ "alarmHighRpm": "高转速",
+ "alarmHardAcceleration": "急加速",
+ "alarmHardBraking": "急刹车",
+ "alarmHardCornering": "急转弯",
+ "alarmLaneChange": "车道改变",
+ "alarmFatigueDriving": "疲劳驾驶",
+ "alarmPowerCut": "断电",
+ "alarmPowerRestored": "电源恢复",
+ "alarmJamming": "干扰",
+ "alarmTemperature": "温度",
+ "alarmParking": "停车中",
+ "alarmBonnet": "阀盖",
+ "alarmFootBrake": "脚踏制动器",
+ "alarmFuelLeak": "燃油泄漏",
+ "alarmTampering": "干预",
+ "alarmRemoving": "移除中",
+ "notificationType": "通知类型",
+ "notificationAlways": "所有设备",
+ "notificationNotificators": "通道",
+ "notificatorCommand": "命令",
+ "notificatorWeb": "网页",
+ "notificatorMail": "邮件",
+ "notificatorSms": "短信",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram 账号",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "轨迹回放",
+ "reportCombined": "组合",
+ "reportRoute": "轨迹",
+ "reportEvents": "事件",
+ "reportTrips": "行程",
+ "reportStops": "停留点",
+ "reportSummary": "总览",
+ "reportDaily": "每日汇报",
+ "reportChart": "图表",
+ "reportConfigure": "筛选",
+ "reportEventTypes": "事件类型",
+ "reportChartType": "图表类型",
+ "reportShowMarkers": "显示图标",
+ "reportExport": "导出",
+ "reportEmail": "邮件发送",
+ "reportSchedule": "工作计划",
+ "reportPeriod": "时段",
+ "reportCustom": "自定义",
+ "reportToday": "今天",
+ "reportYesterday": "昨天",
+ "reportThisWeek": "本周",
+ "reportPreviousWeek": "上周",
+ "reportThisMonth": "本月",
+ "reportPreviousMonth": "上月",
+ "reportDeviceName": "设备名称",
+ "reportAverageSpeed": "平均速度",
+ "reportMaximumSpeed": "最大速度",
+ "reportEngineHours": "发动机使用时间",
+ "reportDuration": "持续时间",
+ "reportStartDate": "开始日期",
+ "reportStartTime": "开始时间",
+ "reportStartAddress": "开始地址",
+ "reportEndTime": "结束时间",
+ "reportEndAddress": "结束地址",
+ "reportSpentFuel": "燃油消耗",
+ "reportStartOdometer": "起始里程",
+ "reportEndOdometer": "结束里程",
+ "statisticsTitle": "统计",
+ "statisticsCaptureTime": "获取时间",
+ "statisticsActiveUsers": "活动用户",
+ "statisticsActiveDevices": "活动设备",
+ "statisticsRequests": "请求",
+ "statisticsMessagesReceived": "已收到消息",
+ "statisticsMessagesStored": "已存储消息",
+ "statisticsGeocoder": "地理编码请求",
+ "statisticsGeolocation": "地理定位请求",
+ "categoryArrow": "箭头",
+ "categoryDefault": "默认",
+ "categoryAnimal": "动物",
+ "categoryBicycle": "单车",
+ "categoryBoat": "船只",
+ "categoryBus": "巴士",
+ "categoryCar": "汽车",
+ "categoryCamper": "露营车",
+ "categoryCrane": "起重机",
+ "categoryHelicopter": "直升机",
+ "categoryMotorcycle": "摩托车",
+ "categoryOffroad": "越野车",
+ "categoryPerson": "人员",
+ "categoryPickup": "皮卡",
+ "categoryPlane": "飞机",
+ "categoryShip": "游轮",
+ "categoryTractor": "拖拉机",
+ "categoryTrain": "火车",
+ "categoryTram": "有轨电车",
+ "categoryTrolleybus": "无轨电车",
+ "categoryTruck": "卡车",
+ "categoryVan": "厢式货车",
+ "categoryScooter": "小型摩托车",
+ "maintenanceStart": "开启",
+ "maintenancePeriod": "时段"
+} \ No newline at end of file
diff --git a/src/resources/l10n/zh_TW.json b/src/resources/l10n/zh_TW.json
new file mode 100644
index 00000000..10cb88a8
--- /dev/null
+++ b/src/resources/l10n/zh_TW.json
@@ -0,0 +1,600 @@
+{
+ "sharedLoading": "讀取資料中……",
+ "sharedHide": "隱藏",
+ "sharedSave": "存檔",
+ "sharedUpload": "上傳",
+ "sharedSet": "設定",
+ "sharedCancel": "取消",
+ "sharedCopy": "Copy",
+ "sharedAdd": "新增",
+ "sharedEdit": "編輯",
+ "sharedRemove": "移除",
+ "sharedRemoveConfirm": "確認要移除嗎?",
+ "sharedNoData": "沒有資料",
+ "sharedSubject": "Subject",
+ "sharedYes": "是",
+ "sharedNo": "否",
+ "sharedKm": "公里",
+ "sharedMi": "英里",
+ "sharedNmi": "海里",
+ "sharedMeters": "米",
+ "sharedFeet": "英尺",
+ "sharedKn": "節",
+ "sharedKmh": "公里/小時",
+ "sharedMph": "英里/小時",
+ "sharedHour": "小時",
+ "sharedMinute": "分鐘",
+ "sharedSecond": "秒",
+ "sharedDays": "天",
+ "sharedHours": "小時",
+ "sharedMinutes": "分鐘",
+ "sharedDecimalDegrees": "度",
+ "sharedDegreesDecimalMinutes": "分",
+ "sharedDegreesMinutesSeconds": "秒",
+ "sharedName": "名稱",
+ "sharedDescription": "描述",
+ "sharedSearch": "搜尋",
+ "sharedIconScale": "圖標比例",
+ "sharedGeofence": "地理圍籬",
+ "sharedGeofences": "地理圍籬",
+ "sharedCreateGeofence": "建立地理圍籬",
+ "sharedNotifications": "通知",
+ "sharedNotification": "通知",
+ "sharedAttributes": "屬性",
+ "sharedAttribute": "屬性",
+ "sharedDrivers": "駕駛",
+ "sharedDriver": "駕駛",
+ "sharedArea": "區域",
+ "sharedSound": "通知音效",
+ "sharedType": "類型",
+ "sharedDistance": "距離",
+ "sharedHourAbbreviation": "時",
+ "sharedMinuteAbbreviation": "分",
+ "sharedSecondAbbreviation": "秒",
+ "sharedVoltAbbreviation": "伏特",
+ "sharedLiterAbbreviation": "公升",
+ "sharedGallonAbbreviation": "加侖",
+ "sharedLiter": "升",
+ "sharedImpGallon": "英制加侖",
+ "sharedUsGallon": "美制加侖",
+ "sharedLiterPerHourAbbreviation": "公升/小時",
+ "sharedGetMapState": "取得地圖狀態",
+ "sharedComputedAttribute": "計算屬性",
+ "sharedComputedAttributes": "計算屬性",
+ "sharedCheckComputedAttribute": "檢查計算屬性",
+ "sharedExpression": "表示式",
+ "sharedDevice": "設備",
+ "sharedTest": "測試",
+ "sharedTestNotification": "傳送測試通知",
+ "sharedTestNotificators": "測試頻道",
+ "sharedTestExpression": "測試表示式",
+ "sharedCalendar": "日曆",
+ "sharedCalendars": "日曆",
+ "sharedFile": "檔案",
+ "sharedSearchDevices": "搜尋設備",
+ "sharedSortBy": "排序方式",
+ "sharedFilterMap": "在地圖上篩選",
+ "sharedSelectFile": "選擇檔案",
+ "sharedPhone": "手機",
+ "sharedRequired": "必需",
+ "sharedPreferences": "偏好",
+ "sharedPermissions": "權限",
+ "sharedConnections": "關聯",
+ "sharedExtra": "更多",
+ "sharedPrimary": "首要",
+ "sharedSecondary": "次要",
+ "sharedTypeString": "字串",
+ "sharedTypeNumber": "整數",
+ "sharedTypeBoolean": "布林值",
+ "sharedTimezone": "時區",
+ "sharedInfoTitle": "資訊",
+ "sharedSavedCommand": "儲存指令",
+ "sharedSavedCommands": "儲存指令",
+ "sharedNew": "新建",
+ "sharedShowAddress": "顯示地址",
+ "sharedShowDetails": "詳情請見",
+ "sharedDisabled": "關閉",
+ "sharedMaintenance": "保養",
+ "sharedDeviceAccumulators": "累加器",
+ "sharedAlarms": "警告",
+ "sharedLocation": "位置",
+ "sharedImport": "接入",
+ "sharedColumns": "列",
+ "sharedDropzoneText": "將文件拖放到此處或者點擊",
+ "sharedLogs": "Logs",
+ "sharedLink": "Link",
+ "calendarSimple": "簡單",
+ "calendarRecurrence": "復現",
+ "calendarOnce": "僅一次",
+ "calendarDaily": "每日",
+ "calendarWeekly": "每週",
+ "calendarMonthly": "每月",
+ "calendarDays": "天",
+ "calendarSunday": "周日",
+ "calendarMonday": "周一",
+ "calendarTuesday": "周二",
+ "calendarWednesday": "周三",
+ "calendarThursday": "周四",
+ "calendarFriday": "周五",
+ "calendarSaturday": "周六",
+ "attributeShowGeofences": "顯示地理圍籬",
+ "attributeSpeedLimit": "速限",
+ "attributeFuelDropThreshold": "燃油下降閾值",
+ "attributeFuelIncreaseThreshold": "燃料增加閾值",
+ "attributePolylineDistance": "折線距離",
+ "attributeReportIgnoreOdometer": "報告:忽略里程表",
+ "attributeWebReportColor": "網頁:報告顏色",
+ "attributeDevicePassword": "設備密碼",
+ "attributeDeviceImage": "設備圖片",
+ "attributeDeviceInactivityStart": "設備無回應起始",
+ "attributeDeviceInactivityPeriod": "設備無回應期間",
+ "attributeProcessingCopyAttributes": "處理中:複製屬性",
+ "attributeColor": "顏色",
+ "attributeWebLiveRouteLength": "網頁:即時軌跡長度",
+ "attributeWebSelectZoom": "網頁:選擇縮放等級",
+ "attributeWebMaxZoom": "網頁∶最大縮放",
+ "attributeTelegramChatId": "Telegram帳號",
+ "attributePushoverUserKey": "Pushover用戶密鑰",
+ "attributePushoverDeviceNames": "Pushover設備名稱",
+ "attributeMailSmtpHost": "電子郵件:SMTP 主機",
+ "attributeMailSmtpPort": "電子郵件:SMTP 埠口",
+ "attributeMailSmtpStarttlsEnable": "電子郵件:啟用 SMTP STARTTLS",
+ "attributeMailSmtpStarttlsRequired": "電子郵件:需要 SMTP STARTTLS",
+ "attributeMailSmtpSslEnable": "電子郵件:啟用 SMTP SSL",
+ "attributeMailSmtpSslTrust": "電子郵件:信任 SMTP SSL",
+ "attributeMailSmtpSslProtocols": "電子郵件:SMTP SSL 協定",
+ "attributeMailSmtpFrom": "電子郵件:SMTP 來源",
+ "attributeMailSmtpAuth": "電子郵件:啟用 SMTP 認證",
+ "attributeMailSmtpUsername": "電子郵件:SMTP 用戶名稱",
+ "attributeMailSmtpPassword": "電子郵件:SMTP 密碼",
+ "attributeUiDisableSavedCommands": "UI: Disable Saved Commands",
+ "attributeUiDisableAttributes": "用戶界面:禁用屬性",
+ "attributeUiDisableGroups": "用戶界面:禁用分組",
+ "attributeUiDisableEvents": "用戶界面:禁用事件",
+ "attributeUiDisableVehicleFeatures": "UI: Disable Vehicle Features",
+ "attributeUiDisableDrivers": "用戶界面:禁用駕駛員",
+ "attributeUiDisableComputedAttributes": "用戶界面:禁用計算屬性",
+ "attributeUiDisableCalendars": "用戶界面:禁用日曆",
+ "attributeUiDisableMaintenance": "用戶界面:禁用保養",
+ "attributeUiHidePositionAttributes": "用戶界面:隱藏位置屬性",
+ "attributeUiDisableLoginLanguage": "用戶界面:禁用登入語言",
+ "attributeNotificationTokens": "通知令牌",
+ "attributePopupInfo": "資訊卡",
+ "errorTitle": "錯誤",
+ "errorGeneral": "非法的參數或違反限制",
+ "errorConnection": "連線錯誤",
+ "errorSocket": "網頁接口連線錯誤",
+ "errorZero": "不能為零",
+ "userEmail": "電子郵件",
+ "userPassword": "密碼",
+ "userAdmin": "管理員",
+ "userRemember": "記住密碼",
+ "userExpirationTime": "有效期限",
+ "userDeviceLimit": "設備限制",
+ "userUserLimit": "用戶限制",
+ "userDeviceReadonly": "唯讀設備",
+ "userLimitCommands": "禁用指令",
+ "userDisableReports": "禁用報表",
+ "userFixedEmail": "禁止更改電子郵件",
+ "userToken": "簽證",
+ "userDeleteAccount": "刪除帳號",
+ "userTemporary": "Temporary",
+ "loginTitle": "登入",
+ "loginLanguage": "語言",
+ "loginReset": "密碼重設",
+ "loginRegister": "註冊",
+ "loginLogin": "登入",
+ "loginOpenId": "使用 OpenID 登入",
+ "loginFailed": "錯誤的電子信箱地址或密碼",
+ "loginCreated": "已成功註冊新用戶",
+ "loginResetSuccess": "郵件查詢",
+ "loginUpdateSuccess": "新密碼已重置",
+ "loginLogout": "登出",
+ "loginLogo": "標識",
+ "loginTotpCode": "One-time Password Code",
+ "loginTotpKey": "One-time Password Key",
+ "devicesAndState": "設備與狀態",
+ "deviceSelected": "已選定的設備",
+ "deviceTitle": "設備",
+ "devicePrimaryInfo": "設備標題",
+ "deviceSecondaryInfo": "設備詳情",
+ "deviceIdentifier": "識別號碼",
+ "deviceModel": "型號",
+ "deviceContact": "聯絡人",
+ "deviceCategory": "分類",
+ "deviceLastUpdate": "最近更新",
+ "deviceCommand": "命令",
+ "deviceFollow": "跟隨",
+ "deviceTotalDistance": "總距離",
+ "deviceStatus": "狀態",
+ "deviceStatusOnline": "上線",
+ "deviceStatusOffline": "下線",
+ "deviceStatusUnknown": "未知",
+ "deviceRegisterFirst": "註冊您的首個設備",
+ "deviceIdentifierHelp": "IMEI、序列號或其他 ID。必須與設備回報到伺服器的識別碼相同。",
+ "deviceShare": "Share Device",
+ "groupDialog": "群組",
+ "groupParent": "上層群組",
+ "groupNoGroup": "無所屬群組",
+ "settingsTitle": "設定",
+ "settingsUser": "用戶",
+ "settingsGroups": "群組",
+ "settingsServer": "伺服器",
+ "settingsUsers": "用戶",
+ "settingsDistanceUnit": "距離單位",
+ "settingsAltitudeUnit": "高度單位",
+ "settingsSpeedUnit": "速度單位",
+ "settingsVolumeUnit": "體積單位",
+ "settingsTwelveHourFormat": "十二小時制",
+ "settingsCoordinateFormat": "座標格式",
+ "settingsServerVersion": "服務器版本",
+ "settingsAppVersion": "應用版本",
+ "settingsConnection": "連接",
+ "settingsDarkMode": "夜間模式",
+ "settingsTotpEnable": "Enable One-time Password",
+ "settingsTotpForce": "Force One-time Password",
+ "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval",
+ "settingsUpdateAvailable": "There is an update available.",
+ "settingsSupport": "Support",
+ "reportTitle": "報告",
+ "reportScheduled": "已排程的報告",
+ "reportDevice": "設備",
+ "reportGroup": "群組",
+ "reportFrom": "開始",
+ "reportTo": "至",
+ "reportShow": "顯示",
+ "reportClear": "清空",
+ "linkGoogleMaps": "Google 地圖",
+ "linkAppleMaps": "蘋果地圖",
+ "linkStreetView": "街景",
+ "positionFixTime": "定位時間",
+ "positionDeviceTime": "設備時間",
+ "positionServerTime": "伺服器時間",
+ "positionValid": "有效",
+ "positionAccuracy": "精準度",
+ "positionLatitude": "緯度",
+ "positionLongitude": "經度",
+ "positionAltitude": "海拔",
+ "positionSpeed": "速率",
+ "positionCourse": "方向",
+ "positionAddress": "地址",
+ "positionProtocol": "協定",
+ "positionDistance": "距離",
+ "positionRpm": "轉速",
+ "positionFuel": "燃油",
+ "positionPower": "電源",
+ "positionBattery": "電池",
+ "positionRaw": "Raw",
+ "positionIndex": "索引",
+ "positionHdop": "HDOP",
+ "positionVdop": "VDOP",
+ "positionPdop": "PDOP",
+ "positionSat": "衛星",
+ "positionSatVisible": "可見衛星",
+ "positionRssi": "RSSI",
+ "positionGps": "GPS",
+ "positionRoaming": "漫遊",
+ "positionEvent": "事件",
+ "positionAlarm": "提醒",
+ "positionStatus": "狀態",
+ "positionOdometer": "里程表",
+ "positionServiceOdometer": "伺服器里程表",
+ "positionTripOdometer": "旅程里程表",
+ "positionHours": "小時",
+ "positionSteps": "步驟",
+ "positionInput": "輸入",
+ "positionHeartRate": "心跳",
+ "positionOutput": "輸出",
+ "positionBatteryLevel": "電池計量",
+ "positionFuelConsumption": "燃油消耗",
+ "positionRfid": "RFID",
+ "positionVersionFw": "韌體版本",
+ "positionVersionHw": "更體版本",
+ "positionIgnition": "點火",
+ "positionFlags": "旗標",
+ "positionCharge": "充電",
+ "positionIp": "IP",
+ "positionArchive": "車籍",
+ "positionVin": "車輛識別號碼",
+ "positionApproximate": "約",
+ "positionThrottle": "油門",
+ "positionMotion": "移動",
+ "positionArmed": "警報",
+ "positionAcceleration": "加速",
+ "positionTemp": "溫度",
+ "positionDeviceTemp": "設備溫度",
+ "positionCoolantTemp": "水冷液溫度",
+ "positionOperator": "司機",
+ "positionCommand": "命令",
+ "positionBlocked": "已封鎖",
+ "positionDtcs": "動態扭力控制",
+ "positionObdSpeed": "車上診斷系統速率",
+ "positionObdOdometer": "車上診斷系統里程表",
+ "positionDrivingTime": "行駛時間",
+ "positionDriverUniqueId": "駕駛唯一識別號碼",
+ "positionCard": "卡片",
+ "positionImage": "圖片",
+ "positionVideo": "影片",
+ "positionAudio": "音頻",
+ "serverTitle": "伺服器設定",
+ "serverZoom": "縮放",
+ "serverRegistration": "註冊",
+ "serverReadonly": "唯讀",
+ "serverForceSettings": "強制覆寫設定",
+ "serverAnnouncement": "公告",
+ "serverName": "伺服器名稱",
+ "serverDescription": "伺服器描述",
+ "serverColorPrimary": "主顏色",
+ "serverColorSecondary": "次顏色",
+ "serverLogo": "Logo 圖片",
+ "serverLogoInverted": "倒置 Logo 圖片",
+ "serverChangeDisable": "禁改伺服器",
+ "serverDisableShare": "Disable Device Sharing",
+ "mapTitle": "地圖",
+ "mapActive": "啟用地圖",
+ "mapOverlay": "地圖疊層",
+ "mapOverlayCustom": "自定義疊層",
+ "mapOpenSeaMap": "OpenSea地圖",
+ "mapOpenRailwayMap": "鐵路地圖",
+ "mapOpenWeatherKey": "OpenWeather API密鑰",
+ "mapOpenWeatherClouds": "OpenWeather雲層圖",
+ "mapOpenWeatherPrecipitation": "OpenWeather降水圖",
+ "mapOpenWeatherPressure": "OpenWeather氣壓圖",
+ "mapOpenWeatherWind": "OpenWeather 風力圖",
+ "mapOpenWeatherTemperature": "OpenWeather氣溫圖",
+ "mapLayer": "地圖圖層",
+ "mapCustom": "自定義(XYZ)",
+ "mapCustomArcgis": "自定義(ArcGIS)",
+ "mapCustomLabel": "自定義地圖",
+ "mapCarto": "CARTO 地圖",
+ "mapOsm": "OpenStreet地圖",
+ "mapGoogleRoad": "Google 道路地圖",
+ "mapGoogleHybrid": "Google 混合地圖",
+ "mapGoogleSatellite": "Google 衛星地圖",
+ "mapOpenTopoMap": "OpenTopo地圖",
+ "mapBingKey": "Bing 地圖金鑰",
+ "mapBingRoad": "Bing 道路地圖",
+ "mapBingAerial": "Bing 空照圖地圖",
+ "mapBingHybrid": "Bing 溫合地圖",
+ "mapBaidu": "百度",
+ "mapAutoNavi": "高德地圖",
+ "mapYandexMap": "Yandex 地圖",
+ "mapYandexSat": "Yandex 衛星",
+ "mapWikimedia": "維基百科",
+ "mapOrdnanceSurvey": "英國地形測量局",
+ "mapMapboxStreets": "Mapbox 街道",
+ "mapMapboxStreetsDark": "Mapbox Streets Dark",
+ "mapMapboxOutdoors": "Mapbox 外景",
+ "mapMapboxSatellite": "Mapbox 衛星地圖",
+ "mapMapboxKey": "Maxbox 訪問令牌",
+ "mapMapTilerBasic": "MapTiler基礎地圖",
+ "mapMapTilerHybrid": "MapTiler混合地圖",
+ "mapMapTilerKey": "MapTiler API密鑰",
+ "mapLocationIqStreets": "LocationIQ街道地圖",
+ "mapLocationIqDark": "LocationIQ 黑主題",
+ "mapLocationIqKey": "LocationIQ訪問指令牌",
+ "mapTomTomBasic": "TomTom基礎地圖",
+ "mapTomTomFlow": "TomTom交通路況",
+ "mapTomTomIncidents": "TomTom交通事故",
+ "mapTomTomKey": "TomTom API密鑰",
+ "mapHereBasic": "Here基礎地圖",
+ "mapHereHybrid": "Here混合地圖",
+ "mapHereSatellite": "Here衛星地圖",
+ "mapHereFlow": "Here交通路况",
+ "mapHereKey": "Here API密鑰",
+ "mapShapePolygon": "多邊形",
+ "mapShapeCircle": "圓形",
+ "mapShapePolyline": "多邊形",
+ "mapLiveRoutes": "即時軌跡",
+ "mapDirection": "顯示方向",
+ "mapCurrentLocation": "當前位置",
+ "mapPoiLayer": "POI 圖層",
+ "mapClustering": "標記叢集",
+ "mapOnSelect": "選擇設備時顯示地圖",
+ "mapDefault": "預設地圖",
+ "stateTitle": "狀態",
+ "stateName": "名稱",
+ "stateValue": "數值",
+ "commandTitle": "命令",
+ "commandSend": "傳送",
+ "commandSent": "指令已發送",
+ "commandQueued": "指令已排隊",
+ "commandUnit": "單位",
+ "commandCustom": "自訂命令",
+ "commandDeviceIdentification": "設備識別號碼",
+ "commandPositionSingle": "個別報告",
+ "commandPositionPeriodic": "定期報告",
+ "commandPositionStop": "停止報告",
+ "commandEngineStop": "停止引擎",
+ "commandEngineResume": "恢復引擎",
+ "commandAlarmArm": "啟動警報",
+ "commandAlarmDisarm": "解除警報",
+ "commandAlarmDismiss": "解除警報",
+ "commandSetTimezone": "設定時區",
+ "commandRequestPhoto": "請求照片",
+ "commandPowerOff": "設備關機",
+ "commandRebootDevice": "重啟設備",
+ "commandFactoryReset": "回復原廠設定",
+ "commandSendSms": "傳送簡訊",
+ "commandSendUssd": "傳送 USSD 簡訊",
+ "commandSosNumber": "設定救援號碼",
+ "commandSilenceTime": "設定靜音時段",
+ "commandSetPhonebook": "設定電話簿",
+ "commandVoiceMessage": "語音訊息",
+ "commandOutputControl": "輸出控制",
+ "commandVoiceMonitoring": "語音監控",
+ "commandSetAgps": "設定 AGPS",
+ "commandSetIndicator": "設定指標",
+ "commandConfiguration": "設置",
+ "commandGetVersion": "獲取版本",
+ "commandFirmwareUpdate": "更新韌體",
+ "commandSetConnection": "設置連接",
+ "commandSetOdometer": "設置里程表",
+ "commandGetModemStatus": "獲取數據機狀態",
+ "commandGetDeviceStatus": "獲取設備狀態",
+ "commandSetSpeedLimit": "設置速度限制",
+ "commandModePowerSaving": "省電模式",
+ "commandModeDeepSleep": "睡眠模式",
+ "commandAlarmGeofence": "設定地理圍籬警報",
+ "commandAlarmBattery": "設置電池警報",
+ "commandAlarmSos": "設置SOS警報",
+ "commandAlarmRemove": "設置刪除警報",
+ "commandAlarmClock": "設置時鐘警報",
+ "commandAlarmSpeed": "設置速度警報",
+ "commandAlarmFall": "設置掉落警報",
+ "commandAlarmVibration": "設置震動警報",
+ "commandFrequency": "頻率",
+ "commandTimezone": "時區",
+ "commandMessage": "訊息",
+ "commandRadius": "半徑",
+ "commandEnable": "啟用",
+ "commandData": "資料",
+ "commandIndex": "索引",
+ "commandPhone": "電話號碼",
+ "commandServer": "服務器",
+ "commandPort": "端口",
+ "eventAll": "所有事件",
+ "eventDeviceOnline": "已上線",
+ "eventDeviceUnknown": "未知狀態",
+ "eventDeviceOffline": "已下線",
+ "eventDeviceInactive": "設備無回應",
+ "eventQueuedCommandSent": "已發送排列命令",
+ "eventDeviceMoving": "設備移動中",
+ "eventDeviceStopped": "設備已停止",
+ "eventDeviceOverspeed": "超速限制",
+ "eventDeviceFuelDrop": "燃油下降",
+ "eventDeviceFuelIncrease": "燃油增加",
+ "eventCommandResult": "命令結果",
+ "eventGeofenceEnter": "已進入地理圍籬",
+ "eventGeofenceExit": "已離開地理圍籬",
+ "eventAlarm": "警報",
+ "eventIgnitionOn": "點火",
+ "eventIgnitionOff": "熄火",
+ "eventMaintenance": "需要保養",
+ "eventTextMessage": "已接收文字訊息",
+ "eventDriverChanged": "駕駛員變更",
+ "eventMedia": "多媒體",
+ "eventsScrollToLast": "卷軸已拉到底",
+ "eventsSoundEvents": "事件鈴聲",
+ "eventsSoundAlarms": "警報鈴聲",
+ "alarmGeneral": "一般",
+ "alarmSos": "求救",
+ "alarmVibration": "震動",
+ "alarmMovement": "移動",
+ "alarmLowspeed": "低速",
+ "alarmOverspeed": "超速",
+ "alarmFallDown": "跌落",
+ "alarmLowPower": "低電量",
+ "alarmLowBattery": "電池低電量",
+ "alarmFault": "故障",
+ "alarmPowerOff": "關機",
+ "alarmPowerOn": "開機",
+ "alarmDoor": "車門",
+ "alarmLock": "鎖定",
+ "alarmUnlock": "解鎖",
+ "alarmGeofence": "地理圍籬",
+ "alarmGeofenceEnter": "進入地理圍籬",
+ "alarmGeofenceExit": "離開地理圍籬",
+ "alarmGpsAntennaCut": "GPS天線斷開",
+ "alarmAccident": "事故",
+ "alarmTow": "牽引",
+ "alarmIdle": "怠速",
+ "alarmHighRpm": "高轉速",
+ "alarmHardAcceleration": "急加速",
+ "alarmHardBraking": "急剎車",
+ "alarmHardCornering": "急轉彎",
+ "alarmLaneChange": "改變車道",
+ "alarmFatigueDriving": "疲勞駕駛",
+ "alarmPowerCut": "斷電",
+ "alarmPowerRestored": "恢復電源",
+ "alarmJamming": "干擾",
+ "alarmTemperature": "溫度",
+ "alarmParking": "停車處",
+ "alarmBonnet": "引擎蓋",
+ "alarmFootBrake": "腳剎",
+ "alarmFuelLeak": "漏油",
+ "alarmTampering": "篡改",
+ "alarmRemoving": "移除中",
+ "notificationType": "通知類型",
+ "notificationAlways": "所有設備",
+ "notificationNotificators": "通道",
+ "notificatorCommand": "指令",
+ "notificatorWeb": "網頁",
+ "notificatorMail": "電子郵件",
+ "notificatorSms": "簡訊",
+ "notificatorFirebase": "Firebase",
+ "notificatorTraccar": "Traccar",
+ "notificatorTelegram": "Telegram",
+ "notificatorPushover": "Pushover",
+ "reportReplay": "軌跡回放",
+ "reportCombined": "合併",
+ "reportRoute": "路線",
+ "reportEvents": "事件",
+ "reportTrips": "旅程",
+ "reportStops": "站點",
+ "reportSummary": "摘要",
+ "reportDaily": "每日匯報",
+ "reportChart": "圖表",
+ "reportConfigure": "設置",
+ "reportEventTypes": "事件類型",
+ "reportChartType": "圖表類型",
+ "reportShowMarkers": "顯示地標",
+ "reportExport": "匯出",
+ "reportEmail": "郵件發送",
+ "reportSchedule": "排程",
+ "reportPeriod": "期間",
+ "reportCustom": "自定義",
+ "reportToday": "今天",
+ "reportYesterday": "昨天",
+ "reportThisWeek": "本週",
+ "reportPreviousWeek": "上週",
+ "reportThisMonth": "本月",
+ "reportPreviousMonth": "上月",
+ "reportDeviceName": "設備名稱",
+ "reportAverageSpeed": "平均速率",
+ "reportMaximumSpeed": "最高速率",
+ "reportEngineHours": "引擎時數",
+ "reportDuration": "持續時間",
+ "reportStartDate": "起始日期",
+ "reportStartTime": "起始時間",
+ "reportStartAddress": "起始地點",
+ "reportEndTime": "結束時間",
+ "reportEndAddress": "結束地點",
+ "reportSpentFuel": "燃油消耗",
+ "reportStartOdometer": "起始里程",
+ "reportEndOdometer": "結束里程",
+ "statisticsTitle": "統計",
+ "statisticsCaptureTime": "截取時間",
+ "statisticsActiveUsers": "活躍用戶量",
+ "statisticsActiveDevices": "活躍設備量",
+ "statisticsRequests": "請求數",
+ "statisticsMessagesReceived": "已接收訊息量",
+ "statisticsMessagesStored": "已儲存訊息量",
+ "statisticsGeocoder": "地理碼請求數",
+ "statisticsGeolocation": "地理座標請求數",
+ "categoryArrow": "箭頭",
+ "categoryDefault": "預設",
+ "categoryAnimal": "動物",
+ "categoryBicycle": "自行車",
+ "categoryBoat": "船舶",
+ "categoryBus": "公車",
+ "categoryCar": "小客車",
+ "categoryCamper": "Camper",
+ "categoryCrane": "起重機",
+ "categoryHelicopter": "直昇機",
+ "categoryMotorcycle": "機車",
+ "categoryOffroad": "越野車",
+ "categoryPerson": "行人",
+ "categoryPickup": "皮卡車",
+ "categoryPlane": "飛機",
+ "categoryShip": "船艦",
+ "categoryTractor": "曳引機",
+ "categoryTrain": "火車",
+ "categoryTram": "有軌電車",
+ "categoryTrolleybus": "無軌電車",
+ "categoryTruck": "貨車",
+ "categoryVan": "箱型車",
+ "categoryScooter": "小型摩拖車",
+ "maintenanceStart": "起始值",
+ "maintenancePeriod": "期間"
+} \ No newline at end of file
diff --git a/src/settings/AccumulatorsPage.jsx b/src/settings/AccumulatorsPage.jsx
new file mode 100644
index 00000000..1c9b6e65
--- /dev/null
+++ b/src/settings/AccumulatorsPage.jsx
@@ -0,0 +1,107 @@
+import React, { useEffect, useState } from 'react';
+import { useSelector } from 'react-redux';
+import { useNavigate, useParams } from 'react-router-dom';
+import {
+ Accordion,
+ AccordionSummary,
+ AccordionDetails,
+ Typography,
+ Container,
+ TextField,
+ Button,
+} from '@mui/material';
+import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import PageLayout from '../common/components/PageLayout';
+import SettingsMenu from './components/SettingsMenu';
+import { useCatch } from '../reactHelper';
+import { useAttributePreference } from '../common/util/preferences';
+import { distanceFromMeters, distanceToMeters, distanceUnitString } from '../common/util/converter';
+import useSettingsStyles from './common/useSettingsStyles';
+
+const AccumulatorsPage = () => {
+ const navigate = useNavigate();
+ const classes = useSettingsStyles();
+ const t = useTranslation();
+
+ const distanceUnit = useAttributePreference('distanceUnit');
+
+ const { deviceId } = useParams();
+ const position = useSelector((state) => state.session.positions[deviceId]);
+
+ const [item, setItem] = useState();
+
+ useEffect(() => {
+ if (position) {
+ setItem({
+ deviceId: parseInt(deviceId, 10),
+ hours: position.attributes.hours || 0,
+ totalDistance: position.attributes.totalDistance || 0,
+ });
+ }
+ }, [deviceId, position]);
+
+ const handleSave = useCatch(async () => {
+ const response = await fetch(`/api/devices/${deviceId}/accumulators`, {
+ method: 'PUT',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(item),
+ });
+
+ if (response.ok) {
+ navigate(-1);
+ } else {
+ throw Error(await response.text());
+ }
+ });
+
+ return (
+ <PageLayout menu={<SettingsMenu />} breadcrumbs={['sharedDeviceAccumulators']}>
+ {item && (
+ <Container maxWidth="xs" className={classes.container}>
+ <Accordion defaultExpanded>
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography variant="subtitle1">
+ {t('sharedRequired')}
+ </Typography>
+ </AccordionSummary>
+ <AccordionDetails className={classes.details}>
+ <TextField
+ type="number"
+ value={item.hours / 3600000}
+ onChange={(event) => setItem({ ...item, hours: Number(event.target.value) * 3600000 })}
+ label={t('positionHours')}
+ />
+ <TextField
+ type="number"
+ value={distanceFromMeters(item.totalDistance, distanceUnit)}
+ onChange={(event) => setItem({ ...item, totalDistance: distanceToMeters(Number(event.target.value), distanceUnit) })}
+ label={`${t('deviceTotalDistance')} (${distanceUnitString(distanceUnit, t)})`}
+ />
+ </AccordionDetails>
+ </Accordion>
+ <div className={classes.buttons}>
+ <Button
+ type="button"
+ color="primary"
+ variant="outlined"
+ onClick={() => navigate(-1)}
+ >
+ {t('sharedCancel')}
+ </Button>
+ <Button
+ type="button"
+ color="primary"
+ variant="contained"
+ onClick={handleSave}
+ >
+ {t('sharedSave')}
+ </Button>
+ </div>
+ </Container>
+ )}
+ </PageLayout>
+ );
+};
+
+export default AccumulatorsPage;
diff --git a/src/settings/AnnouncementPage.jsx b/src/settings/AnnouncementPage.jsx
new file mode 100644
index 00000000..39488f02
--- /dev/null
+++ b/src/settings/AnnouncementPage.jsx
@@ -0,0 +1,106 @@
+import React, { useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import {
+ Accordion,
+ AccordionSummary,
+ AccordionDetails,
+ Typography,
+ Container,
+ TextField,
+ Button,
+} from '@mui/material';
+import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import PageLayout from '../common/components/PageLayout';
+import SettingsMenu from './components/SettingsMenu';
+import { useCatchCallback } from '../reactHelper';
+import useSettingsStyles from './common/useSettingsStyles';
+import SelectField from '../common/components/SelectField';
+import { prefixString } from '../common/util/stringUtils';
+
+const AnnouncementPage = () => {
+ const navigate = useNavigate();
+ const classes = useSettingsStyles();
+ const t = useTranslation();
+
+ const [users, setUsers] = useState([]);
+ const [notificator, setNotificator] = useState();
+ const [message, setMessage] = useState({});
+
+ const handleSend = useCatchCallback(async () => {
+ const query = new URLSearchParams();
+ users.forEach((userId) => query.append('userId', userId));
+ const response = await fetch(`/api/notifications/send/${notificator}?${query.toString()}`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(message),
+ });
+ if (response.ok) {
+ navigate(-1);
+ } else {
+ throw Error(await response.text());
+ }
+ }, [users, notificator, message, navigate]);
+
+ return (
+ <PageLayout menu={<SettingsMenu />} breadcrumbs={['serverAnnouncement']}>
+ <Container maxWidth="xs" className={classes.container}>
+ <Accordion defaultExpanded>
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography variant="subtitle1">
+ {t('sharedRequired')}
+ </Typography>
+ </AccordionSummary>
+ <AccordionDetails className={classes.details}>
+ <SelectField
+ multiple
+ value={users}
+ onChange={(e) => setUsers(e.target.value)}
+ endpoint="/api/users"
+ label={t('settingsUsers')}
+ />
+ <SelectField
+ value={notificator}
+ onChange={(e) => setNotificator(e.target.value)}
+ endpoint="/api/notifications/notificators"
+ keyGetter={(it) => it.type}
+ titleGetter={(it) => t(prefixString('notificator', it.type))}
+ label={t('notificationNotificators')}
+ />
+ <TextField
+ value={message.subject}
+ onChange={(e) => setMessage({ ...message, subject: e.target.value })}
+ label={t('sharedSubject')}
+ />
+ <TextField
+ value={message.body}
+ onChange={(e) => setMessage({ ...message, body: e.target.value })}
+ label={t('commandMessage')}
+ />
+ </AccordionDetails>
+ </Accordion>
+ <div className={classes.buttons}>
+ <Button
+ type="button"
+ color="primary"
+ variant="outlined"
+ onClick={() => navigate(-1)}
+ >
+ {t('sharedCancel')}
+ </Button>
+ <Button
+ type="button"
+ color="primary"
+ variant="contained"
+ onClick={handleSend}
+ disabled={!notificator || !message.subject || !message.body}
+ >
+ {t('commandSend')}
+ </Button>
+ </div>
+ </Container>
+ </PageLayout>
+ );
+};
+
+export default AnnouncementPage;
diff --git a/src/settings/CalendarPage.jsx b/src/settings/CalendarPage.jsx
new file mode 100644
index 00000000..8a3dc986
--- /dev/null
+++ b/src/settings/CalendarPage.jsx
@@ -0,0 +1,208 @@
+import dayjs from 'dayjs';
+import React, { useState } from 'react';
+import { useDispatch } from 'react-redux';
+import TextField from '@mui/material/TextField';
+import {
+ Accordion, AccordionSummary, AccordionDetails, Typography, FormControl, InputLabel, Select, MenuItem,
+} from '@mui/material';
+import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
+import { DropzoneArea } from 'react-mui-dropzone';
+import EditItemView from './components/EditItemView';
+import EditAttributesAccordion from './components/EditAttributesAccordion';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import SettingsMenu from './components/SettingsMenu';
+import { prefixString } from '../common/util/stringUtils';
+import { calendarsActions } from '../store';
+import { useCatch } from '../reactHelper';
+import useSettingsStyles from './common/useSettingsStyles';
+
+const formatCalendarTime = (time) => {
+ const tzid = Intl.DateTimeFormat().resolvedOptions().timeZone;
+ return `TZID=${tzid}:${time.locale('en').format('YYYYMMDDTHHmmss')}`;
+};
+
+const parseRule = (rule) => {
+ if (rule.endsWith('COUNT=1')) {
+ return { frequency: 'ONCE' };
+ }
+ const fragments = rule.split(';');
+ const frequency = fragments[0].substring(11);
+ const by = fragments.length > 1 ? fragments[1].split('=')[1].split(',') : null;
+ return { frequency, by };
+};
+
+const formatRule = (rule) => {
+ const by = rule.by && rule.by.join(',');
+ switch (rule.frequency) {
+ case 'DAILY':
+ return `RRULE:FREQ=${rule.frequency}`;
+ case 'WEEKLY':
+ return `RRULE:FREQ=${rule.frequency};BYDAY=${by || 'SU'}`;
+ case 'MONTHLY':
+ return `RRULE:FREQ=${rule.frequency};BYMONTHDAY=${by || 1}`;
+ default:
+ return 'RRULE:FREQ=DAILY;COUNT=1';
+ }
+};
+
+const updateCalendar = (lines, index, element) => window.btoa(lines.map((e, i) => (i !== index ? e : element)).join('\n'));
+
+const simpleCalendar = () => window.btoa([
+ 'BEGIN:VCALENDAR',
+ 'VERSION:2.0',
+ 'PRODID:-//Traccar//NONSGML Traccar//EN',
+ 'BEGIN:VEVENT',
+ 'UID:00000000-0000-0000-0000-000000000000',
+ `DTSTART;${formatCalendarTime(dayjs())}`,
+ `DTEND;${formatCalendarTime(dayjs().add(1, 'hours'))}`,
+ 'RRULE:FREQ=DAILY',
+ 'SUMMARY:Event',
+ 'END:VEVENT',
+ 'END:VCALENDAR',
+].join('\n'));
+
+const CalendarPage = () => {
+ const classes = useSettingsStyles();
+ const dispatch = useDispatch();
+ const t = useTranslation();
+
+ const [item, setItem] = useState();
+
+ const decoded = item && item.data && window.atob(item.data);
+
+ const simple = decoded && decoded.indexOf('//Traccar//') > 0;
+
+ const lines = decoded && decoded.split('\n');
+
+ const rule = simple && parseRule(lines[7]);
+
+ const handleFiles = (files) => {
+ if (files.length > 0) {
+ const reader = new FileReader();
+ reader.onload = (event) => {
+ const { result } = event.target;
+ setItem({ ...item, data: result.substr(result.indexOf(',') + 1) });
+ };
+ reader.readAsDataURL(files[0]);
+ }
+ };
+
+ const onItemSaved = useCatch(async () => {
+ const response = await fetch('/api/calendars');
+ if (response.ok) {
+ dispatch(calendarsActions.refresh(await response.json()));
+ } else {
+ throw Error(await response.text());
+ }
+ });
+
+ const validate = () => item && item.name && item.data;
+
+ return (
+ <EditItemView
+ endpoint="calendars"
+ item={item}
+ setItem={setItem}
+ defaultItem={{ data: simpleCalendar() }}
+ validate={validate}
+ onItemSaved={onItemSaved}
+ menu={<SettingsMenu />}
+ breadcrumbs={['settingsTitle', 'sharedCalendar']}
+ >
+ {item && (
+ <>
+ <Accordion defaultExpanded>
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography variant="subtitle1">
+ {t('sharedRequired')}
+ </Typography>
+ </AccordionSummary>
+ <AccordionDetails className={classes.details}>
+ <TextField
+ value={item.name || ''}
+ onChange={(event) => setItem({ ...item, name: event.target.value })}
+ label={t('sharedName')}
+ />
+ <FormControl>
+ <InputLabel>{t('sharedType')}</InputLabel>
+ <Select
+ label={t('sharedType')}
+ value={simple ? 'simple' : 'custom'}
+ onChange={(e) => setItem({ ...item, data: (e.target.value === 'simple' ? simpleCalendar() : null) })}
+ >
+ <MenuItem value="simple">{t('calendarSimple')}</MenuItem>
+ <MenuItem value="custom">{t('reportCustom')}</MenuItem>
+ </Select>
+ </FormControl>
+ {simple ? (
+ <>
+ <TextField
+ label={t('reportFrom')}
+ type="datetime-local"
+ value={dayjs(lines[5].slice(-15)).locale('en').format('YYYY-MM-DDTHH:mm')}
+ onChange={(e) => {
+ const time = formatCalendarTime(dayjs(e.target.value, 'YYYY-MM-DDTHH:mm'));
+ setItem({ ...item, data: updateCalendar(lines, 5, `DTSTART;${time}`) });
+ }}
+ />
+ <TextField
+ label={t('reportTo')}
+ type="datetime-local"
+ value={dayjs(lines[6].slice(-15)).locale('en').format('YYYY-MM-DDTHH:mm')}
+ onChange={(e) => {
+ const time = formatCalendarTime(dayjs(e.target.value, 'YYYY-MM-DDTHH:mm'));
+ setItem({ ...item, data: updateCalendar(lines, 6, `DTEND;${time}`) });
+ }}
+ />
+ <FormControl>
+ <InputLabel>{t('calendarRecurrence')}</InputLabel>
+ <Select
+ label={t('calendarRecurrence')}
+ value={rule.frequency}
+ onChange={(e) => setItem({ ...item, data: updateCalendar(lines, 7, formatRule({ frequency: e.target.value })) })}
+ >
+ {['ONCE', 'DAILY', 'WEEKLY', 'MONTHLY'].map((it) => (
+ <MenuItem key={it} value={it}>{t(prefixString('calendar', it.toLowerCase()))}</MenuItem>
+ ))}
+ </Select>
+ </FormControl>
+ {['WEEKLY', 'MONTHLY'].includes(rule.frequency) && (
+ <FormControl>
+ <InputLabel>{t('calendarDays')}</InputLabel>
+ <Select
+ multiple
+ label={t('calendarDays')}
+ value={rule.by}
+ onChange={(e) => setItem({ ...item, data: updateCalendar(lines, 7, formatRule({ ...rule, by: e.target.value })) })}
+ >
+ {rule.frequency === 'WEEKLY' ? ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'].map((it) => (
+ <MenuItem key={it} value={it.substring(0, 2).toUpperCase()}>{t(prefixString('calendar', it))}</MenuItem>
+ )) : Array.from({ length: 31 }, (_, i) => i + 1).map((it) => (
+ <MenuItem key={it} value={it}>{it}</MenuItem>
+ ))}
+ </Select>
+ </FormControl>
+ )}
+ </>
+ ) : (
+ <DropzoneArea
+ dropzoneText={t('sharedDropzoneText')}
+ filesLimit={1}
+ onChange={handleFiles}
+ showAlerts={false}
+ />
+ )}
+ </AccordionDetails>
+ </Accordion>
+ <EditAttributesAccordion
+ attributes={item.attributes}
+ setAttributes={(attributes) => setItem({ ...item, attributes })}
+ definitions={{}}
+ />
+ </>
+ )}
+ </EditItemView>
+ );
+};
+
+export default CalendarPage;
diff --git a/src/settings/CalendarsPage.jsx b/src/settings/CalendarsPage.jsx
new file mode 100644
index 00000000..de27a451
--- /dev/null
+++ b/src/settings/CalendarsPage.jsx
@@ -0,0 +1,64 @@
+import React, { useState } from 'react';
+import {
+ Table, TableRow, TableCell, TableHead, TableBody,
+} from '@mui/material';
+import { useEffectAsync } from '../reactHelper';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import PageLayout from '../common/components/PageLayout';
+import SettingsMenu from './components/SettingsMenu';
+import CollectionFab from './components/CollectionFab';
+import CollectionActions from './components/CollectionActions';
+import TableShimmer from '../common/components/TableShimmer';
+import SearchHeader, { filterByKeyword } from './components/SearchHeader';
+import useSettingsStyles from './common/useSettingsStyles';
+
+const CalendarsPage = () => {
+ const classes = useSettingsStyles();
+ const t = useTranslation();
+
+ const [timestamp, setTimestamp] = useState(Date.now());
+ const [items, setItems] = useState([]);
+ const [searchKeyword, setSearchKeyword] = useState('');
+ const [loading, setLoading] = useState(false);
+
+ useEffectAsync(async () => {
+ setLoading(true);
+ try {
+ const response = await fetch('/api/calendars');
+ if (response.ok) {
+ setItems(await response.json());
+ } else {
+ throw Error(await response.text());
+ }
+ } finally {
+ setLoading(false);
+ }
+ }, [timestamp]);
+
+ return (
+ <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'sharedCalendars']}>
+ <SearchHeader keyword={searchKeyword} setKeyword={setSearchKeyword} />
+ <Table className={classes.table}>
+ <TableHead>
+ <TableRow>
+ <TableCell>{t('sharedName')}</TableCell>
+ <TableCell className={classes.columnAction} />
+ </TableRow>
+ </TableHead>
+ <TableBody>
+ {!loading ? items.filter(filterByKeyword(searchKeyword)).map((item) => (
+ <TableRow key={item.id}>
+ <TableCell>{item.name}</TableCell>
+ <TableCell className={classes.columnAction} padding="none">
+ <CollectionActions itemId={item.id} editPath="/settings/calendar" endpoint="calendars" setTimestamp={setTimestamp} />
+ </TableCell>
+ </TableRow>
+ )) : (<TableShimmer columns={2} endAction />)}
+ </TableBody>
+ </Table>
+ <CollectionFab editPath="/settings/calendar" />
+ </PageLayout>
+ );
+};
+
+export default CalendarsPage;
diff --git a/src/settings/CommandDevicePage.jsx b/src/settings/CommandDevicePage.jsx
new file mode 100644
index 00000000..b3144cd0
--- /dev/null
+++ b/src/settings/CommandDevicePage.jsx
@@ -0,0 +1,111 @@
+import React, { useState } from 'react';
+import { useNavigate, useParams } from 'react-router-dom';
+import {
+ Accordion,
+ AccordionSummary,
+ AccordionDetails,
+ Typography,
+ Container,
+ Button,
+} from '@mui/material';
+import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import BaseCommandView from './components/BaseCommandView';
+import SelectField from '../common/components/SelectField';
+import PageLayout from '../common/components/PageLayout';
+import SettingsMenu from './components/SettingsMenu';
+import { useCatch } from '../reactHelper';
+import { useRestriction } from '../common/util/permissions';
+import useSettingsStyles from './common/useSettingsStyles';
+
+const CommandDevicePage = () => {
+ const navigate = useNavigate();
+ const classes = useSettingsStyles();
+ const t = useTranslation();
+
+ const { id } = useParams();
+
+ const [savedId, setSavedId] = useState(0);
+ const [item, setItem] = useState({});
+
+ const limitCommands = useRestriction('limitCommands');
+
+ const handleSend = useCatch(async () => {
+ let command;
+ if (savedId) {
+ const response = await fetch(`/api/commands/${savedId}`);
+ if (response.ok) {
+ command = await response.json();
+ } else {
+ throw Error(await response.text());
+ }
+ } else {
+ command = item;
+ }
+
+ command.deviceId = parseInt(id, 10);
+
+ const response = await fetch('/api/commands/send', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(command),
+ });
+
+ if (response.ok) {
+ navigate(-1);
+ } else {
+ throw Error(await response.text());
+ }
+ });
+
+ const validate = () => savedId || (item && item.type);
+
+ return (
+ <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'deviceCommand']}>
+ <Container maxWidth="xs" className={classes.container}>
+ <Accordion defaultExpanded>
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography variant="subtitle1">
+ {t('sharedRequired')}
+ </Typography>
+ </AccordionSummary>
+ <AccordionDetails className={classes.details}>
+ <SelectField
+ value={savedId}
+ emptyValue={limitCommands ? null : 0}
+ emptyTitle={t('sharedNew')}
+ onChange={(e) => setSavedId(e.target.value)}
+ endpoint={`/api/commands/send?deviceId=${id}`}
+ titleGetter={(it) => it.description}
+ label={t('sharedSavedCommand')}
+ />
+ {!limitCommands && !savedId && (
+ <BaseCommandView deviceId={id} item={item} setItem={setItem} />
+ )}
+ </AccordionDetails>
+ </Accordion>
+ <div className={classes.buttons}>
+ <Button
+ type="button"
+ color="primary"
+ variant="outlined"
+ onClick={() => navigate(-1)}
+ >
+ {t('sharedCancel')}
+ </Button>
+ <Button
+ type="button"
+ color="primary"
+ variant="contained"
+ onClick={handleSend}
+ disabled={!validate()}
+ >
+ {t('commandSend')}
+ </Button>
+ </div>
+ </Container>
+ </PageLayout>
+ );
+};
+
+export default CommandDevicePage;
diff --git a/src/settings/CommandGroupPage.jsx b/src/settings/CommandGroupPage.jsx
new file mode 100644
index 00000000..e55a235d
--- /dev/null
+++ b/src/settings/CommandGroupPage.jsx
@@ -0,0 +1,105 @@
+import React, { useState } from 'react';
+import { useSelector } from 'react-redux';
+import { useNavigate, useParams } from 'react-router-dom';
+import {
+ Accordion,
+ AccordionSummary,
+ AccordionDetails,
+ Typography,
+ Container,
+ Button,
+ FormControl,
+ InputLabel,
+ Select,
+ MenuItem,
+ FormControlLabel,
+ Checkbox,
+ TextField,
+} from '@mui/material';
+import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import PageLayout from '../common/components/PageLayout';
+import SettingsMenu from './components/SettingsMenu';
+import { useCatch } from '../reactHelper';
+import useSettingsStyles from './common/useSettingsStyles';
+
+const CommandDevicePage = () => {
+ const navigate = useNavigate();
+ const classes = useSettingsStyles();
+ const t = useTranslation();
+
+ const { id } = useParams();
+
+ const textEnabled = useSelector((state) => state.session.server.textEnabled);
+
+ const [item, setItem] = useState({ type: 'custom', attributes: {} });
+
+ const handleSend = useCatch(async () => {
+ const query = new URLSearchParams({ groupId: id });
+ const response = await fetch(`/api/commands/send?${query.toString()}`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(item),
+ });
+
+ if (response.ok) {
+ navigate(-1);
+ } else {
+ throw Error(await response.text());
+ }
+ });
+
+ return (
+ <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'deviceCommand']}>
+ <Container maxWidth="xs" className={classes.container}>
+ <Accordion defaultExpanded>
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography variant="subtitle1">
+ {t('sharedRequired')}
+ </Typography>
+ </AccordionSummary>
+ <AccordionDetails className={classes.details}>
+ <FormControl fullWidth>
+ <InputLabel>{t('sharedType')}</InputLabel>
+ <Select label={t('sharedType')} value="custom" disabled>
+ <MenuItem value="custom">{t('commandCustom')}</MenuItem>
+ </Select>
+ </FormControl>
+ <TextField
+ value={item.attributes.data}
+ onChange={(e) => setItem({ ...item, attributes: { ...item.attributes, data: e.target.value } })}
+ label={t('commandData')}
+ />
+ {textEnabled && (
+ <FormControlLabel
+ control={<Checkbox checked={item.textChannel} onChange={(event) => setItem({ ...item, textChannel: event.target.checked })} />}
+ label={t('commandSendSms')}
+ />
+ )}
+ </AccordionDetails>
+ </Accordion>
+ <div className={classes.buttons}>
+ <Button
+ type="button"
+ color="primary"
+ variant="outlined"
+ onClick={() => navigate(-1)}
+ >
+ {t('sharedCancel')}
+ </Button>
+ <Button
+ type="button"
+ color="primary"
+ variant="contained"
+ onClick={handleSend}
+ disabled={!item.attributes.data}
+ >
+ {t('commandSend')}
+ </Button>
+ </div>
+ </Container>
+ </PageLayout>
+ );
+};
+
+export default CommandDevicePage;
diff --git a/src/settings/CommandPage.jsx b/src/settings/CommandPage.jsx
new file mode 100644
index 00000000..e65ecd76
--- /dev/null
+++ b/src/settings/CommandPage.jsx
@@ -0,0 +1,50 @@
+import React, { useState } from 'react';
+import {
+ Accordion, AccordionSummary, AccordionDetails, Typography, TextField,
+} from '@mui/material';
+import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
+import EditItemView from './components/EditItemView';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import BaseCommandView from './components/BaseCommandView';
+import SettingsMenu from './components/SettingsMenu';
+import useSettingsStyles from './common/useSettingsStyles';
+
+const CommandPage = () => {
+ const classes = useSettingsStyles();
+ const t = useTranslation();
+
+ const [item, setItem] = useState();
+
+ const validate = () => item && item.type;
+
+ return (
+ <EditItemView
+ endpoint="commands"
+ item={item}
+ setItem={setItem}
+ validate={validate}
+ menu={<SettingsMenu />}
+ breadcrumbs={['settingsTitle', 'sharedSavedCommand']}
+ >
+ {item && (
+ <Accordion defaultExpanded>
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography variant="subtitle1">
+ {t('sharedRequired')}
+ </Typography>
+ </AccordionSummary>
+ <AccordionDetails className={classes.details}>
+ <TextField
+ value={item.description || ''}
+ onChange={(event) => setItem({ ...item, description: event.target.value })}
+ label={t('sharedDescription')}
+ />
+ <BaseCommandView item={item} setItem={setItem} />
+ </AccordionDetails>
+ </Accordion>
+ )}
+ </EditItemView>
+ );
+};
+
+export default CommandPage;
diff --git a/src/settings/CommandsPage.jsx b/src/settings/CommandsPage.jsx
new file mode 100644
index 00000000..1b893831
--- /dev/null
+++ b/src/settings/CommandsPage.jsx
@@ -0,0 +1,74 @@
+import React, { useState } from 'react';
+import {
+ Table, TableRow, TableCell, TableHead, TableBody,
+} from '@mui/material';
+import { useEffectAsync } from '../reactHelper';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import { formatBoolean } from '../common/util/formatter';
+import { prefixString } from '../common/util/stringUtils';
+import PageLayout from '../common/components/PageLayout';
+import SettingsMenu from './components/SettingsMenu';
+import CollectionFab from './components/CollectionFab';
+import CollectionActions from './components/CollectionActions';
+import TableShimmer from '../common/components/TableShimmer';
+import SearchHeader, { filterByKeyword } from './components/SearchHeader';
+import { useRestriction } from '../common/util/permissions';
+import useSettingsStyles from './common/useSettingsStyles';
+
+const CommandsPage = () => {
+ const classes = useSettingsStyles();
+ const t = useTranslation();
+
+ const [timestamp, setTimestamp] = useState(Date.now());
+ const [items, setItems] = useState([]);
+ const [searchKeyword, setSearchKeyword] = useState('');
+ const [loading, setLoading] = useState(false);
+ const limitCommands = useRestriction('limitCommands');
+
+ useEffectAsync(async () => {
+ setLoading(true);
+ try {
+ const response = await fetch('/api/commands');
+ if (response.ok) {
+ setItems(await response.json());
+ } else {
+ throw Error(await response.text());
+ }
+ } finally {
+ setLoading(false);
+ }
+ }, [timestamp]);
+
+ return (
+ <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'sharedSavedCommands']}>
+ <SearchHeader keyword={searchKeyword} setKeyword={setSearchKeyword} />
+ <Table className={classes.table}>
+ <TableHead>
+ <TableRow>
+ <TableCell>{t('sharedDescription')}</TableCell>
+ <TableCell>{t('sharedType')}</TableCell>
+ <TableCell>{t('commandSendSms')}</TableCell>
+ {!limitCommands && <TableCell className={classes.columnAction} />}
+ </TableRow>
+ </TableHead>
+ <TableBody>
+ {!loading ? items.filter(filterByKeyword(searchKeyword)).map((item) => (
+ <TableRow key={item.id}>
+ <TableCell>{item.description}</TableCell>
+ <TableCell>{t(prefixString('command', item.type))}</TableCell>
+ <TableCell>{formatBoolean(item.textChannel, t)}</TableCell>
+ {!limitCommands && (
+ <TableCell className={classes.columnAction} padding="none">
+ <CollectionActions itemId={item.id} editPath="/settings/command" endpoint="commands" setTimestamp={setTimestamp} />
+ </TableCell>
+ )}
+ </TableRow>
+ )) : (<TableShimmer columns={limitCommands ? 3 : 4} endAction />)}
+ </TableBody>
+ </Table>
+ <CollectionFab editPath="/settings/command" disabled={limitCommands} />
+ </PageLayout>
+ );
+};
+
+export default CommandsPage;
diff --git a/src/settings/ComputedAttributePage.jsx b/src/settings/ComputedAttributePage.jsx
new file mode 100644
index 00000000..1b19033c
--- /dev/null
+++ b/src/settings/ComputedAttributePage.jsx
@@ -0,0 +1,177 @@
+import React, { useState } from 'react';
+import {
+ Accordion,
+ AccordionSummary,
+ AccordionDetails,
+ Typography,
+ FormControl,
+ InputLabel,
+ MenuItem,
+ Select,
+ TextField,
+ createFilterOptions,
+ Autocomplete,
+ Button,
+ Snackbar,
+} from '@mui/material';
+import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
+import EditItemView from './components/EditItemView';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import usePositionAttributes from '../common/attributes/usePositionAttributes';
+import SettingsMenu from './components/SettingsMenu';
+import SelectField from '../common/components/SelectField';
+import { useCatch } from '../reactHelper';
+import { snackBarDurationLongMs } from '../common/util/duration';
+import useSettingsStyles from './common/useSettingsStyles';
+
+const allowedProperties = ['valid', 'latitude', 'longitude', 'altitude', 'speed', 'course', 'address', 'accuracy'];
+
+const ComputedAttributePage = () => {
+ const classes = useSettingsStyles();
+ const t = useTranslation();
+
+ const positionAttributes = usePositionAttributes(t);
+
+ const [item, setItem] = useState();
+ const [deviceId, setDeviceId] = useState();
+ const [result, setResult] = useState();
+
+ const options = Object.entries(positionAttributes).filter(([key, value]) => !value.property || allowedProperties.includes(key)).map(([key, value]) => ({
+ key,
+ name: value.name,
+ type: value.type,
+ }));
+
+ const filter = createFilterOptions({
+ stringify: (option) => option.name,
+ });
+
+ const testAttribute = useCatch(async () => {
+ const query = new URLSearchParams({ deviceId });
+ const url = `/api/attributes/computed/test?${query.toString()}`;
+ const response = await fetch(url, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(item),
+ });
+ if (response.ok) {
+ setResult(await response.text());
+ } else {
+ throw Error(await response.text());
+ }
+ });
+
+ const validate = () => item && item.description && item.expression;
+
+ return (
+ <EditItemView
+ endpoint="attributes/computed"
+ item={item}
+ setItem={setItem}
+ validate={validate}
+ menu={<SettingsMenu />}
+ breadcrumbs={['settingsTitle', 'sharedComputedAttribute']}
+ >
+ {item && (
+ <>
+ <Accordion defaultExpanded>
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography variant="subtitle1">
+ {t('sharedRequired')}
+ </Typography>
+ </AccordionSummary>
+ <AccordionDetails className={classes.details}>
+ <TextField
+ value={item.description || ''}
+ onChange={(e) => setItem({ ...item, description: e.target.value })}
+ label={t('sharedDescription')}
+ />
+ <Autocomplete
+ value={options.find((option) => option.key === item.attribute) || item.attribute}
+ onChange={(_, option) => {
+ const attribute = option ? option.key || option : null;
+ if (option && option.type) {
+ setItem({ ...item, attribute, type: option.type });
+ } else {
+ setItem({ ...item, attribute });
+ }
+ }}
+ filterOptions={(options, params) => {
+ const filtered = filter(options, params);
+ if (params.inputValue) {
+ filtered.push({
+ key: params.inputValue,
+ name: params.inputValue,
+ });
+ }
+ return filtered;
+ }}
+ options={options}
+ getOptionLabel={(option) => option.name || option}
+ renderOption={(props, option) => (
+ <li {...props}>
+ {option.name}
+ </li>
+ )}
+ renderInput={(params) => (
+ <TextField {...params} label={t('sharedAttribute')} />
+ )}
+ freeSolo
+ />
+ <TextField
+ value={item.expression || ''}
+ onChange={(e) => setItem({ ...item, expression: e.target.value })}
+ label={t('sharedExpression')}
+ multiline
+ rows={4}
+ />
+ <FormControl disabled={item.attribute in positionAttributes}>
+ <InputLabel>{t('sharedType')}</InputLabel>
+ <Select
+ label={t('sharedType')}
+ value={item.type || ''}
+ onChange={(e) => setItem({ ...item, type: e.target.value })}
+ >
+ <MenuItem value="string">{t('sharedTypeString')}</MenuItem>
+ <MenuItem value="number">{t('sharedTypeNumber')}</MenuItem>
+ <MenuItem value="boolean">{t('sharedTypeBoolean')}</MenuItem>
+ </Select>
+ </FormControl>
+ </AccordionDetails>
+ </Accordion>
+ <Accordion>
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography variant="subtitle1">
+ {t('sharedTest')}
+ </Typography>
+ </AccordionSummary>
+ <AccordionDetails className={classes.details}>
+ <SelectField
+ value={deviceId}
+ onChange={(e) => setDeviceId(Number(e.target.value))}
+ endpoint="/api/devices"
+ label={t('sharedDevice')}
+ />
+ <Button
+ variant="outlined"
+ color="primary"
+ onClick={testAttribute}
+ disabled={!deviceId}
+ >
+ {t('sharedTestExpression')}
+ </Button>
+ <Snackbar
+ open={!!result}
+ onClose={() => setResult(null)}
+ autoHideDuration={snackBarDurationLongMs}
+ message={result}
+ />
+ </AccordionDetails>
+ </Accordion>
+ </>
+ )}
+ </EditItemView>
+ );
+};
+
+export default ComputedAttributePage;
diff --git a/src/settings/ComputedAttributesPage.jsx b/src/settings/ComputedAttributesPage.jsx
new file mode 100644
index 00000000..6d098547
--- /dev/null
+++ b/src/settings/ComputedAttributesPage.jsx
@@ -0,0 +1,74 @@
+import React, { useState } from 'react';
+import {
+ Table, TableRow, TableCell, TableHead, TableBody,
+} from '@mui/material';
+import { useEffectAsync } from '../reactHelper';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import { useAdministrator } from '../common/util/permissions';
+import PageLayout from '../common/components/PageLayout';
+import SettingsMenu from './components/SettingsMenu';
+import CollectionFab from './components/CollectionFab';
+import CollectionActions from './components/CollectionActions';
+import TableShimmer from '../common/components/TableShimmer';
+import SearchHeader, { filterByKeyword } from './components/SearchHeader';
+import useSettingsStyles from './common/useSettingsStyles';
+
+const ComputedAttributesPage = () => {
+ const classes = useSettingsStyles();
+ const t = useTranslation();
+
+ const [timestamp, setTimestamp] = useState(Date.now());
+ const [items, setItems] = useState([]);
+ const [searchKeyword, setSearchKeyword] = useState('');
+ const [loading, setLoading] = useState(false);
+ const administrator = useAdministrator();
+
+ useEffectAsync(async () => {
+ setLoading(true);
+ try {
+ const response = await fetch('/api/attributes/computed');
+ if (response.ok) {
+ setItems(await response.json());
+ } else {
+ throw Error(await response.text());
+ }
+ } finally {
+ setLoading(false);
+ }
+ }, [timestamp]);
+
+ return (
+ <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'sharedComputedAttributes']}>
+ <SearchHeader keyword={searchKeyword} setKeyword={setSearchKeyword} />
+ <Table className={classes.table}>
+ <TableHead>
+ <TableRow>
+ <TableCell>{t('sharedDescription')}</TableCell>
+ <TableCell>{t('sharedAttribute')}</TableCell>
+ <TableCell>{t('sharedExpression')}</TableCell>
+ <TableCell>{t('sharedType')}</TableCell>
+ {administrator && <TableCell className={classes.columnAction} />}
+ </TableRow>
+ </TableHead>
+ <TableBody>
+ {!loading ? items.filter(filterByKeyword(searchKeyword)).map((item) => (
+ <TableRow key={item.id}>
+ <TableCell>{item.description}</TableCell>
+ <TableCell>{item.attribute}</TableCell>
+ <TableCell>{item.expression}</TableCell>
+ <TableCell>{item.type}</TableCell>
+ {administrator && (
+ <TableCell className={classes.columnAction} padding="none">
+ <CollectionActions itemId={item.id} editPath="/settings/attribute" endpoint="attributes/computed" setTimestamp={setTimestamp} />
+ </TableCell>
+ )}
+ </TableRow>
+ )) : (<TableShimmer columns={administrator ? 5 : 4} endAction={administrator} />)}
+ </TableBody>
+ </Table>
+ <CollectionFab editPath="/settings/attribute" disabled={!administrator} />
+ </PageLayout>
+ );
+};
+
+export default ComputedAttributesPage;
diff --git a/src/settings/DeviceConnectionsPage.jsx b/src/settings/DeviceConnectionsPage.jsx
new file mode 100644
index 00000000..c711d719
--- /dev/null
+++ b/src/settings/DeviceConnectionsPage.jsx
@@ -0,0 +1,107 @@
+import React from 'react';
+import { useParams } from 'react-router-dom';
+import {
+ Accordion,
+ AccordionSummary,
+ AccordionDetails,
+ Typography,
+ Container,
+} from '@mui/material';
+import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
+import LinkField from '../common/components/LinkField';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import SettingsMenu from './components/SettingsMenu';
+import { formatNotificationTitle } from '../common/util/formatter';
+import PageLayout from '../common/components/PageLayout';
+import useFeatures from '../common/util/useFeatures';
+import useSettingsStyles from './common/useSettingsStyles';
+
+const DeviceConnectionsPage = () => {
+ const classes = useSettingsStyles();
+ const t = useTranslation();
+
+ const { id } = useParams();
+
+ const features = useFeatures();
+
+ return (
+ <PageLayout
+ menu={<SettingsMenu />}
+ breadcrumbs={['settingsTitle', 'sharedDevice', 'sharedConnections']}
+ >
+ <Container maxWidth="xs" className={classes.container}>
+ <Accordion defaultExpanded>
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography variant="subtitle1">
+ {t('sharedConnections')}
+ </Typography>
+ </AccordionSummary>
+ <AccordionDetails className={classes.details}>
+ <LinkField
+ endpointAll="/api/geofences"
+ endpointLinked={`/api/geofences?deviceId=${id}`}
+ baseId={id}
+ keyBase="deviceId"
+ keyLink="geofenceId"
+ label={t('sharedGeofences')}
+ />
+ <LinkField
+ endpointAll="/api/notifications"
+ endpointLinked={`/api/notifications?deviceId=${id}`}
+ baseId={id}
+ keyBase="deviceId"
+ keyLink="notificationId"
+ titleGetter={(it) => formatNotificationTitle(t, it)}
+ label={t('sharedNotifications')}
+ />
+ {!features.disableDrivers && (
+ <LinkField
+ endpointAll="/api/drivers"
+ endpointLinked={`/api/drivers?deviceId=${id}`}
+ baseId={id}
+ keyBase="deviceId"
+ keyLink="driverId"
+ titleGetter={(it) => `${it.name} (${it.uniqueId})`}
+ label={t('sharedDrivers')}
+ />
+ )}
+ {!features.disableComputedAttributes && (
+ <LinkField
+ endpointAll="/api/attributes/computed"
+ endpointLinked={`/api/attributes/computed?deviceId=${id}`}
+ baseId={id}
+ keyBase="deviceId"
+ keyLink="attributeId"
+ titleGetter={(it) => it.description}
+ label={t('sharedComputedAttributes')}
+ />
+ )}
+ {!features.disableSavedCommands && (
+ <LinkField
+ endpointAll="/api/commands"
+ endpointLinked={`/api/commands?deviceId=${id}`}
+ baseId={id}
+ keyBase="deviceId"
+ keyLink="commandId"
+ titleGetter={(it) => it.description}
+ label={t('sharedSavedCommands')}
+ />
+ )}
+ {!features.disableMaintenance && (
+ <LinkField
+ endpointAll="/api/maintenance"
+ endpointLinked={`/api/maintenance?deviceId=${id}`}
+ baseId={id}
+ keyBase="deviceId"
+ keyLink="maintenanceId"
+ label={t('sharedMaintenance')}
+ />
+ )}
+ </AccordionDetails>
+ </Accordion>
+ </Container>
+ </PageLayout>
+ );
+};
+
+export default DeviceConnectionsPage;
diff --git a/src/settings/DevicePage.jsx b/src/settings/DevicePage.jsx
new file mode 100644
index 00000000..8933a210
--- /dev/null
+++ b/src/settings/DevicePage.jsx
@@ -0,0 +1,176 @@
+import React, { useState } from 'react';
+import dayjs from 'dayjs';
+import {
+ Accordion,
+ AccordionSummary,
+ AccordionDetails,
+ Typography,
+ FormControlLabel,
+ Checkbox,
+ TextField,
+} from '@mui/material';
+import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
+import { DropzoneArea } from 'react-mui-dropzone';
+import EditItemView from './components/EditItemView';
+import EditAttributesAccordion from './components/EditAttributesAccordion';
+import SelectField from '../common/components/SelectField';
+import deviceCategories from '../common/util/deviceCategories';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import useDeviceAttributes from '../common/attributes/useDeviceAttributes';
+import { useAdministrator } from '../common/util/permissions';
+import SettingsMenu from './components/SettingsMenu';
+import useCommonDeviceAttributes from '../common/attributes/useCommonDeviceAttributes';
+import { useCatch } from '../reactHelper';
+import useQuery from '../common/util/useQuery';
+import useSettingsStyles from './common/useSettingsStyles';
+
+const DevicePage = () => {
+ const classes = useSettingsStyles();
+ const t = useTranslation();
+
+ const admin = useAdministrator();
+
+ const commonDeviceAttributes = useCommonDeviceAttributes(t);
+ const deviceAttributes = useDeviceAttributes(t);
+
+ const query = useQuery();
+ const uniqueId = query.get('uniqueId');
+
+ const [item, setItem] = useState(uniqueId ? { uniqueId } : null);
+
+ const handleFiles = useCatch(async (files) => {
+ if (files.length > 0) {
+ const response = await fetch(`/api/devices/${item.id}/image`, {
+ method: 'POST',
+ body: files[0],
+ });
+ if (response.ok) {
+ setItem({ ...item, attributes: { ...item.attributes, deviceImage: await response.text() } });
+ } else {
+ throw Error(await response.text());
+ }
+ }
+ });
+
+ const validate = () => item && item.name && item.uniqueId;
+
+ return (
+ <EditItemView
+ endpoint="devices"
+ item={item}
+ setItem={setItem}
+ validate={validate}
+ menu={<SettingsMenu />}
+ breadcrumbs={['settingsTitle', 'sharedDevice']}
+ >
+ {item && (
+ <>
+ <Accordion defaultExpanded>
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography variant="subtitle1">
+ {t('sharedRequired')}
+ </Typography>
+ </AccordionSummary>
+ <AccordionDetails className={classes.details}>
+ <TextField
+ value={item.name || ''}
+ onChange={(event) => setItem({ ...item, name: event.target.value })}
+ label={t('sharedName')}
+ />
+ <TextField
+ value={item.uniqueId || ''}
+ onChange={(event) => setItem({ ...item, uniqueId: event.target.value })}
+ label={t('deviceIdentifier')}
+ helperText={t('deviceIdentifierHelp')}
+ disabled={Boolean(uniqueId)}
+ />
+ </AccordionDetails>
+ </Accordion>
+ <Accordion>
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography variant="subtitle1">
+ {t('sharedExtra')}
+ </Typography>
+ </AccordionSummary>
+ <AccordionDetails className={classes.details}>
+ <SelectField
+ value={item.groupId}
+ onChange={(event) => setItem({ ...item, groupId: Number(event.target.value) })}
+ endpoint="/api/groups"
+ label={t('groupParent')}
+ />
+ <TextField
+ value={item.phone || ''}
+ onChange={(event) => setItem({ ...item, phone: event.target.value })}
+ label={t('sharedPhone')}
+ />
+ <TextField
+ value={item.model || ''}
+ onChange={(event) => setItem({ ...item, model: event.target.value })}
+ label={t('deviceModel')}
+ />
+ <TextField
+ value={item.contact || ''}
+ onChange={(event) => setItem({ ...item, contact: event.target.value })}
+ label={t('deviceContact')}
+ />
+ <SelectField
+ value={item.category || 'default'}
+ onChange={(event) => setItem({ ...item, category: event.target.value })}
+ data={deviceCategories.map((category) => ({
+ id: category,
+ name: t(`category${category.replace(/^\w/, (c) => c.toUpperCase())}`),
+ }))}
+ label={t('deviceCategory')}
+ />
+ <SelectField
+ value={item.calendarId}
+ onChange={(event) => setItem({ ...item, calendarId: Number(event.target.value) })}
+ endpoint="/api/calendars"
+ label={t('sharedCalendar')}
+ />
+ <TextField
+ label={t('userExpirationTime')}
+ type="date"
+ value={(item.expirationTime && dayjs(item.expirationTime).locale('en').format('YYYY-MM-DD')) || '2099-01-01'}
+ onChange={(e) => setItem({ ...item, expirationTime: dayjs(e.target.value, 'YYYY-MM-DD').locale('en').format() })}
+ disabled={!admin}
+ />
+ <FormControlLabel
+ control={<Checkbox checked={item.disabled} onChange={(event) => setItem({ ...item, disabled: event.target.checked })} />}
+ label={t('sharedDisabled')}
+ disabled={!admin}
+ />
+ </AccordionDetails>
+ </Accordion>
+ {item.id && (
+ <Accordion>
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography variant="subtitle1">
+ {t('attributeDeviceImage')}
+ </Typography>
+ </AccordionSummary>
+ <AccordionDetails className={classes.details}>
+ <DropzoneArea
+ dropzoneText={t('sharedDropzoneText')}
+ acceptedFiles={['image/*']}
+ filesLimit={1}
+ onChange={handleFiles}
+ showAlerts={false}
+ maxFileSize={500000}
+ />
+ </AccordionDetails>
+ </Accordion>
+ )}
+ <EditAttributesAccordion
+ attributes={item.attributes}
+ setAttributes={(attributes) => setItem({ ...item, attributes })}
+ definitions={{ ...commonDeviceAttributes, ...deviceAttributes }}
+ />
+ </>
+ )}
+ </EditItemView>
+ );
+};
+
+export default DevicePage;
diff --git a/src/settings/DevicesPage.jsx b/src/settings/DevicesPage.jsx
new file mode 100644
index 00000000..c0da0ba7
--- /dev/null
+++ b/src/settings/DevicesPage.jsx
@@ -0,0 +1,114 @@
+import React, { useState } from 'react';
+import { useSelector } from 'react-redux';
+import { useNavigate } from 'react-router-dom';
+import {
+ Table, TableRow, TableCell, TableHead, TableBody, Button, TableFooter,
+} from '@mui/material';
+import LinkIcon from '@mui/icons-material/Link';
+import { useEffectAsync } from '../reactHelper';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import PageLayout from '../common/components/PageLayout';
+import SettingsMenu from './components/SettingsMenu';
+import CollectionFab from './components/CollectionFab';
+import CollectionActions from './components/CollectionActions';
+import TableShimmer from '../common/components/TableShimmer';
+import SearchHeader, { filterByKeyword } from './components/SearchHeader';
+import { usePreference } from '../common/util/preferences';
+import { formatTime } from '../common/util/formatter';
+import { useDeviceReadonly } from '../common/util/permissions';
+import useSettingsStyles from './common/useSettingsStyles';
+
+const DevicesPage = () => {
+ const classes = useSettingsStyles();
+ const navigate = useNavigate();
+ const t = useTranslation();
+
+ const groups = useSelector((state) => state.groups.items);
+
+ const hours12 = usePreference('twelveHourFormat');
+
+ const deviceReadonly = useDeviceReadonly();
+
+ const [timestamp, setTimestamp] = useState(Date.now());
+ const [items, setItems] = useState([]);
+ const [searchKeyword, setSearchKeyword] = useState('');
+ const [loading, setLoading] = useState(false);
+
+ useEffectAsync(async () => {
+ setLoading(true);
+ try {
+ const response = await fetch('/api/devices');
+ if (response.ok) {
+ setItems(await response.json());
+ } else {
+ throw Error(await response.text());
+ }
+ } finally {
+ setLoading(false);
+ }
+ }, [timestamp]);
+
+ const handleExport = () => {
+ window.location.assign('/api/reports/devices/xlsx');
+ };
+
+ const actionConnections = {
+ key: 'connections',
+ title: t('sharedConnections'),
+ icon: <LinkIcon fontSize="small" />,
+ handler: (deviceId) => navigate(`/settings/device/${deviceId}/connections`),
+ };
+
+ return (
+ <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'deviceTitle']}>
+ <SearchHeader keyword={searchKeyword} setKeyword={setSearchKeyword} />
+ <Table className={classes.table}>
+ <TableHead>
+ <TableRow>
+ <TableCell>{t('sharedName')}</TableCell>
+ <TableCell>{t('deviceIdentifier')}</TableCell>
+ <TableCell>{t('groupParent')}</TableCell>
+ <TableCell>{t('sharedPhone')}</TableCell>
+ <TableCell>{t('deviceModel')}</TableCell>
+ <TableCell>{t('deviceContact')}</TableCell>
+ <TableCell>{t('userExpirationTime')}</TableCell>
+ <TableCell className={classes.columnAction} />
+ </TableRow>
+ </TableHead>
+ <TableBody>
+ {!loading ? items.filter(filterByKeyword(searchKeyword)).map((item) => (
+ <TableRow key={item.id}>
+ <TableCell>{item.name}</TableCell>
+ <TableCell>{item.uniqueId}</TableCell>
+ <TableCell>{item.groupId ? groups[item.groupId]?.name : null}</TableCell>
+ <TableCell>{item.phone}</TableCell>
+ <TableCell>{item.model}</TableCell>
+ <TableCell>{item.contact}</TableCell>
+ <TableCell>{formatTime(item.expirationTime, 'date', hours12)}</TableCell>
+ <TableCell className={classes.columnAction} padding="none">
+ <CollectionActions
+ itemId={item.id}
+ editPath="/settings/device"
+ endpoint="devices"
+ setTimestamp={setTimestamp}
+ customActions={[actionConnections]}
+ readonly={deviceReadonly}
+ />
+ </TableCell>
+ </TableRow>
+ )) : (<TableShimmer columns={7} endAction />)}
+ </TableBody>
+ <TableFooter>
+ <TableRow>
+ <TableCell colSpan={8} align="right">
+ <Button onClick={handleExport} variant="text">{t('reportExport')}</Button>
+ </TableCell>
+ </TableRow>
+ </TableFooter>
+ </Table>
+ <CollectionFab editPath="/settings/device" />
+ </PageLayout>
+ );
+};
+
+export default DevicesPage;
diff --git a/src/settings/DriverPage.jsx b/src/settings/DriverPage.jsx
new file mode 100644
index 00000000..5f70a44a
--- /dev/null
+++ b/src/settings/DriverPage.jsx
@@ -0,0 +1,62 @@
+import React, { useState } from 'react';
+import TextField from '@mui/material/TextField';
+import {
+ Accordion, AccordionSummary, AccordionDetails, Typography,
+} from '@mui/material';
+import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
+import EditItemView from './components/EditItemView';
+import EditAttributesAccordion from './components/EditAttributesAccordion';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import SettingsMenu from './components/SettingsMenu';
+import useSettingsStyles from './common/useSettingsStyles';
+
+const DriverPage = () => {
+ const classes = useSettingsStyles();
+ const t = useTranslation();
+
+ const [item, setItem] = useState();
+
+ const validate = () => item && item.name && item.uniqueId;
+
+ return (
+ <EditItemView
+ endpoint="drivers"
+ item={item}
+ setItem={setItem}
+ validate={validate}
+ menu={<SettingsMenu />}
+ breadcrumbs={['settingsTitle', 'sharedDriver']}
+ >
+ {item && (
+ <>
+ <Accordion defaultExpanded>
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography variant="subtitle1">
+ {t('sharedRequired')}
+ </Typography>
+ </AccordionSummary>
+ <AccordionDetails className={classes.details}>
+ <TextField
+ value={item.name || ''}
+ onChange={(event) => setItem({ ...item, name: event.target.value })}
+ label={t('sharedName')}
+ />
+ <TextField
+ value={item.uniqueId || ''}
+ onChange={(event) => setItem({ ...item, uniqueId: event.target.value })}
+ label={t('deviceIdentifier')}
+ />
+ </AccordionDetails>
+ </Accordion>
+ <EditAttributesAccordion
+ attributes={item.attributes}
+ setAttributes={(attributes) => setItem({ ...item, attributes })}
+ definitions={{}}
+ />
+ </>
+ )}
+ </EditItemView>
+ );
+};
+
+export default DriverPage;
diff --git a/src/settings/DriversPage.jsx b/src/settings/DriversPage.jsx
new file mode 100644
index 00000000..72834860
--- /dev/null
+++ b/src/settings/DriversPage.jsx
@@ -0,0 +1,66 @@
+import React, { useState } from 'react';
+import {
+ Table, TableRow, TableCell, TableHead, TableBody,
+} from '@mui/material';
+import { useEffectAsync } from '../reactHelper';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import PageLayout from '../common/components/PageLayout';
+import SettingsMenu from './components/SettingsMenu';
+import CollectionFab from './components/CollectionFab';
+import CollectionActions from './components/CollectionActions';
+import TableShimmer from '../common/components/TableShimmer';
+import SearchHeader, { filterByKeyword } from './components/SearchHeader';
+import useSettingsStyles from './common/useSettingsStyles';
+
+const DriversPage = () => {
+ const classes = useSettingsStyles();
+ const t = useTranslation();
+
+ const [timestamp, setTimestamp] = useState(Date.now());
+ const [items, setItems] = useState([]);
+ const [searchKeyword, setSearchKeyword] = useState('');
+ const [loading, setLoading] = useState(false);
+
+ useEffectAsync(async () => {
+ setLoading(true);
+ try {
+ const response = await fetch('/api/drivers');
+ if (response.ok) {
+ setItems(await response.json());
+ } else {
+ throw Error(await response.text());
+ }
+ } finally {
+ setLoading(false);
+ }
+ }, [timestamp]);
+
+ return (
+ <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'sharedDrivers']}>
+ <SearchHeader keyword={searchKeyword} setKeyword={setSearchKeyword} />
+ <Table className={classes.table}>
+ <TableHead>
+ <TableRow>
+ <TableCell>{t('sharedName')}</TableCell>
+ <TableCell>{t('deviceIdentifier')}</TableCell>
+ <TableCell className={classes.columnAction} />
+ </TableRow>
+ </TableHead>
+ <TableBody>
+ {!loading ? items.filter(filterByKeyword(searchKeyword)).map((item) => (
+ <TableRow key={item.id}>
+ <TableCell>{item.name}</TableCell>
+ <TableCell>{item.uniqueId}</TableCell>
+ <TableCell className={classes.columnAction} padding="none">
+ <CollectionActions itemId={item.id} editPath="/settings/driver" endpoint="drivers" setTimestamp={setTimestamp} />
+ </TableCell>
+ </TableRow>
+ )) : (<TableShimmer columns={3} endAction />)}
+ </TableBody>
+ </Table>
+ <CollectionFab editPath="/settings/driver" />
+ </PageLayout>
+ );
+};
+
+export default DriversPage;
diff --git a/src/settings/GeofencePage.jsx b/src/settings/GeofencePage.jsx
new file mode 100644
index 00000000..c3c96ef8
--- /dev/null
+++ b/src/settings/GeofencePage.jsx
@@ -0,0 +1,88 @@
+import React, { useState } from 'react';
+import { useDispatch } from 'react-redux';
+import {
+ Accordion, AccordionSummary, AccordionDetails, Typography, TextField,
+} from '@mui/material';
+import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
+import EditItemView from './components/EditItemView';
+import EditAttributesAccordion from './components/EditAttributesAccordion';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import useGeofenceAttributes from '../common/attributes/useGeofenceAttributes';
+import SettingsMenu from './components/SettingsMenu';
+import SelectField from '../common/components/SelectField';
+import { geofencesActions } from '../store';
+import useSettingsStyles from './common/useSettingsStyles';
+
+const GeofencePage = () => {
+ const classes = useSettingsStyles();
+ const dispatch = useDispatch();
+ const t = useTranslation();
+
+ const geofenceAttributes = useGeofenceAttributes(t);
+
+ const [item, setItem] = useState();
+
+ const onItemSaved = (result) => {
+ dispatch(geofencesActions.update([result]));
+ };
+
+ const validate = () => item && item.name;
+
+ return (
+ <EditItemView
+ endpoint="geofences"
+ item={item}
+ setItem={setItem}
+ validate={validate}
+ onItemSaved={onItemSaved}
+ menu={<SettingsMenu />}
+ breadcrumbs={['settingsTitle', 'sharedGeofence']}
+ >
+ {item && (
+ <>
+ <Accordion defaultExpanded>
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography variant="subtitle1">
+ {t('sharedRequired')}
+ </Typography>
+ </AccordionSummary>
+ <AccordionDetails className={classes.details}>
+ <TextField
+ value={item.name || ''}
+ onChange={(event) => setItem({ ...item, name: event.target.value })}
+ label={t('sharedName')}
+ />
+ </AccordionDetails>
+ </Accordion>
+ <Accordion>
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography variant="subtitle1">
+ {t('sharedExtra')}
+ </Typography>
+ </AccordionSummary>
+ <AccordionDetails className={classes.details}>
+ <TextField
+ value={item.description || ''}
+ onChange={(event) => setItem({ ...item, description: event.target.value })}
+ label={t('sharedDescription')}
+ />
+ <SelectField
+ value={item.calendarId}
+ onChange={(event) => setItem({ ...item, calendarId: Number(event.target.value) })}
+ endpoint="/api/calendars"
+ label={t('sharedCalendar')}
+ />
+ </AccordionDetails>
+ </Accordion>
+ <EditAttributesAccordion
+ attributes={item.attributes}
+ setAttributes={(attributes) => setItem({ ...item, attributes })}
+ definitions={geofenceAttributes}
+ />
+ </>
+ )}
+ </EditItemView>
+ );
+};
+
+export default GeofencePage;
diff --git a/src/settings/GroupConnectionsPage.jsx b/src/settings/GroupConnectionsPage.jsx
new file mode 100644
index 00000000..980bd9da
--- /dev/null
+++ b/src/settings/GroupConnectionsPage.jsx
@@ -0,0 +1,107 @@
+import React from 'react';
+import { useParams } from 'react-router-dom';
+import {
+ Accordion,
+ AccordionSummary,
+ AccordionDetails,
+ Typography,
+ Container,
+} from '@mui/material';
+import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
+import LinkField from '../common/components/LinkField';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import SettingsMenu from './components/SettingsMenu';
+import { formatNotificationTitle } from '../common/util/formatter';
+import PageLayout from '../common/components/PageLayout';
+import useFeatures from '../common/util/useFeatures';
+import useSettingsStyles from './common/useSettingsStyles';
+
+const GroupConnectionsPage = () => {
+ const classes = useSettingsStyles();
+ const t = useTranslation();
+
+ const { id } = useParams();
+
+ const features = useFeatures();
+
+ return (
+ <PageLayout
+ menu={<SettingsMenu />}
+ breadcrumbs={['settingsTitle', 'groupDialog', 'sharedConnections']}
+ >
+ <Container maxWidth="xs" className={classes.container}>
+ <Accordion defaultExpanded>
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography variant="subtitle1">
+ {t('sharedConnections')}
+ </Typography>
+ </AccordionSummary>
+ <AccordionDetails className={classes.details}>
+ <LinkField
+ endpointAll="/api/geofences"
+ endpointLinked={`/api/geofences?groupId=${id}`}
+ baseId={id}
+ keyBase="groupId"
+ keyLink="geofenceId"
+ label={t('sharedGeofences')}
+ />
+ <LinkField
+ endpointAll="/api/notifications"
+ endpointLinked={`/api/notifications?groupId=${id}`}
+ baseId={id}
+ keyBase="groupId"
+ keyLink="notificationId"
+ titleGetter={(it) => formatNotificationTitle(t, it)}
+ label={t('sharedNotifications')}
+ />
+ {!features.disableDrivers && (
+ <LinkField
+ endpointAll="/api/drivers"
+ endpointLinked={`/api/drivers?groupId=${id}`}
+ baseId={id}
+ keyBase="groupId"
+ keyLink="driverId"
+ titleGetter={(it) => `${it.name} (${it.uniqueId})`}
+ label={t('sharedDrivers')}
+ />
+ )}
+ {!features.disableComputedAttributes && (
+ <LinkField
+ endpointAll="/api/attributes/computed"
+ endpointLinked={`/api/attributes/computed?groupId=${id}`}
+ baseId={id}
+ keyBase="groupId"
+ keyLink="attributeId"
+ titleGetter={(it) => it.description}
+ label={t('sharedComputedAttributes')}
+ />
+ )}
+ {!features.disableSavedCommands && (
+ <LinkField
+ endpointAll="/api/commands"
+ endpointLinked={`/api/commands?groupId=${id}`}
+ baseId={id}
+ keyBase="groupId"
+ keyLink="commandId"
+ titleGetter={(it) => it.description}
+ label={t('sharedSavedCommands')}
+ />
+ )}
+ {!features.disableMaintenance && (
+ <LinkField
+ endpointAll="/api/maintenance"
+ endpointLinked={`/api/maintenance?groupId=${id}`}
+ baseId={id}
+ keyBase="groupId"
+ keyLink="maintenanceId"
+ label={t('sharedMaintenance')}
+ />
+ )}
+ </AccordionDetails>
+ </Accordion>
+ </Container>
+ </PageLayout>
+ );
+};
+
+export default GroupConnectionsPage;
diff --git a/src/settings/GroupPage.jsx b/src/settings/GroupPage.jsx
new file mode 100644
index 00000000..ba1cbc76
--- /dev/null
+++ b/src/settings/GroupPage.jsx
@@ -0,0 +1,93 @@
+import React, { useState } from 'react';
+import { useDispatch } from 'react-redux';
+import TextField from '@mui/material/TextField';
+
+import {
+ Accordion, AccordionSummary, AccordionDetails, Typography,
+} from '@mui/material';
+import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
+import EditItemView from './components/EditItemView';
+import EditAttributesAccordion from './components/EditAttributesAccordion';
+import SelectField from '../common/components/SelectField';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import SettingsMenu from './components/SettingsMenu';
+import useCommonDeviceAttributes from '../common/attributes/useCommonDeviceAttributes';
+import useGroupAttributes from '../common/attributes/useGroupAttributes';
+import { useCatch } from '../reactHelper';
+import { groupsActions } from '../store';
+import useSettingsStyles from './common/useSettingsStyles';
+
+const GroupPage = () => {
+ const classes = useSettingsStyles();
+ const dispatch = useDispatch();
+ const t = useTranslation();
+
+ const commonDeviceAttributes = useCommonDeviceAttributes(t);
+ const groupAttributes = useGroupAttributes(t);
+
+ const [item, setItem] = useState();
+
+ const onItemSaved = useCatch(async () => {
+ const response = await fetch('/api/groups');
+ if (response.ok) {
+ dispatch(groupsActions.refresh(await response.json()));
+ } else {
+ throw Error(await response.text());
+ }
+ });
+
+ const validate = () => item && item.name;
+
+ return (
+ <EditItemView
+ endpoint="groups"
+ item={item}
+ setItem={setItem}
+ validate={validate}
+ onItemSaved={onItemSaved}
+ menu={<SettingsMenu />}
+ breadcrumbs={['settingsTitle', 'groupDialog']}
+ >
+ {item && (
+ <>
+ <Accordion defaultExpanded>
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography variant="subtitle1">
+ {t('sharedRequired')}
+ </Typography>
+ </AccordionSummary>
+ <AccordionDetails className={classes.details}>
+ <TextField
+ value={item.name || ''}
+ onChange={(event) => setItem({ ...item, name: event.target.value })}
+ label={t('sharedName')}
+ />
+ </AccordionDetails>
+ </Accordion>
+ <Accordion>
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography variant="subtitle1">
+ {t('sharedExtra')}
+ </Typography>
+ </AccordionSummary>
+ <AccordionDetails className={classes.details}>
+ <SelectField
+ value={item.groupId}
+ onChange={(event) => setItem({ ...item, groupId: Number(event.target.value) })}
+ endpoint="/api/groups"
+ label={t('groupParent')}
+ />
+ </AccordionDetails>
+ </Accordion>
+ <EditAttributesAccordion
+ attributes={item.attributes}
+ setAttributes={(attributes) => setItem({ ...item, attributes })}
+ definitions={{ ...commonDeviceAttributes, ...groupAttributes }}
+ />
+ </>
+ )}
+ </EditItemView>
+ );
+};
+
+export default GroupPage;
diff --git a/src/settings/GroupsPage.jsx b/src/settings/GroupsPage.jsx
new file mode 100644
index 00000000..baba7f72
--- /dev/null
+++ b/src/settings/GroupsPage.jsx
@@ -0,0 +1,91 @@
+import React, { useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import {
+ Table, TableRow, TableCell, TableHead, TableBody,
+} from '@mui/material';
+import LinkIcon from '@mui/icons-material/Link';
+import PublishIcon from '@mui/icons-material/Publish';
+import { useEffectAsync } from '../reactHelper';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import PageLayout from '../common/components/PageLayout';
+import SettingsMenu from './components/SettingsMenu';
+import CollectionFab from './components/CollectionFab';
+import CollectionActions from './components/CollectionActions';
+import TableShimmer from '../common/components/TableShimmer';
+import SearchHeader, { filterByKeyword } from './components/SearchHeader';
+import { useRestriction } from '../common/util/permissions';
+import useSettingsStyles from './common/useSettingsStyles';
+
+const GroupsPage = () => {
+ const classes = useSettingsStyles();
+ const navigate = useNavigate();
+ const t = useTranslation();
+
+ const limitCommands = useRestriction('limitCommands');
+
+ const [timestamp, setTimestamp] = useState(Date.now());
+ const [items, setItems] = useState([]);
+ const [searchKeyword, setSearchKeyword] = useState('');
+ const [loading, setLoading] = useState(false);
+
+ useEffectAsync(async () => {
+ setLoading(true);
+ try {
+ const response = await fetch('/api/groups');
+ if (response.ok) {
+ setItems(await response.json());
+ } else {
+ throw Error(await response.text());
+ }
+ } finally {
+ setLoading(false);
+ }
+ }, [timestamp]);
+
+ const actionCommand = {
+ key: 'command',
+ title: t('deviceCommand'),
+ icon: <PublishIcon fontSize="small" />,
+ handler: (groupId) => navigate(`/settings/group/${groupId}/command`),
+ };
+
+ const actionConnections = {
+ key: 'connections',
+ title: t('sharedConnections'),
+ icon: <LinkIcon fontSize="small" />,
+ handler: (groupId) => navigate(`/settings/group/${groupId}/connections`),
+ };
+
+ return (
+ <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'settingsGroups']}>
+ <SearchHeader keyword={searchKeyword} setKeyword={setSearchKeyword} />
+ <Table className={classes.table}>
+ <TableHead>
+ <TableRow>
+ <TableCell>{t('sharedName')}</TableCell>
+ <TableCell className={classes.columnAction} />
+ </TableRow>
+ </TableHead>
+ <TableBody>
+ {!loading ? items.filter(filterByKeyword(searchKeyword)).map((item) => (
+ <TableRow key={item.id}>
+ <TableCell>{item.name}</TableCell>
+ <TableCell className={classes.columnAction} padding="none">
+ <CollectionActions
+ itemId={item.id}
+ editPath="/settings/group"
+ endpoint="groups"
+ setTimestamp={setTimestamp}
+ customActions={limitCommands ? [actionConnections] : [actionConnections, actionCommand]}
+ />
+ </TableCell>
+ </TableRow>
+ )) : (<TableShimmer columns={2} endAction />)}
+ </TableBody>
+ </Table>
+ <CollectionFab editPath="/settings/group" />
+ </PageLayout>
+ );
+};
+
+export default GroupsPage;
diff --git a/src/settings/MaintenancePage.jsx b/src/settings/MaintenancePage.jsx
new file mode 100644
index 00000000..491a0d3b
--- /dev/null
+++ b/src/settings/MaintenancePage.jsx
@@ -0,0 +1,174 @@
+import React, { useEffect, useState } from 'react';
+import dayjs from 'dayjs';
+import {
+ Accordion,
+ AccordionSummary,
+ AccordionDetails,
+ Typography,
+ TextField,
+ FormControl,
+ InputLabel,
+ MenuItem,
+ Select,
+} from '@mui/material';
+import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
+import { prefixString } from '../common/util/stringUtils';
+import EditItemView from './components/EditItemView';
+import EditAttributesAccordion from './components/EditAttributesAccordion';
+import { useAttributePreference } from '../common/util/preferences';
+import {
+ speedFromKnots, speedToKnots, distanceFromMeters, distanceToMeters,
+} from '../common/util/converter';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import usePositionAttributes from '../common/attributes/usePositionAttributes';
+import SettingsMenu from './components/SettingsMenu';
+import useSettingsStyles from './common/useSettingsStyles';
+
+const MaintenancePage = () => {
+ const classes = useSettingsStyles();
+ const t = useTranslation();
+
+ const positionAttributes = usePositionAttributes(t);
+
+ const [item, setItem] = useState();
+ const [labels, setLabels] = useState({ start: '', period: '' });
+
+ const speedUnit = useAttributePreference('speedUnit', 'kn');
+ const distanceUnit = useAttributePreference('distanceUnit', 'km');
+
+ const convertToList = (attributes) => {
+ const otherList = [];
+ Object.keys(attributes).forEach((key) => {
+ const value = attributes[key];
+ if (value.type === 'number' || key.endsWith('Time')) {
+ otherList.push({ key, name: value.name, type: value.type });
+ }
+ });
+ return otherList;
+ };
+
+ useEffect(() => {
+ const attribute = positionAttributes[item?.type];
+ if (item?.type?.endsWith('Time')) {
+ setLabels({ ...labels, start: null, period: t('sharedDays') });
+ } else if (attribute && attribute.dataType) {
+ switch (attribute.dataType) {
+ case 'distance':
+ setLabels({ ...labels, start: t(prefixString('shared', distanceUnit)), period: t(prefixString('shared', distanceUnit)) });
+ break;
+ case 'speed':
+ setLabels({ ...labels, start: t(prefixString('shared', speedUnit)), period: t(prefixString('shared', speedUnit)) });
+ break;
+ default:
+ setLabels({ ...labels, start: null, period: null });
+ break;
+ }
+ } else {
+ setLabels({ ...labels, start: null, period: null });
+ }
+ }, [item?.type]);
+
+ const rawToValue = (start, value) => {
+ const attribute = positionAttributes[item.type];
+ if (item.type?.endsWith('Time')) {
+ if (start) {
+ return dayjs(value).locale('en').format('YYYY-MM-DD');
+ }
+ return value / 86400000;
+ }
+ if (attribute && attribute.dataType) {
+ switch (attribute.dataType) {
+ case 'speed':
+ return speedFromKnots(value, speedUnit);
+ case 'distance':
+ return distanceFromMeters(value, distanceUnit);
+ default:
+ return value;
+ }
+ }
+ return value;
+ };
+
+ const valueToRaw = (start, value) => {
+ const attribute = positionAttributes[item.type];
+ if (item.type?.endsWith('Time')) {
+ if (start) {
+ return dayjs(value, 'YYYY-MM-DD').valueOf();
+ }
+ return value * 86400000;
+ } if (attribute && attribute.dataType) {
+ switch (attribute.dataType) {
+ case 'speed':
+ return speedToKnots(value, speedUnit);
+ case 'distance':
+ return distanceToMeters(value, distanceUnit);
+ default:
+ return value;
+ }
+ }
+ return value;
+ };
+
+ const validate = () => item && item.name && item.type && item.start && item.period;
+
+ return (
+ <EditItemView
+ endpoint="maintenance"
+ item={item}
+ setItem={setItem}
+ validate={validate}
+ menu={<SettingsMenu />}
+ breadcrumbs={['settingsTitle', 'sharedMaintenance']}
+ >
+ {item && (
+ <>
+ <Accordion defaultExpanded>
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography variant="subtitle1">
+ {t('sharedRequired')}
+ </Typography>
+ </AccordionSummary>
+ <AccordionDetails className={classes.details}>
+ <TextField
+ value={item.name || ''}
+ onChange={(e) => setItem({ ...item, name: e.target.value })}
+ label={t('sharedName')}
+ />
+ <FormControl>
+ <InputLabel>{t('sharedType')}</InputLabel>
+ <Select
+ label={t('sharedType')}
+ value={item.type || ''}
+ onChange={(e) => setItem({ ...item, type: e.target.value, start: 0, period: 0 })}
+ >
+ {convertToList(positionAttributes).map(({ key, name }) => (
+ <MenuItem key={key} value={key}>{name}</MenuItem>
+ ))}
+ </Select>
+ </FormControl>
+ <TextField
+ type={item.type?.endsWith('Time') ? 'date' : 'number'}
+ value={rawToValue(true, item.start) || ''}
+ onChange={(e) => setItem({ ...item, start: valueToRaw(true, e.target.value) })}
+ label={labels.start ? `${t('maintenanceStart')} (${labels.start})` : t('maintenanceStart')}
+ />
+ <TextField
+ type="number"
+ value={rawToValue(false, item.period) || ''}
+ onChange={(e) => setItem({ ...item, period: valueToRaw(false, e.target.value) })}
+ label={labels.period ? `${t('maintenancePeriod')} (${labels.period})` : t('maintenancePeriod')}
+ />
+ </AccordionDetails>
+ </Accordion>
+ <EditAttributesAccordion
+ attributes={item.attributes}
+ setAttributes={(attributes) => setItem({ ...item, attributes })}
+ definitions={{}}
+ />
+ </>
+ )}
+ </EditItemView>
+ );
+};
+
+export default MaintenancePage;
diff --git a/src/settings/MaintenancesPage.jsx b/src/settings/MaintenancesPage.jsx
new file mode 100644
index 00000000..9241eb3e
--- /dev/null
+++ b/src/settings/MaintenancesPage.jsx
@@ -0,0 +1,100 @@
+import React, { useState } from 'react';
+import dayjs from 'dayjs';
+import {
+ Table, TableRow, TableCell, TableHead, TableBody,
+} from '@mui/material';
+import { useEffectAsync } from '../reactHelper';
+import usePositionAttributes from '../common/attributes/usePositionAttributes';
+import { formatDistance, formatSpeed } from '../common/util/formatter';
+import { useAttributePreference } from '../common/util/preferences';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import PageLayout from '../common/components/PageLayout';
+import SettingsMenu from './components/SettingsMenu';
+import CollectionFab from './components/CollectionFab';
+import CollectionActions from './components/CollectionActions';
+import TableShimmer from '../common/components/TableShimmer';
+import SearchHeader, { filterByKeyword } from './components/SearchHeader';
+import useSettingsStyles from './common/useSettingsStyles';
+
+const MaintenacesPage = () => {
+ const classes = useSettingsStyles();
+ const t = useTranslation();
+
+ const positionAttributes = usePositionAttributes(t);
+
+ const [timestamp, setTimestamp] = useState(Date.now());
+ const [items, setItems] = useState([]);
+ const [searchKeyword, setSearchKeyword] = useState('');
+ const [loading, setLoading] = useState(false);
+ const speedUnit = useAttributePreference('speedUnit');
+ const distanceUnit = useAttributePreference('distanceUnit');
+
+ useEffectAsync(async () => {
+ setLoading(true);
+ try {
+ const response = await fetch('/api/maintenance');
+ if (response.ok) {
+ setItems(await response.json());
+ } else {
+ throw Error(await response.text());
+ }
+ } finally {
+ setLoading(false);
+ }
+ }, [timestamp]);
+
+ const convertAttribute = (key, start, value) => {
+ const attribute = positionAttributes[key];
+ if (key.endsWith('Time')) {
+ if (start) {
+ return dayjs(value).locale('en').format('YYYY-MM-DD');
+ }
+ return `${value / 86400000} ${t('sharedDays')}`;
+ }
+ if (attribute && attribute.dataType) {
+ switch (attribute.dataType) {
+ case 'speed':
+ return formatSpeed(value, speedUnit, t);
+ case 'distance':
+ return formatDistance(value, distanceUnit, t);
+ default:
+ return value;
+ }
+ }
+
+ return value;
+ };
+
+ return (
+ <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'sharedMaintenance']}>
+ <SearchHeader keyword={searchKeyword} setKeyword={setSearchKeyword} />
+ <Table className={classes.table}>
+ <TableHead>
+ <TableRow>
+ <TableCell>{t('sharedName')}</TableCell>
+ <TableCell>{t('sharedType')}</TableCell>
+ <TableCell>{t('maintenanceStart')}</TableCell>
+ <TableCell>{t('maintenancePeriod')}</TableCell>
+ <TableCell className={classes.columnAction} />
+ </TableRow>
+ </TableHead>
+ <TableBody>
+ {!loading ? items.filter(filterByKeyword(searchKeyword)).map((item) => (
+ <TableRow key={item.id}>
+ <TableCell>{item.name}</TableCell>
+ <TableCell>{item.type}</TableCell>
+ <TableCell>{convertAttribute(item.type, true, item.start)}</TableCell>
+ <TableCell>{convertAttribute(item.type, false, item.period)}</TableCell>
+ <TableCell className={classes.columnAction} padding="none">
+ <CollectionActions itemId={item.id} editPath="/settings/maintenance" endpoint="maintenance" setTimestamp={setTimestamp} />
+ </TableCell>
+ </TableRow>
+ )) : (<TableShimmer columns={5} endAction />)}
+ </TableBody>
+ </Table>
+ <CollectionFab editPath="/settings/maintenance" />
+ </PageLayout>
+ );
+};
+
+export default MaintenacesPage;
diff --git a/src/settings/NotificationPage.jsx b/src/settings/NotificationPage.jsx
new file mode 100644
index 00000000..63aa9b95
--- /dev/null
+++ b/src/settings/NotificationPage.jsx
@@ -0,0 +1,144 @@
+import React, { useState } from 'react';
+
+import {
+ Accordion,
+ AccordionSummary,
+ AccordionDetails,
+ Typography,
+ FormControlLabel,
+ Checkbox,
+ FormGroup,
+ Button,
+} from '@mui/material';
+import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
+import { useTranslation, useTranslationKeys } from '../common/components/LocalizationProvider';
+import EditItemView from './components/EditItemView';
+import { prefixString, unprefixString } from '../common/util/stringUtils';
+import SelectField from '../common/components/SelectField';
+import SettingsMenu from './components/SettingsMenu';
+import { useCatch } from '../reactHelper';
+import useSettingsStyles from './common/useSettingsStyles';
+
+const NotificationPage = () => {
+ const classes = useSettingsStyles();
+ const t = useTranslation();
+
+ const [item, setItem] = useState();
+
+ const alarms = useTranslationKeys((it) => it.startsWith('alarm')).map((it) => ({
+ key: unprefixString('alarm', it),
+ name: t(it),
+ }));
+
+ const testNotificators = useCatch(async () => {
+ await Promise.all(item.notificators.split(/[, ]+/).map(async (notificator) => {
+ const response = await fetch(`/api/notifications/test/${notificator}`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(item),
+ });
+ if (!response.ok) {
+ throw Error(await response.text());
+ }
+ }));
+ });
+
+ const validate = () => item && item.type && item.notificators && (!item.notificators?.includes('command') || item.commandId);
+
+ return (
+ <EditItemView
+ endpoint="notifications"
+ item={item}
+ setItem={setItem}
+ validate={validate}
+ menu={<SettingsMenu />}
+ breadcrumbs={['settingsTitle', 'sharedNotification']}
+ >
+ {item && (
+ <>
+ <Accordion defaultExpanded>
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography variant="subtitle1">
+ {t('sharedRequired')}
+ </Typography>
+ </AccordionSummary>
+ <AccordionDetails className={classes.details}>
+ <SelectField
+ value={item.type}
+ onChange={(e) => setItem({ ...item, type: e.target.value })}
+ endpoint="/api/notifications/types"
+ keyGetter={(it) => it.type}
+ titleGetter={(it) => t(prefixString('event', it.type))}
+ label={t('sharedType')}
+ />
+ {item.type === 'alarm' && (
+ <SelectField
+ multiple
+ value={item.attributes && item.attributes.alarms ? item.attributes.alarms.split(/[, ]+/) : []}
+ onChange={(e) => setItem({ ...item, attributes: { ...item.attributes, alarms: e.target.value.join() } })}
+ data={alarms}
+ keyGetter={(it) => it.key}
+ label={t('sharedAlarms')}
+ />
+ )}
+ <SelectField
+ multiple
+ value={item.notificators ? item.notificators.split(/[, ]+/) : []}
+ onChange={(e) => setItem({ ...item, notificators: e.target.value.join() })}
+ endpoint="/api/notifications/notificators"
+ keyGetter={(it) => it.type}
+ titleGetter={(it) => t(prefixString('notificator', it.type))}
+ label={t('notificationNotificators')}
+ />
+ {item.notificators?.includes('command') && (
+ <SelectField
+ value={item.commandId}
+ onChange={(event) => setItem({ ...item, commandId: Number(event.target.value) })}
+ endpoint="/api/commands"
+ titleGetter={(it) => it.description}
+ label={t('sharedSavedCommand')}
+ />
+ )}
+ <Button
+ variant="outlined"
+ color="primary"
+ onClick={testNotificators}
+ disabled={!item.notificators}
+ >
+ {t('sharedTestNotificators')}
+ </Button>
+ <FormGroup>
+ <FormControlLabel
+ control={(
+ <Checkbox
+ checked={item.always}
+ onChange={(event) => setItem({ ...item, always: event.target.checked })}
+ />
+ )}
+ label={t('notificationAlways')}
+ />
+ </FormGroup>
+ </AccordionDetails>
+ </Accordion>
+ <Accordion>
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography variant="subtitle1">
+ {t('sharedExtra')}
+ </Typography>
+ </AccordionSummary>
+ <AccordionDetails className={classes.details}>
+ <SelectField
+ value={item.calendarId}
+ onChange={(event) => setItem({ ...item, calendarId: Number(event.target.value) })}
+ endpoint="/api/calendars"
+ label={t('sharedCalendar')}
+ />
+ </AccordionDetails>
+ </Accordion>
+ </>
+ )}
+ </EditItemView>
+ );
+};
+
+export default NotificationPage;
diff --git a/src/settings/NotificationsPage.jsx b/src/settings/NotificationsPage.jsx
new file mode 100644
index 00000000..f1e70a85
--- /dev/null
+++ b/src/settings/NotificationsPage.jsx
@@ -0,0 +1,83 @@
+import React, { useState } from 'react';
+import {
+ Table, TableRow, TableCell, TableHead, TableBody,
+} from '@mui/material';
+import { useEffectAsync } from '../reactHelper';
+import { prefixString } from '../common/util/stringUtils';
+import { formatBoolean } from '../common/util/formatter';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import PageLayout from '../common/components/PageLayout';
+import SettingsMenu from './components/SettingsMenu';
+import CollectionFab from './components/CollectionFab';
+import CollectionActions from './components/CollectionActions';
+import TableShimmer from '../common/components/TableShimmer';
+import SearchHeader, { filterByKeyword } from './components/SearchHeader';
+import useSettingsStyles from './common/useSettingsStyles';
+
+const NotificationsPage = () => {
+ const classes = useSettingsStyles();
+ const t = useTranslation();
+
+ const [timestamp, setTimestamp] = useState(Date.now());
+ const [items, setItems] = useState([]);
+ const [searchKeyword, setSearchKeyword] = useState('');
+ const [loading, setLoading] = useState(false);
+
+ useEffectAsync(async () => {
+ setLoading(true);
+ try {
+ const response = await fetch('/api/notifications');
+ if (response.ok) {
+ setItems(await response.json());
+ } else {
+ throw Error(await response.text());
+ }
+ } finally {
+ setLoading(false);
+ }
+ }, [timestamp]);
+
+ const formatList = (prefix, value) => {
+ if (value) {
+ return value
+ .split(/[, ]+/)
+ .filter(Boolean)
+ .map((it) => t(prefixString(prefix, it)))
+ .join(', ');
+ }
+ return '';
+ };
+
+ return (
+ <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'sharedNotifications']}>
+ <SearchHeader keyword={searchKeyword} setKeyword={setSearchKeyword} />
+ <Table className={classes.table}>
+ <TableHead>
+ <TableRow>
+ <TableCell>{t('notificationType')}</TableCell>
+ <TableCell>{t('notificationAlways')}</TableCell>
+ <TableCell>{t('sharedAlarms')}</TableCell>
+ <TableCell>{t('notificationNotificators')}</TableCell>
+ <TableCell className={classes.columnAction} />
+ </TableRow>
+ </TableHead>
+ <TableBody>
+ {!loading ? items.filter(filterByKeyword(searchKeyword)).map((item) => (
+ <TableRow key={item.id}>
+ <TableCell>{t(prefixString('event', item.type))}</TableCell>
+ <TableCell>{formatBoolean(item.always, t)}</TableCell>
+ <TableCell>{formatList('alarm', item.attributes.alarms)}</TableCell>
+ <TableCell>{formatList('notificator', item.notificators)}</TableCell>
+ <TableCell className={classes.columnAction} padding="none">
+ <CollectionActions itemId={item.id} editPath="/settings/notification" endpoint="notifications" setTimestamp={setTimestamp} />
+ </TableCell>
+ </TableRow>
+ )) : (<TableShimmer columns={5} endAction />)}
+ </TableBody>
+ </Table>
+ <CollectionFab editPath="/settings/notification" />
+ </PageLayout>
+ );
+};
+
+export default NotificationsPage;
diff --git a/src/settings/PreferencesPage.jsx b/src/settings/PreferencesPage.jsx
new file mode 100644
index 00000000..2d6df62f
--- /dev/null
+++ b/src/settings/PreferencesPage.jsx
@@ -0,0 +1,375 @@
+import React, { useState } from 'react';
+import dayjs from 'dayjs';
+import { useDispatch, useSelector } from 'react-redux';
+import { useNavigate } from 'react-router-dom';
+import {
+ Accordion, AccordionSummary, AccordionDetails, Typography, Container, FormControl, InputLabel, Select, MenuItem, Checkbox, FormControlLabel, FormGroup, InputAdornment, IconButton, OutlinedInput, Autocomplete, TextField, createFilterOptions, Button,
+} from '@mui/material';
+import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
+import CachedIcon from '@mui/icons-material/Cached';
+import ContentCopyIcon from '@mui/icons-material/ContentCopy';
+import { useTranslation, useTranslationKeys } from '../common/components/LocalizationProvider';
+import PageLayout from '../common/components/PageLayout';
+import SettingsMenu from './components/SettingsMenu';
+import usePositionAttributes from '../common/attributes/usePositionAttributes';
+import { prefixString, unprefixString } from '../common/util/stringUtils';
+import SelectField from '../common/components/SelectField';
+import useMapStyles from '../map/core/useMapStyles';
+import useMapOverlays from '../map/overlay/useMapOverlays';
+import { useCatch } from '../reactHelper';
+import { sessionActions } from '../store';
+import { useRestriction } from '../common/util/permissions';
+import useSettingsStyles from './common/useSettingsStyles';
+
+const deviceFields = [
+ { id: 'name', name: 'sharedName' },
+ { id: 'uniqueId', name: 'deviceIdentifier' },
+ { id: 'phone', name: 'sharedPhone' },
+ { id: 'model', name: 'deviceModel' },
+ { id: 'contact', name: 'deviceContact' },
+];
+
+const PreferencesPage = () => {
+ const classes = useSettingsStyles();
+ const dispatch = useDispatch();
+ const navigate = useNavigate();
+ const t = useTranslation();
+
+ const readonly = useRestriction('readonly');
+
+ const user = useSelector((state) => state.session.user);
+ const [attributes, setAttributes] = useState(user.attributes);
+
+ const versionApp = import.meta.env.VITE_APP_VERSION.slice(0, -2);
+ const versionServer = useSelector((state) => state.session.server.version);
+ const socket = useSelector((state) => state.session.socket);
+
+ const [token, setToken] = useState(null);
+ const [tokenExpiration, setTokenExpiration] = useState(dayjs().add(1, 'week').locale('en').format('YYYY-MM-DD'));
+
+ const mapStyles = useMapStyles();
+ const mapOverlays = useMapOverlays();
+
+ const positionAttributes = usePositionAttributes(t);
+
+ const filter = createFilterOptions();
+
+ const generateToken = useCatch(async () => {
+ const expiration = dayjs(tokenExpiration, 'YYYY-MM-DD').toISOString();
+ const response = await fetch('/api/session/token', {
+ method: 'POST',
+ body: new URLSearchParams(`expiration=${expiration}`),
+ });
+ if (response.ok) {
+ setToken(await response.text());
+ } else {
+ throw Error(await response.text());
+ }
+ });
+
+ const alarms = useTranslationKeys((it) => it.startsWith('alarm')).map((it) => ({
+ key: unprefixString('alarm', it),
+ name: t(it),
+ }));
+
+ const handleSave = useCatch(async () => {
+ const response = await fetch(`/api/users/${user.id}`, {
+ method: 'PUT',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ ...user, attributes }),
+ });
+ if (response.ok) {
+ dispatch(sessionActions.updateUser(await response.json()));
+ navigate(-1);
+ } else {
+ throw Error(await response.text());
+ }
+ });
+
+ return (
+ <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'sharedPreferences']}>
+ <Container maxWidth="xs" className={classes.container}>
+ {!readonly && (
+ <>
+ <Accordion defaultExpanded>
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography variant="subtitle1">
+ {t('mapTitle')}
+ </Typography>
+ </AccordionSummary>
+ <AccordionDetails className={classes.details}>
+ <FormControl>
+ <InputLabel>{t('mapActive')}</InputLabel>
+ <Select
+ label={t('mapActive')}
+ value={attributes.activeMapStyles?.split(',') || ['locationIqStreets', 'osm', 'carto']}
+ onChange={(e, child) => {
+ const clicked = mapStyles.find((s) => s.id === child.props.value);
+ if (clicked.available) {
+ setAttributes({ ...attributes, activeMapStyles: e.target.value.join(',') });
+ } else if (clicked.id !== 'custom') {
+ const query = new URLSearchParams({ attribute: clicked.attribute });
+ navigate(`/settings/user/${user.id}?${query.toString()}`);
+ }
+ }}
+ multiple
+ >
+ {mapStyles.map((style) => (
+ <MenuItem key={style.id} value={style.id}>
+ <Typography component="span" color={style.available ? 'textPrimary' : 'error'}>{style.title}</Typography>
+ </MenuItem>
+ ))}
+ </Select>
+ </FormControl>
+ <FormControl>
+ <InputLabel>{t('mapOverlay')}</InputLabel>
+ <Select
+ label={t('mapOverlay')}
+ value={attributes.selectedMapOverlay || ''}
+ onChange={(e) => {
+ const clicked = mapOverlays.find((o) => o.id === e.target.value);
+ if (!clicked || clicked.available) {
+ setAttributes({ ...attributes, selectedMapOverlay: e.target.value });
+ } else if (clicked.id !== 'custom') {
+ const query = new URLSearchParams({ attribute: clicked.attribute });
+ navigate(`/settings/user/${user.id}?${query.toString()}`);
+ }
+ }}
+ >
+ <MenuItem value="">{'\u00a0'}</MenuItem>
+ {mapOverlays.map((overlay) => (
+ <MenuItem key={overlay.id} value={overlay.id}>
+ <Typography component="span" color={overlay.available ? 'textPrimary' : 'error'}>{overlay.title}</Typography>
+ </MenuItem>
+ ))}
+ </Select>
+ </FormControl>
+ <Autocomplete
+ multiple
+ freeSolo
+ options={Object.keys(positionAttributes)}
+ getOptionLabel={(option) => (positionAttributes[option]?.name || option)}
+ value={attributes.positionItems?.split(',') || ['speed', 'address', 'totalDistance', 'course']}
+ onChange={(_, option) => {
+ setAttributes({ ...attributes, positionItems: option.join(',') });
+ }}
+ filterOptions={(options, params) => {
+ const filtered = filter(options, params);
+ if (params.inputValue && !filtered.includes(params.inputValue)) {
+ filtered.push(params.inputValue);
+ }
+ return filtered;
+ }}
+ renderInput={(params) => (
+ <TextField
+ {...params}
+ label={t('attributePopupInfo')}
+ />
+ )}
+ />
+ <FormControl>
+ <InputLabel>{t('mapLiveRoutes')}</InputLabel>
+ <Select
+ label={t('mapLiveRoutes')}
+ value={attributes.mapLiveRoutes || 'none'}
+ onChange={(e) => setAttributes({ ...attributes, mapLiveRoutes: e.target.value })}
+ >
+ <MenuItem value="none">{t('sharedDisabled')}</MenuItem>
+ <MenuItem value="selected">{t('deviceSelected')}</MenuItem>
+ <MenuItem value="all">{t('notificationAlways')}</MenuItem>
+ </Select>
+ </FormControl>
+ <FormControl>
+ <InputLabel>{t('mapDirection')}</InputLabel>
+ <Select
+ label={t('mapDirection')}
+ value={attributes.mapDirection || 'selected'}
+ onChange={(e) => setAttributes({ ...attributes, mapDirection: e.target.value })}
+ >
+ <MenuItem value="none">{t('sharedDisabled')}</MenuItem>
+ <MenuItem value="selected">{t('deviceSelected')}</MenuItem>
+ <MenuItem value="all">{t('notificationAlways')}</MenuItem>
+ </Select>
+ </FormControl>
+ <FormGroup>
+ <FormControlLabel
+ control={(
+ <Checkbox
+ checked={attributes.hasOwnProperty('mapGeofences') ? attributes.mapGeofences : true}
+ onChange={(e) => setAttributes({ ...attributes, mapGeofences: e.target.checked })}
+ />
+ )}
+ label={t('attributeShowGeofences')}
+ />
+ <FormControlLabel
+ control={(
+ <Checkbox
+ checked={attributes.hasOwnProperty('mapFollow') ? attributes.mapFollow : false}
+ onChange={(e) => setAttributes({ ...attributes, mapFollow: e.target.checked })}
+ />
+ )}
+ label={t('deviceFollow')}
+ />
+ <FormControlLabel
+ control={(
+ <Checkbox
+ checked={attributes.hasOwnProperty('mapCluster') ? attributes.mapCluster : true}
+ onChange={(e) => setAttributes({ ...attributes, mapCluster: e.target.checked })}
+ />
+ )}
+ label={t('mapClustering')}
+ />
+ <FormControlLabel
+ control={(
+ <Checkbox
+ checked={attributes.hasOwnProperty('mapOnSelect') ? attributes.mapOnSelect : true}
+ onChange={(e) => setAttributes({ ...attributes, mapOnSelect: e.target.checked })}
+ />
+ )}
+ label={t('mapOnSelect')}
+ />
+ </FormGroup>
+ </AccordionDetails>
+ </Accordion>
+ <Accordion>
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography variant="subtitle1">
+ {t('deviceTitle')}
+ </Typography>
+ </AccordionSummary>
+ <AccordionDetails className={classes.details}>
+ <SelectField
+ value={attributes.devicePrimary || 'name'}
+ onChange={(e) => setAttributes({ ...attributes, devicePrimary: e.target.value })}
+ data={deviceFields}
+ titleGetter={(it) => t(it.name)}
+ label={t('devicePrimaryInfo')}
+ />
+ <SelectField
+ value={attributes.deviceSecondary}
+ onChange={(e) => setAttributes({ ...attributes, deviceSecondary: e.target.value })}
+ data={deviceFields}
+ titleGetter={(it) => t(it.name)}
+ label={t('deviceSecondaryInfo')}
+ />
+ </AccordionDetails>
+ </Accordion>
+ <Accordion>
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography variant="subtitle1">
+ {t('sharedSound')}
+ </Typography>
+ </AccordionSummary>
+ <AccordionDetails className={classes.details}>
+ <SelectField
+ multiple
+ value={attributes.soundEvents?.split(',') || []}
+ onChange={(e) => setAttributes({ ...attributes, soundEvents: e.target.value.join(',') })}
+ endpoint="/api/notifications/types"
+ keyGetter={(it) => it.type}
+ titleGetter={(it) => t(prefixString('event', it.type))}
+ label={t('eventsSoundEvents')}
+ />
+ <SelectField
+ multiple
+ value={attributes.soundAlarms?.split(',') || ['sos']}
+ onChange={(e) => setAttributes({ ...attributes, soundAlarms: e.target.value.join(',') })}
+ data={alarms}
+ keyGetter={(it) => it.key}
+ label={t('eventsSoundAlarms')}
+ />
+ </AccordionDetails>
+ </Accordion>
+ </>
+ )}
+ <Accordion>
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography variant="subtitle1">
+ {t('userToken')}
+ </Typography>
+ </AccordionSummary>
+ <AccordionDetails className={classes.details}>
+ <TextField
+ label={t('userExpirationTime')}
+ type="date"
+ value={tokenExpiration}
+ onChange={(e) => {
+ setTokenExpiration(e.target.value);
+ setToken(null);
+ }}
+ />
+ <FormControl>
+ <OutlinedInput
+ multiline
+ rows={6}
+ readOnly
+ type="text"
+ value={token || ''}
+ endAdornment={(
+ <InputAdornment position="end">
+ <div className={classes.verticalActions}>
+ <IconButton size="small" edge="end" onClick={generateToken} disabled={!!token}>
+ <CachedIcon fontSize="small" />
+ </IconButton>
+ <IconButton size="small" edge="end" onClick={() => navigator.clipboard.writeText(token)} disabled={!token}>
+ <ContentCopyIcon fontSize="small" />
+ </IconButton>
+ </div>
+ </InputAdornment>
+ )}
+ />
+ </FormControl>
+ </AccordionDetails>
+ </Accordion>
+ {!readonly && (
+ <>
+ <Accordion>
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography variant="subtitle1">
+ {t('sharedInfoTitle')}
+ </Typography>
+ </AccordionSummary>
+ <AccordionDetails className={classes.details}>
+ <TextField
+ value={versionApp}
+ label={t('settingsAppVersion')}
+ disabled
+ />
+ <TextField
+ value={versionServer || '-'}
+ label={t('settingsServerVersion')}
+ disabled
+ />
+ <TextField
+ value={socket ? t('deviceStatusOnline') : t('deviceStatusOffline')}
+ label={t('settingsConnection')}
+ disabled
+ />
+ </AccordionDetails>
+ </Accordion>
+ <div className={classes.buttons}>
+ <Button
+ type="button"
+ color="primary"
+ variant="outlined"
+ onClick={() => navigate(-1)}
+ >
+ {t('sharedCancel')}
+ </Button>
+ <Button
+ type="button"
+ color="primary"
+ variant="contained"
+ onClick={handleSave}
+ >
+ {t('sharedSave')}
+ </Button>
+ </div>
+ </>
+ )}
+ </Container>
+ </PageLayout>
+ );
+};
+
+export default PreferencesPage;
diff --git a/src/settings/ServerPage.jsx b/src/settings/ServerPage.jsx
new file mode 100644
index 00000000..0ac76334
--- /dev/null
+++ b/src/settings/ServerPage.jsx
@@ -0,0 +1,316 @@
+import React, { useState } from 'react';
+import TextField from '@mui/material/TextField';
+
+import {
+ Accordion,
+ AccordionSummary,
+ AccordionDetails,
+ Typography,
+ Button,
+ FormControl,
+ Container,
+ Checkbox,
+ FormControlLabel,
+ InputLabel,
+ Select,
+ MenuItem,
+ FormGroup,
+} from '@mui/material';
+import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
+import { useNavigate } from 'react-router-dom';
+import { useDispatch, useSelector } from 'react-redux';
+import { DropzoneArea } from 'react-mui-dropzone';
+import { sessionActions } from '../store';
+import EditAttributesAccordion from './components/EditAttributesAccordion';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import SelectField from '../common/components/SelectField';
+import PageLayout from '../common/components/PageLayout';
+import SettingsMenu from './components/SettingsMenu';
+import useCommonDeviceAttributes from '../common/attributes/useCommonDeviceAttributes';
+import useCommonUserAttributes from '../common/attributes/useCommonUserAttributes';
+import { useCatch } from '../reactHelper';
+import useServerAttributes from '../common/attributes/useServerAttributes';
+import useMapStyles from '../map/core/useMapStyles';
+import { map } from '../map/core/MapView';
+import useSettingsStyles from './common/useSettingsStyles';
+
+const ServerPage = () => {
+ const classes = useSettingsStyles();
+ const navigate = useNavigate();
+ const dispatch = useDispatch();
+ const t = useTranslation();
+
+ const mapStyles = useMapStyles();
+ const commonUserAttributes = useCommonUserAttributes(t);
+ const commonDeviceAttributes = useCommonDeviceAttributes(t);
+ const serverAttributes = useServerAttributes(t);
+
+ const original = useSelector((state) => state.session.server);
+ const [item, setItem] = useState({ ...original });
+
+ const handleFiles = useCatch(async (files) => {
+ if (files.length > 0) {
+ const file = files[0];
+ const response = await fetch(`/api/server/file/${file.path}`, {
+ method: 'POST',
+ body: file,
+ });
+ if (!response.ok) {
+ throw Error(await response.text());
+ }
+ }
+ });
+
+ const handleSave = useCatch(async () => {
+ const response = await fetch('/api/server', {
+ method: 'PUT',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(item),
+ });
+
+ if (response.ok) {
+ dispatch(sessionActions.updateServer(await response.json()));
+ navigate(-1);
+ } else {
+ throw Error(await response.text());
+ }
+ });
+
+ return (
+ <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'settingsServer']}>
+ <Container maxWidth="xs" className={classes.container}>
+ {item && (
+ <>
+ <Accordion defaultExpanded>
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography variant="subtitle1">
+ {t('sharedPreferences')}
+ </Typography>
+ </AccordionSummary>
+ <AccordionDetails className={classes.details}>
+ <TextField
+ value={item.mapUrl || ''}
+ onChange={(event) => setItem({ ...item, mapUrl: event.target.value })}
+ label={t('mapCustomLabel')}
+ />
+ <TextField
+ value={item.overlayUrl || ''}
+ onChange={(event) => setItem({ ...item, overlayUrl: event.target.value })}
+ label={t('mapOverlayCustom')}
+ />
+ <FormControl>
+ <InputLabel>{t('mapDefault')}</InputLabel>
+ <Select
+ label={t('mapDefault')}
+ value={item.map || 'locationIqStreets'}
+ onChange={(e) => setItem({ ...item, map: e.target.value })}
+ >
+ {mapStyles.filter((style) => style.available).map((style) => (
+ <MenuItem key={style.id} value={style.id}>
+ <Typography component="span">{style.title}</Typography>
+ </MenuItem>
+ ))}
+ </Select>
+ </FormControl>
+ <FormControl>
+ <InputLabel>{t('settingsCoordinateFormat')}</InputLabel>
+ <Select
+ label={t('settingsCoordinateFormat')}
+ value={item.coordinateFormat || 'dd'}
+ onChange={(event) => setItem({ ...item, coordinateFormat: event.target.value })}
+ >
+ <MenuItem value="dd">{t('sharedDecimalDegrees')}</MenuItem>
+ <MenuItem value="ddm">{t('sharedDegreesDecimalMinutes')}</MenuItem>
+ <MenuItem value="dms">{t('sharedDegreesMinutesSeconds')}</MenuItem>
+ </Select>
+ </FormControl>
+ <FormControl>
+ <InputLabel>{t('settingsSpeedUnit')}</InputLabel>
+ <Select
+ label={t('settingsSpeedUnit')}
+ value={item.attributes.speedUnit || 'kn'}
+ onChange={(e) => setItem({ ...item, attributes: { ...item.attributes, speedUnit: e.target.value } })}
+ >
+ <MenuItem value="kn">{t('sharedKn')}</MenuItem>
+ <MenuItem value="kmh">{t('sharedKmh')}</MenuItem>
+ <MenuItem value="mph">{t('sharedMph')}</MenuItem>
+ </Select>
+ </FormControl>
+ <FormControl>
+ <InputLabel>{t('settingsDistanceUnit')}</InputLabel>
+ <Select
+ label={t('settingsDistanceUnit')}
+ value={item.attributes.distanceUnit || 'km'}
+ onChange={(e) => setItem({ ...item, attributes: { ...item.attributes, distanceUnit: e.target.value } })}
+ >
+ <MenuItem value="km">{t('sharedKm')}</MenuItem>
+ <MenuItem value="mi">{t('sharedMi')}</MenuItem>
+ <MenuItem value="nmi">{t('sharedNmi')}</MenuItem>
+ </Select>
+ </FormControl>
+ <FormControl>
+ <InputLabel>{t('settingsAltitudeUnit')}</InputLabel>
+ <Select
+ label={t('settingsAltitudeUnit')}
+ value={item.attributes.altitudeUnit || 'm'}
+ onChange={(e) => setItem({ ...item, attributes: { ...item.attributes, altitudeUnit: e.target.value } })}
+ >
+ <MenuItem value="m">{t('sharedMeters')}</MenuItem>
+ <MenuItem value="ft">{t('sharedFeet')}</MenuItem>
+ </Select>
+ </FormControl>
+ <FormControl>
+ <InputLabel>{t('settingsVolumeUnit')}</InputLabel>
+ <Select
+ label={t('settingsVolumeUnit')}
+ value={item.attributes.volumeUnit || 'ltr'}
+ onChange={(e) => setItem({ ...item, attributes: { ...item.attributes, volumeUnit: e.target.value } })}
+ >
+ <MenuItem value="ltr">{t('sharedLiter')}</MenuItem>
+ <MenuItem value="usGal">{t('sharedUsGallon')}</MenuItem>
+ <MenuItem value="impGal">{t('sharedImpGallon')}</MenuItem>
+ </Select>
+ </FormControl>
+ <SelectField
+ value={item.attributes.timezone}
+ onChange={(e) => setItem({ ...item, attributes: { ...item.attributes, timezone: e.target.value } })}
+ endpoint="/api/server/timezones"
+ keyGetter={(it) => it}
+ titleGetter={(it) => it}
+ label={t('sharedTimezone')}
+ />
+ <TextField
+ value={item.poiLayer || ''}
+ onChange={(event) => setItem({ ...item, poiLayer: event.target.value })}
+ label={t('mapPoiLayer')}
+ />
+ <TextField
+ value={item.announcement || ''}
+ onChange={(event) => setItem({ ...item, announcement: event.target.value })}
+ label={t('serverAnnouncement')}
+ />
+ <FormGroup>
+ <FormControlLabel
+ control={<Checkbox checked={item.twelveHourFormat} onChange={(event) => setItem({ ...item, twelveHourFormat: event.target.checked })} />}
+ label={t('settingsTwelveHourFormat')}
+ />
+ <FormControlLabel
+ control={<Checkbox checked={item.forceSettings} onChange={(event) => setItem({ ...item, forceSettings: event.target.checked })} />}
+ label={t('serverForceSettings')}
+ />
+ </FormGroup>
+ </AccordionDetails>
+ </Accordion>
+ <Accordion>
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography variant="subtitle1">
+ {t('sharedLocation')}
+ </Typography>
+ </AccordionSummary>
+ <AccordionDetails className={classes.details}>
+ <TextField
+ type="number"
+ value={item.latitude || 0}
+ onChange={(event) => setItem({ ...item, latitude: Number(event.target.value) })}
+ label={t('positionLatitude')}
+ />
+ <TextField
+ type="number"
+ value={item.longitude || 0}
+ onChange={(event) => setItem({ ...item, longitude: Number(event.target.value) })}
+ label={t('positionLongitude')}
+ />
+ <TextField
+ type="number"
+ value={item.zoom || 0}
+ onChange={(event) => setItem({ ...item, zoom: Number(event.target.value) })}
+ label={t('serverZoom')}
+ />
+ <Button
+ variant="outlined"
+ color="primary"
+ onClick={() => {
+ const { lng, lat } = map.getCenter();
+ setItem({
+ ...item,
+ latitude: Number(lat.toFixed(6)),
+ longitude: Number(lng.toFixed(6)),
+ zoom: Number(map.getZoom().toFixed(1)),
+ });
+ }}
+ >
+ {t('mapCurrentLocation')}
+ </Button>
+ </AccordionDetails>
+ </Accordion>
+ <Accordion>
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography variant="subtitle1">
+ {t('sharedPermissions')}
+ </Typography>
+ </AccordionSummary>
+ <AccordionDetails className={classes.details}>
+ <FormGroup>
+ <FormControlLabel
+ control={<Checkbox checked={item.registration} onChange={(event) => setItem({ ...item, registration: event.target.checked })} />}
+ label={t('serverRegistration')}
+ />
+ <FormControlLabel
+ control={<Checkbox checked={item.readonly} onChange={(event) => setItem({ ...item, readonly: event.target.checked })} />}
+ label={t('serverReadonly')}
+ />
+ <FormControlLabel
+ control={<Checkbox checked={item.deviceReadonly} onChange={(event) => setItem({ ...item, deviceReadonly: event.target.checked })} />}
+ label={t('userDeviceReadonly')}
+ />
+ <FormControlLabel
+ control={<Checkbox checked={item.limitCommands} onChange={(event) => setItem({ ...item, limitCommands: event.target.checked })} />}
+ label={t('userLimitCommands')}
+ />
+ <FormControlLabel
+ control={<Checkbox checked={item.disableReports} onChange={(event) => setItem({ ...item, disableReports: event.target.checked })} />}
+ label={t('userDisableReports')}
+ />
+ <FormControlLabel
+ control={<Checkbox checked={item.fixedEmail} onChange={(e) => setItem({ ...item, fixedEmail: e.target.checked })} />}
+ label={t('userFixedEmail')}
+ />
+ </FormGroup>
+ </AccordionDetails>
+ </Accordion>
+ <Accordion>
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography variant="subtitle1">
+ {t('sharedFile')}
+ </Typography>
+ </AccordionSummary>
+ <AccordionDetails className={classes.details}>
+ <DropzoneArea
+ dropzoneText={t('sharedDropzoneText')}
+ filesLimit={1}
+ onChange={handleFiles}
+ showAlerts={false}
+ />
+ </AccordionDetails>
+ </Accordion>
+ <EditAttributesAccordion
+ attributes={item.attributes}
+ setAttributes={(attributes) => setItem({ ...item, attributes })}
+ definitions={{ ...commonUserAttributes, ...commonDeviceAttributes, ...serverAttributes }}
+ />
+ </>
+ )}
+ <div className={classes.buttons}>
+ <Button type="button" color="primary" variant="outlined" onClick={() => navigate(-1)}>
+ {t('sharedCancel')}
+ </Button>
+ <Button type="button" color="primary" variant="contained" onClick={handleSave}>
+ {t('sharedSave')}
+ </Button>
+ </div>
+ </Container>
+ </PageLayout>
+ );
+};
+
+export default ServerPage;
diff --git a/src/settings/SharePage.jsx b/src/settings/SharePage.jsx
new file mode 100644
index 00000000..d16fe44d
--- /dev/null
+++ b/src/settings/SharePage.jsx
@@ -0,0 +1,109 @@
+import React, { useState } from 'react';
+import { useSelector } from 'react-redux';
+import { useNavigate, useParams } from 'react-router-dom';
+import dayjs from 'dayjs';
+import {
+ Accordion,
+ AccordionSummary,
+ AccordionDetails,
+ Typography,
+ Container,
+ TextField,
+ Button,
+} from '@mui/material';
+import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import PageLayout from '../common/components/PageLayout';
+import SettingsMenu from './components/SettingsMenu';
+import { useCatchCallback } from '../reactHelper';
+import useSettingsStyles from './common/useSettingsStyles';
+
+const SharePage = () => {
+ const navigate = useNavigate();
+ const classes = useSettingsStyles();
+ const t = useTranslation();
+
+ const { id } = useParams();
+
+ const device = useSelector((state) => state.devices.items[id]);
+
+ const [expiration, setExpiration] = useState(dayjs().add(1, 'week').locale('en').format('YYYY-MM-DD'));
+ const [link, setLink] = useState();
+
+ const handleShare = useCatchCallback(async () => {
+ const expirationTime = dayjs(expiration).toISOString();
+ const response = await fetch('/api/devices/share', {
+ method: 'POST',
+ body: new URLSearchParams(`deviceId=${id}&expiration=${expirationTime}`),
+ });
+ if (response.ok) {
+ const token = await response.text();
+ setLink(`${window.location.origin}?token=${token}`);
+ } else {
+ throw Error(await response.text());
+ }
+ }, [id, expiration, setLink]);
+
+ return (
+ <PageLayout menu={<SettingsMenu />} breadcrumbs={['deviceShare']}>
+ <Container maxWidth="xs" className={classes.container}>
+ <Accordion defaultExpanded>
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography variant="subtitle1">
+ {t('sharedRequired')}
+ </Typography>
+ </AccordionSummary>
+ <AccordionDetails className={classes.details}>
+ <TextField
+ value={device.name}
+ label={t('sharedDevice')}
+ disabled
+ />
+ <TextField
+ label={t('userExpirationTime')}
+ type="date"
+ value={(expiration && dayjs(expiration).locale('en').format('YYYY-MM-DD')) || '2099-01-01'}
+ onChange={(e) => setExpiration(dayjs(e.target.value, 'YYYY-MM-DD').locale('en').format())}
+ />
+ <Button
+ variant="outlined"
+ color="primary"
+ onClick={handleShare}
+ >
+ {t('reportShow')}
+ </Button>
+ <TextField
+ value={link || ''}
+ onChange={(e) => setLink(e.target.value)}
+ label={t('sharedLink')}
+ InputProps={{
+ readOnly: true,
+ }}
+ />
+ </AccordionDetails>
+ </Accordion>
+ <div className={classes.buttons}>
+ <Button
+ type="button"
+ color="primary"
+ variant="outlined"
+ onClick={() => navigate(-1)}
+ >
+ {t('sharedCancel')}
+ </Button>
+ <Button
+ type="button"
+ color="primary"
+ variant="contained"
+ onClick={() => navigator.clipboard?.writeText(link)}
+ disabled={!link}
+ >
+ {t('sharedCopy')}
+ </Button>
+ </div>
+ </Container>
+ </PageLayout>
+ );
+};
+
+export default SharePage;
diff --git a/src/settings/UserConnectionsPage.jsx b/src/settings/UserConnectionsPage.jsx
new file mode 100644
index 00000000..3ca0bdc1
--- /dev/null
+++ b/src/settings/UserConnectionsPage.jsx
@@ -0,0 +1,129 @@
+import React from 'react';
+import { useParams } from 'react-router-dom';
+import {
+ Accordion,
+ AccordionSummary,
+ AccordionDetails,
+ Typography,
+ Container,
+} from '@mui/material';
+import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
+import LinkField from '../common/components/LinkField';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import SettingsMenu from './components/SettingsMenu';
+import { formatNotificationTitle } from '../common/util/formatter';
+import PageLayout from '../common/components/PageLayout';
+import useSettingsStyles from './common/useSettingsStyles';
+
+const UserConnectionsPage = () => {
+ const classes = useSettingsStyles();
+ const t = useTranslation();
+
+ const { id } = useParams();
+
+ return (
+ <PageLayout
+ menu={<SettingsMenu />}
+ breadcrumbs={['settingsTitle', 'settingsUser', 'sharedConnections']}
+ >
+ <Container maxWidth="xs" className={classes.container}>
+ <Accordion defaultExpanded>
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography variant="subtitle1">
+ {t('sharedConnections')}
+ </Typography>
+ </AccordionSummary>
+ <AccordionDetails className={classes.details}>
+ <LinkField
+ endpointAll="/api/devices?all=true"
+ endpointLinked={`/api/devices?userId=${id}`}
+ baseId={id}
+ keyBase="userId"
+ keyLink="deviceId"
+ titleGetter={(it) => `${it.name} (${it.uniqueId})`}
+ label={t('deviceTitle')}
+ />
+ <LinkField
+ endpointAll="/api/groups?all=true"
+ endpointLinked={`/api/groups?userId=${id}`}
+ baseId={id}
+ keyBase="userId"
+ keyLink="groupId"
+ label={t('settingsGroups')}
+ />
+ <LinkField
+ endpointAll="/api/geofences?all=true"
+ endpointLinked={`/api/geofences?userId=${id}`}
+ baseId={id}
+ keyBase="userId"
+ keyLink="geofenceId"
+ label={t('sharedGeofences')}
+ />
+ <LinkField
+ endpointAll="/api/notifications?all=true"
+ endpointLinked={`/api/notifications?userId=${id}`}
+ baseId={id}
+ keyBase="userId"
+ keyLink="notificationId"
+ titleGetter={(it) => formatNotificationTitle(t, it, true)}
+ label={t('sharedNotifications')}
+ />
+ <LinkField
+ endpointAll="/api/calendars?all=true"
+ endpointLinked={`/api/calendars?userId=${id}`}
+ baseId={id}
+ keyBase="userId"
+ keyLink="calendarId"
+ label={t('sharedCalendars')}
+ />
+ <LinkField
+ endpointAll="/api/users?all=true"
+ endpointLinked={`/api/users?userId=${id}`}
+ baseId={id}
+ keyBase="userId"
+ keyLink="managedUserId"
+ label={t('settingsUsers')}
+ />
+ <LinkField
+ endpointAll="/api/attributes/computed?all=true"
+ endpointLinked={`/api/attributes/computed?userId=${id}`}
+ baseId={id}
+ keyBase="userId"
+ keyLink="attributeId"
+ titleGetter={(it) => it.description}
+ label={t('sharedComputedAttributes')}
+ />
+ <LinkField
+ endpointAll="/api/drivers?all=true"
+ endpointLinked={`/api/drivers?userId=${id}`}
+ baseId={id}
+ keyBase="userId"
+ keyLink="driverId"
+ titleGetter={(it) => `${it.name} (${it.uniqueId})`}
+ label={t('sharedDrivers')}
+ />
+ <LinkField
+ endpointAll="/api/commands?all=true"
+ endpointLinked={`/api/commands?userId=${id}`}
+ baseId={id}
+ keyBase="userId"
+ keyLink="commandId"
+ titleGetter={(it) => it.description}
+ label={t('sharedSavedCommands')}
+ />
+ <LinkField
+ endpointAll="/api/maintenance?all=true"
+ endpointLinked={`/api/maintenance?userId=${id}`}
+ baseId={id}
+ keyBase="userId"
+ keyLink="maintenanceId"
+ label={t('sharedMaintenance')}
+ />
+ </AccordionDetails>
+ </Accordion>
+ </Container>
+ </PageLayout>
+ );
+};
+
+export default UserConnectionsPage;
diff --git a/src/settings/UserPage.jsx b/src/settings/UserPage.jsx
new file mode 100644
index 00000000..6748dd31
--- /dev/null
+++ b/src/settings/UserPage.jsx
@@ -0,0 +1,428 @@
+import React, { useEffect, useState } from 'react';
+import { useNavigate, useParams } from 'react-router-dom';
+import {
+ Accordion,
+ AccordionSummary,
+ AccordionDetails,
+ Typography,
+ FormControl,
+ InputLabel,
+ Select,
+ MenuItem,
+ FormControlLabel,
+ Checkbox,
+ FormGroup,
+ TextField,
+ Button,
+ InputAdornment,
+ IconButton,
+ OutlinedInput,
+} from '@mui/material';
+import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
+import DeleteForeverIcon from '@mui/icons-material/DeleteForever';
+import CachedIcon from '@mui/icons-material/Cached';
+import CloseIcon from '@mui/icons-material/Close';
+import { useDispatch, useSelector } from 'react-redux';
+import dayjs from 'dayjs';
+import EditItemView from './components/EditItemView';
+import EditAttributesAccordion from './components/EditAttributesAccordion';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import useUserAttributes from '../common/attributes/useUserAttributes';
+import { sessionActions } from '../store';
+import SelectField from '../common/components/SelectField';
+import SettingsMenu from './components/SettingsMenu';
+import useCommonUserAttributes from '../common/attributes/useCommonUserAttributes';
+import { useAdministrator, useRestriction, useManager } from '../common/util/permissions';
+import useQuery from '../common/util/useQuery';
+import { useCatch } from '../reactHelper';
+import useMapStyles from '../map/core/useMapStyles';
+import { map } from '../map/core/MapView';
+import useSettingsStyles from './common/useSettingsStyles';
+
+const UserPage = () => {
+ const classes = useSettingsStyles();
+ const navigate = useNavigate();
+ const dispatch = useDispatch();
+ const t = useTranslation();
+
+ const admin = useAdministrator();
+ const manager = useManager();
+ const fixedEmail = useRestriction('fixedEmail');
+
+ const currentUser = useSelector((state) => state.session.user);
+ const registrationEnabled = useSelector((state) => state.session.server.registration);
+ const openIdForced = useSelector((state) => state.session.server.openIdForce);
+ const totpEnable = useSelector((state) => state.session.server.attributes.totpEnable);
+ const totpForce = useSelector((state) => state.session.server.attributes.totpForce);
+
+ const mapStyles = useMapStyles();
+ const commonUserAttributes = useCommonUserAttributes(t);
+ const userAttributes = useUserAttributes(t);
+
+ const { id } = useParams();
+ const [item, setItem] = useState(id === currentUser.id.toString() ? currentUser : null);
+
+ const [deleteEmail, setDeleteEmail] = useState();
+ const [deleteFailed, setDeleteFailed] = useState(false);
+
+ const handleDelete = useCatch(async () => {
+ if (deleteEmail === currentUser.email) {
+ setDeleteFailed(false);
+ const response = await fetch(`/api/users/${currentUser.id}`, { method: 'DELETE' });
+ if (response.ok) {
+ navigate('/login');
+ dispatch(sessionActions.updateUser(null));
+ } else {
+ throw Error(await response.text());
+ }
+ } else {
+ setDeleteFailed(true);
+ }
+ });
+
+ const handleGenerateTotp = useCatch(async () => {
+ const response = await fetch('/api/users/totp', { method: 'POST' });
+ if (response.ok) {
+ setItem({ ...item, totpKey: await response.text() });
+ } else {
+ throw Error(await response.text());
+ }
+ });
+
+ const query = useQuery();
+ const [queryHandled, setQueryHandled] = useState(false);
+ const attribute = query.get('attribute');
+
+ useEffect(() => {
+ if (!queryHandled && item && attribute) {
+ if (!item.attributes.hasOwnProperty('attribute')) {
+ const updatedAttributes = { ...item.attributes };
+ updatedAttributes[attribute] = '';
+ setItem({ ...item, attributes: updatedAttributes });
+ }
+ setQueryHandled(true);
+ }
+ }, [item, queryHandled, setQueryHandled, attribute]);
+
+ const onItemSaved = (result) => {
+ if (result.id === currentUser.id) {
+ dispatch(sessionActions.updateUser(result));
+ }
+ };
+
+ const validate = () => item && item.name && item.email && (item.id || item.password) && (admin || !totpForce || item.totpKey);
+
+ return (
+ <EditItemView
+ endpoint="users"
+ item={item}
+ setItem={setItem}
+ defaultItem={admin ? { deviceLimit: -1 } : {}}
+ validate={validate}
+ onItemSaved={onItemSaved}
+ menu={<SettingsMenu />}
+ breadcrumbs={['settingsTitle', 'settingsUser']}
+ >
+ {item && (
+ <>
+ <Accordion defaultExpanded={!attribute}>
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography variant="subtitle1">
+ {t('sharedRequired')}
+ </Typography>
+ </AccordionSummary>
+ <AccordionDetails className={classes.details}>
+ <TextField
+ value={item.name || ''}
+ onChange={(e) => setItem({ ...item, name: e.target.value })}
+ label={t('sharedName')}
+ />
+ <TextField
+ value={item.email || ''}
+ onChange={(e) => setItem({ ...item, email: e.target.value })}
+ label={t('userEmail')}
+ disabled={fixedEmail}
+ />
+ {!openIdForced && (
+ <TextField
+ type="password"
+ onChange={(e) => setItem({ ...item, password: e.target.value })}
+ label={t('userPassword')}
+ />
+ )}
+ {totpEnable && (
+ <FormControl>
+ <InputLabel>{t('loginTotpKey')}</InputLabel>
+ <OutlinedInput
+ readOnly
+ label={t('loginTotpKey')}
+ value={item.totpKey || ''}
+ endAdornment={(
+ <InputAdornment position="end">
+ <IconButton size="small" edge="end" onClick={handleGenerateTotp}>
+ <CachedIcon fontSize="small" />
+ </IconButton>
+ <IconButton size="small" edge="end" onClick={() => setItem({ ...item, totpKey: null })}>
+ <CloseIcon fontSize="small" />
+ </IconButton>
+ </InputAdornment>
+ )}
+ />
+ </FormControl>
+ )}
+ </AccordionDetails>
+ </Accordion>
+ <Accordion>
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography variant="subtitle1">
+ {t('sharedPreferences')}
+ </Typography>
+ </AccordionSummary>
+ <AccordionDetails className={classes.details}>
+ <TextField
+ value={item.phone || ''}
+ onChange={(e) => setItem({ ...item, phone: e.target.value })}
+ label={t('sharedPhone')}
+ />
+ <FormControl>
+ <InputLabel>{t('mapDefault')}</InputLabel>
+ <Select
+ label={t('mapDefault')}
+ value={item.map || 'locationIqStreets'}
+ onChange={(e) => setItem({ ...item, map: e.target.value })}
+ >
+ {mapStyles.filter((style) => style.available).map((style) => (
+ <MenuItem key={style.id} value={style.id}>
+ <Typography component="span">{style.title}</Typography>
+ </MenuItem>
+ ))}
+ </Select>
+ </FormControl>
+ <FormControl>
+ <InputLabel>{t('settingsCoordinateFormat')}</InputLabel>
+ <Select
+ label={t('settingsCoordinateFormat')}
+ value={item.coordinateFormat || 'dd'}
+ onChange={(e) => setItem({ ...item, coordinateFormat: e.target.value })}
+ >
+ <MenuItem value="dd">{t('sharedDecimalDegrees')}</MenuItem>
+ <MenuItem value="ddm">{t('sharedDegreesDecimalMinutes')}</MenuItem>
+ <MenuItem value="dms">{t('sharedDegreesMinutesSeconds')}</MenuItem>
+ </Select>
+ </FormControl>
+ <FormControl>
+ <InputLabel>{t('settingsSpeedUnit')}</InputLabel>
+ <Select
+ label={t('settingsSpeedUnit')}
+ value={(item.attributes && item.attributes.speedUnit) || 'kn'}
+ onChange={(e) => setItem({ ...item, attributes: { ...item.attributes, speedUnit: e.target.value } })}
+ >
+ <MenuItem value="kn">{t('sharedKn')}</MenuItem>
+ <MenuItem value="kmh">{t('sharedKmh')}</MenuItem>
+ <MenuItem value="mph">{t('sharedMph')}</MenuItem>
+ </Select>
+ </FormControl>
+ <FormControl>
+ <InputLabel>{t('settingsDistanceUnit')}</InputLabel>
+ <Select
+ label={t('settingsDistanceUnit')}
+ value={(item.attributes && item.attributes.distanceUnit) || 'km'}
+ onChange={(e) => setItem({ ...item, attributes: { ...item.attributes, distanceUnit: e.target.value } })}
+ >
+ <MenuItem value="km">{t('sharedKm')}</MenuItem>
+ <MenuItem value="mi">{t('sharedMi')}</MenuItem>
+ <MenuItem value="nmi">{t('sharedNmi')}</MenuItem>
+ </Select>
+ </FormControl>
+ <FormControl>
+ <InputLabel>{t('settingsAltitudeUnit')}</InputLabel>
+ <Select
+ label={t('settingsAltitudeUnit')}
+ value={(item.attributes && item.attributes.altitudeUnit) || 'm'}
+ onChange={(e) => setItem({ ...item, attributes: { ...item.attributes, altitudeUnit: e.target.value } })}
+ >
+ <MenuItem value="m">{t('sharedMeters')}</MenuItem>
+ <MenuItem value="ft">{t('sharedFeet')}</MenuItem>
+ </Select>
+ </FormControl>
+ <FormControl>
+ <InputLabel>{t('settingsVolumeUnit')}</InputLabel>
+ <Select
+ label={t('settingsVolumeUnit')}
+ value={(item.attributes && item.attributes.volumeUnit) || 'ltr'}
+ onChange={(e) => setItem({ ...item, attributes: { ...item.attributes, volumeUnit: e.target.value } })}
+ >
+ <MenuItem value="ltr">{t('sharedLiter')}</MenuItem>
+ <MenuItem value="usGal">{t('sharedUsGallon')}</MenuItem>
+ <MenuItem value="impGal">{t('sharedImpGallon')}</MenuItem>
+ </Select>
+ </FormControl>
+ <SelectField
+ value={item.attributes && item.attributes.timezone}
+ onChange={(e) => setItem({ ...item, attributes: { ...item.attributes, timezone: e.target.value } })}
+ endpoint="/api/server/timezones"
+ keyGetter={(it) => it}
+ titleGetter={(it) => it}
+ label={t('sharedTimezone')}
+ />
+ <TextField
+ value={item.poiLayer || ''}
+ onChange={(e) => setItem({ ...item, poiLayer: e.target.value })}
+ label={t('mapPoiLayer')}
+ />
+ <FormGroup>
+ <FormControlLabel
+ control={<Checkbox checked={item.twelveHourFormat} onChange={(e) => setItem({ ...item, twelveHourFormat: e.target.checked })} />}
+ label={t('settingsTwelveHourFormat')}
+ />
+ </FormGroup>
+ </AccordionDetails>
+ </Accordion>
+ <Accordion>
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography variant="subtitle1">
+ {t('sharedLocation')}
+ </Typography>
+ </AccordionSummary>
+ <AccordionDetails className={classes.details}>
+ <TextField
+ type="number"
+ value={item.latitude || 0}
+ onChange={(e) => setItem({ ...item, latitude: Number(e.target.value) })}
+ label={t('positionLatitude')}
+ />
+ <TextField
+ type="number"
+ value={item.longitude || 0}
+ onChange={(e) => setItem({ ...item, longitude: Number(e.target.value) })}
+ label={t('positionLongitude')}
+ />
+ <TextField
+ type="number"
+ value={item.zoom || 0}
+ onChange={(e) => setItem({ ...item, zoom: Number(e.target.value) })}
+ label={t('serverZoom')}
+ />
+ <Button
+ variant="outlined"
+ color="primary"
+ onClick={() => {
+ const { lng, lat } = map.getCenter();
+ setItem({
+ ...item,
+ latitude: Number(lat.toFixed(6)),
+ longitude: Number(lng.toFixed(6)),
+ zoom: Number(map.getZoom().toFixed(1)),
+ });
+ }}
+ >
+ {t('mapCurrentLocation')}
+ </Button>
+ </AccordionDetails>
+ </Accordion>
+ <Accordion>
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography variant="subtitle1">
+ {t('sharedPermissions')}
+ </Typography>
+ </AccordionSummary>
+ <AccordionDetails className={classes.details}>
+ <TextField
+ label={t('userExpirationTime')}
+ type="date"
+ value={(item.expirationTime && dayjs(item.expirationTime).locale('en').format('YYYY-MM-DD')) || '2099-01-01'}
+ onChange={(e) => setItem({ ...item, expirationTime: dayjs(e.target.value, 'YYYY-MM-DD').locale('en').format() })}
+ disabled={!manager}
+ />
+ <TextField
+ type="number"
+ value={item.deviceLimit || 0}
+ onChange={(e) => setItem({ ...item, deviceLimit: Number(e.target.value) })}
+ label={t('userDeviceLimit')}
+ disabled={!admin}
+ />
+ <TextField
+ type="number"
+ value={item.userLimit || 0}
+ onChange={(e) => setItem({ ...item, userLimit: Number(e.target.value) })}
+ label={t('userUserLimit')}
+ disabled={!admin}
+ />
+ <FormGroup>
+ <FormControlLabel
+ control={<Checkbox checked={item.disabled} onChange={(e) => setItem({ ...item, disabled: e.target.checked })} />}
+ label={t('sharedDisabled')}
+ disabled={!manager}
+ />
+ <FormControlLabel
+ control={<Checkbox checked={item.administrator} onChange={(e) => setItem({ ...item, administrator: e.target.checked })} />}
+ label={t('userAdmin')}
+ disabled={!admin}
+ />
+ <FormControlLabel
+ control={<Checkbox checked={item.readonly} onChange={(e) => setItem({ ...item, readonly: e.target.checked })} />}
+ label={t('serverReadonly')}
+ disabled={!manager}
+ />
+ <FormControlLabel
+ control={<Checkbox checked={item.deviceReadonly} onChange={(e) => setItem({ ...item, deviceReadonly: e.target.checked })} />}
+ label={t('userDeviceReadonly')}
+ disabled={!manager}
+ />
+ <FormControlLabel
+ control={<Checkbox checked={item.limitCommands} onChange={(e) => setItem({ ...item, limitCommands: e.target.checked })} />}
+ label={t('userLimitCommands')}
+ disabled={!manager}
+ />
+ <FormControlLabel
+ control={<Checkbox checked={item.disableReports} onChange={(e) => setItem({ ...item, disableReports: e.target.checked })} />}
+ label={t('userDisableReports')}
+ disabled={!manager}
+ />
+ <FormControlLabel
+ control={<Checkbox checked={item.fixedEmail} onChange={(e) => setItem({ ...item, fixedEmail: e.target.checked })} />}
+ label={t('userFixedEmail')}
+ disabled={!manager}
+ />
+ </FormGroup>
+ </AccordionDetails>
+ </Accordion>
+ <EditAttributesAccordion
+ attribute={attribute}
+ attributes={item.attributes}
+ setAttributes={(attributes) => setItem({ ...item, attributes })}
+ definitions={{ ...commonUserAttributes, ...userAttributes }}
+ focusAttribute={attribute}
+ />
+ {registrationEnabled && item.id === currentUser.id && !manager && (
+ <Accordion>
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography variant="subtitle1" color="error">
+ {t('userDeleteAccount')}
+ </Typography>
+ </AccordionSummary>
+ <AccordionDetails className={classes.details}>
+ <TextField
+ value={deleteEmail}
+ onChange={(e) => setDeleteEmail(e.target.value)}
+ label={t('userEmail')}
+ error={deleteFailed}
+ />
+ <Button
+ variant="outlined"
+ color="error"
+ onClick={handleDelete}
+ startIcon={<DeleteForeverIcon />}
+ >
+ {t('userDeleteAccount')}
+ </Button>
+ </AccordionDetails>
+ </Accordion>
+ )}
+ </>
+ )}
+ </EditItemView>
+ );
+};
+
+export default UserPage;
diff --git a/src/settings/UsersPage.jsx b/src/settings/UsersPage.jsx
new file mode 100644
index 00000000..2941965b
--- /dev/null
+++ b/src/settings/UsersPage.jsx
@@ -0,0 +1,130 @@
+import React, { useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import {
+ Table, TableRow, TableCell, TableHead, TableBody, Switch, TableFooter, FormControlLabel,
+} from '@mui/material';
+import LoginIcon from '@mui/icons-material/Login';
+import LinkIcon from '@mui/icons-material/Link';
+import { useCatch, useEffectAsync } from '../reactHelper';
+import { formatBoolean, formatTime } from '../common/util/formatter';
+import { useTranslation } from '../common/components/LocalizationProvider';
+import PageLayout from '../common/components/PageLayout';
+import SettingsMenu from './components/SettingsMenu';
+import CollectionFab from './components/CollectionFab';
+import CollectionActions from './components/CollectionActions';
+import TableShimmer from '../common/components/TableShimmer';
+import { useManager } from '../common/util/permissions';
+import SearchHeader, { filterByKeyword } from './components/SearchHeader';
+import { usePreference } from '../common/util/preferences';
+import useSettingsStyles from './common/useSettingsStyles';
+
+const UsersPage = () => {
+ const classes = useSettingsStyles();
+ const navigate = useNavigate();
+ const t = useTranslation();
+
+ const manager = useManager();
+
+ const hours12 = usePreference('twelveHourFormat');
+
+ const [timestamp, setTimestamp] = useState(Date.now());
+ const [items, setItems] = useState([]);
+ const [searchKeyword, setSearchKeyword] = useState('');
+ const [loading, setLoading] = useState(false);
+ const [temporary, setTemporary] = useState(false);
+
+ const handleLogin = useCatch(async (userId) => {
+ const response = await fetch(`/api/session/${userId}`);
+ if (response.ok) {
+ window.location.replace('/');
+ } else {
+ throw Error(await response.text());
+ }
+ });
+
+ const actionLogin = {
+ key: 'login',
+ title: t('loginLogin'),
+ icon: <LoginIcon fontSize="small" />,
+ handler: handleLogin,
+ };
+
+ const actionConnections = {
+ key: 'connections',
+ title: t('sharedConnections'),
+ icon: <LinkIcon fontSize="small" />,
+ handler: (userId) => navigate(`/settings/user/${userId}/connections`),
+ };
+
+ useEffectAsync(async () => {
+ setLoading(true);
+ try {
+ const response = await fetch('/api/users');
+ if (response.ok) {
+ setItems(await response.json());
+ } else {
+ throw Error(await response.text());
+ }
+ } finally {
+ setLoading(false);
+ }
+ }, [timestamp]);
+
+ return (
+ <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'settingsUsers']}>
+ <SearchHeader keyword={searchKeyword} setKeyword={setSearchKeyword} />
+ <Table className={classes.table}>
+ <TableHead>
+ <TableRow>
+ <TableCell>{t('sharedName')}</TableCell>
+ <TableCell>{t('userEmail')}</TableCell>
+ <TableCell>{t('userAdmin')}</TableCell>
+ <TableCell>{t('sharedDisabled')}</TableCell>
+ <TableCell>{t('userExpirationTime')}</TableCell>
+ <TableCell className={classes.columnAction} />
+ </TableRow>
+ </TableHead>
+ <TableBody>
+ {!loading ? items.filter((u) => temporary || !u.temporary).filter(filterByKeyword(searchKeyword)).map((item) => (
+ <TableRow key={item.id}>
+ <TableCell>{item.name}</TableCell>
+ <TableCell>{item.email}</TableCell>
+ <TableCell>{formatBoolean(item.administrator, t)}</TableCell>
+ <TableCell>{formatBoolean(item.disabled, t)}</TableCell>
+ <TableCell>{formatTime(item.expirationTime, 'date', hours12)}</TableCell>
+ <TableCell className={classes.columnAction} padding="none">
+ <CollectionActions
+ itemId={item.id}
+ editPath="/settings/user"
+ endpoint="users"
+ setTimestamp={setTimestamp}
+ customActions={manager ? [actionLogin, actionConnections] : [actionConnections]}
+ />
+ </TableCell>
+ </TableRow>
+ )) : (<TableShimmer columns={6} endAction />)}
+ </TableBody>
+ <TableFooter>
+ <TableRow>
+ <TableCell colSpan={6} align="right">
+ <FormControlLabel
+ control={(
+ <Switch
+ value={temporary}
+ onChange={(e) => setTemporary(e.target.checked)}
+ size="small"
+ />
+ )}
+ label={t('userTemporary')}
+ labelPlacement="start"
+ />
+ </TableCell>
+ </TableRow>
+ </TableFooter>
+ </Table>
+ <CollectionFab editPath="/settings/user" />
+ </PageLayout>
+ );
+};
+
+export default UsersPage;
diff --git a/src/settings/common/useSettingsStyles.js b/src/settings/common/useSettingsStyles.js
new file mode 100644
index 00000000..b276e0b7
--- /dev/null
+++ b/src/settings/common/useSettingsStyles.js
@@ -0,0 +1,33 @@
+import { makeStyles } from '@mui/styles';
+
+export default makeStyles((theme) => ({
+ table: {
+ marginBottom: theme.spacing(10),
+ },
+ columnAction: {
+ width: '1%',
+ paddingRight: theme.spacing(1),
+ },
+ container: {
+ marginTop: theme.spacing(2),
+ },
+ buttons: {
+ marginTop: theme.spacing(2),
+ marginBottom: theme.spacing(2),
+ display: 'flex',
+ justifyContent: 'space-evenly',
+ '& > *': {
+ flexBasis: '33%',
+ },
+ },
+ details: {
+ display: 'flex',
+ flexDirection: 'column',
+ gap: theme.spacing(2),
+ paddingBottom: theme.spacing(3),
+ },
+ verticalActions: {
+ display: 'flex',
+ flexDirection: 'column',
+ },
+}));
diff --git a/src/settings/components/AddAttributeDialog.jsx b/src/settings/components/AddAttributeDialog.jsx
new file mode 100644
index 00000000..86ff64ea
--- /dev/null
+++ b/src/settings/components/AddAttributeDialog.jsx
@@ -0,0 +1,104 @@
+import React, { useState } from 'react';
+import {
+ Button, Dialog, DialogActions, DialogContent, FormControl, InputLabel, MenuItem, Select, TextField, Autocomplete,
+} from '@mui/material';
+
+import { createFilterOptions } from '@mui/material/useAutocomplete';
+import { makeStyles } from '@mui/styles';
+import { useTranslation } from '../../common/components/LocalizationProvider';
+
+const useStyles = makeStyles((theme) => ({
+ details: {
+ display: 'flex',
+ flexDirection: 'column',
+ gap: theme.spacing(2),
+ paddingBottom: theme.spacing(1),
+ paddingTop: theme.spacing(3),
+ },
+}));
+
+const AddAttributeDialog = ({ open, onResult, definitions }) => {
+ const classes = useStyles();
+ const t = useTranslation();
+
+ const filter = createFilterOptions({
+ stringify: (option) => option.name,
+ });
+
+ const options = Object.entries(definitions).map(([key, value]) => ({
+ key,
+ name: value.name,
+ type: value.type,
+ }));
+
+ const [key, setKey] = useState();
+ const [type, setType] = useState('string');
+
+ return (
+ <Dialog open={open} fullWidth maxWidth="xs">
+ <DialogContent className={classes.details}>
+ <Autocomplete
+ onChange={(_, option) => {
+ setKey(option && typeof option === 'object' ? option.key : option);
+ if (option && option.type) {
+ setType(option.type);
+ }
+ }}
+ filterOptions={(options, params) => {
+ const filtered = filter(options, params);
+ if (params.inputValue) {
+ filtered.push({
+ key: params.inputValue,
+ name: params.inputValue,
+ });
+ }
+ return filtered;
+ }}
+ options={options}
+ getOptionLabel={(option) => (option && typeof option === 'object' ? option.name : option)}
+ renderOption={(props, option) => (
+ <li {...props}>
+ {option.name}
+ </li>
+ )}
+ renderInput={(params) => (
+ <TextField {...params} label={t('sharedAttribute')} />
+ )}
+ freeSolo
+ />
+ <FormControl
+ fullWidth
+ disabled={key in definitions}
+ >
+ <InputLabel>{t('sharedType')}</InputLabel>
+ <Select
+ label={t('sharedType')}
+ value={type}
+ onChange={(e) => setType(e.target.value)}
+ >
+ <MenuItem value="string">{t('sharedTypeString')}</MenuItem>
+ <MenuItem value="number">{t('sharedTypeNumber')}</MenuItem>
+ <MenuItem value="boolean">{t('sharedTypeBoolean')}</MenuItem>
+ </Select>
+ </FormControl>
+ </DialogContent>
+ <DialogActions>
+ <Button
+ color="primary"
+ disabled={!key}
+ onClick={() => onResult({ key, type })}
+ >
+ {t('sharedAdd')}
+ </Button>
+ <Button
+ autoFocus
+ onClick={() => onResult(null)}
+ >
+ {t('sharedCancel')}
+ </Button>
+ </DialogActions>
+ </Dialog>
+ );
+};
+
+export default AddAttributeDialog;
diff --git a/src/settings/components/BaseCommandView.jsx b/src/settings/components/BaseCommandView.jsx
new file mode 100644
index 00000000..bb70c3b9
--- /dev/null
+++ b/src/settings/components/BaseCommandView.jsx
@@ -0,0 +1,79 @@
+import React, { useEffect, useState } from 'react';
+import { useSelector } from 'react-redux';
+import {
+ TextField, FormControlLabel, Checkbox,
+} from '@mui/material';
+import { useTranslation } from '../../common/components/LocalizationProvider';
+import SelectField from '../../common/components/SelectField';
+import { prefixString } from '../../common/util/stringUtils';
+import useCommandAttributes from '../../common/attributes/useCommandAttributes';
+
+const BaseCommandView = ({ deviceId, item, setItem }) => {
+ const t = useTranslation();
+
+ const textEnabled = useSelector((state) => state.session.server.textEnabled);
+
+ const availableAttributes = useCommandAttributes(t);
+
+ const [attributes, setAttributes] = useState([]);
+
+ useEffect(() => {
+ if (item && item.type) {
+ setAttributes(availableAttributes[item.type] || []);
+ } else {
+ setAttributes([]);
+ }
+ }, [availableAttributes, item]);
+
+ return (
+ <>
+ <SelectField
+ value={item.type}
+ onChange={(e) => setItem({ ...item, type: e.target.value, attributes: {} })}
+ endpoint={deviceId ? `/api/commands/types?${new URLSearchParams({ deviceId }).toString()}` : '/api/commands/types'}
+ keyGetter={(it) => it.type}
+ titleGetter={(it) => t(prefixString('command', it.type))}
+ label={t('sharedType')}
+ />
+ {attributes.map(({ key, name, type }) => {
+ if (type === 'boolean') {
+ return (
+ <FormControlLabel
+ control={(
+ <Checkbox
+ checked={item.attributes[key]}
+ onChange={(e) => {
+ const updateItem = { ...item, attributes: { ...item.attributes } };
+ updateItem.attributes[key] = e.target.checked;
+ setItem(updateItem);
+ }}
+ />
+ )}
+ label={name}
+ />
+ );
+ }
+ return (
+ <TextField
+ type={type === 'number' ? 'number' : 'text'}
+ value={item.attributes[key]}
+ onChange={(e) => {
+ const updateItem = { ...item, attributes: { ...item.attributes } };
+ updateItem.attributes[key] = type === 'number' ? Number(e.target.value) : e.target.value;
+ setItem(updateItem);
+ }}
+ label={name}
+ />
+ );
+ })}
+ {textEnabled && (
+ <FormControlLabel
+ control={<Checkbox checked={item.textChannel} onChange={(event) => setItem({ ...item, textChannel: event.target.checked })} />}
+ label={t('commandSendSms')}
+ />
+ )}
+ </>
+ );
+};
+
+export default BaseCommandView;
diff --git a/src/settings/components/CollectionActions.jsx b/src/settings/components/CollectionActions.jsx
new file mode 100644
index 00000000..666052d5
--- /dev/null
+++ b/src/settings/components/CollectionActions.jsx
@@ -0,0 +1,104 @@
+import React, { useState } from 'react';
+import {
+ IconButton, Menu, MenuItem, useMediaQuery, useTheme,
+} from '@mui/material';
+import Tooltip from '@mui/material/Tooltip';
+import MoreVertIcon from '@mui/icons-material/MoreVert';
+import EditIcon from '@mui/icons-material/Edit';
+import DeleteIcon from '@mui/icons-material/Delete';
+import { useNavigate } from 'react-router-dom';
+import { makeStyles } from '@mui/styles';
+import RemoveDialog from '../../common/components/RemoveDialog';
+import { useTranslation } from '../../common/components/LocalizationProvider';
+
+const useStyles = makeStyles(() => ({
+ row: {
+ display: 'flex',
+ },
+}));
+
+const CollectionActions = ({
+ itemId, editPath, endpoint, setTimestamp, customActions, readonly,
+}) => {
+ const theme = useTheme();
+ const classes = useStyles();
+ const navigate = useNavigate();
+ const t = useTranslation();
+
+ const phone = useMediaQuery(theme.breakpoints.down('sm'));
+
+ const [menuAnchorEl, setMenuAnchorEl] = useState(null);
+ const [removing, setRemoving] = useState(false);
+
+ const handleEdit = () => {
+ navigate(`${editPath}/${itemId}`);
+ setMenuAnchorEl(null);
+ };
+
+ const handleRemove = () => {
+ setRemoving(true);
+ setMenuAnchorEl(null);
+ };
+
+ const handleCustom = (action) => {
+ action.handler(itemId);
+ setMenuAnchorEl(null);
+ };
+
+ const handleRemoveResult = (removed) => {
+ setRemoving(false);
+ if (removed) {
+ setTimestamp(Date.now());
+ }
+ };
+
+ return (
+ <>
+ {phone ? (
+ <>
+ <IconButton size="small" onClick={(event) => setMenuAnchorEl(event.currentTarget)}>
+ <MoreVertIcon fontSize="small" />
+ </IconButton>
+ <Menu open={!!menuAnchorEl} anchorEl={menuAnchorEl} onClose={() => setMenuAnchorEl(null)}>
+ {customActions && customActions.map((action) => (
+ <MenuItem onClick={() => handleCustom(action)} key={action.key}>{action.title}</MenuItem>
+ ))}
+ {!readonly && (
+ <>
+ <MenuItem onClick={handleEdit}>{t('sharedEdit')}</MenuItem>
+ <MenuItem onClick={handleRemove}>{t('sharedRemove')}</MenuItem>
+ </>
+ )}
+ </Menu>
+ </>
+ ) : (
+ <div className={classes.row}>
+ {customActions && customActions.map((action) => (
+ <Tooltip title={action.title} key={action.key}>
+ <IconButton size="small" onClick={() => handleCustom(action)}>
+ {action.icon}
+ </IconButton>
+ </Tooltip>
+ ))}
+ {!readonly && (
+ <>
+ <Tooltip title={t('sharedEdit')}>
+ <IconButton size="small" onClick={handleEdit}>
+ <EditIcon fontSize="small" />
+ </IconButton>
+ </Tooltip>
+ <Tooltip title={t('sharedRemove')}>
+ <IconButton size="small" onClick={handleRemove}>
+ <DeleteIcon fontSize="small" />
+ </IconButton>
+ </Tooltip>
+ </>
+ )}
+ </div>
+ )}
+ <RemoveDialog style={{ transform: 'none' }} open={removing} endpoint={endpoint} itemId={itemId} onResult={handleRemoveResult} />
+ </>
+ );
+};
+
+export default CollectionActions;
diff --git a/src/settings/components/CollectionFab.jsx b/src/settings/components/CollectionFab.jsx
new file mode 100644
index 00000000..3c1fa783
--- /dev/null
+++ b/src/settings/components/CollectionFab.jsx
@@ -0,0 +1,35 @@
+import React from 'react';
+import { Fab } from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+import AddIcon from '@mui/icons-material/Add';
+import { useNavigate } from 'react-router-dom';
+import { useRestriction } from '../../common/util/permissions';
+
+const useStyles = makeStyles((theme) => ({
+ fab: {
+ position: 'fixed',
+ bottom: theme.spacing(2),
+ right: theme.spacing(2),
+ [theme.breakpoints.down('md')]: {
+ bottom: `calc(${theme.dimensions.bottomBarHeight}px + ${theme.spacing(2)})`,
+ },
+ },
+}));
+
+const CollectionFab = ({ editPath, disabled }) => {
+ const classes = useStyles();
+ const navigate = useNavigate();
+
+ const readonly = useRestriction('readonly');
+
+ if (!readonly && !disabled) {
+ return (
+ <Fab size="medium" color="primary" className={classes.fab} onClick={() => navigate(editPath)}>
+ <AddIcon />
+ </Fab>
+ );
+ }
+ return '';
+};
+
+export default CollectionFab;
diff --git a/src/settings/components/EditAttributesAccordion.jsx b/src/settings/components/EditAttributesAccordion.jsx
new file mode 100644
index 00000000..4d4ae254
--- /dev/null
+++ b/src/settings/components/EditAttributesAccordion.jsx
@@ -0,0 +1,217 @@
+import React, { useState } from 'react';
+
+import {
+ Button,
+ Checkbox,
+ OutlinedInput,
+ FormControl,
+ FormControlLabel,
+ Grid,
+ IconButton,
+ InputAdornment,
+ InputLabel,
+ Accordion,
+ AccordionSummary,
+ Typography,
+ AccordionDetails,
+} from '@mui/material';
+import CloseIcon from '@mui/icons-material/Close';
+import AddIcon from '@mui/icons-material/Add';
+import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
+import AddAttributeDialog from './AddAttributeDialog';
+import { useTranslation } from '../../common/components/LocalizationProvider';
+import { useAttributePreference } from '../../common/util/preferences';
+import {
+ distanceFromMeters, distanceToMeters, distanceUnitString, speedFromKnots, speedToKnots, speedUnitString, volumeFromLiters, volumeToLiters, volumeUnitString,
+} from '../../common/util/converter';
+import useFeatures from '../../common/util/useFeatures';
+import useSettingsStyles from '../common/useSettingsStyles';
+
+const EditAttributesAccordion = ({ attribute, attributes, setAttributes, definitions, focusAttribute }) => {
+ const classes = useSettingsStyles();
+ const t = useTranslation();
+
+ const features = useFeatures();
+
+ const speedUnit = useAttributePreference('speedUnit');
+ const distanceUnit = useAttributePreference('distanceUnit');
+ const volumeUnit = useAttributePreference('volumeUnit');
+
+ const [addDialogShown, setAddDialogShown] = useState(false);
+
+ const updateAttribute = (key, value, type, subtype) => {
+ const updatedAttributes = { ...attributes };
+ switch (subtype) {
+ case 'speed':
+ updatedAttributes[key] = speedToKnots(Number(value), speedUnit);
+ break;
+ case 'distance':
+ updatedAttributes[key] = distanceToMeters(Number(value), distanceUnit);
+ break;
+ case 'volume':
+ updatedAttributes[key] = volumeToLiters(Number(value), volumeUnit);
+ break;
+ default:
+ updatedAttributes[key] = type === 'number' ? Number(value) : value;
+ break;
+ }
+ setAttributes(updatedAttributes);
+ };
+
+ const deleteAttribute = (key) => {
+ const updatedAttributes = { ...attributes };
+ delete updatedAttributes[key];
+ setAttributes(updatedAttributes);
+ };
+
+ const getAttributeName = (key, subtype) => {
+ const definition = definitions[key];
+ const name = definition ? definition.name : key;
+ switch (subtype) {
+ case 'speed':
+ return `${name} (${speedUnitString(speedUnit, t)})`;
+ case 'distance':
+ return `${name} (${distanceUnitString(distanceUnit, t)})`;
+ case 'volume':
+ return `${name} (${volumeUnitString(volumeUnit, t)})`;
+ default:
+ return name;
+ }
+ };
+
+ const getAttributeType = (value) => {
+ if (typeof value === 'number') {
+ return 'number';
+ } if (typeof value === 'boolean') {
+ return 'boolean';
+ }
+ return 'string';
+ };
+
+ const getAttributeSubtype = (key) => {
+ const definition = definitions[key];
+ return definition && definition.subtype;
+ };
+
+ const getDisplayValue = (value, subtype) => {
+ if (value) {
+ switch (subtype) {
+ case 'speed':
+ return speedFromKnots(value, speedUnit);
+ case 'distance':
+ return distanceFromMeters(value, distanceUnit);
+ case 'volume':
+ return volumeFromLiters(value, volumeUnit);
+ default:
+ return value;
+ }
+ }
+ return '';
+ };
+
+ const convertToList = (attributes) => {
+ const booleanList = [];
+ const otherList = [];
+ const excludeAttributes = ['speedUnit', 'distanceUnit', 'volumeUnit', 'timezone'];
+ Object.keys(attributes || []).filter((key) => !excludeAttributes.includes(key)).forEach((key) => {
+ const value = attributes[key];
+ const type = getAttributeType(value);
+ const subtype = getAttributeSubtype(key);
+ if (type === 'boolean') {
+ booleanList.push({
+ key, value, type, subtype,
+ });
+ } else {
+ otherList.push({
+ key, value, type, subtype,
+ });
+ }
+ });
+ return [...otherList, ...booleanList];
+ };
+
+ const handleAddResult = (definition) => {
+ setAddDialogShown(false);
+ if (definition) {
+ switch (definition.type) {
+ case 'number':
+ updateAttribute(definition.key, 0);
+ break;
+ case 'boolean':
+ updateAttribute(definition.key, false);
+ break;
+ default:
+ updateAttribute(definition.key, '');
+ break;
+ }
+ }
+ };
+
+ return features.disableAttributes ? '' : (
+ <Accordion defaultExpanded={!!attribute}>
+ <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+ <Typography variant="subtitle1">
+ {t('sharedAttributes')}
+ </Typography>
+ </AccordionSummary>
+ <AccordionDetails className={classes.details}>
+ {convertToList(attributes).map(({
+ key, value, type, subtype,
+ }) => {
+ if (type === 'boolean') {
+ return (
+ <Grid container direction="row" justifyContent="space-between" key={key}>
+ <FormControlLabel
+ control={(
+ <Checkbox
+ checked={value}
+ onChange={(e) => updateAttribute(key, e.target.checked)}
+ />
+ )}
+ label={getAttributeName(key, subtype)}
+ />
+ <IconButton size="small" className={classes.removeButton} onClick={() => deleteAttribute(key)}>
+ <CloseIcon fontSize="small" />
+ </IconButton>
+ </Grid>
+ );
+ }
+ return (
+ <FormControl key={key}>
+ <InputLabel>{getAttributeName(key, subtype)}</InputLabel>
+ <OutlinedInput
+ label={getAttributeName(key, subtype)}
+ type={type === 'number' ? 'number' : 'text'}
+ value={getDisplayValue(value, subtype)}
+ onChange={(e) => updateAttribute(key, e.target.value, type, subtype)}
+ autoFocus={focusAttribute === key}
+ endAdornment={(
+ <InputAdornment position="end">
+ <IconButton size="small" edge="end" onClick={() => deleteAttribute(key)}>
+ <CloseIcon fontSize="small" />
+ </IconButton>
+ </InputAdornment>
+ )}
+ />
+ </FormControl>
+ );
+ })}
+ <Button
+ variant="outlined"
+ color="primary"
+ onClick={() => setAddDialogShown(true)}
+ startIcon={<AddIcon />}
+ >
+ {t('sharedAdd')}
+ </Button>
+ <AddAttributeDialog
+ open={addDialogShown}
+ onResult={handleAddResult}
+ definitions={definitions}
+ />
+ </AccordionDetails>
+ </Accordion>
+ );
+};
+
+export default EditAttributesAccordion;
diff --git a/src/settings/components/EditItemView.jsx b/src/settings/components/EditItemView.jsx
new file mode 100644
index 00000000..61bc4161
--- /dev/null
+++ b/src/settings/components/EditItemView.jsx
@@ -0,0 +1,101 @@
+import React from 'react';
+import { useNavigate, useParams } from 'react-router-dom';
+import {
+ Container, Button, Accordion, AccordionDetails, AccordionSummary, Skeleton, Typography, TextField,
+} from '@mui/material';
+import { useCatch, useEffectAsync } from '../../reactHelper';
+import { useTranslation } from '../../common/components/LocalizationProvider';
+import PageLayout from '../../common/components/PageLayout';
+import useSettingsStyles from '../common/useSettingsStyles';
+
+const EditItemView = ({
+ children, endpoint, item, setItem, defaultItem, validate, onItemSaved, menu, breadcrumbs,
+}) => {
+ const navigate = useNavigate();
+ const classes = useSettingsStyles();
+ const t = useTranslation();
+
+ const { id } = useParams();
+
+ useEffectAsync(async () => {
+ if (!item) {
+ if (id) {
+ const response = await fetch(`/api/${endpoint}/${id}`);
+ if (response.ok) {
+ setItem(await response.json());
+ } else {
+ throw Error(await response.text());
+ }
+ } else {
+ setItem(defaultItem || {});
+ }
+ }
+ }, [id, item, defaultItem]);
+
+ const handleSave = useCatch(async () => {
+ let url = `/api/${endpoint}`;
+ if (id) {
+ url += `/${id}`;
+ }
+
+ const response = await fetch(url, {
+ method: !id ? 'POST' : 'PUT',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(item),
+ });
+
+ if (response.ok) {
+ if (onItemSaved) {
+ onItemSaved(await response.json());
+ }
+ navigate(-1);
+ } else {
+ throw Error(await response.text());
+ }
+ });
+
+ return (
+ <PageLayout menu={menu} breadcrumbs={breadcrumbs}>
+ <Container maxWidth="xs" className={classes.container}>
+ {item ? children : (
+ <Accordion defaultExpanded>
+ <AccordionSummary>
+ <Typography variant="subtitle1">
+ <Skeleton width="10em" />
+ </Typography>
+ </AccordionSummary>
+ <AccordionDetails>
+ {[...Array(3)].map((_, i) => (
+ <Skeleton key={-i} width="100%">
+ <TextField />
+ </Skeleton>
+ ))}
+ </AccordionDetails>
+ </Accordion>
+ )}
+ <div className={classes.buttons}>
+ <Button
+ type="button"
+ color="primary"
+ variant="outlined"
+ onClick={() => navigate(-1)}
+ disabled={!item}
+ >
+ {t('sharedCancel')}
+ </Button>
+ <Button
+ type="button"
+ color="primary"
+ variant="contained"
+ onClick={handleSave}
+ disabled={!item || !validate()}
+ >
+ {t('sharedSave')}
+ </Button>
+ </div>
+ </Container>
+ </PageLayout>
+ );
+};
+
+export default EditItemView;
diff --git a/src/settings/components/SearchHeader.jsx b/src/settings/components/SearchHeader.jsx
new file mode 100644
index 00000000..25757ed2
--- /dev/null
+++ b/src/settings/components/SearchHeader.jsx
@@ -0,0 +1,38 @@
+import React from 'react';
+import { TextField, useTheme, useMediaQuery } from '@mui/material';
+import makeStyles from '@mui/styles/makeStyles';
+import { useTranslation } from '../../common/components/LocalizationProvider';
+
+export const filterByKeyword = (keyword) => (item) => !keyword || JSON.stringify(item).toLowerCase().includes(keyword.toLowerCase());
+
+const useStyles = makeStyles((theme) => ({
+ header: {
+ position: 'sticky',
+ left: 0,
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'stretch',
+ padding: theme.spacing(3, 2, 2),
+ },
+}));
+
+const SearchHeader = ({ keyword, setKeyword }) => {
+ const theme = useTheme();
+ const classes = useStyles();
+ const t = useTranslation();
+
+ const phone = useMediaQuery(theme.breakpoints.down('sm'));
+
+ return phone ? (
+ <div className={classes.header}>
+ <TextField
+ variant="outlined"
+ placeholder={t('sharedSearch')}
+ value={keyword}
+ onChange={(e) => setKeyword(e.target.value)}
+ />
+ </div>
+ ) : '';
+};
+
+export default SearchHeader;
diff --git a/src/settings/components/SettingsMenu.jsx b/src/settings/components/SettingsMenu.jsx
new file mode 100644
index 00000000..7085d47a
--- /dev/null
+++ b/src/settings/components/SettingsMenu.jsx
@@ -0,0 +1,173 @@
+import React from 'react';
+import {
+ Divider, List, ListItemButton, ListItemIcon, ListItemText,
+} from '@mui/material';
+import SettingsIcon from '@mui/icons-material/Settings';
+import CreateIcon from '@mui/icons-material/Create';
+import NotificationsIcon from '@mui/icons-material/Notifications';
+import FolderIcon from '@mui/icons-material/Folder';
+import PersonIcon from '@mui/icons-material/Person';
+import StorageIcon from '@mui/icons-material/Storage';
+import BuildIcon from '@mui/icons-material/Build';
+import PeopleIcon from '@mui/icons-material/People';
+import TodayIcon from '@mui/icons-material/Today';
+import PublishIcon from '@mui/icons-material/Publish';
+import SmartphoneIcon from '@mui/icons-material/Smartphone';
+import HelpIcon from '@mui/icons-material/Help';
+import CampaignIcon from '@mui/icons-material/Campaign';
+import { Link, useLocation } from 'react-router-dom';
+import { useSelector } from 'react-redux';
+import { useTranslation } from '../../common/components/LocalizationProvider';
+import {
+ useAdministrator, useManager, useRestriction,
+} from '../../common/util/permissions';
+import useFeatures from '../../common/util/useFeatures';
+
+const MenuItem = ({
+ title, link, icon, selected,
+}) => (
+ <ListItemButton key={link} component={Link} to={link} selected={selected}>
+ <ListItemIcon>{icon}</ListItemIcon>
+ <ListItemText primary={title} />
+ </ListItemButton>
+);
+
+const SettingsMenu = () => {
+ const t = useTranslation();
+ const location = useLocation();
+
+ const readonly = useRestriction('readonly');
+ const admin = useAdministrator();
+ const manager = useManager();
+ const userId = useSelector((state) => state.session.user.id);
+ const supportLink = useSelector((state) => state.session.server.attributes.support);
+
+ const features = useFeatures();
+
+ return (
+ <>
+ <List>
+ <MenuItem
+ title={t('sharedPreferences')}
+ link="/settings/preferences"
+ icon={<SettingsIcon />}
+ selected={location.pathname === '/settings/preferences'}
+ />
+ {!readonly && (
+ <>
+ <MenuItem
+ title={t('sharedNotifications')}
+ link="/settings/notifications"
+ icon={<NotificationsIcon />}
+ selected={location.pathname.startsWith('/settings/notification')}
+ />
+ <MenuItem
+ title={t('settingsUser')}
+ link={`/settings/user/${userId}`}
+ icon={<PersonIcon />}
+ selected={location.pathname === `/settings/user/${userId}`}
+ />
+ <MenuItem
+ title={t('deviceTitle')}
+ link="/settings/devices"
+ icon={<SmartphoneIcon />}
+ selected={location.pathname.startsWith('/settings/device')}
+ />
+ <MenuItem
+ title={t('sharedGeofences')}
+ link="/geofences"
+ icon={<CreateIcon />}
+ selected={location.pathname.startsWith('/settings/geofence')}
+ />
+ {!features.disableGroups && (
+ <MenuItem
+ title={t('settingsGroups')}
+ link="/settings/groups"
+ icon={<FolderIcon />}
+ selected={location.pathname.startsWith('/settings/group')}
+ />
+ )}
+ {!features.disableDrivers && (
+ <MenuItem
+ title={t('sharedDrivers')}
+ link="/settings/drivers"
+ icon={<PersonIcon />}
+ selected={location.pathname.startsWith('/settings/driver')}
+ />
+ )}
+ {!features.disableCalendars && (
+ <MenuItem
+ title={t('sharedCalendars')}
+ link="/settings/calendars"
+ icon={<TodayIcon />}
+ selected={location.pathname.startsWith('/settings/calendar')}
+ />
+ )}
+ {!features.disableComputedAttributes && (
+ <MenuItem
+ title={t('sharedComputedAttributes')}
+ link="/settings/attributes"
+ icon={<StorageIcon />}
+ selected={location.pathname.startsWith('/settings/attribute')}
+ />
+ )}
+ {!features.disableMaintenance && (
+ <MenuItem
+ title={t('sharedMaintenance')}
+ link="/settings/maintenances"
+ icon={<BuildIcon />}
+ selected={location.pathname.startsWith('/settings/maintenance')}
+ />
+ )}
+ {!features.disableSavedCommands && (
+ <MenuItem
+ title={t('sharedSavedCommands')}
+ link="/settings/commands"
+ icon={<PublishIcon />}
+ selected={location.pathname.startsWith('/settings/command')}
+ />
+ )}
+ {supportLink && (
+ <MenuItem
+ title={t('settingsSupport')}
+ link={supportLink}
+ icon={<HelpIcon />}
+ />
+ )}
+ </>
+ )}
+ </List>
+ {manager && (
+ <>
+ <Divider />
+ <List>
+ {admin && (
+ <>
+ <MenuItem
+ title={t('serverAnnouncement')}
+ link="/settings/announcement"
+ icon={<CampaignIcon />}
+ selected={location.pathname === '/settings/announcement'}
+ />
+ <MenuItem
+ title={t('settingsServer')}
+ link="/settings/server"
+ icon={<StorageIcon />}
+ selected={location.pathname === '/settings/server'}
+ />
+ </>
+ )}
+ <MenuItem
+ title={t('settingsUsers')}
+ link="/settings/users"
+ icon={<PeopleIcon />}
+ selected={location.pathname.startsWith('/settings/user') && location.pathname !== `/settings/user/${userId}`}
+ />
+ </List>
+ </>
+ )}
+ </>
+ );
+};
+
+export default SettingsMenu;
diff --git a/src/store/calendars.js b/src/store/calendars.js
new file mode 100644
index 00000000..2d92c004
--- /dev/null
+++ b/src/store/calendars.js
@@ -0,0 +1,17 @@
+import { createSlice } from '@reduxjs/toolkit';
+
+const { reducer, actions } = createSlice({
+ name: 'calendars',
+ initialState: {
+ items: {},
+ },
+ reducers: {
+ refresh(state, action) {
+ state.items = {};
+ action.payload.forEach((item) => state.items[item.id] = item);
+ },
+ },
+});
+
+export { actions as calendarsActions };
+export { reducer as calendarsReducer };
diff --git a/src/store/devices.js b/src/store/devices.js
new file mode 100644
index 00000000..f2f6263b
--- /dev/null
+++ b/src/store/devices.js
@@ -0,0 +1,36 @@
+import { createSlice } from '@reduxjs/toolkit';
+
+const { reducer, actions } = createSlice({
+ name: 'devices',
+ initialState: {
+ items: {},
+ selectedId: null,
+ selectedIds: [],
+ },
+ reducers: {
+ refresh(state, action) {
+ state.items = {};
+ action.payload.forEach((item) => state.items[item.id] = item);
+ },
+ update(state, action) {
+ action.payload.forEach((item) => state.items[item.id] = item);
+ },
+ select(state, action) {
+ state.selectedId = action.payload;
+ },
+ selectId(state, action) {
+ state.selectedId = action.payload;
+ state.selectedIds = state.selectedId ? [state.selectedId] : [];
+ },
+ selectIds(state, action) {
+ state.selectedIds = action.payload;
+ [state.selectedId] = state.selectedIds;
+ },
+ remove(state, action) {
+ delete state.items[action.payload];
+ },
+ },
+});
+
+export { actions as devicesActions };
+export { reducer as devicesReducer };
diff --git a/src/store/drivers.js b/src/store/drivers.js
new file mode 100644
index 00000000..d62bd476
--- /dev/null
+++ b/src/store/drivers.js
@@ -0,0 +1,17 @@
+import { createSlice } from '@reduxjs/toolkit';
+
+const { reducer, actions } = createSlice({
+ name: 'drivers',
+ initialState: {
+ items: {},
+ },
+ reducers: {
+ refresh(state, action) {
+ state.items = {};
+ action.payload.forEach((item) => state.items[item.uniqueId] = item);
+ },
+ },
+});
+
+export { actions as driversActions };
+export { reducer as driversReducer };
diff --git a/src/store/errors.js b/src/store/errors.js
new file mode 100644
index 00000000..a484239a
--- /dev/null
+++ b/src/store/errors.js
@@ -0,0 +1,21 @@
+import { createSlice } from '@reduxjs/toolkit';
+
+const { reducer, actions } = createSlice({
+ name: 'errors',
+ initialState: {
+ errors: [],
+ },
+ reducers: {
+ push(state, action) {
+ state.errors.push(action.payload);
+ },
+ pop(state) {
+ if (state.errors.length) {
+ state.errors.shift();
+ }
+ },
+ },
+});
+
+export { actions as errorsActions };
+export { reducer as errorsReducer };
diff --git a/src/store/events.js b/src/store/events.js
new file mode 100644
index 00000000..35e7081e
--- /dev/null
+++ b/src/store/events.js
@@ -0,0 +1,23 @@
+import { createSlice } from '@reduxjs/toolkit';
+
+const { reducer, actions } = createSlice({
+ name: 'events',
+ initialState: {
+ items: [],
+ },
+ reducers: {
+ add(state, action) {
+ state.items.unshift(...action.payload);
+ state.items.splice(50);
+ },
+ delete(state, action) {
+ state.items = state.items.filter((item) => item.id !== action.payload.id);
+ },
+ deleteAll(state) {
+ state.items = [];
+ },
+ },
+});
+
+export { actions as eventsActions };
+export { reducer as eventsReducer };
diff --git a/src/store/geofences.js b/src/store/geofences.js
new file mode 100644
index 00000000..f2b7666a
--- /dev/null
+++ b/src/store/geofences.js
@@ -0,0 +1,20 @@
+import { createSlice } from '@reduxjs/toolkit';
+
+const { reducer, actions } = createSlice({
+ name: 'geofences',
+ initialState: {
+ items: {},
+ },
+ reducers: {
+ refresh(state, action) {
+ state.items = {};
+ action.payload.forEach((item) => state.items[item.id] = item);
+ },
+ update(state, action) {
+ action.payload.forEach((item) => state.items[item.id] = item);
+ },
+ },
+});
+
+export { actions as geofencesActions };
+export { reducer as geofencesReducer };
diff --git a/src/store/groups.js b/src/store/groups.js
new file mode 100644
index 00000000..607b8609
--- /dev/null
+++ b/src/store/groups.js
@@ -0,0 +1,17 @@
+import { createSlice } from '@reduxjs/toolkit';
+
+const { reducer, actions } = createSlice({
+ name: 'groups',
+ initialState: {
+ items: {},
+ },
+ reducers: {
+ refresh(state, action) {
+ state.items = {};
+ action.payload.forEach((item) => state.items[item.id] = item);
+ },
+ },
+});
+
+export { actions as groupsActions };
+export { reducer as groupsReducer };
diff --git a/src/store/index.js b/src/store/index.js
new file mode 100644
index 00000000..ea685ff3
--- /dev/null
+++ b/src/store/index.js
@@ -0,0 +1,42 @@
+import { combineReducers, configureStore } from '@reduxjs/toolkit';
+
+import { errorsReducer as errors } from './errors';
+import { sessionReducer as session } from './session';
+import { devicesReducer as devices } from './devices';
+import { eventsReducer as events } from './events';
+import { geofencesReducer as geofences } from './geofences';
+import { groupsReducer as groups } from './groups';
+import { driversReducer as drivers } from './drivers';
+import { maintenancesReducer as maintenances } from './maintenances';
+import { calendarsReducer as calendars } from './calendars';
+import { reportsReducer as reports } from './reports';
+import throttleMiddleware from './throttleMiddleware';
+
+const reducer = combineReducers({
+ errors,
+ session,
+ devices,
+ events,
+ geofences,
+ groups,
+ drivers,
+ maintenances,
+ calendars,
+ reports,
+});
+
+export { errorsActions } from './errors';
+export { sessionActions } from './session';
+export { devicesActions } from './devices';
+export { eventsActions } from './events';
+export { geofencesActions } from './geofences';
+export { groupsActions } from './groups';
+export { driversActions } from './drivers';
+export { maintenancesActions } from './maintenances';
+export { calendarsActions } from './calendars';
+export { reportsActions } from './reports';
+
+export default configureStore({
+ reducer,
+ middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(throttleMiddleware),
+});
diff --git a/src/store/maintenances.js b/src/store/maintenances.js
new file mode 100644
index 00000000..0f5e41d1
--- /dev/null
+++ b/src/store/maintenances.js
@@ -0,0 +1,17 @@
+import { createSlice } from '@reduxjs/toolkit';
+
+const { reducer, actions } = createSlice({
+ name: 'maintenances',
+ initialState: {
+ items: {},
+ },
+ reducers: {
+ refresh(state, action) {
+ state.items = {};
+ action.payload.forEach((item) => state.items[item.id] = item);
+ },
+ },
+});
+
+export { actions as maintenancesActions };
+export { reducer as maintenancesReducer };
diff --git a/src/store/reports.js b/src/store/reports.js
new file mode 100644
index 00000000..d0c1f6dd
--- /dev/null
+++ b/src/store/reports.js
@@ -0,0 +1,29 @@
+import { createSlice } from '@reduxjs/toolkit';
+import dayjs from 'dayjs';
+
+const { reducer, actions } = createSlice({
+ name: 'reports',
+ initialState: {
+ groupIds: [],
+ period: 'today',
+ from: dayjs().subtract(1, 'hour').locale('en').format('YYYY-MM-DDTHH:mm'),
+ to: dayjs().locale('en').format('YYYY-MM-DDTHH:mm'),
+ },
+ reducers: {
+ updateGroupIds(state, action) {
+ state.groupIds = action.payload;
+ },
+ updatePeriod(state, action) {
+ state.period = action.payload;
+ },
+ updateFrom(state, action) {
+ state.from = action.payload;
+ },
+ updateTo(state, action) {
+ state.to = action.payload;
+ },
+ },
+});
+
+export { actions as reportsActions };
+export { reducer as reportsReducer };
diff --git a/src/store/session.js b/src/store/session.js
new file mode 100644
index 00000000..bfe94a41
--- /dev/null
+++ b/src/store/session.js
@@ -0,0 +1,53 @@
+import { createSlice } from '@reduxjs/toolkit';
+
+const { reducer, actions } = createSlice({
+ name: 'session',
+ initialState: {
+ server: null,
+ user: null,
+ socket: null,
+ includeLogs: false,
+ logs: [],
+ positions: {},
+ history: {},
+ },
+ reducers: {
+ updateServer(state, action) {
+ state.server = action.payload;
+ },
+ updateUser(state, action) {
+ state.user = action.payload;
+ },
+ updateSocket(state, action) {
+ state.socket = action.payload;
+ },
+ enableLogs(state, action) {
+ state.includeLogs = action.payload;
+ if (!action.payload) {
+ state.logs = [];
+ }
+ },
+ updateLogs(state, action) {
+ state.logs.push(...action.payload);
+ },
+ updatePositions(state, action) {
+ const liveRoutes = state.user.attributes.mapLiveRoutes || state.server.attributes.mapLiveRoutes || 'none';
+ const liveRoutesLimit = state.user.attributes['web.liveRouteLength'] || state.server.attributes['web.liveRouteLength'] || 10;
+ action.payload.forEach((position) => {
+ state.positions[position.deviceId] = position;
+ if (liveRoutes !== 'none') {
+ const route = state.history[position.deviceId] || [];
+ const last = route.at(-1);
+ if (!last || (last[0] !== position.longitude && last[1] !== position.latitude)) {
+ state.history[position.deviceId] = [...route.slice(1 - liveRoutesLimit), [position.longitude, position.latitude]];
+ }
+ } else {
+ state.history = {};
+ }
+ });
+ },
+ },
+});
+
+export { actions as sessionActions };
+export { reducer as sessionReducer };
diff --git a/src/store/throttleMiddleware.js b/src/store/throttleMiddleware.js
new file mode 100644
index 00000000..d5a98add
--- /dev/null
+++ b/src/store/throttleMiddleware.js
@@ -0,0 +1,36 @@
+import { batch } from 'react-redux';
+
+const threshold = 3;
+const interval = 1500;
+
+export default () => (next) => {
+ const buffer = [];
+ let throttle = false;
+ let counter = 0;
+
+ setInterval(() => {
+ if (throttle) {
+ if (buffer.length < threshold) {
+ throttle = false;
+ }
+ batch(() => buffer.splice(0, buffer.length).forEach((action) => next(action)));
+ } else {
+ if (counter > threshold) {
+ throttle = true;
+ }
+ counter = 0;
+ }
+ }, interval);
+
+ return (action) => {
+ if (action.type === 'devices/update' || action.type === 'positions/update') {
+ if (throttle) {
+ buffer.push(action);
+ return null;
+ }
+ counter += 1;
+ return next(action);
+ }
+ return next(action);
+ };
+};