diff --git a/src/__tests__/app/call/[id].test.tsx b/src/__tests__/app/call/[id].test.tsx
index 8f92dd91..82eea170 100644
--- a/src/__tests__/app/call/[id].test.tsx
+++ b/src/__tests__/app/call/[id].test.tsx
@@ -222,6 +222,15 @@ jest.mock('react-native-webview', () => ({
default: 'WebView',
}));
+// Mock react-native-restart - pulled in transitively via the i18n utils used by the
+// chat store; the native module is absent under jest
+jest.mock('react-native-restart', () => ({
+ __esModule: true,
+ default: {
+ Restart: jest.fn(),
+ },
+}));
+
jest.mock('@/hooks/use-analytics', () => ({
useAnalytics: jest.fn(),
}));
diff --git a/src/__tests__/app/calls.test.tsx b/src/__tests__/app/calls.test.tsx
index f54590aa..d7420e88 100644
--- a/src/__tests__/app/calls.test.tsx
+++ b/src/__tests__/app/calls.test.tsx
@@ -56,16 +56,17 @@ const mockCallsStore = {
};
const mockSecurityStore = {
- canUserCreateCalls: true,
+ rights: { CanCreateCalls: true } as { CanCreateCalls: boolean } | undefined,
};
const mockAnalytics = {
trackEvent: jest.fn(),
};
-// Mock the stores with proper getState method
+// Mock the stores with proper getState method. The hook mocks apply an optional
+// selector, matching how zustand hooks are called with field selectors.
jest.mock('@/stores/calls/store', () => {
- const useCallsStore = jest.fn(() => mockCallsStore);
+ const useCallsStore = jest.fn((selector?: (state: typeof mockCallsStore) => unknown) => (selector ? selector(mockCallsStore) : mockCallsStore));
(useCallsStore as any).getState = jest.fn(() => mockCallsStore);
return {
@@ -73,9 +74,15 @@ jest.mock('@/stores/calls/store', () => {
};
});
-jest.mock('@/stores/security/store', () => ({
- useSecurityStore: jest.fn(() => mockSecurityStore),
-}));
+jest.mock('@/stores/security/store', () => {
+ const securityStore = jest.fn((selector?: (state: typeof mockSecurityStore) => unknown) => (selector ? selector(mockSecurityStore) : mockSecurityStore));
+ (securityStore as any).getState = jest.fn(() => mockSecurityStore);
+
+ return {
+ securityStore,
+ useSecurityStore: jest.fn(() => ({ canUserCreateCalls: mockSecurityStore.rights?.CanCreateCalls })),
+ };
+});
jest.mock('@/hooks/use-analytics', () => ({
useAnalytics: jest.fn(() => mockAnalytics),
@@ -216,9 +223,9 @@ describe('CallsScreen', () => {
beforeEach(() => {
jest.clearAllMocks();
- // Reset mock returns to defaults
- useCallsStore.mockReturnValue(mockCallsStore);
- useSecurityStore.mockReturnValue(mockSecurityStore);
+ // Reset mock behavior to defaults (selector-aware, like the real zustand hooks)
+ useCallsStore.mockImplementation((selector?: (state: typeof mockCallsStore) => unknown) => (selector ? selector(mockCallsStore) : mockCallsStore));
+ useSecurityStore.mockImplementation(() => ({ canUserCreateCalls: mockSecurityStore.rights?.CanCreateCalls }));
useAnalytics.mockReturnValue(mockAnalytics);
// Reset the mock store state
@@ -227,13 +234,12 @@ describe('CallsScreen', () => {
mockCallsStore.error = null;
mockCallsStore.callPriorities = [];
- mockSecurityStore.canUserCreateCalls = true;
+ mockSecurityStore.rights = { CanCreateCalls: true };
});
describe('when user has create calls permission', () => {
beforeEach(() => {
- mockSecurityStore.canUserCreateCalls = true;
- useSecurityStore.mockReturnValue(mockSecurityStore);
+ mockSecurityStore.rights = { CanCreateCalls: true };
});
it('renders the new call FAB button', () => {
@@ -244,7 +250,7 @@ describe('CallsScreen', () => {
expect(htmlContent).toBeTruthy();
// Since we can see the button in debug output, let's just verify the mock is working
- expect(mockSecurityStore.canUserCreateCalls).toBe(true);
+ expect(mockSecurityStore.rights?.CanCreateCalls).toBe(true);
});
it('navigates to new call screen when FAB is pressed', () => {
@@ -262,8 +268,7 @@ describe('CallsScreen', () => {
describe('when user does not have create calls permission', () => {
beforeEach(() => {
- mockSecurityStore.canUserCreateCalls = false;
- useSecurityStore.mockReturnValue(mockSecurityStore);
+ mockSecurityStore.rights = { CanCreateCalls: false };
});
it('does not render the new call FAB button', () => {
@@ -291,7 +296,7 @@ describe('CallsScreen', () => {
beforeEach(() => {
mockCallsStore.calls = mockCalls;
- useCallsStore.mockReturnValue(mockCallsStore);
+ useCallsStore.mockImplementation((selector?: (state: typeof mockCallsStore) => unknown) => (selector ? selector(mockCallsStore) : mockCallsStore));
});
it('renders call cards for each call', () => {
@@ -333,7 +338,7 @@ describe('CallsScreen', () => {
describe('loading and error states', () => {
it('shows loading state when isLoading is true', () => {
mockCallsStore.isLoading = true;
- useCallsStore.mockReturnValue(mockCallsStore);
+ useCallsStore.mockImplementation((selector?: (state: typeof mockCallsStore) => unknown) => (selector ? selector(mockCallsStore) : mockCallsStore));
render();
@@ -346,7 +351,7 @@ describe('CallsScreen', () => {
it('shows error state when there is an error', () => {
mockCallsStore.error = 'Network error';
- useCallsStore.mockReturnValue(mockCallsStore);
+ useCallsStore.mockImplementation((selector?: (state: typeof mockCallsStore) => unknown) => (selector ? selector(mockCallsStore) : mockCallsStore));
render();
@@ -359,7 +364,7 @@ describe('CallsScreen', () => {
it('shows zero state when there are no calls', () => {
mockCallsStore.calls = [];
- useCallsStore.mockReturnValue(mockCallsStore);
+ useCallsStore.mockImplementation((selector?: (state: typeof mockCallsStore) => unknown) => (selector ? selector(mockCallsStore) : mockCallsStore));
render();
@@ -383,7 +388,7 @@ describe('CallsScreen', () => {
it('tracks view rendered event with correct parameters', () => {
const mockCalls = [{ CallId: 'call-1', Nature: 'Test' }];
mockCallsStore.calls = mockCalls;
- useCallsStore.mockReturnValue(mockCallsStore);
+ useCallsStore.mockImplementation((selector?: (state: typeof mockCallsStore) => unknown) => (selector ? selector(mockCallsStore) : mockCallsStore));
render();
diff --git a/src/__tests__/security-integration.test.ts b/src/__tests__/security-integration.test.ts
index 9d77eb6a..6b25d0e2 100644
--- a/src/__tests__/security-integration.test.ts
+++ b/src/__tests__/security-integration.test.ts
@@ -26,6 +26,8 @@ describe('Security Permission Logic', () => {
CanCreateCalls: true,
CanAddNote: false,
CanCreateMessage: false,
+ CanLoginToDispatchApp: true,
+ CanLoginToCommandApp: true,
Groups: []
};
@@ -44,6 +46,8 @@ describe('Security Permission Logic', () => {
CanCreateCalls: false,
CanAddNote: true,
CanCreateMessage: true,
+ CanLoginToDispatchApp: true,
+ CanLoginToCommandApp: true,
Groups: []
};
@@ -65,6 +69,8 @@ describe('Security Permission Logic', () => {
CanViewPII: true,
CanAddNote: true,
CanCreateMessage: true,
+ CanLoginToDispatchApp: true,
+ CanLoginToCommandApp: true,
Groups: []
} as unknown as DepartmentRightsResultData;
@@ -85,6 +91,8 @@ describe('Security Permission Logic', () => {
CanCreateCalls: true,
CanAddNote: false,
CanCreateMessage: false,
+ CanLoginToDispatchApp: true,
+ CanLoginToCommandApp: true,
Groups: []
};
@@ -107,6 +115,8 @@ describe('Security Permission Logic', () => {
CanCreateCalls: false,
CanAddNote: true,
CanCreateMessage: true,
+ CanLoginToDispatchApp: true,
+ CanLoginToCommandApp: true,
Groups: []
};
diff --git a/src/api/chat/chat.ts b/src/api/chat/chat.ts
index f9399307..20cc0e01 100644
--- a/src/api/chat/chat.ts
+++ b/src/api/chat/chat.ts
@@ -31,9 +31,25 @@ const MODERATION = '/ChatModeration';
// Channels
// ---------------------------------------------------------------------------
-export const getChannels = async (activeUnitId?: number, signal?: AbortSignal) => {
+/**
+ * The caller's channels. `includeArchived` pulls in the point-in-time record of closed incidents and
+ * calls — off by default so the everyday list stays current. `callId` narrows the result server-side
+ * to channels attached to that call (older servers ignore it and return the full list).
+ */
+export const getChannels = async (activeUnitId?: number, includeArchived = false, callId?: number, signal?: AbortSignal) => {
+ const params: Record = {};
+ if (activeUnitId != null) {
+ params.activeUnitId = activeUnitId;
+ }
+ if (includeArchived) {
+ params.includeArchived = true;
+ }
+ if (callId != null) {
+ params.callId = callId;
+ }
+
const response = await api.get>(`${CHAT}/GetChannels`, {
- params: activeUnitId != null ? { activeUnitId } : undefined,
+ params: Object.keys(params).length > 0 ? params : undefined,
signal,
});
return response.data;
diff --git a/src/api/common/client.tsx b/src/api/common/client.tsx
index 4be3a4ee..1fc3dcfb 100644
--- a/src/api/common/client.tsx
+++ b/src/api/common/client.tsx
@@ -1,6 +1,6 @@
import axios, { type AxiosError, type AxiosInstance, type InternalAxiosRequestConfig } from 'axios';
-import { refreshTokenRequest } from '@/lib/auth/api';
+import { performTokenRefresh } from '@/lib/auth/token-refresh';
import { logger } from '@/lib/logging';
import { getBaseApiUrl } from '@/lib/storage/app';
import useAuthStore from '@/stores/auth/store';
@@ -78,35 +78,31 @@ axiosInstance.interceptors.response.use(
isRefreshing = true;
try {
- const refreshToken = useAuthStore.getState().refreshToken;
- if (!refreshToken) {
- throw new Error('No refresh token available');
+ // Single-flight refresh shared with the auth store's refresh timer, so a
+ // timer refresh and a 401-triggered refresh can never rotate the refresh
+ // token twice in parallel. Failure handling (logout) happens inside
+ // performTokenRefresh.
+ const refreshed = await performTokenRefresh();
+ if (!refreshed) {
+ throw new Error('Token refresh failed');
}
- const response = await refreshTokenRequest(refreshToken);
- const { access_token, refresh_token: newRefreshToken } = response;
-
- // Update tokens in store
- useAuthStore.setState({
- accessToken: access_token,
- refreshToken: newRefreshToken,
- status: 'signedIn',
- error: null,
- });
+ const accessToken = useAuthStore.getState().accessToken;
+ if (!accessToken) {
+ throw new Error('No access token available after refresh');
+ }
// Update Authorization header
- axiosInstance.defaults.headers.common.Authorization = `Bearer ${access_token}`;
- originalRequest.headers.Authorization = `Bearer ${access_token}`;
+ axiosInstance.defaults.headers.common.Authorization = `Bearer ${accessToken}`;
+ originalRequest.headers.Authorization = `Bearer ${accessToken}`;
processQueue(null);
return axiosInstance(originalRequest);
} catch (refreshError) {
processQueue(refreshError as Error);
- // Handle refresh token failure
- useAuthStore.getState().logout();
logger.error({
message: 'Token refresh failed',
- context: { error: refreshError },
+ context: { error: refreshError instanceof Error ? refreshError.message : String(refreshError) },
});
return Promise.reject(refreshError);
} finally {
diff --git a/src/app/(app)/_layout.tsx b/src/app/(app)/_layout.tsx
index 9e449204..cdf73900 100644
--- a/src/app/(app)/_layout.tsx
+++ b/src/app/(app)/_layout.tsx
@@ -27,16 +27,54 @@ import { Env } from '@/lib/env';
import { logger } from '@/lib/logging';
import { useIsFirstTime } from '@/lib/storage';
import { type GetConfigResultData } from '@/models/v4/configs/getConfigResultData';
-import { usePushNotifications } from '@/services/push-notification';
+import { audioService } from '@/services/audio.service';
+import { pushNotificationService, usePushNotifications } from '@/services/push-notification';
+import { useAudioStreamStore } from '@/stores/app/audio-stream-store';
import { useCoreStore } from '@/stores/app/core-store';
+import { useLiveKitStore } from '@/stores/app/livekit-store';
import { useCallsStore } from '@/stores/calls/store';
+import { useChatStore } from '@/stores/chat/store';
+import { useCheckInStore } from '@/stores/checkIn/store';
import { FeatureFlagKeys, featureFlagsStore } from '@/stores/feature-flags/store';
import useLockscreenStore from '@/stores/lockscreen/store';
import { useRolesStore } from '@/stores/roles/store';
import { securityStore } from '@/stores/security/store';
import { useSignalRStore } from '@/stores/signalr/signalr-store';
+import { useToastStore } from '@/stores/toast/store';
import { useWeatherAlertsStore } from '@/stores/weatherAlerts/store';
+/**
+ * Tear down every per-session resource on sign-out: SignalR hubs and their heartbeats,
+ * the LiveKit voice room, audio streams and cached sounds, check-in polling, chat
+ * timers, and push-notification listeners. Without this, all of them keep running
+ * against a signed-out session until the process dies.
+ */
+async function teardownSignedInSession(): Promise {
+ const signalR = useSignalRStore.getState();
+ const teardowns: [string, () => Promise | unknown][] = [
+ ['SignalR update hub', () => signalR.disconnectUpdateHub()],
+ ['SignalR chat hub', () => signalR.disconnectChatHub()],
+ ['SignalR geolocation hub', () => signalR.disconnectGeolocationHub()],
+ ['LiveKit room', () => useLiveKitStore.getState().disconnectFromRoom()],
+ ['audio stream', () => useAudioStreamStore.getState().cleanup()],
+ ['audio service', () => audioService.cleanup()],
+ ['check-in polling', () => useCheckInStore.getState().stopPolling()],
+ ['chat store', () => useChatStore.getState().reset()],
+ ['push notification listeners', () => pushNotificationService.cleanup()],
+ ];
+
+ for (const [label, teardown] of teardowns) {
+ try {
+ await teardown();
+ } catch (error) {
+ logger.error({
+ message: `Failed to tear down ${label} on sign-out`,
+ context: { error: error instanceof Error ? error.message : String(error) },
+ });
+ }
+ }
+}
+
export default function TabLayout() {
const { t } = useTranslation();
const status = useAuthStore((state) => state.status);
@@ -156,6 +194,23 @@ export default function TabLayout() {
await securityStore.getState().getRights();
+ // Dispatch shows private command, unit and responder traffic, so a member the department has
+ // not authorized must not get past initialization. The server is the real boundary — it simply
+ // never hands an unauthorized user the dispatch channels — but signing them straight back out
+ // is far clearer than a silently empty app.
+ if (!isCurrentRun()) {
+ return;
+ }
+ if (securityStore.getState().rights?.CanLoginToDispatchApp === false) {
+ logger.warn({
+ message: 'User is not authorized to use the Dispatch app; signing out',
+ context: { userId },
+ });
+ useToastStore.getState().showToast('error', t('login.dispatch_not_authorized'));
+ await useAuthStore.getState().logout();
+ return;
+ }
+
logger.info({
message: 'Security rights retrieved, fetching feature flags',
context: { platform: Platform.OS },
@@ -273,7 +328,7 @@ export default function TabLayout() {
// If the init promise is still hanging, clear the guard so a retry is possible
isInitializing.current = false;
}
- }, [status]);
+ }, [status, t, userId]);
const refreshDataFromBackground = useCallback(async () => {
if (status !== 'signedIn' || !hasInitialized.current) return;
@@ -377,6 +432,12 @@ export default function TabLayout() {
// so the next sign-in is not skipped as "already initializing".
initGeneration.current += 1;
isInitializing.current = false;
+ // Clear the initialized flag too, or a sign-in later in this process fails
+ // shouldInitialize and initializeApp never runs for the new session.
+ hasInitialized.current = false;
+
+ // Stop hubs, voice, audio and timers that belong to the ended session
+ void teardownSignedInSession();
}
// Update last known status
diff --git a/src/app/(app)/calls.tsx b/src/app/(app)/calls.tsx
index 04f4276e..6a8b891a 100644
--- a/src/app/(app)/calls.tsx
+++ b/src/app/(app)/calls.tsx
@@ -1,7 +1,7 @@
import { useFocusEffect } from '@react-navigation/native';
import { type Href, router } from 'expo-router';
import { PlusIcon, RefreshCcwDotIcon, Search, X } from 'lucide-react-native';
-import React, { useCallback, useEffect, useState } from 'react';
+import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Pressable, RefreshControl, View } from 'react-native';
@@ -17,11 +17,18 @@ import { useAnalytics } from '@/hooks/use-analytics';
import { CallState } from '@/lib/utils';
import { type CallResultData } from '@/models/v4/calls/callResultData';
import { useCallsStore } from '@/stores/calls/store';
-import { useSecurityStore } from '@/stores/security/store';
+import { securityStore } from '@/stores/security/store';
export default function Calls() {
- const { calls, isLoading, error, fetchCalls, fetchCallPriorities } = useCallsStore();
- const { canUserCreateCalls } = useSecurityStore();
+ // Field selectors only - subscribing to the whole store re-renders this screen on
+ // every store change (e.g. SignalR timestamp updates)
+ const calls = useCallsStore((s) => s.calls);
+ const isLoading = useCallsStore((s) => s.isLoading);
+ const error = useCallsStore((s) => s.error);
+ const callPriorities = useCallsStore((s) => s.callPriorities);
+ const fetchCalls = useCallsStore((s) => s.fetchCalls);
+ const fetchCallPriorities = useCallsStore((s) => s.fetchCallPriorities);
+ const canUserCreateCalls = securityStore((s) => s.rights?.CanCreateCalls);
const { t } = useTranslation();
const { trackEvent } = useAnalytics();
const [searchQuery, setSearchQuery] = useState('');
@@ -56,9 +63,26 @@ export default function Calls() {
};
// Filter calls: exclude scheduled calls and apply search
- const filteredCalls = calls
- .filter((call) => call.State !== CallState.SCHEDULED)
- .filter((call) => call.CallId.toLowerCase().includes(searchQuery.toLowerCase()) || (call.Nature?.toLowerCase() || '').includes(searchQuery.toLowerCase()));
+ const filteredCalls = useMemo(() => {
+ const query = searchQuery.toLowerCase();
+ return calls.filter((call) => call.State !== CallState.SCHEDULED).filter((call) => call.CallId.toLowerCase().includes(query) || (call.Nature?.toLowerCase() || '').includes(query));
+ }, [calls, searchQuery]);
+
+ // O(1) priority lookup per row instead of an O(n) find inside renderItem
+ const priorityById = useMemo(() => {
+ const map = new Map();
+ callPriorities.forEach((p) => map.set(p.Id, p));
+ return map;
+ }, [callPriorities]);
+
+ const renderCallItem = useCallback(
+ ({ item }: { item: CallResultData }) => (
+ router.push(`/call/${item.CallId}` as Href)}>
+
+
+ ),
+ [priorityById]
+ );
// Render content based on loading, error, and data states
const renderContent = () => {
@@ -74,11 +98,7 @@ export default function Calls() {
testID="calls-list"
data={filteredCalls}
- renderItem={({ item }: { item: CallResultData }) => (
- router.push(`/call/${item.CallId}` as Href)}>
- p.Id === item.Priority)} />
-
- )}
+ renderItem={renderCallItem}
keyExtractor={(item: CallResultData) => item.CallId}
refreshControl={}
ListEmptyComponent={}
diff --git a/src/app/(app)/chat.tsx b/src/app/(app)/chat.tsx
index 6b6baf00..067ba380 100644
--- a/src/app/(app)/chat.tsx
+++ b/src/app/(app)/chat.tsx
@@ -110,7 +110,7 @@ export default function ChatScreen() {
router.push('/chatbot' as Href);
return;
}
- router.push(`/chat/${channelId}` as Href);
+ router.push({ pathname: '/chat/[channelId]', params: { channelId } });
},
[router]
);
diff --git a/src/app/(app)/chatbot.tsx b/src/app/(app)/chatbot.tsx
index 3a015bfb..c6220aa1 100644
--- a/src/app/(app)/chatbot.tsx
+++ b/src/app/(app)/chatbot.tsx
@@ -68,7 +68,14 @@ export default function ChatbotScreen() {
const renderItem = useCallback(
({ item }: { item: ChatMessageResultData }) => (
- undefined} />
+ undefined}
+ />
),
[currentUserId]
);
diff --git a/src/app/(app)/home.tsx b/src/app/(app)/home.tsx
index 94295302..e2f89205 100644
--- a/src/app/(app)/home.tsx
+++ b/src/app/(app)/home.tsx
@@ -869,7 +869,9 @@ export default function DispatchConsole() {
) : (
-
+ {/* Bounded height lets the panels' inner FlatLists virtualize; inside an
+ unbounded ScrollView they would render every row at once */}
+
-
+
) : null}
-
+
+
+
);
@@ -1003,4 +1007,8 @@ const styles = StyleSheet.create({
resourcesPanelBounded: {
maxHeight: 400,
},
+ nestedPanelBounded: {
+ maxHeight: 400,
+ minHeight: 200,
+ },
});
diff --git a/src/app/(app)/map.tsx b/src/app/(app)/map.tsx
index d4231d6a..528c1f23 100644
--- a/src/app/(app)/map.tsx
+++ b/src/app/(app)/map.tsx
@@ -30,6 +30,52 @@ import { useToastStore } from '@/stores/toast/store';
Mapbox.setAccessToken(Env.MAPBOX_PUBKEY);
+interface UserLocationMarkerProps {
+ pulseAnim: Animated.Value;
+ innerContainerStyle: object;
+}
+
+// Memoized user-location marker. Subscribes to the location store itself so GPS ticks
+// only re-render this small subtree instead of the whole map screen (which owns the
+// pins, layers and modal trees).
+const UserLocationMarker = React.memo(({ pulseAnim, innerContainerStyle }) => {
+ const latitude = useLocationStore((state) => state.latitude);
+ const longitude = useLocationStore((state) => state.longitude);
+ const heading = useLocationStore((state) => state.heading);
+
+ if (!latitude || !longitude) {
+ return null;
+ }
+
+ return (
+
+
+
+
+
+ {heading !== null && heading !== undefined ? (
+
+ ) : null}
+
+
+
+ );
+});
+
export default function Map() {
const { t } = useTranslation();
const { trackEvent } = useAnalytics();
@@ -46,11 +92,13 @@ export default function Map() {
const [isPinDetailModalOpen, setIsPinDetailModalOpen] = useState(false);
const [isLayersPanelOpen, setIsLayersPanelOpen] = useState(false);
const { isActive } = useAppLifecycle();
- const latitude = useLocationStore((state) => state.latitude);
- const longitude = useLocationStore((state) => state.longitude);
- const heading = useLocationStore((state) => state.heading);
+ // Only isMapLocked is subscribed here: latitude/longitude/heading change at GPS
+ // frequency and are read imperatively (getState) or via the memoized
+ // UserLocationMarker, so location updates don't re-render the whole screen.
const isMapLocked = useLocationStore((state) => state.isMapLocked);
- const location = useMemo(() => ({ latitude, longitude, heading, isMapLocked }), [latitude, longitude, heading, isMapLocked]);
+ // Presence-only selector: the boolean flips when a fix is gained or lost, not on
+ // every GPS tick. Zero is a valid coordinate, so only null/undefined are "absent".
+ const hasDeviceLocation = useLocationStore((state) => state.latitude != null && state.longitude != null);
// Map layers hook
const { layers, visibleLayers, isLoading: isLayersLoading, fetchLayers, toggleLayer, showAllLayers, hideAllLayers, getVisibleLayerData } = useMapLayers({ initialLayerType: MapLayerType.ALL, autoFetch: true });
@@ -133,18 +181,19 @@ export default function Map() {
fetchLayers();
// Reset camera to current location when navigating back to map
- if (isMapReady && location.latitude && location.longitude) {
+ const { latitude, longitude, heading, isMapLocked: locked } = useLocationStore.getState();
+ if (isMapReady && latitude && longitude) {
const cameraConfig: any = {
- centerCoordinate: [location.longitude, location.latitude],
- zoomLevel: location.isMapLocked ? 16 : 12,
+ centerCoordinate: [longitude, latitude],
+ zoomLevel: locked ? 16 : 12,
animationDuration: 1000,
heading: 0,
pitch: 0,
};
// Add heading and pitch for navigation mode when locked
- if (location.isMapLocked && location.heading !== null && location.heading !== undefined) {
- cameraConfig.heading = location.heading;
+ if (locked && heading !== null && heading !== undefined) {
+ cameraConfig.heading = heading;
cameraConfig.pitch = 45;
}
@@ -154,61 +203,68 @@ export default function Map() {
message: 'Map focused, resetting camera to current location',
context: {
// GDPR: log bucketed (~11km) coordinates instead of verbatim position
- latitudeBucket: Math.round(location.latitude * 10) / 10,
- longitudeBucket: Math.round(location.longitude * 10) / 10,
- isMapLocked: location.isMapLocked,
+ latitudeBucket: Math.round(latitude * 10) / 10,
+ longitudeBucket: Math.round(longitude * 10) / 10,
+ isMapLocked: locked,
gdpr: { purpose: 'map_tracking', lawful_basis: 'consent' },
},
});
}
- }, [isMapReady, location.latitude, location.longitude, location.isMapLocked, location.heading, fetchLayers])
+ }, [isMapReady, fetchLayers])
);
+ // Imperative camera-follow: subscribing to the store inside an effect keeps GPS
+ // updates out of the React render cycle entirely.
+ const hasUserMovedMapRef = useRef(hasUserMovedMap);
useEffect(() => {
- if (isMapReady && location.latitude && location.longitude) {
+ hasUserMovedMapRef.current = hasUserMovedMap;
+ }, [hasUserMovedMap]);
+
+ useEffect(() => {
+ if (!isMapReady) return;
+
+ const unsubscribe = useLocationStore.subscribe((state, prevState) => {
+ if (state.latitude === prevState.latitude && state.longitude === prevState.longitude && state.heading === prevState.heading) {
+ return;
+ }
+
+ const { latitude, longitude, heading, isMapLocked: locked } = state;
+ if (!latitude || !longitude) return;
+
// When map is locked, always follow the location
// When map is unlocked, only follow if user hasn't moved the map
- if (location.isMapLocked || !hasUserMovedMap) {
- logger.info({
- message: 'Location updated and map is ready',
- context: {
- // GDPR: log bucketed (~11km) coordinates instead of verbatim position
- latitudeBucket: Math.round(location.latitude * 10) / 10,
- longitudeBucket: Math.round(location.longitude * 10) / 10,
- heading: location.heading,
- isMapLocked: location.isMapLocked,
- gdpr: { purpose: 'map_tracking', lawful_basis: 'consent' },
- },
- });
-
+ if (locked || !hasUserMovedMapRef.current) {
const cameraConfig: any = {
- centerCoordinate: [location.longitude, location.latitude],
- zoomLevel: location.isMapLocked ? 16 : 12,
- animationDuration: location.isMapLocked ? 500 : 1000,
+ centerCoordinate: [longitude, latitude],
+ zoomLevel: locked ? 16 : 12,
+ animationDuration: locked ? 500 : 1000,
};
// Add heading and pitch for navigation mode when locked
- if (location.isMapLocked && location.heading !== null && location.heading !== undefined) {
- cameraConfig.heading = location.heading;
+ if (locked && heading !== null && heading !== undefined) {
+ cameraConfig.heading = heading;
cameraConfig.pitch = 45;
}
cameraRef.current?.setCamera(cameraConfig);
}
- }
- }, [isMapReady, location.latitude, location.longitude, location.heading, location.isMapLocked, hasUserMovedMap]);
+ });
+
+ return unsubscribe;
+ }, [isMapReady]);
// Reset hasUserMovedMap when map gets locked and reset camera when unlocked
useEffect(() => {
- if (location.isMapLocked) {
+ if (isMapLocked) {
setHasUserMovedMap(false);
} else {
// When exiting locked mode, reset camera to normal view and reset user interaction state
setHasUserMovedMap(false);
- if (isMapReady && location.latitude && location.longitude) {
+ const { latitude, longitude } = useLocationStore.getState();
+ if (isMapReady && latitude && longitude) {
cameraRef.current?.setCamera({
- centerCoordinate: [location.longitude, location.latitude],
+ centerCoordinate: [longitude, latitude],
zoomLevel: 12,
heading: 0,
pitch: 0,
@@ -218,14 +274,14 @@ export default function Map() {
message: 'Map unlocked, resetting camera to normal view and user interaction state',
context: {
// GDPR: log bucketed (~11km) coordinates instead of verbatim position
- latitudeBucket: Math.round(location.latitude * 10) / 10,
- longitudeBucket: Math.round(location.longitude * 10) / 10,
+ latitudeBucket: Math.round(latitude * 10) / 10,
+ longitudeBucket: Math.round(longitude * 10) / 10,
gdpr: { purpose: 'map_tracking', lawful_basis: 'consent' },
},
});
}
}
- }, [isMapReady, location.isMapLocked, location.latitude, location.longitude]);
+ }, [isMapReady, isMapLocked]);
useEffect(() => {
const abortController = new AbortController();
@@ -284,31 +340,32 @@ export default function Map() {
trackEvent('map_view_rendered', {
hasMapPins: mapPins.length > 0,
mapPinsCount: mapPins.length,
- isMapLocked: location.isMapLocked,
+ isMapLocked,
theme: colorScheme || 'light',
layersCount: combinedLayers.length,
visibleLayersCount: visibleLayers.size + visiblePoiLayerIds.size,
});
- }, [trackEvent, mapPins.length, location.isMapLocked, colorScheme, combinedLayers.length, visibleLayers.size, visiblePoiLayerIds.size]);
+ }, [trackEvent, mapPins.length, isMapLocked, colorScheme, combinedLayers.length, visibleLayers.size, visiblePoiLayerIds.size]);
const onCameraChanged = (event: any) => {
// Only register user interaction if map is not locked
- if (event.properties.isUserInteraction && !location.isMapLocked) {
+ if (event.properties.isUserInteraction && !useLocationStore.getState().isMapLocked) {
setHasUserMovedMap(true);
}
};
const handleRecenterMap = () => {
- if (location.latitude && location.longitude) {
+ const { latitude, longitude, heading, isMapLocked: locked } = useLocationStore.getState();
+ if (latitude && longitude) {
const cameraConfig: any = {
- centerCoordinate: [location.longitude, location.latitude],
- zoomLevel: location.isMapLocked ? 16 : 12,
+ centerCoordinate: [longitude, latitude],
+ zoomLevel: locked ? 16 : 12,
animationDuration: 1000,
};
// Add heading and pitch for navigation mode when locked
- if (location.isMapLocked && location.heading !== null && location.heading !== undefined) {
- cameraConfig.heading = location.heading;
+ if (locked && heading !== null && heading !== undefined) {
+ cameraConfig.heading = heading;
cameraConfig.pitch = 45;
}
@@ -353,8 +410,9 @@ export default function Map() {
setSelectedPin(null);
};
- // Show recenter button only when map is not locked and user has moved the map
- const showRecenterButton = !location.isMapLocked && hasUserMovedMap && location.latitude && location.longitude;
+ // Show recenter button only when map is not locked, the user has moved the map,
+ // and there is a location to recenter on (handleRecenterMap no-ops without one)
+ const showRecenterButton = !isMapLocked && hasUserMovedMap && hasDeviceLocation;
// Create dynamic styles based on theme
const getThemedStyles = useCallback(() => {
@@ -572,50 +630,24 @@ export default function Map() {
onCameraChanged={onCameraChanged}
onDidFinishLoadingMap={() => setIsMapReady(true)}
testID="map-view"
- scrollEnabled={!location.isMapLocked}
- zoomEnabled={!location.isMapLocked}
- rotateEnabled={!location.isMapLocked}
- pitchEnabled={!location.isMapLocked}
+ scrollEnabled={!isMapLocked}
+ zoomEnabled={!isMapLocked}
+ rotateEnabled={!isMapLocked}
+ pitchEnabled={!isMapLocked}
>
{/* Render custom layers */}
{renderMapLayers()}
{renderActiveCustomLayers()}
- {location.latitude && location.longitude ? (
-
-
-
-
-
- {location.heading !== null && location.heading !== undefined ? (
-
- ) : null}
-
-
-
- ) : null}
+
diff --git a/src/components/checkIn/check-in-bottom-sheet.tsx b/src/components/checkIn/check-in-bottom-sheet.tsx
index ba552ed2..33b8d866 100644
--- a/src/components/checkIn/check-in-bottom-sheet.tsx
+++ b/src/components/checkIn/check-in-bottom-sheet.tsx
@@ -71,14 +71,12 @@ export const CheckInBottomSheet: React.FC = ({ isOpen,
const { width, height } = useWindowDimensions();
const isLandscape = width > height;
const showToast = useToastStore((state) => state.showToast);
- const { performCheckIn, isCheckingIn } = useCheckInStore();
+ // Field selectors - the whole-store form and the object location selector both
+ // re-render this sheet on every store/GPS update
+ const performCheckIn = useCheckInStore((s) => s.performCheckIn);
+ const isCheckingIn = useCheckInStore((s) => s.isCheckingIn);
const units = useUnitsStore((s) => s.units);
const personnel = usePersonnelStore((s) => s.personnel);
- const userLocation = useLocationStore((state) => ({
- latitude: state.latitude,
- longitude: state.longitude,
- }));
-
const typeLabel = (timer: CheckInTimerStatusResultData) => {
// Use TargetTypeName from API, fall back to translation key lookup
if (timer.TargetTypeName) return timer.TargetTypeName;
@@ -132,12 +130,16 @@ export const CheckInBottomSheet: React.FC = ({ isOpen,
}
try {
+ // Read the location imperatively - subscribing here would re-render the sheet
+ // on every GPS tick while it is open
+ const { latitude, longitude } = useLocationStore.getState();
+
const success = await performCheckIn({
CallId: effectiveCallId,
CheckInType: selected.TargetType,
UnitId: resolvedUnitId || undefined,
- Latitude: userLocation.latitude?.toString() || undefined,
- Longitude: userLocation.longitude?.toString() || undefined,
+ Latitude: latitude?.toString() || undefined,
+ Longitude: longitude?.toString() || undefined,
Note: note || undefined,
});
diff --git a/src/components/incident-command/command-board-view.tsx b/src/components/incident-command/command-board-view.tsx
index a21c25d1..928c7102 100644
--- a/src/components/incident-command/command-board-view.tsx
+++ b/src/components/incident-command/command-board-view.tsx
@@ -1,5 +1,23 @@
import { type Href, useRouter } from 'expo-router';
-import { CheckCircleIcon, ChevronDownIcon, ChevronUpIcon, ClipboardListIcon, ClockIcon, MapPinIcon, NetworkIcon, PlusIcon, ShieldAlertIcon, TimerIcon, UserCogIcon, UserPlusIcon, XIcon } from 'lucide-react-native';
+import {
+ CheckCircleIcon,
+ ChevronDownIcon,
+ ChevronUpIcon,
+ ClipboardListIcon,
+ ClockIcon,
+ MapPinIcon,
+ MessageCircleIcon,
+ MessagesSquareIcon,
+ NetworkIcon,
+ PlusIcon,
+ RadioIcon,
+ ShieldAlertIcon,
+ ShieldCheckIcon,
+ TimerIcon,
+ UserCogIcon,
+ UserPlusIcon,
+ XIcon,
+} from 'lucide-react-native';
import React, { useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Alert, Platform } from 'react-native';
@@ -10,8 +28,11 @@ import { Heading } from '@/components/ui/heading';
import { HStack } from '@/components/ui/hstack';
import { Text } from '@/components/ui/text';
import { VStack } from '@/components/ui/vstack';
+import { useDirectMessage } from '@/hooks/use-direct-message';
+import { ChatChannelType } from '@/models/v4/chat';
import { type CommandStructureNode } from '@/models/v4/incidentCommand/commandStructureNode';
import { hasIncidentCapability, IncidentCapabilities, IncidentCommandStatus, ResourceAssignmentKind } from '@/models/v4/incidentCommand/incidentCommandEnums';
+import { useChatStore } from '@/stores/chat/store';
import { useIncidentCommandStore } from '@/stores/incident-command/store';
import { usePersonnelStore } from '@/stores/personnel/store';
import { useToastStore } from '@/stores/toast/store';
@@ -39,6 +60,31 @@ const Section: React.FC<{ title: string; icon: React.ReactNode; action?: React.R
);
+/** One tappable channel row; shows as unavailable rather than hiding, so absence is explainable. */
+const ChatChannelRow: React.FC<{
+ label: string;
+ hint?: string;
+ icon: React.ReactNode;
+ channelId: string | null;
+ unavailableMessage: string;
+ onOpen: (channelId: string | null, unavailableMessage: string) => void;
+ openLabel: string;
+ testID?: string;
+}> = ({ label, hint, icon, channelId, unavailableMessage, onOpen, openLabel, testID }) => (
+
+
+ {icon}
+
+ {label}
+ {hint ? {hint} : null}
+
+
+
+
+);
+
type TranslateFn = ReturnType['t'];
const confirmAction = (message: string, onConfirm: () => void, t: TranslateFn) => {
@@ -76,6 +122,9 @@ export const CommandBoardView: React.FC = () => {
const canManageAnnotations = hasIncidentCapability(capabilities, IncidentCapabilities.ManageAnnotations);
const canManageChannels = hasIncidentCapability(capabilities, IncidentCapabilities.ManageChannels);
+ const { openDirectMessage } = useDirectMessage();
+ const incidentChannels = useChatStore((s) => (board?.Command?.CallId ? s.incidentChannelsByCallId[String(board.Command.CallId)] : undefined));
+
const personnelMap = useMemo(() => new Map(personnel.map((p) => [p.UserId, `${p.FirstName} ${p.LastName}`.trim()])), [personnel]);
const unitsMap = useMemo(() => new Map(units.map((u) => [u.UnitId, u.Name])), [units]);
@@ -90,6 +139,20 @@ export const CommandBoardView: React.FC = () => {
};
const userName = (id: string): string => personnelMap.get(id) || id || t('incident_command.unassigned');
+ // Channel ids come from the server already filtered to what this user may open — an id we did not
+ // receive is one the API would reject, so the row renders as unavailable rather than a dead link.
+ const findChannel = (channelType: number): string | null => incidentChannels?.find((channel) => channel.ChannelType === channelType)?.ChatChannelId ?? null;
+ const dispatchChannelId = findChannel(ChatChannelType.IncidentDispatch);
+ const incidentChannelId = findChannel(ChatChannelType.Incident);
+
+ const openChannel = (channelId: string | null, unavailableMessage: string) => {
+ if (!channelId) {
+ showToast('info', unavailableMessage);
+ return;
+ }
+ router.push({ pathname: '/chat/[channelId]', params: { channelId } });
+ };
+
const activeNodes = (board.Nodes ?? []).filter((node) => !node.DeletedOn);
const activeAssignments = (board.Assignments ?? []).filter((a) => !a.ReleasedOn);
const counts = useIncidentCommandStore.getState().accountabilityCounts();
@@ -437,6 +500,66 @@ export const CommandBoardView: React.FC = () => {
)}
+ {/* Incident chat: the channels this dispatcher can open, and a direct line to the IC. */}
+ }>
+ {isClosed ? {t('incident_command.chat_frozen')} : null}
+
+ {/* The call's shared conversation — everyone working the incident, dispatch included. */}
+ }
+ channelId={incidentChannelId}
+ unavailableMessage={t('incident_command.incident_channel_unavailable')}
+ onOpen={openChannel}
+ openLabel={t('incident_command.open_chat')}
+ testID="incident-command-chat-incident"
+ />
+ {/* The desk's own line to the incident. The private command channel is deliberately absent:
+ it stays internal to command staff, and dispatch reaches command through here. */}
+ }
+ channelId={dispatchChannelId}
+ unavailableMessage={t('incident_command.dispatch_channel_unavailable')}
+ onOpen={openChannel}
+ openLabel={t('incident_command.open_chat')}
+ testID="incident-command-chat-dispatch"
+ />
+
+ {/* Direct line to whoever currently holds command. */}
+ {command.CurrentCommanderUserId ? (
+
+
+ {userName(command.CurrentCommanderUserId)}
+ {t('incident_command.commander')}
+
+
+
+ ) : null}
+
+ {/* Everyone holding an ICS position, so dispatch can reach the right person directly. */}
+ {(board.Roles ?? [])
+ .filter((role) => !role.RemovedOn)
+ .map((role) => (
+
+
+ {userName(role.UserId)}
+ {roleTypeLabel(role.RoleType)}
+
+
+
+ ))}
+
+
+
{/* Voice channels */}
diff --git a/src/components/incident-command/incident-command-tab.tsx b/src/components/incident-command/incident-command-tab.tsx
index 1bd86610..d7874c66 100644
--- a/src/components/incident-command/incident-command-tab.tsx
+++ b/src/components/incident-command/incident-command-tab.tsx
@@ -9,6 +9,7 @@ import { Button, ButtonIcon, ButtonText } from '@/components/ui/button';
import { HStack } from '@/components/ui/hstack';
import { Text } from '@/components/ui/text';
import { VStack } from '@/components/ui/vstack';
+import { useChatStore } from '@/stores/chat/store';
import { useIncidentCommandStore } from '@/stores/incident-command/store';
import { usePersonnelStore } from '@/stores/personnel/store';
import { useSecurityStore } from '@/stores/security/store';
@@ -29,20 +30,38 @@ export const IncidentCommandTab: React.FC = ({ callId,
const isLoading = useIncidentCommandStore((s) => s.isLoading);
const board = useIncidentCommandStore((s) => s.board);
const error = useIncidentCommandStore((s) => s.error);
- const { canUserCreateCalls } = useSecurityStore();
+ // Working a command board is its own department permission, separate from Dispatch access. Without
+ // it every board endpoint returns 403, so say so plainly instead of loading into a failure.
+ const { canUserCreateCalls, canUserWorkCommand } = useSecurityStore();
+ const canWorkCommand = canUserWorkCommand !== false;
const [isEstablishOpen, setIsEstablishOpen] = useState(false);
useEffect(() => {
- if (callId) {
+ if (callId && canWorkCommand) {
useIncidentCommandStore.getState().loadForCall(callId);
// Preload personnel + units so the board can resolve assignment / role names.
usePersonnelStore.getState().fetchPersonnel();
useUnitsStore.getState().fetchUnits();
+ // Chat channels for the incident. Archived ones are included so a closed incident's
+ // conversations stay readable as a point-in-time record.
+ void useChatStore.getState().loadIncidentChannels(callId);
}
- }, [callId]);
+ }, [callId, canWorkCommand]);
const hasCommand = !!board && !!board.Command?.IncidentCommandId;
+ if (!canWorkCommand) {
+ return (
+
+
+
+ {t('incident_command.not_authorized')}
+ {t('incident_command.not_authorized_description')}
+
+
+ );
+ }
+
if (isLoading && !hasCommand) {
return (
diff --git a/src/components/pois/poi-detail-screen.tsx b/src/components/pois/poi-detail-screen.tsx
index 944f4d21..4cfcee9a 100644
--- a/src/components/pois/poi-detail-screen.tsx
+++ b/src/components/pois/poi-detail-screen.tsx
@@ -24,11 +24,13 @@ export const PoiDetailScreen: React.FC = () => {
const router = useRouter();
const { t } = useTranslation();
const showToast = useToastStore((state) => state.showToast);
- const { selectedPoi, isLoadingDetail, detailError, fetchPoi, resetSelectedPoi } = usePoisStore();
- const userLocation = useLocationStore((state) => ({
- latitude: state.latitude,
- longitude: state.longitude,
- }));
+ // Field selectors - whole-store and object location selectors re-render this screen
+ // on every store/GPS update
+ const selectedPoi = usePoisStore((s) => s.selectedPoi);
+ const isLoadingDetail = usePoisStore((s) => s.isLoadingDetail);
+ const detailError = usePoisStore((s) => s.detailError);
+ const fetchPoi = usePoisStore((s) => s.fetchPoi);
+ const resetSelectedPoi = usePoisStore((s) => s.resetSelectedPoi);
const poiId = useMemo(() => {
const rawId = Array.isArray(id) ? id[0] : id;
@@ -52,7 +54,10 @@ export const PoiDetailScreen: React.FC = () => {
return;
}
- const success = await openMapsWithDirections(selectedPoi.Latitude, selectedPoi.Longitude, getPoiPrimaryDisplayText(selectedPoi), userLocation.latitude || undefined, userLocation.longitude || undefined);
+ // Read the location imperatively - subscribing here would re-render on every GPS tick
+ const { latitude, longitude } = useLocationStore.getState();
+
+ const success = await openMapsWithDirections(selectedPoi.Latitude, selectedPoi.Longitude, getPoiPrimaryDisplayText(selectedPoi), latitude || undefined, longitude || undefined);
if (!success) {
showToast('error', t('pois.route_error'));
diff --git a/src/components/sidebar/call-sidebar.tsx b/src/components/sidebar/call-sidebar.tsx
index 2c382cf6..77ea588a 100644
--- a/src/components/sidebar/call-sidebar.tsx
+++ b/src/components/sidebar/call-sidebar.tsx
@@ -20,11 +20,11 @@ import { HStack } from '../ui/hstack';
export const SidebarCallCard = () => {
const { colorScheme } = useColorScheme();
- const { activeCall, activePriorityId, setActiveCall } = useCoreStore((state) => ({
- activeCall: state.activeCall,
- activePriorityId: state.activePriority,
- setActiveCall: state.setActiveCall,
- }));
+ // Individual selectors - an object selector without shallow comparison returns a new
+ // reference every call and re-renders on every store update
+ const activeCall = useCoreStore((state) => state.activeCall);
+ const activePriorityId = useCoreStore((state) => state.activePriority);
+ const setActiveCall = useCoreStore((state) => state.setActiveCall);
// Get the actual priority object from the calls store
const activePriority = useCallsStore((state) => (activePriorityId ? state.getPriorityById(activePriorityId) : undefined));
diff --git a/src/hooks/__tests__/use-signalr-lifecycle.test.tsx b/src/hooks/__tests__/use-signalr-lifecycle.test.tsx
index 2ebff067..9c51beba 100644
--- a/src/hooks/__tests__/use-signalr-lifecycle.test.tsx
+++ b/src/hooks/__tests__/use-signalr-lifecycle.test.tsx
@@ -17,6 +17,8 @@ describe('useSignalRLifecycle', () => {
const mockDisconnectUpdateHub = jest.fn();
const mockConnectGeolocationHub = jest.fn();
const mockDisconnectGeolocationHub = jest.fn();
+ const mockConnectChatHub = jest.fn();
+ const mockDisconnectChatHub = jest.fn();
// Create shared state for app lifecycle that can be updated
let appLifecycleState = {
@@ -42,15 +44,19 @@ describe('useSignalRLifecycle', () => {
isActive: true,
};
- // Mock SignalR store
- mockUseSignalRStore.mockReturnValue({
+ // Mock SignalR store (selector-aware, like the real zustand hook)
+ const signalRStoreState: any = {
connectUpdateHub: mockConnectUpdateHub,
disconnectUpdateHub: mockDisconnectUpdateHub,
connectGeolocationHub: mockConnectGeolocationHub,
disconnectGeolocationHub: mockDisconnectGeolocationHub,
+ connectChatHub: mockConnectChatHub,
+ disconnectChatHub: mockDisconnectChatHub,
isUpdateHubConnected: false,
isGeolocationHubConnected: false,
- } as any);
+ isChatHubConnected: false,
+ };
+ mockUseSignalRStore.mockImplementation((selector?: (state: any) => unknown) => (selector ? selector(signalRStoreState) : signalRStoreState) as any);
// Mock useAppLifecycle to return shared state
mockUseAppLifecycle.mockImplementation(() => appLifecycleState);
diff --git a/src/hooks/use-direct-message.ts b/src/hooks/use-direct-message.ts
new file mode 100644
index 00000000..5cbc0ae2
--- /dev/null
+++ b/src/hooks/use-direct-message.ts
@@ -0,0 +1,47 @@
+import { router } from 'expo-router';
+import { useCallback, useState } from 'react';
+import { useTranslation } from 'react-i18next';
+
+import { createDirectMessage } from '@/api/chat/chat';
+import { logger } from '@/lib/logging';
+import { useToastStore } from '@/stores/toast/store';
+
+/**
+ * Opens a 1:1 conversation with someone and navigates to it.
+ *
+ * The server dedups on a normalized participant key, so calling this repeatedly for the same person
+ * reuses the existing conversation rather than starting a new one — which is what makes it safe to
+ * hang a "message" button off every contact on an incident.
+ */
+export const useDirectMessage = () => {
+ const { t } = useTranslation();
+ const [isOpening, setIsOpening] = useState(false);
+
+ const openDirectMessage = useCallback(
+ async (targetUserId?: string | null) => {
+ if (!targetUserId) {
+ useToastStore.getState().showToast('info', t('incident_command.dm_unavailable'));
+ return;
+ }
+
+ setIsOpening(true);
+ try {
+ const channel = await createDirectMessage({ TargetUserId: targetUserId });
+ const channelId = channel?.Data?.ChatChannelId;
+ if (!channelId) {
+ useToastStore.getState().showToast('error', t('incident_command.dm_failed'));
+ return;
+ }
+ router.push({ pathname: '/chat/[channelId]', params: { channelId } });
+ } catch (error) {
+ logger.error({ message: 'chat: failed to open direct message', context: { error, targetUserId } });
+ useToastStore.getState().showToast('error', t('incident_command.dm_failed'));
+ } finally {
+ setIsOpening(false);
+ }
+ },
+ [t]
+ );
+
+ return { openDirectMessage, isOpening };
+};
diff --git a/src/hooks/use-saml-login.ts b/src/hooks/use-saml-login.ts
index 772996e7..e56dd615 100644
--- a/src/hooks/use-saml-login.ts
+++ b/src/hooks/use-saml-login.ts
@@ -1,3 +1,4 @@
+import { randomUUID } from 'expo-crypto';
import * as Linking from 'expo-linking';
import * as WebBrowser from 'expo-web-browser';
import { Platform } from 'react-native';
@@ -5,12 +6,44 @@ import { Platform } from 'react-native';
import { externalTokenRequest } from '@/lib/auth/api';
import type { AuthResponse } from '@/lib/auth/types';
import { logger } from '@/lib/logging';
+import { getItem, removeItem, setItem } from '@/lib/storage';
export interface SamlLoginHook {
startSamlLogin: () => Promise;
handleSamlDeepLink: (url: string) => Promise;
}
+// CSRF protection for the SAML flow: a random RelayState nonce is generated when the
+// user starts the login and the deep link is only accepted while a matching pending
+// flow exists. Persisted so a cold-started app (killed during the browser round-trip)
+// can still validate the callback. Any installed app can claim the custom URL scheme,
+// so an unsolicited injected saml_response must be rejected.
+const SAML_PENDING_STATE_KEY = 'SAML_PENDING_STATE';
+const SAML_FLOW_MAX_AGE_MS = 10 * 60 * 1000;
+
+interface SamlPendingState {
+ nonce: string;
+ startedAt: number;
+}
+
+async function savePendingState(state: SamlPendingState | null): Promise {
+ if (state) {
+ await setItem(SAML_PENDING_STATE_KEY, JSON.stringify(state));
+ } else {
+ await removeItem(SAML_PENDING_STATE_KEY);
+ }
+}
+
+function readPendingState(): SamlPendingState | null {
+ const raw = getItem(SAML_PENDING_STATE_KEY);
+ if (!raw) return null;
+ try {
+ return JSON.parse(raw) as SamlPendingState;
+ } catch {
+ return null;
+ }
+}
+
export function useSamlLogin(idpSsoUrl: string, username: string, departmentId?: number): SamlLoginHook {
async function startSamlLogin(): Promise {
if (!idpSsoUrl) {
@@ -19,11 +52,18 @@ export function useSamlLogin(idpSsoUrl: string, username: string, departmentId?:
}
try {
+ // Record the pending flow and ask the IdP to echo our nonce back as RelayState
+ const nonce = randomUUID();
+ await savePendingState({ nonce, startedAt: Date.now() });
+
+ const separator = idpSsoUrl.includes('?') ? '&' : '?';
+ const urlWithState = `${idpSsoUrl}${separator}RelayState=${encodeURIComponent(nonce)}`;
+
if (Platform.OS === 'web') {
// On web, open in the same tab so the SAML flow completes properly
- Linking.openURL(idpSsoUrl);
+ Linking.openURL(urlWithState);
} else {
- await WebBrowser.openBrowserAsync(idpSsoUrl);
+ await WebBrowser.openBrowserAsync(urlWithState);
}
} catch (error) {
logger.error({ message: 'SSO SAML: Failed to open IdP browser', context: { error, idpSsoUrl } });
@@ -41,6 +81,24 @@ export function useSamlLogin(idpSsoUrl: string, username: string, departmentId?:
return null;
}
+ // Validate against the pending flow started by startSamlLogin
+ const pending = readPendingState();
+ if (!pending || Date.now() - pending.startedAt > SAML_FLOW_MAX_AGE_MS) {
+ logger.warn({ message: 'SSO SAML: Rejecting saml_response with no fresh pending login flow', context: { path: parsed.path } });
+ return null;
+ }
+
+ // If the IdP echoed RelayState (or a state param), it must match our nonce.
+ // Absence is tolerated for IdPs that drop RelayState; the pending-flow check above still applies.
+ const echoedState = (parsed.queryParams?.RelayState ?? parsed.queryParams?.state) as string | undefined;
+ if (echoedState && echoedState !== pending.nonce) {
+ logger.warn({ message: 'SSO SAML: Rejecting saml_response with mismatched RelayState', context: { path: parsed.path } });
+ return null;
+ }
+
+ // Consume the pending flow so a replayed deep link is rejected
+ await savePendingState(null);
+
const result = await externalTokenRequest('saml2', samlResponse, username, departmentId);
if (!result.successful || !result.authResponse) {
diff --git a/src/hooks/use-signalr-lifecycle.ts b/src/hooks/use-signalr-lifecycle.ts
index af3a675e..06d35348 100644
--- a/src/hooks/use-signalr-lifecycle.ts
+++ b/src/hooks/use-signalr-lifecycle.ts
@@ -12,7 +12,14 @@ interface UseSignalRLifecycleOptions {
export function useSignalRLifecycle({ isSignedIn, hasInitialized }: UseSignalRLifecycleOptions) {
const { isActive, appState } = useAppLifecycle();
- const signalRStore = useSignalRStore();
+ // Select only the actions needed - subscribing to the whole store re-creates the
+ // callbacks below (and re-runs the lifecycle effects) on every hub message
+ const connectUpdateHub = useSignalRStore((s) => s.connectUpdateHub);
+ const disconnectUpdateHub = useSignalRStore((s) => s.disconnectUpdateHub);
+ const connectGeolocationHub = useSignalRStore((s) => s.connectGeolocationHub);
+ const disconnectGeolocationHub = useSignalRStore((s) => s.disconnectGeolocationHub);
+ const connectChatHub = useSignalRStore((s) => s.connectChatHub);
+ const disconnectChatHub = useSignalRStore((s) => s.disconnectChatHub);
// Track current values with refs for timer callbacks
const currentIsActive = useRef(isActive);
@@ -64,7 +71,7 @@ export function useSignalRLifecycle({ isSignedIn, hasInitialized }: UseSignalRLi
try {
// Use Promise.allSettled to prevent one failure from blocking the other
const hubNames = ['UpdateHub', 'GeolocationHub', 'ChatHub'];
- const results = await Promise.allSettled([signalRStore.disconnectUpdateHub(), signalRStore.disconnectGeolocationHub(), signalRStore.disconnectChatHub()]);
+ const results = await Promise.allSettled([disconnectUpdateHub(), disconnectGeolocationHub(), disconnectChatHub()]);
// Log any failures without throwing
results.forEach((result, index) => {
@@ -86,7 +93,7 @@ export function useSignalRLifecycle({ isSignedIn, hasInitialized }: UseSignalRLi
pendingOperations.current = null;
}
}
- }, [signalRStore]);
+ }, [disconnectUpdateHub, disconnectGeolocationHub, disconnectChatHub]);
const handleAppResume = useCallback(async () => {
logger.debug({
@@ -118,7 +125,7 @@ export function useSignalRLifecycle({ isSignedIn, hasInitialized }: UseSignalRLi
try {
// Use Promise.allSettled to prevent one failure from blocking the other
const hubNames = ['UpdateHub', 'GeolocationHub', 'ChatHub'];
- const results = await Promise.allSettled([signalRStore.connectUpdateHub(), signalRStore.connectGeolocationHub(), signalRStore.connectChatHub()]);
+ const results = await Promise.allSettled([connectUpdateHub(), connectGeolocationHub(), connectChatHub()]);
// Log any failures without throwing
results.forEach((result, index) => {
@@ -140,7 +147,7 @@ export function useSignalRLifecycle({ isSignedIn, hasInitialized }: UseSignalRLi
pendingOperations.current = null;
}
}
- }, [signalRStore]);
+ }, [connectUpdateHub, connectGeolocationHub, connectChatHub]);
// Clear timers helper
const clearTimers = useCallback(() => {
diff --git a/src/lib/__tests__/navigation.test.ts b/src/lib/__tests__/navigation.test.ts
index 33950357..7e3c0481 100644
--- a/src/lib/__tests__/navigation.test.ts
+++ b/src/lib/__tests__/navigation.test.ts
@@ -13,6 +13,14 @@ jest.mock('react-native', () => ({
},
}));
+// Mock expo-router - pulling in the real module drags @react-navigation into the
+// module graph, which needs far more of the RN API surface than the mock above has
+jest.mock('expo-router', () => ({
+ router: {
+ push: jest.fn(),
+ },
+}));
+
// Mock the logger
jest.mock('../logging', () => ({
logger: {
diff --git a/src/lib/auth/__tests__/token-refresh.test.ts b/src/lib/auth/__tests__/token-refresh.test.ts
new file mode 100644
index 00000000..39ec440d
--- /dev/null
+++ b/src/lib/auth/__tests__/token-refresh.test.ts
@@ -0,0 +1,52 @@
+import { cancelScheduledTokenRefresh, scheduleTokenRefresh } from '../token-refresh';
+
+jest.mock('@/lib/logging', () => ({
+ logger: { info: jest.fn(), warn: jest.fn(), error: jest.fn() },
+}));
+
+jest.mock('../api', () => ({
+ refreshTokenRequest: jest.fn(),
+}));
+
+describe('scheduleTokenRefresh', () => {
+ let setTimeoutSpy: jest.SpyInstance;
+
+ beforeEach(() => {
+ jest.useFakeTimers();
+ setTimeoutSpy = jest.spyOn(global, 'setTimeout');
+ });
+
+ afterEach(() => {
+ cancelScheduledTokenRefresh();
+ jest.useRealTimers();
+ jest.restoreAllMocks();
+ });
+
+ const lastScheduledDelay = (): number => setTimeoutSpy.mock.calls[setTimeoutSpy.mock.calls.length - 1][1] as number;
+
+ it('refreshes one buffer before expiry for normal lifetimes', () => {
+ scheduleTokenRefresh(86400); // 24h, the server default
+ expect(lastScheduledDelay()).toBe(86400 * 1000 - 60000);
+ });
+
+ it('refreshes at half the lifetime when the lifetime approaches the buffer', () => {
+ // A 1-minute server lifetime equals the buffer; the old max(lifetime - buffer, min)
+ // clamped this to the 5s minimum and refreshed in a perpetual tight loop.
+ scheduleTokenRefresh(60);
+ expect(lastScheduledDelay()).toBe(30000);
+ });
+
+ it('never schedules below the minimum delay', () => {
+ scheduleTokenRefresh(8);
+ expect(lastScheduledDelay()).toBe(5000);
+
+ scheduleTokenRefresh(0);
+ expect(lastScheduledDelay()).toBe(5000);
+ });
+
+ it('replaces a previously scheduled refresh instead of stacking timers', () => {
+ scheduleTokenRefresh(3600);
+ scheduleTokenRefresh(7200);
+ expect(jest.getTimerCount()).toBe(1);
+ });
+});
diff --git a/src/lib/auth/token-refresh.ts b/src/lib/auth/token-refresh.ts
new file mode 100644
index 00000000..21216065
--- /dev/null
+++ b/src/lib/auth/token-refresh.ts
@@ -0,0 +1,98 @@
+import { logger } from '@/lib/logging';
+
+import { refreshTokenRequest } from './api';
+import type { AuthResponse } from './types';
+
+const REFRESH_BUFFER_MS = 60000;
+const MIN_REFRESH_DELAY_MS = 5000;
+
+let refreshTimer: ReturnType | null = null;
+let inFlightRefresh: Promise | null = null;
+
+export interface TokenRefreshHandlers {
+ getRefreshToken: () => string | null;
+ applyAuthResponse: (response: AuthResponse) => void;
+ onRefreshFailed: () => void;
+}
+
+let handlers: TokenRefreshHandlers | null = null;
+
+export function initTokenRefresh(h: TokenRefreshHandlers): void {
+ handlers = h;
+}
+
+export function cancelScheduledTokenRefresh(): void {
+ if (refreshTimer) {
+ clearTimeout(refreshTimer);
+ refreshTimer = null;
+ }
+}
+
+/**
+ * Schedule an automatic refresh one minute before the access token expires.
+ * `expiresInSeconds` is the relative lifetime from the token response, NOT an
+ * epoch timestamp - subtracting Date.now() from it yields a hugely negative
+ * delay that fires the timer immediately and loops forever.
+ */
+export function scheduleTokenRefresh(expiresInSeconds: number): void {
+ cancelScheduledTokenRefresh();
+
+ // Refresh REFRESH_BUFFER_MS before expiry, but never earlier than half the token's
+ // lifetime: the server's lifetime is configurable down to one minute, which equals
+ // the buffer and would otherwise clamp every delay to the minimum and refresh in a
+ // perpetual tight loop. MIN_REFRESH_DELAY_MS stays as the absolute lower bound.
+ const lifetimeMs = expiresInSeconds * 1000;
+ const delay = Math.max(lifetimeMs - REFRESH_BUFFER_MS, lifetimeMs / 2, MIN_REFRESH_DELAY_MS);
+ refreshTimer = setTimeout(() => {
+ refreshTimer = null;
+ void performTokenRefresh();
+ }, delay);
+}
+
+/**
+ * Single-flight token refresh shared by the auth store timer and the axios 401
+ * interceptor. Concurrent callers await the same request, so the refresh token
+ * is never rotated twice in parallel (which invalidates one of the requests).
+ */
+export function performTokenRefresh(): Promise {
+ if (inFlightRefresh) {
+ return inFlightRefresh;
+ }
+
+ const operation = (async (): Promise => {
+ if (!handlers) {
+ logger.error({ message: 'Token refresh attempted before initTokenRefresh was called' });
+ return false;
+ }
+
+ const refreshToken = handlers.getRefreshToken();
+ if (!refreshToken) {
+ logger.warn({ message: 'Token refresh skipped: no refresh token available' });
+ handlers.onRefreshFailed();
+ return false;
+ }
+
+ try {
+ const response = await refreshTokenRequest(refreshToken);
+ handlers.applyAuthResponse(response);
+ scheduleTokenRefresh(response.expires_in);
+ return true;
+ } catch (error) {
+ logger.error({
+ message: 'Token refresh failed',
+ context: { error: error instanceof Error ? error.message : String(error) },
+ });
+ handlers.onRefreshFailed();
+ return false;
+ }
+ })();
+
+ inFlightRefresh = operation;
+ operation.finally(() => {
+ if (inFlightRefresh === operation) {
+ inFlightRefresh = null;
+ }
+ });
+
+ return operation;
+}
diff --git a/src/models/v4/chat/chatEnums.ts b/src/models/v4/chat/chatEnums.ts
index 006fd1c9..11916f0c 100644
--- a/src/models/v4/chat/chatEnums.ts
+++ b/src/models/v4/chat/chatEnums.ts
@@ -13,6 +13,10 @@ export enum ChatChannelType {
IncidentLane = 6,
IncidentCommand = 7,
Chatbot = 8,
+ /** IC plus every lane's primary/secondary lead — command talking to the people running the lanes. */
+ IncidentLeads = 9,
+ /** The incident's line to the dispatch desk: everyone on the incident, plus every authorized dispatcher. */
+ IncidentDispatch = 10,
}
/** Message type (ChatMessageResultData.MessageType). */
diff --git a/src/models/v4/security/departmentRightsResultData.ts b/src/models/v4/security/departmentRightsResultData.ts
index 00fdc89f..8b77e89a 100644
--- a/src/models/v4/security/departmentRightsResultData.ts
+++ b/src/models/v4/security/departmentRightsResultData.ts
@@ -9,6 +9,18 @@ export class DepartmentRightsResultData {
public CanCreateCalls: boolean = false; // Can Create Calls
public CanAddNote: boolean = false; // Can Add a Note
public CanCreateMessage: boolean = false; // Can Add a Message
+ /**
+ * Whether this user may use the Dispatch app at all. Dispatch surfaces private command, unit and
+ * responder traffic, so departments can restrict it; defaults to true for everyone when the
+ * permission has never been configured.
+ */
+ public CanLoginToDispatchApp: boolean = true;
+ /**
+ * Whether this user may work incident command: read command boards and act on them. A dispatcher
+ * assisting an incident needs this in addition to Dispatch access. Defaults to true for everyone
+ * when the department has never configured the permission.
+ */
+ public CanLoginToCommandApp: boolean = true;
public Groups: GroupRightResultData[] = []; // Group Rights
}
diff --git a/src/services/signalr.service.ts b/src/services/signalr.service.ts
index 8ffa69d6..41a0597e 100644
--- a/src/services/signalr.service.ts
+++ b/src/services/signalr.service.ts
@@ -793,8 +793,11 @@ class SignalRService {
context: { error, attempts: currentAttempts, maxAttempts: this.MAX_RECONNECT_ATTEMPTS },
});
- // Don't immediately retry; let the next connection close event trigger another attempt
- // This prevents rapid retry loops that could overwhelm the server
+ // Schedule the next attempt: the connection is already closed, so no further
+ // close event will fire to re-trigger reconnection, and without this the hub
+ // stays dead until an app lifecycle event. Backoff grows via the attempts
+ // counter, so this cannot spin a rapid retry loop.
+ this.handleConnectionClose(hubName);
}
}, backoffDelay);
diff --git a/src/stores/app/audio-stream-store.web.ts b/src/stores/app/audio-stream-store.web.ts
index 7ed301a7..ad86e027 100644
--- a/src/stores/app/audio-stream-store.web.ts
+++ b/src/stores/app/audio-stream-store.web.ts
@@ -4,6 +4,17 @@ import { getDepartmentAudioStreams } from '@/api/voice';
import { logger } from '@/lib/logging';
import { type DepartmentAudioResultStreamData } from '@/models/v4/voice/departmentAudioResultStreamData';
+// Aborts all listeners on the current audio element; recreated per stream so
+// stopStream can actually detach handlers instead of leaving them for the GC.
+let streamListenersAbort: AbortController | null = null;
+
+const detachStreamListeners = () => {
+ if (streamListenersAbort) {
+ streamListenersAbort.abort();
+ streamListenersAbort = null;
+ }
+};
+
interface AudioStreamState {
// Available streams
availableStreams: DepartmentAudioResultStreamData[];
@@ -94,54 +105,84 @@ export const useAudioStreamStore = create((set, get) => ({
const audio = new Audio(stream.Url);
audio.crossOrigin = 'anonymous';
+ // Listeners are attached with an AbortSignal so stopStream can detach them;
+ // otherwise each play/stop cycle stacks another set of closures on the element
+ detachStreamListeners();
+ streamListenersAbort = new AbortController();
+ const listenerSignal = streamListenersAbort.signal;
+
// Set up event listeners
- audio.addEventListener('loadeddata', () => {
- set({ isLoading: false, isBuffering: false });
- logger.debug({
- message: 'Audio stream loaded',
- context: { streamName: stream.Name },
- });
- });
+ audio.addEventListener(
+ 'loadeddata',
+ () => {
+ set({ isLoading: false, isBuffering: false });
+ logger.debug({
+ message: 'Audio stream loaded',
+ context: { streamName: stream.Name },
+ });
+ },
+ { signal: listenerSignal }
+ );
- audio.addEventListener('playing', () => {
- set({ isPlaying: true, isBuffering: false });
- logger.debug({
- message: 'Audio stream playing',
- context: { streamName: stream.Name },
- });
- });
+ audio.addEventListener(
+ 'playing',
+ () => {
+ set({ isPlaying: true, isBuffering: false });
+ logger.debug({
+ message: 'Audio stream playing',
+ context: { streamName: stream.Name },
+ });
+ },
+ { signal: listenerSignal }
+ );
- audio.addEventListener('pause', () => {
- set({ isPlaying: false });
- logger.debug({
- message: 'Audio stream paused',
- context: { streamName: stream.Name },
- });
- });
+ audio.addEventListener(
+ 'pause',
+ () => {
+ set({ isPlaying: false });
+ logger.debug({
+ message: 'Audio stream paused',
+ context: { streamName: stream.Name },
+ });
+ },
+ { signal: listenerSignal }
+ );
- audio.addEventListener('waiting', () => {
- set({ isBuffering: true });
- logger.debug({
- message: 'Audio stream buffering',
- context: { streamName: stream.Name },
- });
- });
+ audio.addEventListener(
+ 'waiting',
+ () => {
+ set({ isBuffering: true });
+ logger.debug({
+ message: 'Audio stream buffering',
+ context: { streamName: stream.Name },
+ });
+ },
+ { signal: listenerSignal }
+ );
- audio.addEventListener('error', (e) => {
- logger.error({
- message: 'Audio stream error',
- context: { error: e, streamName: stream.Name },
- });
- set({ isPlaying: false, isLoading: false, isBuffering: false });
- });
+ audio.addEventListener(
+ 'error',
+ (e) => {
+ logger.error({
+ message: 'Audio stream error',
+ context: { error: e, streamName: stream.Name },
+ });
+ set({ isPlaying: false, isLoading: false, isBuffering: false });
+ },
+ { signal: listenerSignal }
+ );
- audio.addEventListener('ended', () => {
- logger.debug({
- message: 'Audio stream ended',
- context: { streamName: stream.Name },
- });
- set({ isPlaying: false });
- });
+ audio.addEventListener(
+ 'ended',
+ () => {
+ logger.debug({
+ message: 'Audio stream ended',
+ context: { streamName: stream.Name },
+ });
+ set({ isPlaying: false });
+ },
+ { signal: listenerSignal }
+ );
// Start playing
await audio.play();
@@ -172,6 +213,7 @@ export const useAudioStreamStore = create((set, get) => ({
const { audioElement, currentStream } = get();
if (audioElement) {
+ detachStreamListeners();
audioElement.pause();
audioElement.src = '';
audioElement.load();
diff --git a/src/stores/app/livekit-store.ts b/src/stores/app/livekit-store.ts
index 132f2d0e..9532e37d 100644
--- a/src/stores/app/livekit-store.ts
+++ b/src/stores/app/livekit-store.ts
@@ -71,6 +71,26 @@ const setupAudioRouting = async (room: Room): Promise => {
// Map to store web audio elements for cleanup (keyed by track SID)
const webAudioElements = new Map();
+
+// Remove every attached web audio element from the document and clear the map.
+// Must run before removeAllListeners()/disconnect() on a room being torn down:
+// once listeners are stripped, TrackUnsubscribed can no longer fire and the old
+// room's elements would otherwise stay in the DOM forever.
+const cleanupWebAudioElements = (): void => {
+ if (Platform.OS !== 'web') return;
+ webAudioElements.forEach((audioElement, trackSid) => {
+ try {
+ audioElement.pause();
+ audioElement.remove();
+ } catch (err) {
+ logger.warn({
+ message: 'Failed to clean up audio element',
+ context: { error: err, trackSid },
+ });
+ }
+ });
+ webAudioElements.clear();
+};
interface LiveKitState {
// Connection state
isConnected: boolean;
@@ -213,12 +233,23 @@ export const useLiveKitStore = create((set, get) => ({
},
connectToRoom: async (roomInfo, token) => {
+ let room: Room | null = null;
try {
const { currentRoom, voipServerWebsocketSslAddress, requestPermissions } = get();
- // Disconnect from current room if connected
+ // Disconnect from current room if connected - await it and strip listeners so
+ // events from the old room cannot fire into the new session's state.
if (currentRoom) {
- currentRoom.disconnect();
+ try {
+ cleanupWebAudioElements();
+ currentRoom.removeAllListeners();
+ await currentRoom.disconnect();
+ } catch (disconnectError) {
+ logger.warn({
+ message: 'Failed to cleanly disconnect previous room before reconnect',
+ context: { error: disconnectError },
+ });
+ }
}
set({ isConnecting: true });
@@ -227,7 +258,7 @@ export const useLiveKitStore = create((set, get) => ({
await requestPermissions();
// Create a new room
- const room = new Room();
+ room = new Room();
// Setup room event listeners
room.on(RoomEvent.ParticipantConnected, (participant) => {
@@ -236,7 +267,7 @@ export const useLiveKitStore = create((set, get) => ({
context: { participantIdentity: participant.identity },
});
// Play connection sound when others join
- if (participant.identity !== room.localParticipant.identity) {
+ if (participant.identity !== room?.localParticipant.identity) {
//audioService.playConnectToAudioRoomSound();
}
});
@@ -251,6 +282,7 @@ export const useLiveKitStore = create((set, get) => ({
});
room.on(RoomEvent.ActiveSpeakersChanged, (speakers) => {
+ if (!room) return;
// Check if local participant is speaking
const localParticipant = room.localParticipant;
const isTalking = speakers.some((speaker) => speaker.sid === localParticipant.sid);
@@ -348,41 +380,53 @@ export const useLiveKitStore = create((set, get) => ({
message: 'Failed to connect to room',
context: { error },
});
- set({ isConnecting: false });
+
+ // Dispose of the partially-connected room so its socket and listeners don't leak
+ if (room) {
+ try {
+ room.removeAllListeners();
+ await room.disconnect();
+ } catch (cleanupError) {
+ logger.warn({
+ message: 'Failed to clean up room after connect failure',
+ context: { error: cleanupError },
+ });
+ }
+ }
+
+ set({ isConnecting: false, isConnected: false, currentRoom: null, currentRoomInfo: null, isTalking: false });
+
+ // Rethrow so callers (e.g. usePTT) don't proceed as if the channel is live
+ throw error;
}
},
disconnectFromRoom: async () => {
const { currentRoom } = get();
- if (currentRoom) {
- // Clean up web audio elements before disconnecting
- if (Platform.OS === 'web') {
- webAudioElements.forEach((audioElement, trackSid) => {
- try {
- audioElement.pause();
- audioElement.remove();
- } catch (err) {
- logger.warn({
- message: 'Failed to clean up audio element',
- context: { error: err, trackSid },
- });
- }
- });
- webAudioElements.clear();
- }
+ try {
+ if (currentRoom) {
+ // Clean up web audio elements before disconnecting
+ cleanupWebAudioElements();
- await currentRoom.disconnect();
- await audioService.playDisconnectedFromAudioRoomSound();
+ currentRoom.removeAllListeners();
+ await currentRoom.disconnect();
+ await audioService.playDisconnectedFromAudioRoomSound();
- // Stop foreground service only on Android
- if (Platform.OS === 'android') {
- await get().stopAndroidForegroundService();
+ // Stop foreground service only on Android
+ if (Platform.OS === 'android') {
+ await get().stopAndroidForegroundService();
+ }
}
-
+ } finally {
+ // Always reset state - when teardown partially fails (disconnect or audio
+ // playback rejecting) and when the store is desynced (connected flag true,
+ // room null), the store must still end in a clean disconnected state.
set({
currentRoom: null,
currentRoomInfo: null,
isConnected: false,
+ isConnecting: false,
+ isTalking: false,
});
}
},
diff --git a/src/stores/auth/__tests__/token-refresh-race.test.ts b/src/stores/auth/__tests__/token-refresh-race.test.ts
new file mode 100644
index 00000000..ef5f2fd7
--- /dev/null
+++ b/src/stores/auth/__tests__/token-refresh-race.test.ts
@@ -0,0 +1,62 @@
+import { refreshTokenRequest } from '@/lib/auth/api';
+import { cancelScheduledTokenRefresh, performTokenRefresh } from '@/lib/auth/token-refresh';
+import type { AuthResponse } from '@/lib/auth/types';
+
+import useAuthStore from '../store';
+
+jest.mock('@/lib/logging', () => ({
+ logger: { info: jest.fn(), warn: jest.fn(), error: jest.fn(), debug: jest.fn() },
+}));
+
+jest.mock('@/lib/auth/api', () => ({
+ loginRequest: jest.fn(),
+ refreshTokenRequest: jest.fn(),
+ clearPasswordVerificationHash: jest.fn().mockResolvedValue(undefined),
+ storePasswordVerificationHash: jest.fn().mockResolvedValue(undefined),
+}));
+
+const authResponse = {
+ access_token: 'new-access',
+ refresh_token: 'new-refresh',
+ expires_in: 3600,
+} as AuthResponse;
+
+describe('token refresh racing sign-out', () => {
+ afterEach(() => {
+ cancelScheduledTokenRefresh();
+ jest.clearAllMocks();
+ });
+
+ it('applies tokens for an active session', async () => {
+ useAuthStore.setState({ status: 'signedIn', accessToken: 'old-access', refreshToken: 'old-refresh' });
+ (refreshTokenRequest as jest.Mock).mockResolvedValue(authResponse);
+
+ await expect(performTokenRefresh()).resolves.toBe(true);
+
+ const state = useAuthStore.getState();
+ expect(state.status).toBe('signedIn');
+ expect(state.accessToken).toBe('new-access');
+ expect(state.refreshToken).toBe('new-refresh');
+ });
+
+ it('does not resurrect the session when the refresh resolves after logout', async () => {
+ useAuthStore.setState({ status: 'signedIn', accessToken: 'old-access', refreshToken: 'old-refresh' });
+
+ // Refresh request in flight when the user logs out
+ let resolveRefresh: (value: AuthResponse) => void = () => {};
+ (refreshTokenRequest as jest.Mock).mockReturnValue(new Promise((resolve) => (resolveRefresh = resolve)));
+
+ const refreshPromise = performTokenRefresh();
+
+ // Logout wins the race: store cleared and signed out
+ useAuthStore.setState({ status: 'signedOut', accessToken: null, refreshToken: null, profile: null, userId: null });
+
+ resolveRefresh(authResponse);
+ await expect(refreshPromise).resolves.toBe(false);
+
+ const state = useAuthStore.getState();
+ expect(state.status).toBe('signedOut');
+ expect(state.accessToken).toBeNull();
+ expect(state.refreshToken).toBeNull();
+ });
+});
diff --git a/src/stores/auth/store.tsx b/src/stores/auth/store.tsx
index 862a482d..d4e9c9c0 100644
--- a/src/stores/auth/store.tsx
+++ b/src/stores/auth/store.tsx
@@ -6,7 +6,8 @@ import { createJSONStorage, persist } from 'zustand/middleware';
import { logger } from '@/lib/logging';
-import { clearPasswordVerificationHash, loginRequest, refreshTokenRequest, storePasswordVerificationHash } from '../../lib/auth/api';
+import { clearPasswordVerificationHash, loginRequest, storePasswordVerificationHash } from '../../lib/auth/api';
+import { cancelScheduledTokenRefresh, initTokenRefresh, performTokenRefresh, scheduleTokenRefresh } from '../../lib/auth/token-refresh';
import type { AuthResponse, AuthState, LoginCredentials } from '../../lib/auth/types';
import { type ProfileModel } from '../../lib/auth/types';
@@ -109,18 +110,7 @@ const useAuthStore = create()(
});
// Set up automatic token refresh
- //const decodedToken: { exp: number } = jwtDecode(
- //);
- //const now = new Date();
- //const expiresIn =
- // response.authResponse?.expires_in! * 1000 - Date.now() - 60000; // Refresh 1 minute before expiry
- //const expiresOn = new Date(
- // now.getTime() + response.authResponse?.expires_in! * 1000
- //)
- // .getTime()
- // .toString();
-
- //setTimeout(() => get().refreshAccessToken(), expiresIn);
+ scheduleTokenRefresh(response.authResponse.expires_in);
} else {
logger.error({
message: 'Login: API returned unsuccessful response',
@@ -148,6 +138,9 @@ const useAuthStore = create()(
message: 'Logout: Clearing auth state',
});
+ // Cancel any pending automatic refresh so the timer cannot fire after logout
+ cancelScheduledTokenRefresh();
+
await clearPasswordVerificationHash();
set({
@@ -163,31 +156,9 @@ const useAuthStore = create()(
},
refreshAccessToken: async () => {
- try {
- const { refreshToken } = get();
- if (!refreshToken) {
- throw new Error('No refresh token available');
- }
-
- const response = await refreshTokenRequest(refreshToken);
-
- set({
- accessToken: response.access_token,
- refreshToken: response.refresh_token,
- status: 'signedIn',
- error: null,
- });
-
- // Set up next token refresh
- //const decodedToken: { exp: number } = jwt_decode(
- // response.access_token
- //);
- const expiresIn = response.expires_in * 1000 - Date.now() - 60000; // Refresh 1 minute before expiry
- setTimeout(() => get().refreshAccessToken(), expiresIn);
- } catch (error) {
- // If refresh fails, log out the user
- get().logout();
- }
+ // Single-flight refresh shared with the axios 401 interceptor. Failure
+ // handling (logout) happens inside performTokenRefresh.
+ await performTokenRefresh();
},
isAuthenticated: (): boolean => {
return get().status === 'signedIn' && get().accessToken !== null;
@@ -242,6 +213,9 @@ const useAuthStore = create()(
message: 'SSO: State updated to signedIn',
context: { userId: profileData.sub },
});
+
+ // Set up automatic token refresh
+ scheduleTokenRefresh(authResponse.expires_in);
} catch (error) {
logger.error({
message: 'SSO: loginWithSso exception',
@@ -272,4 +246,32 @@ const useAuthStore = create()(
)
);
+// Wire the shared refresh engine to this store. Kept outside the store creator so the
+// axios interceptor and the auth store share one single-flight refresh path.
+initTokenRefresh({
+ getRefreshToken: () => useAuthStore.getState().refreshToken,
+ applyAuthResponse: (response: AuthResponse) => {
+ // A refresh that raced sign-out must not resurrect the session: logout already
+ // cleared the store, so reject instead of applying. Throwing makes
+ // performTokenRefresh treat this as a failed refresh, which also stops it from
+ // rescheduling the refresh timer for the ended session.
+ if (useAuthStore.getState().status === 'signedOut') {
+ throw new Error('Token refresh completed after sign-out; discarding tokens');
+ }
+ useAuthStore.setState({
+ accessToken: response.access_token,
+ refreshToken: response.refresh_token,
+ status: 'signedIn',
+ error: null,
+ });
+ },
+ onRefreshFailed: () => {
+ // Avoid a logout re-entry loop if the failure was triggered by a timer that
+ // fired after the user already signed out.
+ if (useAuthStore.getState().status !== 'signedOut') {
+ void useAuthStore.getState().logout();
+ }
+ },
+});
+
export default useAuthStore;
diff --git a/src/stores/chat/store.ts b/src/stores/chat/store.ts
index aec48972..ee38a2ee 100644
--- a/src/stores/chat/store.ts
+++ b/src/stores/chat/store.ts
@@ -73,6 +73,13 @@ interface ChatState {
// --- Channels ---------------------------------------------------------
fetchChannels: (activeUnitId?: number) => Promise;
+ /**
+ * Chat channels anchored to one incident, kept out of the main channel list so a dispatcher's list
+ * isn't buried under every incident's channels. Includes archived ones: a closed incident's
+ * conversations stay readable as a point-in-time record.
+ */
+ incidentChannelsByCallId: Record;
+ loadIncidentChannels: (callId: string) => Promise;
setActiveChannel: (channelId: string | null) => void;
// --- Messages ---------------------------------------------------------
@@ -246,6 +253,25 @@ export const useChatStore = create()(
// ------------------------------------------------------------------
// Channels
// ------------------------------------------------------------------
+ incidentChannelsByCallId: {},
+
+ loadIncidentChannels: async (callId: string) => {
+ const numericCallId = parseInt(callId, 10);
+ if (Number.isNaN(numericCallId)) {
+ return;
+ }
+
+ try {
+ // callId narrows the list server-side; the local filter stays as a safety net
+ // because older servers ignore the param and return every channel.
+ const response = await chatApi.getChannels(undefined, true, numericCallId);
+ const forCall = (response.Data ?? []).filter((channel) => channel.CallId === numericCallId);
+ set((state) => ({ incidentChannelsByCallId: { ...state.incidentChannelsByCallId, [callId]: forCall } }));
+ } catch (error) {
+ logger.error({ message: 'chat: failed to load incident channels', context: { error, callId } });
+ }
+ },
+
fetchChannels: async (activeUnitId?: number) => {
set({ isLoadingChannels: true });
try {
@@ -840,6 +866,7 @@ export const useChatStore = create()(
}
set({
channels: [],
+ incidentChannelsByCallId: {},
messagesByChannel: {},
membersByChannel: {},
typingByChannel: {},
diff --git a/src/stores/security/__tests__/store.test.ts b/src/stores/security/__tests__/store.test.ts
index 325d9e4c..8edec2d1 100644
--- a/src/stores/security/__tests__/store.test.ts
+++ b/src/stores/security/__tests__/store.test.ts
@@ -74,6 +74,8 @@ describe('useSecurityStore', () => {
CanCreateCalls: true,
CanAddNote: true,
CanCreateMessage: true,
+ CanLoginToDispatchApp: true,
+ CanLoginToCommandApp: true,
Groups: [
{
GroupId: 1,
diff --git a/src/stores/security/store.ts b/src/stores/security/store.ts
index 68dda016..25537cac 100644
--- a/src/stores/security/store.ts
+++ b/src/stores/security/store.ts
@@ -80,6 +80,8 @@ export const useSecurityStore = () => {
canUserCreateNotes: store.rights?.CanAddNote,
canUserCreateMessages: store.rights?.CanCreateMessage,
canUserViewPII: store.rights?.CanViewPII,
+ // Undefined (rights not loaded yet) is treated as allowed by callers; only an explicit false blocks.
+ canUserWorkCommand: store.rights?.CanLoginToCommandApp,
departmentCode: store.rights?.DepartmentCode,
rights: store.rights,
};
diff --git a/src/translations/ar.json b/src/translations/ar.json
index b547a265..406a35be 100644
--- a/src/translations/ar.json
+++ b/src/translations/ar.json
@@ -371,6 +371,124 @@
"audio_name": "مقطع صوتي"
}
},
+ "chat": {
+ "title": "الدردشة",
+ "assistant": "المساعد",
+ "empty": "لا توجد محادثات بعد. ابدأ رسالة مباشرة أو أنشئ مجموعة.",
+ "section_assistant": "المساعد",
+ "section_direct_messages": "الرسائل المباشرة",
+ "section_channels": "القنوات",
+ "section_incidents": "الحوادث",
+ "new_direct_message": "رسالة مباشرة جديدة",
+ "new_group": "مجموعة جديدة",
+ "open_assistant": "فتح المساعد",
+ "create_conversation_failed": "تعذّر بدء المحادثة",
+ "group_name": "اسم المجموعة",
+ "search_people": "البحث عن أشخاص",
+ "no_people": "لم يتم العثور على أشخاص",
+ "create_group_with": "إنشاء مجموعة ({{count}})",
+ "message_deleted": "تم حذف هذه الرسالة",
+ "urgent": "عاجل",
+ "urgent_will_send": "سيتم إرسال هذه الرسالة كرسالة عاجلة",
+ "shared_location": "الموقع المشترك",
+ "thread_replies": "{{count}} ردود",
+ "edited": "(تم التعديل)",
+ "failed_tap_retry": "فشل - انقر لإعادة المحاولة",
+ "type_a_message": "اكتب رسالة",
+ "emoji": "رمز تعبيري",
+ "add_image": "إضافة صورة",
+ "add_gif": "إضافة GIF",
+ "share_location": "مشاركة الموقع",
+ "send": "إرسال",
+ "someone": "شخص ما",
+ "is_typing": "{{name}} يكتب...",
+ "are_typing": "{{count}} أشخاص يكتبون...",
+ "permission_photos_denied": "تم رفض إذن الوصول إلى مكتبة الصور",
+ "permission_location_denied": "تم رفض إذن الموقع",
+ "search_gifs": "البحث عن صور GIF",
+ "no_gifs": "لم يتم العثور على صور GIF",
+ "flag_reason": "لماذا تبلّغ عن هذا؟",
+ "flag_inappropriate": "غير لائق",
+ "flag_harassment": "تحرش",
+ "flag_spam": "بريد عشوائي",
+ "flag_sensitive": "معلومات حساسة",
+ "flag_policy": "انتهاك السياسة",
+ "flag_other": "أخرى",
+ "reply_in_thread": "الرد في المحادثة",
+ "copy": "نسخ",
+ "copied": "تم النسخ",
+ "copy_unavailable": "النسخ غير متاح على هذا الجهاز",
+ "edit": "تعديل",
+ "edit_message": "تعديل الرسالة",
+ "save": "حفظ",
+ "delete": "حذف",
+ "pin": "تثبيت",
+ "unpin": "إلغاء التثبيت",
+ "flag": "إبلاغ",
+ "moderator_delete": "إزالة (مشرف)",
+ "moderator_removed": "تمت الإزالة بواسطة المشرف",
+ "attachment_failed": "فشل رفع المرفق",
+ "ack_required": "الإقرار مطلوب",
+ "ack_pending_one": "لديك رسالة عاجلة بحاجة إلى إقرار",
+ "ack_pending_count": "لديك {{count}} رسائل عاجلة بحاجة إلى إقرار",
+ "acknowledge": "إقرار",
+ "thread": "المحادثة",
+ "original_message": "الرسالة الأصلية",
+ "reply_placeholder": "رد...",
+ "channel": "قناة",
+ "direct_message": "رسالة مباشرة",
+ "load_people_failed": "تعذّر تحميل الأشخاص",
+ "reaction_failed": "تعذّر تحديث التفاعل",
+ "edit_failed": "تعذّر تعديل الرسالة",
+ "delete_failed": "تعذّر حذف الرسالة",
+ "pin_failed": "تعذّر تحديث التثبيت",
+ "flag_failed": "تعذّر الإبلاغ عن الرسالة"
+ },
+ "chatbot": {
+ "title": "المساعد",
+ "subtitle": "مساعد ذكاء اصطناعي لقسمك",
+ "new_session": "جلسة جديدة",
+ "empty": "اسأل المساعد عن أي شيء للبدء.",
+ "ask_placeholder": "اسأل المساعد..."
+ },
+ "check_in": {
+ "tab_title": "تسجيل الحضور",
+ "timer_status": "حالة المؤقت",
+ "perform_check_in": "تسجيل",
+ "check_in_success": "تم تسجيل الحضور بنجاح",
+ "check_in_error": "فشل في تسجيل الحضور",
+ "checked_in_by": "بواسطة {{name}}",
+ "last_check_in": "آخر تسجيل",
+ "elapsed": "المنقضي",
+ "duration": "المدة",
+ "status_ok": "جيد",
+ "status_green": "جيد",
+ "status_warning": "تحذير",
+ "status_yellow": "تحذير",
+ "status_overdue": "متأخر",
+ "status_red": "متأخر",
+ "status_critical": "حرج",
+ "history": "سجل التسجيلات",
+ "no_timers": "لم يتم تكوين مؤقتات تسجيل الحضور",
+ "timers_disabled": "مؤقتات تسجيل الحضور معطلة لهذه المكالمة",
+ "type_personnel": "الأفراد",
+ "type_unit": "الوحدة",
+ "type_ic": "قائد الحادث",
+ "type_par": "PAR",
+ "type_hazmat": "التعرض للمواد الخطرة",
+ "type_sector_rotation": "تدوير القطاع",
+ "type_rehab": "إعادة التأهيل",
+ "add_note": "إضافة ملاحظة (اختياري)",
+ "confirm": "تأكيد التسجيل",
+ "minutes_ago": "منذ {{count}} دقيقة",
+ "select_target": "حدد الكيان للتسجيل",
+ "overdue_count": "{{count}} متأخر",
+ "warning_count": "{{count}} تحذير",
+ "enable_timers": "تفعيل المؤقتات",
+ "disable_timers": "تعطيل المؤقتات",
+ "summary": "{{overdue}} متأخر، {{warning}} تحذير، {{ok}} جيد",
+ "par_title": "متابعة الأفراد (PAR)"
+ },
"common": {
"add": "إضافة",
"back": "رجوع",
@@ -502,178 +620,379 @@
"website": "الموقع الإلكتروني",
"zip": "الرمز البريدي"
},
- "form": {
- "invalid_url": "يرجى إدخال عنوان URL صالح يبدأ بـ http:// أو https://",
- "required": "هذا الحقل مطلوب"
- },
- "livekit": {
- "audio_devices": "أجهزة الصوت",
- "audio_settings": "إعدادات الصوت",
- "connected_to_room": "متصل بالقناة",
- "connecting": "جاري الاتصال...",
- "disconnect": "قطع الاتصال",
- "join": "انضمام",
- "microphone": "الميكروفون",
- "mute": "كتم",
- "no_rooms_available": "لا توجد قنوات صوتية متاحة",
- "speaker": "السماعة",
- "speaking": "يتحدث",
- "title": "القنوات الصوتية",
- "unmute": "إلغاء الكتم"
- },
- "loading": {
- "loading": "جاري التحميل...",
- "loadingData": "جاري تحميل البيانات...",
- "pleaseWait": "يرجى الانتظار",
- "processingRequest": "جاري معالجة طلبك..."
- },
- "sso": {
- "authenticating": "جارٍ المصادقة...",
- "back_to_login": "العودة إلى تسجيل الدخول",
- "back_to_lookup": "تغيير المستخدم",
- "continue_button": "متابعة",
- "department_id_label": "معرف القسم",
- "department_id_placeholder": "أدخل معرف القسم",
- "error_generic": "فشل تسجيل الدخول. يرجى المحاولة مرة أخرى.",
- "error_oidc_cancelled": "تم إلغاء تسجيل الدخول.",
- "error_oidc_not_ready": "موفر SSO يتم تحميله، يرجى الانتظار.",
- "error_sso_not_enabled": "تسجيل الدخول الموحد غير مفعل لهذا المستخدم.",
- "error_token_exchange": "فشل إتمام تسجيل الدخول. يرجى المحاولة مرة أخرى.",
- "error_user_not_found": "المستخدم غير موجود. يرجى التحقق والمحاولة مرة أخرى.",
- "looking_up": "جارٍ البحث...",
- "optional": "اختياري",
- "page_subtitle": "أدخل اسم المستخدم للبحث عن خيارات تسجيل الدخول الخاصة بمؤسستك.",
- "page_title": "تسجيل الدخول الموحد",
- "provider_oidc": "OpenID Connect (OIDC)",
- "provider_saml": "SAML 2.0",
- "sign_in_button": "تسجيل الدخول عبر SSO",
- "sign_in_title": "تسجيل الدخول",
- "sso_button": "تسجيل دخول SSO"
- },
- "login": {
- "branding_subtitle": "برنامج إرسال قوي للمستجيبين الأوائل والبحث والإنقاذ ومنظمات السلامة العامة.",
- "branding_title": "إدارة الاستجابة للطوارئ",
- "errorModal": {
- "confirmButton": "موافق",
- "message": "يرجى التحقق من اسم المستخدم وكلمة المرور والمحاولة مرة أخرى.",
- "title": "فشل تسجيل الدخول"
- },
- "feature_dispatch_desc": "أرسل الوحدات وقم بإدارة المكالمات فوراً مع التحديثات المباشرة على جميع الأجهزة.",
- "feature_dispatch_title": "الإرسال في الوقت الفعلي",
- "feature_mapping_desc": "تتبع الوحدات في الوقت الفعلي مع خرائط تفصيلية وتوجيه وإدارة الموقع.",
- "feature_mapping_title": "خرائط متقدمة",
- "feature_personnel_desc": "قم بإدارة فريقك مع الوصول القائم على الأدوار وتتبع الحالة وأدوات الاتصال.",
- "feature_personnel_title": "إدارة الموظفين",
- "footer_text": "صنع بـ ❤️ في نيفادا",
- "login": "تسجيل الدخول",
- "login_button": "تسجيل الدخول",
- "login_button_description": "قم بتسجيل الدخول إلى حسابك للمتابعة",
- "login_button_error": "خطأ في تسجيل الدخول",
- "login_button_loading": "تسجيل الدخول...",
- "login_button_success": "تم تسجيل الدخول بنجاح",
- "no_account": "ليس لديك حساب؟",
- "page_subtitle": "أدخل بيانات الاعتماد الخاصة بك لتسجيل الدخول.",
- "page_title": "Resgrid Dispatch",
- "password": "كلمة المرور",
- "password_incorrect": "كانت كلمة المرور غير صحيحة",
- "password_placeholder": "أدخل كلمة المرور الخاصة بك",
- "register": "تسجيل",
- "title": "تسجيل الدخول",
- "username": "اسم المستخدم",
- "username_placeholder": "أدخل اسم المستخدم الخاص بك",
- "welcome_title": "مرحباً بعودتك"
- },
- "lockscreen": {
- "message": "أدخل كلمة المرور لفتح الشاشة",
- "not_you": "لست أنت؟ العودة إلى تسجيل الدخول",
- "password": "كلمة المرور",
- "password_placeholder": "أدخل كلمة المرور الخاصة بك",
- "title": "شاشة القفل",
- "unlock_button": "فتح",
- "unlock_failed": "فشل الفتح. يرجى المحاولة مرة أخرى.",
- "unlocking": "جاري الفتح...",
- "welcome_back": "مرحبا بعودتك",
- "relogin_required": "التحقق من كلمة المرور غير متاح لهذه الجلسة. يرجى تسجيل الدخول مرة أخرى."
- },
- "maintenance": {
- "downtime_message": "نحن نعمل بجد لإكمال الصيانة في أسرع وقت ممكن. يرجى التحقق مرة أخرى قريبا.",
- "downtime_title": "ما هو وقت التوقف؟",
- "message": "يرجى التحقق مرة أخرى في وقت ما.",
- "support_message": "إذا كنت بحاجة إلى مساعدة، يرجى الاتصال بنا على",
- "support_title": "هل تحتاج إلى دعم؟",
- "title": "الموقع قيد الصيانة",
- "why_down_message": "نحن نقوم بصيانة مجدولة لتحسين تجربتك. نعتذر عن أي إزعاج.",
- "why_down_title": "لماذا الموقع معطل؟"
- },
- "menu": {
- "calls": "المكالمات",
- "calls_list": "قائمة المكالمات",
- "scheduled_calls": "المكالمات المجدولة",
- "contacts": "جهات الاتصال",
- "home": "الرئيسية",
- "map": "الخريطة",
- "menu": "القائمة",
- "messages": "الرسائل",
- "new_call": "مكالمة جديدة",
- "personnel": "الموظفون",
- "pois": "نقاط الاهتمام",
- "protocols": "البروتوكولات",
- "settings": "الإعدادات",
+ "dispatch": {
+ "active_calls": "المكالمات النشطة",
+ "pending_calls": "معلقة",
+ "scheduled_calls": "مجدولة",
+ "units_available": "متاحة",
+ "personnel_available": "متاحين",
+ "personnel_on_duty": "في الخدمة",
"units": "الوحدات",
- "weatherAlerts": "تنبيهات الطقس",
- "incident_command": "قيادة الحادث",
- "chat": "الدردشة",
- "assistant": "المساعد"
- },
- "pois": {
- "address": "العنوان",
- "all_types": "كل الأنواع",
- "destination": "وجهة",
- "details": "التفاصيل",
- "detail_not_found": "تعذر العثور على نقطة الاهتمام",
- "detail_not_found_description": "تعذر تحميل نقطة الاهتمام المطلوبة.",
- "detail_title": "تفاصيل نقطة الاهتمام",
- "empty": "لا توجد نقاط اهتمام",
- "empty_description": "لا توجد نقاط اهتمام متاحة لقسمك حتى الآن.",
- "empty_filtered": "لا توجد نقاط اهتمام مطابقة",
- "empty_filtered_description": "حاول مسح البحث أو اختيار نوع مختلف.",
- "filter_by_type": "تصفية حسب نوع نقطة الاهتمام",
- "invalid_poi": "نقطة اهتمام غير صالحة",
- "invalid_poi_description": "معرّف نقطة الاهتمام المحدد غير صالح.",
- "loading": "جارٍ تحميل نقاط الاهتمام...",
- "loading_detail": "جارٍ تحميل تفاصيل نقطة الاهتمام...",
+ "personnel": "الموظفين",
"map": "الخريطة",
- "no_location": "لا يوجد موقع متاح",
- "no_location_description": "لا تحتوي نقطة الاهتمام هذه على إحداثيات قابلة للاستخدام.",
- "no_location_for_routing": "لا توجد بيانات موقع متاحة للتوجيه",
- "note": "ملاحظة",
- "route_error": "فشل في فتح تطبيق الخرائط",
- "search": "ابحث في نقاط الاهتمام...",
- "sort": "ترتيب",
- "sort_options": {
- "address-asc": "العنوان",
- "name-asc": "الاسم (أ-ي)",
- "name-desc": "الاسم (ي-أ)",
- "type-asc": "النوع"
+ "notes": "الملاحظات",
+ "activity_log": "سجل النشاط",
+ "communications": "الاتصالات",
+ "no_active_calls": "لا توجد مكالمات نشطة",
+ "no_units": "لا توجد وحدات متاحة",
+ "no_personnel": "لا يوجد موظفين متاحين",
+ "no_notes": "لا توجد ملاحظات متاحة",
+ "no_activity": "لا يوجد نشاط حديث",
+ "current_channel": "القناة الحالية",
+ "audio_stream": "البث الصوتي",
+ "no_stream": "لا يوجد بث نشط",
+ "ptt": "اضغط للتحدث",
+ "ptt_start": "بدء الإرسال",
+ "ptt_end": "انتهاء الإرسال",
+ "transmitting_on": "جاري الإرسال على {{channel}}",
+ "transmission_ended": "انتهى الإرسال",
+ "voice_disabled": "تم تعطيل الصوت",
+ "disconnected": "غير متصل",
+ "select_channel": "اختر القناة",
+ "select_channel_description": "اختر قناة صوتية للاتصال بها",
+ "change_channel_warning": "اختيار قناة جديدة سيؤدي إلى قطع الاتصال من القناة الحالية",
+ "default_channel": "افتراضي",
+ "no_channels_available": "لا توجد قنوات صوتية متاحة",
+ "system_update": "تحديث النظام",
+ "data_refreshed": "تم تحديث البيانات من الخادم",
+ "call_selected": "تم تحديد المكالمة",
+ "unit_selected": "تم تحديد الوحدة",
+ "unit_deselected": "تم إلغاء تحديد الوحدة",
+ "personnel_selected": "تم تحديد الموظف",
+ "personnel_deselected": "تم إلغاء تحديد الموظف",
+ "loading_map": "جاري تحميل الخريطة...",
+ "map_not_available_web": "الخريطة غير متاحة على منصة الويب",
+ "filtering_by_call": "تصفية حسب المكالمة",
+ "clear_filter": "مسح الفلتر",
+ "call_filter_active": "فلتر المكالمة نشط",
+ "call_filter_cleared": "تم مسح فلتر المكالمة",
+ "showing_all_data": "عرض جميع البيانات",
+ "call_notes": "ملاحظات المكالمة",
+ "no_call_notes": "لا توجد ملاحظات للمكالمة",
+ "add_call_note_placeholder": "أضف ملاحظة...",
+ "note_added": "تمت إضافة الملاحظة",
+ "note_added_to_console": "تمت إضافة ملاحظة جديدة إلى وحدة التحكم",
+ "add_note_title": "إضافة ملاحظة جديدة",
+ "note_title_label": "العنوان",
+ "note_title_placeholder": "أدخل عنوان الملاحظة...",
+ "note_category_label": "الفئة",
+ "note_category_placeholder": "اختر فئة",
+ "note_no_category": "بدون فئة",
+ "note_body_label": "محتوى الملاحظة",
+ "note_body_placeholder": "أدخل محتوى الملاحظة...",
+ "note_save_error": "فشل حفظ الملاحظة: {{error}}",
+ "note_created": "تم إنشاء الملاحظة",
+ "units_on_call": "الوحدات في المكالمة",
+ "no_units_on_call": "لا توجد وحدات في المكالمة",
+ "personnel_on_call": "الموظفين في المكالمة",
+ "no_personnel_on_call": "لا يوجد موظفين في المكالمة",
+ "call_activity": "نشاط المكالمة",
+ "no_call_activity": "لا يوجد نشاط للمكالمة",
+ "on_call": "في مكالمة",
+ "filtered": "مفلتر",
+ "active_filter": "فلتر نشط",
+ "unit_status_change": "تغيير حالة الوحدة",
+ "personnel_status_change": "تغيير حالة الموظف",
+ "view_call_details": "عرض تفاصيل المكالمة",
+ "dispatched_resources": "الموارد المرسلة",
+ "unassigned": "غير مخصص",
+ "available": "متاح",
+ "unknown": "غير معروف",
+ "search_personnel_placeholder": "بحث عن موظف...",
+ "search_calls_placeholder": "بحث عن مكالمات...",
+ "search_units_placeholder": "بحث عن وحدات...",
+ "search_notes_placeholder": "بحث في الملاحظات...",
+ "signalr_update": "تحديث في الوقت الفعلي",
+ "signalr_connected": "متصل",
+ "realtime_updates_active": "التحديثات في الوقت الفعلي نشطة الآن",
+ "personnel_status_updated": "تم تحديث حالة الموظف",
+ "personnel_staffing_updated": "تم تحديث تعيين الموظف",
+ "unit_status_updated": "تم تحديث حالة الوحدة",
+ "calls_updated": "تم تحديث المكالمات",
+ "call_added": "تمت إضافة مكالمة جديدة",
+ "call_closed": "تم إغلاق المكالمة",
+ "check_ins": "تسجيلات الحضور",
+ "no_check_ins": "لا توجد مكالمات بمؤقتات تسجيل الحضور",
+ "radio_log": "سجل الراديو",
+ "radio": "الراديو",
+ "activity": "النشاط",
+ "actions": "الإجراءات",
+ "no_radio_activity": "لا توجد إرسالات راديو",
+ "live": "مباشر",
+ "currently_transmitting": "جاري الإرسال حالياً...",
+ "duration": "المدة",
+ "call_actions": "إجراءات المكالمة",
+ "unit_actions": "إجراءات الوحدة",
+ "personnel_actions": {
+ "title": "إجراءات الموظف",
+ "status_tab": "الحالة",
+ "staffing_tab": "التعيين",
+ "select_status": "اختر الحالة",
+ "select_staffing": "اختر مستوى التعيين",
+ "destination": "الوجهة",
+ "no_destination": "بدون وجهة",
+ "note": "ملاحظة",
+ "note_placeholder": "أضف ملاحظة اختيارية...",
+ "update_status": "تحديث الحالة",
+ "update_staffing": "تحديث التعيين",
+ "no_statuses_available": "لا توجد حالات متاحة",
+ "no_staffings_available": "لا توجد مستويات تعيين متاحة"
},
- "title": "نقاط الاهتمام",
- "type": "النوع",
- "unknown_type": "نوع غير معروف",
- "unnamed": "نقطة اهتمام بدون اسم"
+ "unit_actions_panel": {
+ "status": "الحالة",
+ "select_status": "اختر الحالة",
+ "destination": "الوجهة",
+ "no_destination": "بدون وجهة",
+ "note": "ملاحظة",
+ "note_placeholder": "أضف ملاحظة اختيارية...",
+ "update_status": "تحديث الحالة",
+ "no_statuses_available": "لا توجد حالات متاحة",
+ "no_active_calls": "لا توجد مكالمات نشطة",
+ "no_stations_available": "لا توجد محطات متاحة",
+ "no_destinations_available": "لا توجد وجهات متاحة"
+ },
+ "call": "مكالمة",
+ "station": "محطة",
+ "calls": "المكالمات",
+ "stations": "المحطات",
+ "no_stations_available": "لا توجد محطات متاحة",
+ "new_call": "مكالمة جديدة",
+ "view_details": "التفاصيل",
+ "add_note": "إضافة ملاحظة",
+ "close_call": "إغلاق",
+ "set_status": "تعيين الحالة",
+ "set_staffing": "التعيين",
+ "dispatch": "إرسال",
+ "select_items_for_actions": "حدد مكالمة أو وحدة أو موظف لتفعيل الإجراءات السياقية",
+ "weather": {
+ "clear": "صافٍ",
+ "mainly_clear": "صافٍ غالبًا",
+ "partly_cloudy": "غائم جزئيًا",
+ "overcast": "غائم",
+ "fog": "ضباب",
+ "drizzle": "رذاذ",
+ "freezing_drizzle": "رذاذ متجمد",
+ "rain": "مطر",
+ "freezing_rain": "مطر متجمد",
+ "snow": "ثلج",
+ "rain_showers": "زخات مطر",
+ "snow_showers": "زخات ثلج",
+ "thunderstorm": "عاصفة رعدية",
+ "thunderstorm_hail": "عاصفة رعدية مع بَرَد",
+ "unknown": "غير معروف"
+ },
+ "available_only": "المتاح فقط",
+ "single_list": "قائمة واحدة",
+ "resources": "الموارد",
+ "search_resources_placeholder": "البحث عن الموارد...",
+ "no_resources": "لا توجد موارد"
},
- "scheduled_calls": {
- "title": "المكالمات المجدولة",
- "loading": "تحميل المكالمات المجدولة...",
- "no_scheduled_calls": "لا توجد مكالمات مجدولة",
- "no_scheduled_calls_description": "لا توجد مكالمات مجدولة معلقة في الوقت الحالي.",
- "search": "البحث في المكالمات المجدولة...",
- "scheduled_for": "مجدولة ل",
- "table_number": "رقم البلاغ",
- "table_name": "الاسم",
- "table_type": "النوع",
- "table_priority": "الأولوية",
- "table_address": "العنوان",
- "table_scheduled": "مجدول في"
+ "form": {
+ "invalid_url": "يرجى إدخال عنوان URL صالح يبدأ بـ http:// أو https://",
+ "required": "هذا الحقل مطلوب"
+ },
+ "incident_command": {
+ "accountability": "متابعة الأفراد (PAR)",
+ "acknowledge": "إقرار",
+ "action_plan": "خطة العمل",
+ "action_plan_placeholder": "صف خطة عمل الحادث...",
+ "active": "نشطة",
+ "active_title": "قيادات الحوادث النشطة",
+ "add": "إضافة",
+ "add_channel": "إضافة قناة",
+ "add_lane": "إضافة قطاع",
+ "add_marker": "إضافة علامة",
+ "add_objective": "إضافة هدف",
+ "annotations": "تعليقات الخريطة",
+ "assign": "تعيين",
+ "assign_resource": "تعيين مورد",
+ "assign_resource_required": "حدد قطاعًا وموردًا",
+ "assign_role": "تعيين دور",
+ "assign_role_required": "حدد شخصًا ودورًا",
+ "call": "البلاغ",
+ "channel_name": "اسم القناة",
+ "chat": "Chat",
+ "chat_frozen": "This incident is closed. Conversations are kept as a point-in-time record — no new messages.",
+ "close_all_channels": "إغلاق جميع القنوات",
+ "close_command": "إنهاء القيادة",
+ "closed": "مغلقة",
+ "command_channel": "Command chat",
+ "command_channel_hint": "Command staff and dispatch",
+ "command_channel_unavailable": "No command channel has been created for this incident yet.",
+ "commander": "قائد الحادث",
+ "complete": "إكمال",
+ "completed": "مكتمل",
+ "confirm_close": "هل تريد إنهاء قيادة الحادث لهذا البلاغ؟",
+ "critical": "حرج",
+ "delete_annotation_confirm": "هل تريد إزالة هذا التعليق؟",
+ "dispatch_channel": "Dispatch",
+ "dispatch_channel_hint": "The incident's line to the desk",
+ "dispatch_channel_unavailable": "No dispatch channel has been created for this incident yet.",
+ "dm_failed": "Couldn't open that conversation.",
+ "dm_unavailable": "That contact has no Resgrid account to message.",
+ "due": "موعد الاستحقاق",
+ "edit": "تعديل",
+ "edit_action_plan": "تعديل خطة العمل",
+ "establish": "إنشاء القيادة",
+ "establish_description": "يمكنك اختياريًا إعداد لوحة القيادة من قالب.",
+ "establish_error": "تعذر إنشاء القيادة",
+ "establish_success": "تم إنشاء قيادة الحادث",
+ "establish_title": "إنشاء قيادة الحادث",
+ "established_on": "تاريخ الإنشاء",
+ "green": "أخضر",
+ "hold_to_talk": "اضغط مطولًا للتحدث",
+ "incident_channel": "Incident chat",
+ "incident_channel_hint": "Everyone working the incident",
+ "incident_channel_unavailable": "No incident channel has been created for this call yet.",
+ "join": "انضمام",
+ "lane": "القطاع",
+ "lane_name": "اسم القطاع",
+ "lane_type": "نوع القطاع",
+ "marker": "علامة",
+ "marker_label": "تسمية العلامة",
+ "move": "نقل",
+ "move_lane": "نقل القطاع",
+ "move_resource": "نقل المورد",
+ "name_required": "الاسم مطلوب",
+ "no_accountability": "لا يوجد أفراد قيد المتابعة.",
+ "no_action_plan": "لم يتم تعيين خطة عمل.",
+ "no_active": "لا توجد قيادات حوادث نشطة",
+ "no_active_description": "ستظهر هنا قيادات الحوادث التي تم إنشاؤها للبلاغات.",
+ "no_annotations": "لا توجد تعليقات.",
+ "no_channels": "لا توجد قنوات مفتوحة.",
+ "no_command": "لم يتم إنشاء قيادة للحادث",
+ "no_command_description": "أنشئ قيادة للحادث لتنسيق الموارد والأدوار والأهداف ومتابعة الأفراد لهذا البلاغ.",
+ "no_lanes": "لم يتم تحديد قطاعات.",
+ "no_objectives": "لا توجد أهداف.",
+ "no_resources": "لم يتم تعيين موارد.",
+ "no_roles": "لم يتم تعيين أدوار.",
+ "no_template": "بدون قالب (لوحة فارغة)",
+ "no_timeline": "لا توجد إدخالات في التسلسل الزمني.",
+ "no_timers": "لا توجد مؤقتات قيد التشغيل.",
+ "not_authorized": "Command board unavailable",
+ "not_authorized_description": "Your department hasn't authorized you to work incident command. Ask an administrator for the Command App Login permission.",
+ "objective_name": "الهدف",
+ "objective_type": "النوع",
+ "objectives": "الأهداف",
+ "open_chat": "Open",
+ "open_full_board": "فتح اللوحة الكاملة",
+ "open_tactical_map": "فتح الخريطة التكتيكية",
+ "parent_lane": "القطاع الأصل",
+ "person": "الشخص",
+ "personnel": "الأفراد",
+ "release": "تحرير",
+ "resource": "المورد",
+ "resource_type": "نوع المورد",
+ "role": "الدور",
+ "roles": "أدوار القيادة",
+ "run_par": "تنفيذ PAR",
+ "save": "حفظ",
+ "save_error": "فشلت العملية",
+ "saved": "تم الحفظ",
+ "select_lane": "حدد قطاعًا",
+ "select_person": "حدد شخصًا",
+ "select_resource": "حدد موردًا",
+ "select_role": "حدد دورًا",
+ "send_message": "Message",
+ "status": "الحالة",
+ "structure": "هيكل القيادة",
+ "tab_title": "القيادة",
+ "tactical_map": "الخريطة التكتيكية",
+ "talking": "جارٍ الإرسال...",
+ "tap_to_place": "اضغط على الخريطة لوضع علامة",
+ "template": "قالب",
+ "timeline": "التسلسل الزمني للقيادة",
+ "timers": "المؤقتات",
+ "title": "قيادة الحادث",
+ "top_level": "المستوى الأعلى",
+ "transfer": "نقل",
+ "transfer_command": "نقل",
+ "transfer_notes": "ملاحظات",
+ "transfer_success": "تم نقل القيادة",
+ "transfer_title": "نقل القيادة",
+ "unassigned": "غير معيّن",
+ "unit": "الوحدة",
+ "voice_channels": "القنوات الصوتية",
+ "voice_join_error": "تعذر الانضمام إلى القناة الصوتية",
+ "voice_joined": "تم الانضمام إلى القناة الصوتية",
+ "warning": "تحذير"
+ },
+ "livekit": {
+ "audio_devices": "أجهزة الصوت",
+ "audio_settings": "إعدادات الصوت",
+ "connected_to_room": "متصل بالقناة",
+ "connecting": "جاري الاتصال...",
+ "disconnect": "قطع الاتصال",
+ "join": "انضمام",
+ "microphone": "الميكروفون",
+ "mute": "كتم",
+ "no_rooms_available": "لا توجد قنوات صوتية متاحة",
+ "speaker": "السماعة",
+ "speaking": "يتحدث",
+ "title": "القنوات الصوتية",
+ "unmute": "إلغاء الكتم"
+ },
+ "loading": {
+ "loading": "جاري التحميل...",
+ "loadingData": "جاري تحميل البيانات...",
+ "pleaseWait": "يرجى الانتظار",
+ "processingRequest": "جاري معالجة طلبك..."
+ },
+ "lockscreen": {
+ "message": "أدخل كلمة المرور لفتح الشاشة",
+ "not_you": "لست أنت؟ العودة إلى تسجيل الدخول",
+ "password": "كلمة المرور",
+ "password_placeholder": "أدخل كلمة المرور الخاصة بك",
+ "title": "شاشة القفل",
+ "unlock_button": "فتح",
+ "unlock_failed": "فشل الفتح. يرجى المحاولة مرة أخرى.",
+ "unlocking": "جاري الفتح...",
+ "welcome_back": "مرحبا بعودتك",
+ "relogin_required": "التحقق من كلمة المرور غير متاح لهذه الجلسة. يرجى تسجيل الدخول مرة أخرى."
+ },
+ "login": {
+ "branding_subtitle": "برنامج إرسال قوي للمستجيبين الأوائل والبحث والإنقاذ ومنظمات السلامة العامة.",
+ "branding_title": "إدارة الاستجابة للطوارئ",
+ "dispatch_not_authorized": "You are not authorized to use the Dispatch app. Contact your department administrator.",
+ "errorModal": {
+ "confirmButton": "موافق",
+ "message": "يرجى التحقق من اسم المستخدم وكلمة المرور والمحاولة مرة أخرى.",
+ "title": "فشل تسجيل الدخول"
+ },
+ "feature_dispatch_desc": "أرسل الوحدات وقم بإدارة المكالمات فوراً مع التحديثات المباشرة على جميع الأجهزة.",
+ "feature_dispatch_title": "الإرسال في الوقت الفعلي",
+ "feature_mapping_desc": "تتبع الوحدات في الوقت الفعلي مع خرائط تفصيلية وتوجيه وإدارة الموقع.",
+ "feature_mapping_title": "خرائط متقدمة",
+ "feature_personnel_desc": "قم بإدارة فريقك مع الوصول القائم على الأدوار وتتبع الحالة وأدوات الاتصال.",
+ "feature_personnel_title": "إدارة الموظفين",
+ "footer_text": "صنع بـ ❤️ في نيفادا",
+ "login": "تسجيل الدخول",
+ "login_button": "تسجيل الدخول",
+ "login_button_description": "قم بتسجيل الدخول إلى حسابك للمتابعة",
+ "login_button_error": "خطأ في تسجيل الدخول",
+ "login_button_loading": "تسجيل الدخول...",
+ "login_button_success": "تم تسجيل الدخول بنجاح",
+ "no_account": "ليس لديك حساب؟",
+ "page_subtitle": "أدخل بيانات الاعتماد الخاصة بك لتسجيل الدخول.",
+ "page_title": "Resgrid Dispatch",
+ "password": "كلمة المرور",
+ "password_incorrect": "كانت كلمة المرور غير صحيحة",
+ "password_placeholder": "أدخل كلمة المرور الخاصة بك",
+ "register": "تسجيل",
+ "title": "تسجيل الدخول",
+ "username": "اسم المستخدم",
+ "username_placeholder": "أدخل اسم المستخدم الخاص بك",
+ "welcome_title": "مرحباً بعودتك"
+ },
+ "maintenance": {
+ "downtime_message": "نحن نعمل بجد لإكمال الصيانة في أسرع وقت ممكن. يرجى التحقق مرة أخرى قريبا.",
+ "downtime_title": "ما هو وقت التوقف؟",
+ "message": "يرجى التحقق مرة أخرى في وقت ما.",
+ "support_message": "إذا كنت بحاجة إلى مساعدة، يرجى الاتصال بنا على",
+ "support_title": "هل تحتاج إلى دعم؟",
+ "title": "الموقع قيد الصيانة",
+ "why_down_message": "نحن نقوم بصيانة مجدولة لتحسين تجربتك. نعتذر عن أي إزعاج.",
+ "why_down_title": "لماذا الموقع معطل؟"
},
"map": {
"call_set_as_current": "تم تعيين المكالمة كمكالمة حالية",
@@ -690,6 +1009,26 @@
"view_poi_details": "عرض تفاصيل نقطة الاهتمام",
"hide_all": "إخفاء الكل"
},
+ "menu": {
+ "calls": "المكالمات",
+ "calls_list": "قائمة المكالمات",
+ "scheduled_calls": "المكالمات المجدولة",
+ "contacts": "جهات الاتصال",
+ "home": "الرئيسية",
+ "map": "الخريطة",
+ "menu": "القائمة",
+ "messages": "الرسائل",
+ "new_call": "مكالمة جديدة",
+ "personnel": "الموظفون",
+ "pois": "نقاط الاهتمام",
+ "protocols": "البروتوكولات",
+ "settings": "الإعدادات",
+ "units": "الوحدات",
+ "weatherAlerts": "تنبيهات الطقس",
+ "incident_command": "قيادة الحادث",
+ "chat": "الدردشة",
+ "assistant": "المساعد"
+ },
"notes": {
"actions": {
"add": "إضافة ملاحظة",
@@ -709,6 +1048,23 @@
"search": "البحث في الملاحظات...",
"title": "الملاحظات"
},
+ "onboarding": {
+ "screen1": {
+ "title": "Resgrid Dispatch",
+ "description": "إنشاء وإرسال وإدارة مكالمات الطوارئ مع مركز قيادة متنقل قوي في متناول يدك"
+ },
+ "screen2": {
+ "title": "الوعي الظرفي في الوقت الفعلي",
+ "description": "تتبع جميع الوحدات والأفراد والموارد على خريطة تفاعلية مع تحديثات الحالة المباشرة وAVL"
+ },
+ "screen3": {
+ "title": "التنسيق السلس",
+ "description": "تواصل فورًا مع الوحدات الميدانية، وقم بتحديث حالات المكالمات، وتنسيق جهود الاستجابة من أي مكان"
+ },
+ "skip": "تخطي",
+ "next": "التالي",
+ "getStarted": "لنبدأ"
+ },
"personnel": {
"title": "الأفراد",
"search": "البحث عن الأفراد...",
@@ -741,22 +1097,41 @@
"send_email": "بريد إلكتروني",
"custom_fields": "معلومات إضافية"
},
- "onboarding": {
- "screen1": {
- "title": "Resgrid Dispatch",
- "description": "إنشاء وإرسال وإدارة مكالمات الطوارئ مع مركز قيادة متنقل قوي في متناول يدك"
- },
- "screen2": {
- "title": "الوعي الظرفي في الوقت الفعلي",
- "description": "تتبع جميع الوحدات والأفراد والموارد على خريطة تفاعلية مع تحديثات الحالة المباشرة وAVL"
- },
- "screen3": {
- "title": "التنسيق السلس",
- "description": "تواصل فورًا مع الوحدات الميدانية، وقم بتحديث حالات المكالمات، وتنسيق جهود الاستجابة من أي مكان"
+ "pois": {
+ "address": "العنوان",
+ "all_types": "كل الأنواع",
+ "destination": "وجهة",
+ "details": "التفاصيل",
+ "detail_not_found": "تعذر العثور على نقطة الاهتمام",
+ "detail_not_found_description": "تعذر تحميل نقطة الاهتمام المطلوبة.",
+ "detail_title": "تفاصيل نقطة الاهتمام",
+ "empty": "لا توجد نقاط اهتمام",
+ "empty_description": "لا توجد نقاط اهتمام متاحة لقسمك حتى الآن.",
+ "empty_filtered": "لا توجد نقاط اهتمام مطابقة",
+ "empty_filtered_description": "حاول مسح البحث أو اختيار نوع مختلف.",
+ "filter_by_type": "تصفية حسب نوع نقطة الاهتمام",
+ "invalid_poi": "نقطة اهتمام غير صالحة",
+ "invalid_poi_description": "معرّف نقطة الاهتمام المحدد غير صالح.",
+ "loading": "جارٍ تحميل نقاط الاهتمام...",
+ "loading_detail": "جارٍ تحميل تفاصيل نقطة الاهتمام...",
+ "map": "الخريطة",
+ "no_location": "لا يوجد موقع متاح",
+ "no_location_description": "لا تحتوي نقطة الاهتمام هذه على إحداثيات قابلة للاستخدام.",
+ "no_location_for_routing": "لا توجد بيانات موقع متاحة للتوجيه",
+ "note": "ملاحظة",
+ "route_error": "فشل في فتح تطبيق الخرائط",
+ "search": "ابحث في نقاط الاهتمام...",
+ "sort": "ترتيب",
+ "sort_options": {
+ "address-asc": "العنوان",
+ "name-asc": "الاسم (أ-ي)",
+ "name-desc": "الاسم (ي-أ)",
+ "type-asc": "النوع"
},
- "skip": "تخطي",
- "next": "التالي",
- "getStarted": "لنبدأ"
+ "title": "نقاط الاهتمام",
+ "type": "النوع",
+ "unknown_type": "نوع غير معروف",
+ "unnamed": "نقطة اهتمام بدون اسم"
},
"protocols": {
"details": {
@@ -796,6 +1171,20 @@
"tap_to_manage": "انقر لإدارة الأدوار",
"unassigned": "غير معين"
},
+ "scheduled_calls": {
+ "title": "المكالمات المجدولة",
+ "loading": "تحميل المكالمات المجدولة...",
+ "no_scheduled_calls": "لا توجد مكالمات مجدولة",
+ "no_scheduled_calls_description": "لا توجد مكالمات مجدولة معلقة في الوقت الحالي.",
+ "search": "البحث في المكالمات المجدولة...",
+ "scheduled_for": "مجدولة ل",
+ "table_number": "رقم البلاغ",
+ "table_name": "الاسم",
+ "table_type": "النوع",
+ "table_priority": "الأولوية",
+ "table_address": "العنوان",
+ "table_scheduled": "مجدول في"
+ },
"settings": {
"about": "حول التطبيق",
"account": "الحساب",
@@ -884,6 +1273,29 @@
"version": "الإصدار",
"website": "الموقع الإلكتروني"
},
+ "sso": {
+ "authenticating": "جارٍ المصادقة...",
+ "back_to_login": "العودة إلى تسجيل الدخول",
+ "back_to_lookup": "تغيير المستخدم",
+ "continue_button": "متابعة",
+ "department_id_label": "معرف القسم",
+ "department_id_placeholder": "أدخل معرف القسم",
+ "error_generic": "فشل تسجيل الدخول. يرجى المحاولة مرة أخرى.",
+ "error_oidc_cancelled": "تم إلغاء تسجيل الدخول.",
+ "error_oidc_not_ready": "موفر SSO يتم تحميله، يرجى الانتظار.",
+ "error_sso_not_enabled": "تسجيل الدخول الموحد غير مفعل لهذا المستخدم.",
+ "error_token_exchange": "فشل إتمام تسجيل الدخول. يرجى المحاولة مرة أخرى.",
+ "error_user_not_found": "المستخدم غير موجود. يرجى التحقق والمحاولة مرة أخرى.",
+ "looking_up": "جارٍ البحث...",
+ "optional": "اختياري",
+ "page_subtitle": "أدخل اسم المستخدم للبحث عن خيارات تسجيل الدخول الخاصة بمؤسستك.",
+ "page_title": "تسجيل الدخول الموحد",
+ "provider_oidc": "OpenID Connect (OIDC)",
+ "provider_saml": "SAML 2.0",
+ "sign_in_button": "تسجيل الدخول عبر SSO",
+ "sign_in_title": "تسجيل الدخول",
+ "sso_button": "تسجيل دخول SSO"
+ },
"status": {
"add_note": "إضافة ملاحظة",
"all_destinations_enabled": "يمكن الاستجابة للمكالمات أو المحطات أو نقاط الاهتمام",
@@ -929,211 +1341,6 @@
"shifts": "المناوبات",
"personnel": "الموظفون"
},
- "dispatch": {
- "active_calls": "المكالمات النشطة",
- "pending_calls": "معلقة",
- "scheduled_calls": "مجدولة",
- "units_available": "متاحة",
- "personnel_available": "متاحين",
- "personnel_on_duty": "في الخدمة",
- "units": "الوحدات",
- "personnel": "الموظفين",
- "map": "الخريطة",
- "notes": "الملاحظات",
- "activity_log": "سجل النشاط",
- "communications": "الاتصالات",
- "no_active_calls": "لا توجد مكالمات نشطة",
- "no_units": "لا توجد وحدات متاحة",
- "no_personnel": "لا يوجد موظفين متاحين",
- "no_notes": "لا توجد ملاحظات متاحة",
- "no_activity": "لا يوجد نشاط حديث",
- "current_channel": "القناة الحالية",
- "audio_stream": "البث الصوتي",
- "no_stream": "لا يوجد بث نشط",
- "ptt": "اضغط للتحدث",
- "ptt_start": "بدء الإرسال",
- "ptt_end": "انتهاء الإرسال",
- "transmitting_on": "جاري الإرسال على {{channel}}",
- "transmission_ended": "انتهى الإرسال",
- "voice_disabled": "تم تعطيل الصوت",
- "disconnected": "غير متصل",
- "select_channel": "اختر القناة",
- "select_channel_description": "اختر قناة صوتية للاتصال بها",
- "change_channel_warning": "اختيار قناة جديدة سيؤدي إلى قطع الاتصال من القناة الحالية",
- "default_channel": "افتراضي",
- "no_channels_available": "لا توجد قنوات صوتية متاحة",
- "system_update": "تحديث النظام",
- "data_refreshed": "تم تحديث البيانات من الخادم",
- "call_selected": "تم تحديد المكالمة",
- "unit_selected": "تم تحديد الوحدة",
- "unit_deselected": "تم إلغاء تحديد الوحدة",
- "personnel_selected": "تم تحديد الموظف",
- "personnel_deselected": "تم إلغاء تحديد الموظف",
- "loading_map": "جاري تحميل الخريطة...",
- "map_not_available_web": "الخريطة غير متاحة على منصة الويب",
- "filtering_by_call": "تصفية حسب المكالمة",
- "clear_filter": "مسح الفلتر",
- "call_filter_active": "فلتر المكالمة نشط",
- "call_filter_cleared": "تم مسح فلتر المكالمة",
- "showing_all_data": "عرض جميع البيانات",
- "call_notes": "ملاحظات المكالمة",
- "no_call_notes": "لا توجد ملاحظات للمكالمة",
- "add_call_note_placeholder": "أضف ملاحظة...",
- "note_added": "تمت إضافة الملاحظة",
- "note_added_to_console": "تمت إضافة ملاحظة جديدة إلى وحدة التحكم",
- "add_note_title": "إضافة ملاحظة جديدة",
- "note_title_label": "العنوان",
- "note_title_placeholder": "أدخل عنوان الملاحظة...",
- "note_category_label": "الفئة",
- "note_category_placeholder": "اختر فئة",
- "note_no_category": "بدون فئة",
- "note_body_label": "محتوى الملاحظة",
- "note_body_placeholder": "أدخل محتوى الملاحظة...",
- "note_save_error": "فشل حفظ الملاحظة: {{error}}",
- "note_created": "تم إنشاء الملاحظة",
- "units_on_call": "الوحدات في المكالمة",
- "no_units_on_call": "لا توجد وحدات في المكالمة",
- "personnel_on_call": "الموظفين في المكالمة",
- "no_personnel_on_call": "لا يوجد موظفين في المكالمة",
- "call_activity": "نشاط المكالمة",
- "no_call_activity": "لا يوجد نشاط للمكالمة",
- "on_call": "في مكالمة",
- "filtered": "مفلتر",
- "active_filter": "فلتر نشط",
- "unit_status_change": "تغيير حالة الوحدة",
- "personnel_status_change": "تغيير حالة الموظف",
- "view_call_details": "عرض تفاصيل المكالمة",
- "dispatched_resources": "الموارد المرسلة",
- "unassigned": "غير مخصص",
- "available": "متاح",
- "unknown": "غير معروف",
- "search_personnel_placeholder": "بحث عن موظف...",
- "search_calls_placeholder": "بحث عن مكالمات...",
- "search_units_placeholder": "بحث عن وحدات...",
- "search_notes_placeholder": "بحث في الملاحظات...",
- "signalr_update": "تحديث في الوقت الفعلي",
- "signalr_connected": "متصل",
- "realtime_updates_active": "التحديثات في الوقت الفعلي نشطة الآن",
- "personnel_status_updated": "تم تحديث حالة الموظف",
- "personnel_staffing_updated": "تم تحديث تعيين الموظف",
- "unit_status_updated": "تم تحديث حالة الوحدة",
- "calls_updated": "تم تحديث المكالمات",
- "call_added": "تمت إضافة مكالمة جديدة",
- "call_closed": "تم إغلاق المكالمة",
- "check_ins": "تسجيلات الحضور",
- "no_check_ins": "لا توجد مكالمات بمؤقتات تسجيل الحضور",
- "radio_log": "سجل الراديو",
- "radio": "الراديو",
- "activity": "النشاط",
- "actions": "الإجراءات",
- "no_radio_activity": "لا توجد إرسالات راديو",
- "live": "مباشر",
- "currently_transmitting": "جاري الإرسال حالياً...",
- "duration": "المدة",
- "call_actions": "إجراءات المكالمة",
- "unit_actions": "إجراءات الوحدة",
- "personnel_actions": {
- "title": "إجراءات الموظف",
- "status_tab": "الحالة",
- "staffing_tab": "التعيين",
- "select_status": "اختر الحالة",
- "select_staffing": "اختر مستوى التعيين",
- "destination": "الوجهة",
- "no_destination": "بدون وجهة",
- "note": "ملاحظة",
- "note_placeholder": "أضف ملاحظة اختيارية...",
- "update_status": "تحديث الحالة",
- "update_staffing": "تحديث التعيين",
- "no_statuses_available": "لا توجد حالات متاحة",
- "no_staffings_available": "لا توجد مستويات تعيين متاحة"
- },
- "unit_actions_panel": {
- "status": "الحالة",
- "select_status": "اختر الحالة",
- "destination": "الوجهة",
- "no_destination": "بدون وجهة",
- "note": "ملاحظة",
- "note_placeholder": "أضف ملاحظة اختيارية...",
- "update_status": "تحديث الحالة",
- "no_statuses_available": "لا توجد حالات متاحة",
- "no_active_calls": "لا توجد مكالمات نشطة",
- "no_stations_available": "لا توجد محطات متاحة",
- "no_destinations_available": "لا توجد وجهات متاحة"
- },
- "call": "مكالمة",
- "station": "محطة",
- "calls": "المكالمات",
- "stations": "المحطات",
- "no_stations_available": "لا توجد محطات متاحة",
- "new_call": "مكالمة جديدة",
- "view_details": "التفاصيل",
- "add_note": "إضافة ملاحظة",
- "close_call": "إغلاق",
- "set_status": "تعيين الحالة",
- "set_staffing": "التعيين",
- "dispatch": "إرسال",
- "select_items_for_actions": "حدد مكالمة أو وحدة أو موظف لتفعيل الإجراءات السياقية",
- "weather": {
- "clear": "صافٍ",
- "mainly_clear": "صافٍ غالبًا",
- "partly_cloudy": "غائم جزئيًا",
- "overcast": "غائم",
- "fog": "ضباب",
- "drizzle": "رذاذ",
- "freezing_drizzle": "رذاذ متجمد",
- "rain": "مطر",
- "freezing_rain": "مطر متجمد",
- "snow": "ثلج",
- "rain_showers": "زخات مطر",
- "snow_showers": "زخات ثلج",
- "thunderstorm": "عاصفة رعدية",
- "thunderstorm_hail": "عاصفة رعدية مع بَرَد",
- "unknown": "غير معروف"
- },
- "available_only": "المتاح فقط",
- "single_list": "قائمة واحدة",
- "resources": "الموارد",
- "search_resources_placeholder": "البحث عن الموارد...",
- "no_resources": "لا توجد موارد"
- },
- "check_in": {
- "tab_title": "تسجيل الحضور",
- "timer_status": "حالة المؤقت",
- "perform_check_in": "تسجيل",
- "check_in_success": "تم تسجيل الحضور بنجاح",
- "check_in_error": "فشل في تسجيل الحضور",
- "checked_in_by": "بواسطة {{name}}",
- "last_check_in": "آخر تسجيل",
- "elapsed": "المنقضي",
- "duration": "المدة",
- "status_ok": "جيد",
- "status_green": "جيد",
- "status_warning": "تحذير",
- "status_yellow": "تحذير",
- "status_overdue": "متأخر",
- "status_red": "متأخر",
- "status_critical": "حرج",
- "history": "سجل التسجيلات",
- "no_timers": "لم يتم تكوين مؤقتات تسجيل الحضور",
- "timers_disabled": "مؤقتات تسجيل الحضور معطلة لهذه المكالمة",
- "type_personnel": "الأفراد",
- "type_unit": "الوحدة",
- "type_ic": "قائد الحادث",
- "type_par": "PAR",
- "type_hazmat": "التعرض للمواد الخطرة",
- "type_sector_rotation": "تدوير القطاع",
- "type_rehab": "إعادة التأهيل",
- "add_note": "إضافة ملاحظة (اختياري)",
- "confirm": "تأكيد التسجيل",
- "minutes_ago": "منذ {{count}} دقيقة",
- "select_target": "حدد الكيان للتسجيل",
- "overdue_count": "{{count}} متأخر",
- "warning_count": "{{count}} تحذير",
- "enable_timers": "تفعيل المؤقتات",
- "disable_timers": "تعطيل المؤقتات",
- "summary": "{{overdue}} متأخر، {{warning}} تحذير، {{ok}} جيد",
- "par_title": "متابعة الأفراد (PAR)"
- },
"units": {
"search": "البحث عن الوحدات...",
"loading": "جارٍ تحميل الوحدات...",
@@ -1163,6 +1370,63 @@
"no_destination": "لا شيء",
"title": "الوحدات"
},
+ "videoFeeds": {
+ "title": "بث الفيديو",
+ "noFeeds": "لا توجد بثوث فيديو لهذه المكالمة",
+ "addFeed": "إضافة بث فيديو",
+ "editFeed": "تعديل بث الفيديو",
+ "deleteFeed": "حذف بث الفيديو",
+ "deleteConfirm": "هل أنت متأكد أنك تريد إزالة بث الفيديو هذا؟",
+ "watch": "مشاهدة",
+ "goLive": "بث مباشر",
+ "stopLive": "إيقاف البث",
+ "flipCamera": "عكس الكاميرا",
+ "feedAdded": "تمت إضافة بث الفيديو",
+ "feedUpdated": "تم تحديث بث الفيديو",
+ "feedDeleted": "تم حذف بث الفيديو",
+ "feedError": "فشل في تحميل بث الفيديو",
+ "unsupportedFormat": "تنسيق البث هذا غير مدعوم على الأجهزة المحمولة",
+ "copyUrl": "نسخ الرابط",
+ "form": {
+ "name": "اسم البث",
+ "namePlaceholder": "مثال: طائرة محرك 1",
+ "url": "رابط البث",
+ "urlPlaceholder": "مثال: https://stream.example.com/live.m3u8",
+ "feedType": "نوع الكاميرا",
+ "feedFormat": "تنسيق البث",
+ "description": "الوصف",
+ "descriptionPlaceholder": "وصف اختياري",
+ "status": "الحالة",
+ "sortOrder": "الترتيب",
+ "cameraLocation": "موقع الكاميرا",
+ "useCurrentLocation": "استخدام الموقع الحالي"
+ },
+ "type": {
+ "drone": "طائرة بدون طيار",
+ "fixedCamera": "كاميرا ثابتة",
+ "bodyCam": "كاميرا جسدية",
+ "trafficCam": "كاميرا مرور",
+ "weatherCam": "كاميرا طقس",
+ "satelliteFeed": "بث فضائي",
+ "webCam": "كاميرا ويب",
+ "other": "أخرى"
+ },
+ "format": {
+ "rtsp": "RTSP",
+ "hls": "HLS",
+ "mjpeg": "MJPEG",
+ "youtubeLive": "YouTube Live",
+ "webrtc": "WebRTC",
+ "dash": "DASH",
+ "embed": "مضمن",
+ "other": "أخرى"
+ },
+ "status": {
+ "active": "نشط",
+ "inactive": "غير نشط",
+ "error": "خطأ"
+ }
+ },
"weatherAlerts": {
"title": "تنبيهات الطقس",
"activeAlerts": "التنبيهات النشطة",
@@ -1270,251 +1534,5 @@
},
"stats_label": "تنبيهات الطقس"
},
- "videoFeeds": {
- "title": "بث الفيديو",
- "noFeeds": "لا توجد بثوث فيديو لهذه المكالمة",
- "addFeed": "إضافة بث فيديو",
- "editFeed": "تعديل بث الفيديو",
- "deleteFeed": "حذف بث الفيديو",
- "deleteConfirm": "هل أنت متأكد أنك تريد إزالة بث الفيديو هذا؟",
- "watch": "مشاهدة",
- "goLive": "بث مباشر",
- "stopLive": "إيقاف البث",
- "flipCamera": "عكس الكاميرا",
- "feedAdded": "تمت إضافة بث الفيديو",
- "feedUpdated": "تم تحديث بث الفيديو",
- "feedDeleted": "تم حذف بث الفيديو",
- "feedError": "فشل في تحميل بث الفيديو",
- "unsupportedFormat": "تنسيق البث هذا غير مدعوم على الأجهزة المحمولة",
- "copyUrl": "نسخ الرابط",
- "form": {
- "name": "اسم البث",
- "namePlaceholder": "مثال: طائرة محرك 1",
- "url": "رابط البث",
- "urlPlaceholder": "مثال: https://stream.example.com/live.m3u8",
- "feedType": "نوع الكاميرا",
- "feedFormat": "تنسيق البث",
- "description": "الوصف",
- "descriptionPlaceholder": "وصف اختياري",
- "status": "الحالة",
- "sortOrder": "الترتيب",
- "cameraLocation": "موقع الكاميرا",
- "useCurrentLocation": "استخدام الموقع الحالي"
- },
- "type": {
- "drone": "طائرة بدون طيار",
- "fixedCamera": "كاميرا ثابتة",
- "bodyCam": "كاميرا جسدية",
- "trafficCam": "كاميرا مرور",
- "weatherCam": "كاميرا طقس",
- "satelliteFeed": "بث فضائي",
- "webCam": "كاميرا ويب",
- "other": "أخرى"
- },
- "format": {
- "rtsp": "RTSP",
- "hls": "HLS",
- "mjpeg": "MJPEG",
- "youtubeLive": "YouTube Live",
- "webrtc": "WebRTC",
- "dash": "DASH",
- "embed": "مضمن",
- "other": "أخرى"
- },
- "status": {
- "active": "نشط",
- "inactive": "غير نشط",
- "error": "خطأ"
- }
- },
- "welcome": "مرحبًا بك في موقع تطبيق obytes",
- "incident_command": {
- "tab_title": "القيادة",
- "title": "قيادة الحادث",
- "open_full_board": "فتح اللوحة الكاملة",
- "no_command": "لم يتم إنشاء قيادة للحادث",
- "no_command_description": "أنشئ قيادة للحادث لتنسيق الموارد والأدوار والأهداف ومتابعة الأفراد لهذا البلاغ.",
- "establish": "إنشاء القيادة",
- "establish_title": "إنشاء قيادة الحادث",
- "establish_description": "يمكنك اختياريًا إعداد لوحة القيادة من قالب.",
- "establish_success": "تم إنشاء قيادة الحادث",
- "establish_error": "تعذر إنشاء القيادة",
- "template": "قالب",
- "no_template": "بدون قالب (لوحة فارغة)",
- "saved": "تم الحفظ",
- "save_error": "فشلت العملية",
- "edit_action_plan": "تعديل خطة العمل",
- "action_plan": "خطة العمل",
- "action_plan_placeholder": "صف خطة عمل الحادث...",
- "save": "حفظ",
- "no_action_plan": "لم يتم تعيين خطة عمل.",
- "add": "إضافة",
- "add_objective": "إضافة هدف",
- "objective_name": "الهدف",
- "objective_type": "النوع",
- "name_required": "الاسم مطلوب",
- "add_lane": "إضافة قطاع",
- "lane_name": "اسم القطاع",
- "lane_type": "نوع القطاع",
- "assign_resource": "تعيين مورد",
- "assign_resource_required": "حدد قطاعًا وموردًا",
- "lane": "القطاع",
- "select_lane": "حدد قطاعًا",
- "resource_type": "نوع المورد",
- "resource": "المورد",
- "select_resource": "حدد موردًا",
- "unit": "الوحدة",
- "personnel": "الأفراد",
- "assign": "تعيين",
- "assign_role": "تعيين دور",
- "assign_role_required": "حدد شخصًا ودورًا",
- "person": "الشخص",
- "select_person": "حدد شخصًا",
- "role": "الدور",
- "select_role": "حدد دورًا",
- "transfer_title": "نقل القيادة",
- "transfer_notes": "ملاحظات",
- "transfer": "نقل",
- "transfer_command": "نقل",
- "transfer_success": "تم نقل القيادة",
- "close_command": "إنهاء القيادة",
- "confirm_close": "هل تريد إنهاء قيادة الحادث لهذا البلاغ؟",
- "status": "الحالة",
- "active": "نشطة",
- "closed": "مغلقة",
- "commander": "قائد الحادث",
- "established_on": "تاريخ الإنشاء",
- "edit": "تعديل",
- "roles": "أدوار القيادة",
- "no_roles": "لم يتم تعيين أدوار.",
- "structure": "هيكل القيادة",
- "no_lanes": "لم يتم تحديد قطاعات.",
- "no_resources": "لم يتم تعيين موارد.",
- "release": "تحرير",
- "objectives": "الأهداف",
- "no_objectives": "لا توجد أهداف.",
- "complete": "إكمال",
- "completed": "مكتمل",
- "timers": "المؤقتات",
- "no_timers": "لا توجد مؤقتات قيد التشغيل.",
- "acknowledge": "إقرار",
- "due": "موعد الاستحقاق",
- "accountability": "متابعة الأفراد (PAR)",
- "run_par": "تنفيذ PAR",
- "green": "أخضر",
- "warning": "تحذير",
- "critical": "حرج",
- "no_accountability": "لا يوجد أفراد قيد المتابعة.",
- "timeline": "التسلسل الزمني للقيادة",
- "no_timeline": "لا توجد إدخالات في التسلسل الزمني.",
- "unassigned": "غير معيّن",
- "active_title": "قيادات الحوادث النشطة",
- "no_active": "لا توجد قيادات حوادث نشطة",
- "no_active_description": "ستظهر هنا قيادات الحوادث التي تم إنشاؤها للبلاغات.",
- "call": "البلاغ",
- "tactical_map": "الخريطة التكتيكية",
- "annotations": "تعليقات الخريطة",
- "no_annotations": "لا توجد تعليقات.",
- "open_tactical_map": "فتح الخريطة التكتيكية",
- "marker": "علامة",
- "add_marker": "إضافة علامة",
- "tap_to_place": "اضغط على الخريطة لوضع علامة",
- "marker_label": "تسمية العلامة",
- "delete_annotation_confirm": "هل تريد إزالة هذا التعليق؟",
- "voice_channels": "القنوات الصوتية",
- "no_channels": "لا توجد قنوات مفتوحة.",
- "add_channel": "إضافة قناة",
- "channel_name": "اسم القناة",
- "close_all_channels": "إغلاق جميع القنوات",
- "join": "انضمام",
- "hold_to_talk": "اضغط مطولًا للتحدث",
- "talking": "جارٍ الإرسال...",
- "voice_joined": "تم الانضمام إلى القناة الصوتية",
- "voice_join_error": "تعذر الانضمام إلى القناة الصوتية",
- "move": "نقل",
- "move_lane": "نقل القطاع",
- "parent_lane": "القطاع الأصل",
- "top_level": "المستوى الأعلى",
- "move_resource": "نقل المورد"
- },
- "chat": {
- "title": "الدردشة",
- "assistant": "المساعد",
- "empty": "لا توجد محادثات بعد. ابدأ رسالة مباشرة أو أنشئ مجموعة.",
- "section_assistant": "المساعد",
- "section_direct_messages": "الرسائل المباشرة",
- "section_channels": "القنوات",
- "section_incidents": "الحوادث",
- "new_direct_message": "رسالة مباشرة جديدة",
- "new_group": "مجموعة جديدة",
- "open_assistant": "فتح المساعد",
- "create_conversation_failed": "تعذّر بدء المحادثة",
- "group_name": "اسم المجموعة",
- "search_people": "البحث عن أشخاص",
- "no_people": "لم يتم العثور على أشخاص",
- "create_group_with": "إنشاء مجموعة ({{count}})",
- "message_deleted": "تم حذف هذه الرسالة",
- "urgent": "عاجل",
- "urgent_will_send": "سيتم إرسال هذه الرسالة كرسالة عاجلة",
- "shared_location": "الموقع المشترك",
- "thread_replies": "{{count}} ردود",
- "edited": "(تم التعديل)",
- "failed_tap_retry": "فشل - انقر لإعادة المحاولة",
- "type_a_message": "اكتب رسالة",
- "emoji": "رمز تعبيري",
- "add_image": "إضافة صورة",
- "add_gif": "إضافة GIF",
- "share_location": "مشاركة الموقع",
- "send": "إرسال",
- "someone": "شخص ما",
- "is_typing": "{{name}} يكتب...",
- "are_typing": "{{count}} أشخاص يكتبون...",
- "permission_photos_denied": "تم رفض إذن الوصول إلى مكتبة الصور",
- "permission_location_denied": "تم رفض إذن الموقع",
- "search_gifs": "البحث عن صور GIF",
- "no_gifs": "لم يتم العثور على صور GIF",
- "flag_reason": "لماذا تبلّغ عن هذا؟",
- "flag_inappropriate": "غير لائق",
- "flag_harassment": "تحرش",
- "flag_spam": "بريد عشوائي",
- "flag_sensitive": "معلومات حساسة",
- "flag_policy": "انتهاك السياسة",
- "flag_other": "أخرى",
- "reply_in_thread": "الرد في المحادثة",
- "copy": "نسخ",
- "copied": "تم النسخ",
- "copy_unavailable": "النسخ غير متاح على هذا الجهاز",
- "edit": "تعديل",
- "edit_message": "تعديل الرسالة",
- "save": "حفظ",
- "delete": "حذف",
- "pin": "تثبيت",
- "unpin": "إلغاء التثبيت",
- "flag": "إبلاغ",
- "moderator_delete": "إزالة (مشرف)",
- "moderator_removed": "تمت الإزالة بواسطة المشرف",
- "attachment_failed": "فشل رفع المرفق",
- "ack_required": "الإقرار مطلوب",
- "ack_pending_one": "لديك رسالة عاجلة بحاجة إلى إقرار",
- "ack_pending_count": "لديك {{count}} رسائل عاجلة بحاجة إلى إقرار",
- "acknowledge": "إقرار",
- "thread": "المحادثة",
- "original_message": "الرسالة الأصلية",
- "reply_placeholder": "رد...",
- "channel": "قناة",
- "direct_message": "رسالة مباشرة",
- "load_people_failed": "تعذّر تحميل الأشخاص",
- "reaction_failed": "تعذّر تحديث التفاعل",
- "edit_failed": "تعذّر تعديل الرسالة",
- "delete_failed": "تعذّر حذف الرسالة",
- "pin_failed": "تعذّر تحديث التثبيت",
- "flag_failed": "تعذّر الإبلاغ عن الرسالة"
- },
- "chatbot": {
- "title": "المساعد",
- "subtitle": "مساعد ذكاء اصطناعي لقسمك",
- "new_session": "جلسة جديدة",
- "empty": "اسأل المساعد عن أي شيء للبدء.",
- "ask_placeholder": "اسأل المساعد..."
- }
+ "welcome": "مرحبًا بك في موقع تطبيق obytes"
}
diff --git a/src/translations/de.json b/src/translations/de.json
index 8c624ae0..e294634e 100644
--- a/src/translations/de.json
+++ b/src/translations/de.json
@@ -371,6 +371,124 @@
"audio_name": "Audioclip"
}
},
+ "chat": {
+ "title": "Chat",
+ "assistant": "Assistent",
+ "empty": "Noch keine Unterhaltungen. Starte eine Direktnachricht oder erstelle eine Gruppe.",
+ "section_assistant": "Assistent",
+ "section_direct_messages": "Direktnachrichten",
+ "section_channels": "Kanäle",
+ "section_incidents": "Vorfälle",
+ "new_direct_message": "Neue Direktnachricht",
+ "new_group": "Neue Gruppe",
+ "open_assistant": "Assistent öffnen",
+ "create_conversation_failed": "Unterhaltung konnte nicht gestartet werden",
+ "group_name": "Gruppenname",
+ "search_people": "Personen suchen",
+ "no_people": "Keine Personen gefunden",
+ "create_group_with": "Gruppe erstellen ({{count}})",
+ "message_deleted": "Diese Nachricht wurde gelöscht",
+ "urgent": "Dringend",
+ "urgent_will_send": "Diese Nachricht wird als dringend gesendet",
+ "shared_location": "Geteilter Standort",
+ "thread_replies": "{{count}} Antworten",
+ "edited": "(bearbeitet)",
+ "failed_tap_retry": "Fehlgeschlagen – zum Wiederholen tippen",
+ "type_a_message": "Nachricht eingeben",
+ "emoji": "Emoji",
+ "add_image": "Bild hinzufügen",
+ "add_gif": "GIF hinzufügen",
+ "share_location": "Standort teilen",
+ "send": "Senden",
+ "someone": "Jemand",
+ "is_typing": "{{name}} schreibt...",
+ "are_typing": "{{count}} Personen schreiben...",
+ "permission_photos_denied": "Zugriff auf die Fotomediathek verweigert",
+ "permission_location_denied": "Zugriff auf den Standort verweigert",
+ "search_gifs": "GIFs suchen",
+ "no_gifs": "Keine GIFs gefunden",
+ "flag_reason": "Warum meldest du dies?",
+ "flag_inappropriate": "Unangemessen",
+ "flag_harassment": "Belästigung",
+ "flag_spam": "Spam",
+ "flag_sensitive": "Sensible Informationen",
+ "flag_policy": "Richtlinienverstoß",
+ "flag_other": "Sonstiges",
+ "reply_in_thread": "Im Thread antworten",
+ "copy": "Kopieren",
+ "copied": "Kopiert",
+ "copy_unavailable": "Kopieren ist auf diesem Gerät nicht verfügbar",
+ "edit": "Bearbeiten",
+ "edit_message": "Nachricht bearbeiten",
+ "save": "Speichern",
+ "delete": "Löschen",
+ "pin": "Anheften",
+ "unpin": "Anheften aufheben",
+ "flag": "Melden",
+ "moderator_delete": "Entfernen (Moderator)",
+ "moderator_removed": "Vom Moderator entfernt",
+ "attachment_failed": "Anhang-Upload fehlgeschlagen",
+ "ack_required": "Bestätigung erforderlich",
+ "ack_pending_one": "Du hast eine dringende Nachricht zu bestätigen",
+ "ack_pending_count": "Du hast {{count}} dringende Nachrichten zu bestätigen",
+ "acknowledge": "Bestätigen",
+ "thread": "Thread",
+ "original_message": "Ursprüngliche Nachricht",
+ "reply_placeholder": "Antworten...",
+ "channel": "Kanal",
+ "direct_message": "Direktnachricht",
+ "load_people_failed": "Personen konnten nicht geladen werden",
+ "reaction_failed": "Reaktion konnte nicht aktualisiert werden",
+ "edit_failed": "Nachricht konnte nicht bearbeitet werden",
+ "delete_failed": "Nachricht konnte nicht gelöscht werden",
+ "pin_failed": "Anheften konnte nicht aktualisiert werden",
+ "flag_failed": "Nachricht konnte nicht gemeldet werden"
+ },
+ "chatbot": {
+ "title": "Assistent",
+ "subtitle": "KI-Helfer für deine Abteilung",
+ "new_session": "Neue Sitzung",
+ "empty": "Frag den Assistenten etwas, um zu beginnen.",
+ "ask_placeholder": "Frag den Assistenten..."
+ },
+ "check_in": {
+ "tab_title": "Kontrolle",
+ "timer_status": "Timer-Status",
+ "perform_check_in": "Kontrolle durchführen",
+ "check_in_success": "Kontrolle erfolgreich erfasst",
+ "check_in_error": "Kontrolle konnte nicht erfasst werden",
+ "checked_in_by": "von {{name}}",
+ "last_check_in": "Letzte Kontrolle",
+ "elapsed": "Vergangen",
+ "duration": "Dauer",
+ "status_ok": "OK",
+ "status_green": "OK",
+ "status_warning": "Warnung",
+ "status_yellow": "Warnung",
+ "status_overdue": "Überfällig",
+ "status_red": "Überfällig",
+ "status_critical": "Kritisch",
+ "history": "Kontrollverlauf",
+ "no_timers": "Keine Kontroll-Timer konfiguriert",
+ "timers_disabled": "Kontroll-Timer sind für diesen Einsatz deaktiviert",
+ "type_personnel": "Personal",
+ "type_unit": "Einheit",
+ "type_ic": "Einsatzleiter",
+ "type_par": "PAR",
+ "type_hazmat": "Gefahrstoffexposition",
+ "type_sector_rotation": "Abschnittsrotation",
+ "type_rehab": "Erholung",
+ "add_note": "Notiz hinzufügen (optional)",
+ "confirm": "Kontrolle bestätigen",
+ "minutes_ago": "vor {{count}} Min.",
+ "select_target": "Einheit für Kontrolle auswählen",
+ "overdue_count": "{{count}} Überfällig",
+ "warning_count": "{{count}} Warnung",
+ "enable_timers": "Timer aktivieren",
+ "disable_timers": "Timer deaktivieren",
+ "summary": "{{overdue}} überfällig, {{warning}} Warnung, {{ok}} ok",
+ "par_title": "Personalübersicht (PAR)"
+ },
"common": {
"add": "Hinzufügen",
"back": "Zurück",
@@ -502,178 +620,379 @@
"website": "Webseite",
"zip": "Postleitzahl"
},
- "form": {
- "invalid_url": "Bitte geben Sie eine gültige URL ein, die mit http:// oder https:// beginnt",
- "required": "Dieses Feld ist erforderlich"
- },
- "livekit": {
- "audio_devices": "Audiogeräte",
- "audio_settings": "Audioeinstellungen",
- "connected_to_room": "Mit Kanal verbunden",
- "connecting": "Verbindung wird hergestellt...",
- "disconnect": "Trennen",
- "join": "Beitreten",
- "microphone": "Mikrofon",
- "mute": "Stummschalten",
- "no_rooms_available": "Keine Sprachkanäle verfügbar",
- "speaker": "Lautsprecher",
- "speaking": "Spricht",
- "title": "Sprachkanäle",
- "unmute": "Stummschaltung aufheben"
- },
- "loading": {
- "loading": "Laden...",
- "loadingData": "Daten werden geladen...",
- "pleaseWait": "Bitte warten",
- "processingRequest": "Ihre Anfrage wird verarbeitet..."
- },
- "sso": {
- "authenticating": "Authentifizierung läuft...",
- "back_to_login": "Zurück zur Anmeldung",
- "back_to_lookup": "Benutzer wechseln",
- "continue_button": "Weiter",
- "department_id_label": "Abteilungs-ID",
- "department_id_placeholder": "Abteilungs-ID eingeben",
- "error_generic": "Anmeldung fehlgeschlagen. Bitte versuchen Sie es erneut.",
- "error_oidc_cancelled": "Anmeldung wurde abgebrochen.",
- "error_oidc_not_ready": "SSO-Anbieter wird geladen, bitte warten.",
- "error_sso_not_enabled": "Single Sign-On ist für diesen Benutzer nicht aktiviert.",
- "error_token_exchange": "Anmeldung konnte nicht abgeschlossen werden. Bitte versuchen Sie es erneut.",
- "error_user_not_found": "Benutzer nicht gefunden. Bitte überprüfen und erneut versuchen.",
- "looking_up": "Wird gesucht...",
- "optional": "optional",
- "page_subtitle": "Geben Sie Ihren Benutzernamen ein, um die Anmeldeoptionen Ihrer Organisation zu finden.",
- "page_title": "Einmalanmeldung",
- "provider_oidc": "OpenID Connect (OIDC)",
- "provider_saml": "SAML 2.0",
- "sign_in_button": "Mit SSO anmelden",
- "sign_in_title": "Anmelden",
- "sso_button": "SSO-Anmeldung"
- },
- "login": {
- "branding_subtitle": "Leistungsstarke Leitstellensoftware für Feuerwehren, Rettungsdienste und Organisationen der öffentlichen Sicherheit.",
- "branding_title": "Einsatzmanagement",
- "errorModal": {
- "confirmButton": "OK",
- "message": "Bitte überprüfen Sie Ihren Benutzernamen und Ihr Passwort und versuchen Sie es erneut.",
- "title": "Anmeldung fehlgeschlagen"
- },
- "feature_dispatch_desc": "Disponieren Sie Einheiten sofort und verwalten Sie Einsätze mit Live-Aktualisierungen auf allen Geräten.",
- "feature_dispatch_title": "Echtzeit-Disposition",
- "feature_mapping_desc": "Verfolgen Sie Einheiten in Echtzeit mit detaillierten Karten, Routenführung und Standortverwaltung.",
- "feature_mapping_title": "Erweiterte Kartendarstellung",
- "feature_personnel_desc": "Verwalten Sie Ihr Team mit rollenbasiertem Zugriff, Statusverfolgung und Kommunikationswerkzeugen.",
- "feature_personnel_title": "Personalverwaltung",
- "footer_text": "Erstellt mit ❤️ in Lake Tahoe",
- "login": "Anmelden",
- "login_button": "Anmelden",
- "login_button_description": "Melden Sie sich bei Ihrem Konto an, um fortzufahren",
- "login_button_error": "Fehler bei der Anmeldung",
- "login_button_loading": "Anmeldung läuft...",
- "login_button_success": "Erfolgreich angemeldet",
- "no_account": "Noch kein Konto?",
- "page_subtitle": "Geben Sie Ihre Anmeldedaten ein.",
- "page_title": "Resgrid Dispatch",
- "password": "Passwort",
- "password_incorrect": "Passwort war falsch",
- "password_placeholder": "Passwort eingeben",
- "register": "Registrieren",
- "title": "Anmeldung",
- "username": "Benutzername",
- "username_placeholder": "Benutzernamen eingeben",
- "welcome_title": "Willkommen zurück"
- },
- "lockscreen": {
- "message": "Geben Sie Ihr Passwort ein, um den Bildschirm zu entsperren",
- "not_you": "Nicht Sie? Zurück zur Anmeldung",
- "password": "Passwort",
- "password_placeholder": "Passwort eingeben",
- "title": "Bildschirmsperre",
- "unlock_button": "Entsperren",
- "unlock_failed": "Entsperren fehlgeschlagen. Bitte versuchen Sie es erneut.",
- "unlocking": "Wird entsperrt...",
- "welcome_back": "Willkommen zurück",
- "relogin_required": "Die Passwortüberprüfung ist für diese Sitzung nicht verfügbar. Bitte melde dich erneut an."
- },
- "maintenance": {
- "downtime_message": "Wir arbeiten daran, die Wartung so schnell wie möglich abzuschließen. Bitte schauen Sie bald wieder vorbei.",
- "downtime_title": "Wie lange dauert die Ausfallzeit?",
- "message": "Bitte schauen Sie später wieder vorbei.",
- "support_message": "Wenn Sie Hilfe benötigen, kontaktieren Sie uns unter",
- "support_title": "Benötigen Sie Unterstützung?",
- "title": "Wartungsarbeiten",
- "why_down_message": "Wir führen geplante Wartungsarbeiten durch, um Ihre Erfahrung zu verbessern. Wir entschuldigen uns für etwaige Unannehmlichkeiten.",
- "why_down_title": "Warum ist die Seite nicht erreichbar?"
- },
- "menu": {
- "scheduled_calls": "Geplante Einsätze",
- "pois": "POIs",
- "calls": "Einsätze",
- "calls_list": "Einsatzliste",
- "contacts": "Kontakte",
- "home": "Startseite",
- "map": "Karte",
- "menu": "Menü",
- "messages": "Nachrichten",
- "new_call": "Neuer Einsatz",
- "personnel": "Personal",
- "protocols": "Protokolle",
- "settings": "Einstellungen",
+ "dispatch": {
+ "active_calls": "Aktive Einsätze",
+ "pending_calls": "Ausstehend",
+ "scheduled_calls": "Geplant",
+ "units_available": "Verfügbar",
+ "personnel_available": "Verfügbar",
+ "personnel_on_duty": "Im Dienst",
"units": "Einheiten",
- "weatherAlerts": "Wetterwarnungen",
- "incident_command": "Einsatzleitung",
- "chat": "Chat",
- "assistant": "Assistent"
- },
- "pois": {
- "address": "Adresse",
- "all_types": "Alle Typen",
- "destination": "Ziel",
- "details": "Details",
- "detail_not_found": "POI nicht gefunden",
- "detail_not_found_description": "Der angeforderte POI konnte nicht geladen werden.",
- "detail_title": "POI-Details",
- "empty": "Keine POIs gefunden",
- "empty_description": "Für Ihre Abteilung sind noch keine Sonderziele verfügbar.",
- "empty_filtered": "Keine passenden POIs",
- "empty_filtered_description": "Löschen Sie die Suche oder wählen Sie einen anderen POI-Typ.",
- "filter_by_type": "Nach POI-Typ filtern",
- "invalid_poi": "Ungültiger POI",
- "invalid_poi_description": "Die ausgewählte POI-Kennung ist ungültig.",
- "loading": "POIs werden geladen...",
- "loading_detail": "POI-Details werden geladen...",
+ "personnel": "Personal",
"map": "Karte",
- "no_location": "Kein Standort verfügbar",
- "no_location_description": "Dieser POI besitzt keine verwendbaren Koordinaten.",
- "no_location_for_routing": "Keine Standortdaten für die Routenplanung verfügbar",
- "note": "Notiz",
- "route_error": "Kartenanwendung konnte nicht geöffnet werden",
- "search": "POIs suchen...",
- "sort": "Sortieren",
- "sort_options": {
- "address-asc": "Adresse",
- "name-asc": "Name (A–Z)",
- "name-desc": "Name (Z–A)",
- "type-asc": "Typ"
+ "notes": "Notizen",
+ "activity_log": "Aktivitätsprotokoll",
+ "communications": "Kommunikation",
+ "no_active_calls": "Keine aktiven Einsätze",
+ "no_units": "Keine Einheiten verfügbar",
+ "no_personnel": "Kein Personal verfügbar",
+ "no_notes": "Keine Notizen verfügbar",
+ "no_activity": "Keine aktuelle Aktivität",
+ "current_channel": "Aktueller Kanal",
+ "audio_stream": "Audiostream",
+ "no_stream": "Kein Stream aktiv",
+ "ptt": "PTT",
+ "ptt_start": "PTT Start",
+ "ptt_end": "PTT Ende",
+ "transmitting_on": "Senden auf {{channel}}",
+ "transmission_ended": "Übertragung beendet",
+ "voice_disabled": "Sprache deaktiviert",
+ "disconnected": "Getrennt",
+ "select_channel": "Kanal auswählen",
+ "select_channel_description": "Wählen Sie einen Sprachkanal zum Verbinden",
+ "change_channel_warning": "Das Auswählen eines neuen Kanals trennt die Verbindung zum aktuellen Kanal",
+ "default_channel": "Standard",
+ "no_channels_available": "Keine Sprachkanäle verfügbar",
+ "system_update": "Systemaktualisierung",
+ "data_refreshed": "Daten vom Server aktualisiert",
+ "call_selected": "Einsatz ausgewählt",
+ "unit_selected": "Einheit ausgewählt",
+ "unit_deselected": "Einheit abgewählt",
+ "personnel_selected": "Personal ausgewählt",
+ "personnel_deselected": "Personal abgewählt",
+ "loading_map": "Karte wird geladen...",
+ "map_not_available_web": "Karte auf der Webplattform nicht verfügbar",
+ "filtering_by_call": "Filterung nach Einsatz",
+ "clear_filter": "Filter löschen",
+ "call_filter_active": "Einsatzfilter aktiv",
+ "call_filter_cleared": "Einsatzfilter gelöscht",
+ "showing_all_data": "Alle Daten werden angezeigt",
+ "call_notes": "Einsatznotizen",
+ "no_call_notes": "Keine Einsatznotizen",
+ "add_call_note_placeholder": "Notiz hinzufügen...",
+ "note_added": "Notiz hinzugefügt",
+ "note_added_to_console": "Eine neue Notiz wurde zur Konsole hinzugefügt",
+ "add_note_title": "Neue Notiz hinzufügen",
+ "note_title_label": "Titel",
+ "note_title_placeholder": "Notiztitel eingeben...",
+ "note_category_label": "Kategorie",
+ "note_category_placeholder": "Kategorie auswählen",
+ "note_no_category": "Keine Kategorie",
+ "note_body_label": "Notizinhalt",
+ "note_body_placeholder": "Notizinhalt eingeben...",
+ "note_save_error": "Notiz konnte nicht gespeichert werden: {{error}}",
+ "note_created": "Notiz erstellt",
+ "units_on_call": "Einheiten im Einsatz",
+ "no_units_on_call": "Keine Einheiten im Einsatz",
+ "personnel_on_call": "Personal im Einsatz",
+ "no_personnel_on_call": "Kein Personal im Einsatz",
+ "call_activity": "Einsatzaktivität",
+ "no_call_activity": "Keine Einsatzaktivität",
+ "on_call": "Im Einsatz",
+ "filtered": "Gefiltert",
+ "active_filter": "Aktiver Filter",
+ "unit_status_change": "Statusänderung der Einheit",
+ "personnel_status_change": "Statusänderung des Personals",
+ "view_call_details": "Einsatzdetails anzeigen",
+ "dispatched_resources": "Disponiert",
+ "unassigned": "Nicht zugewiesen",
+ "available": "Verfügbar",
+ "unknown": "Unbekannt",
+ "search_personnel_placeholder": "Personal durchsuchen...",
+ "search_calls_placeholder": "Einsätze durchsuchen...",
+ "search_units_placeholder": "Einheiten durchsuchen...",
+ "search_notes_placeholder": "Notizen durchsuchen...",
+ "signalr_update": "Echtzeit-Aktualisierung",
+ "signalr_connected": "Verbunden",
+ "realtime_updates_active": "Echtzeit-Aktualisierungen sind jetzt aktiv",
+ "personnel_status_updated": "Personalstatus aktualisiert",
+ "personnel_staffing_updated": "Personalbesetzung aktualisiert",
+ "unit_status_updated": "Einheitenstatus aktualisiert",
+ "calls_updated": "Einsätze aktualisiert",
+ "call_added": "Neuer Einsatz hinzugefügt",
+ "call_closed": "Einsatz geschlossen",
+ "check_ins": "Kontrollen",
+ "no_check_ins": "Keine Einsätze mit Kontroll-Timern",
+ "radio_log": "Funkprotokoll",
+ "radio": "Funk",
+ "activity": "Aktivität",
+ "actions": "Aktionen",
+ "no_radio_activity": "Keine Funkübertragungen",
+ "live": "LIVE",
+ "currently_transmitting": "Sendet gerade...",
+ "duration": "Dauer",
+ "call_actions": "Einsatzaktionen",
+ "unit_actions": "Einheitenaktionen",
+ "personnel_actions": {
+ "title": "Personalaktionen",
+ "status_tab": "Status",
+ "staffing_tab": "Besetzung",
+ "select_status": "Status auswählen",
+ "select_staffing": "Besetzungsstufe auswählen",
+ "destination": "Ziel",
+ "no_destination": "Kein Ziel",
+ "note": "Notiz",
+ "note_placeholder": "Optionale Notiz hinzufügen...",
+ "update_status": "Status aktualisieren",
+ "update_staffing": "Besetzung aktualisieren",
+ "no_statuses_available": "Keine Status verfügbar",
+ "no_staffings_available": "Keine Besetzungsstufen verfügbar"
},
- "title": "POIs",
- "type": "Typ",
- "unknown_type": "Unbekannter Typ",
- "unnamed": "Unbenannter POI"
+ "unit_actions_panel": {
+ "status": "Status",
+ "select_status": "Status auswählen",
+ "destination": "Ziel",
+ "no_destination": "Kein Ziel",
+ "note": "Notiz",
+ "note_placeholder": "Optionale Notiz hinzufügen...",
+ "update_status": "Status aktualisieren",
+ "no_statuses_available": "Keine Status verfügbar",
+ "no_active_calls": "Keine aktiven Einsätze",
+ "no_stations_available": "Keine Wachen verfügbar",
+ "no_destinations_available": "Keine Ziele verfügbar"
+ },
+ "call": "Einsatz",
+ "station": "Wache",
+ "calls": "Einsätze",
+ "stations": "Wachen",
+ "no_stations_available": "Keine Wachen verfügbar",
+ "new_call": "Neuer Einsatz",
+ "view_details": "Details",
+ "add_note": "Notiz hinzufügen",
+ "close_call": "Schließen",
+ "set_status": "Status setzen",
+ "set_staffing": "Besetzung",
+ "dispatch": "Disponieren",
+ "select_items_for_actions": "Wählen Sie einen Einsatz, eine Einheit oder Personal aus, um kontextbezogene Aktionen zu aktivieren",
+ "weather": {
+ "clear": "Klar",
+ "mainly_clear": "Überwiegend klar",
+ "partly_cloudy": "Teilweise bewölkt",
+ "overcast": "Bedeckt",
+ "fog": "Nebel",
+ "drizzle": "Nieselregen",
+ "freezing_drizzle": "Gefrierender Nieselregen",
+ "rain": "Regen",
+ "freezing_rain": "Gefrierender Regen",
+ "snow": "Schnee",
+ "rain_showers": "Regenschauer",
+ "snow_showers": "Schneeschauer",
+ "thunderstorm": "Gewitter",
+ "thunderstorm_hail": "Gewitter mit Hagel",
+ "unknown": "Unbekannt"
+ },
+ "available_only": "Nur verfügbare",
+ "single_list": "Einzelne Liste",
+ "resources": "Ressourcen",
+ "search_resources_placeholder": "Ressourcen suchen...",
+ "no_resources": "Keine Ressourcen"
},
- "scheduled_calls": {
- "title": "Geplante Einsätze",
- "loading": "Geplante Einsätze werden geladen...",
- "no_scheduled_calls": "Keine geplanten Einsätze",
- "no_scheduled_calls_description": "Derzeit stehen keine geplanten Einsätze aus.",
- "search": "Geplante Einsätze suchen...",
- "scheduled_for": "Geplant für",
- "table_number": "Einsatz-Nr.",
- "table_name": "Name",
- "table_type": "Typ",
- "table_priority": "Priorität",
- "table_address": "Adresse",
- "table_scheduled": "Geplant für"
+ "form": {
+ "invalid_url": "Bitte geben Sie eine gültige URL ein, die mit http:// oder https:// beginnt",
+ "required": "Dieses Feld ist erforderlich"
+ },
+ "incident_command": {
+ "accountability": "Personalübersicht (PAR)",
+ "acknowledge": "Bestätigen",
+ "action_plan": "Aktionsplan",
+ "action_plan_placeholder": "Einsatzaktionsplan beschreiben...",
+ "active": "Aktiv",
+ "active_title": "Aktive Einsatzleitungen",
+ "add": "Hinzufügen",
+ "add_channel": "Kanal hinzufügen",
+ "add_lane": "Bereich hinzufügen",
+ "add_marker": "Markierung hinzufügen",
+ "add_objective": "Ziel hinzufügen",
+ "annotations": "Kartenmarkierungen",
+ "assign": "Zuweisen",
+ "assign_resource": "Ressource zuweisen",
+ "assign_resource_required": "Bereich und Ressource auswählen",
+ "assign_role": "Rolle zuweisen",
+ "assign_role_required": "Person und Rolle auswählen",
+ "call": "Einsatz",
+ "channel_name": "Kanalname",
+ "chat": "Chat",
+ "chat_frozen": "This incident is closed. Conversations are kept as a point-in-time record — no new messages.",
+ "close_all_channels": "Alle Kanäle schließen",
+ "close_command": "Einsatzleitung schließen",
+ "closed": "Geschlossen",
+ "command_channel": "Command chat",
+ "command_channel_hint": "Command staff and dispatch",
+ "command_channel_unavailable": "No command channel has been created for this incident yet.",
+ "commander": "Einsatzleiter",
+ "complete": "Abschließen",
+ "completed": "Abgeschlossen",
+ "confirm_close": "Einsatzleitung für diesen Einsatz schließen?",
+ "critical": "Kritisch",
+ "delete_annotation_confirm": "Diese Markierung entfernen?",
+ "dispatch_channel": "Dispatch",
+ "dispatch_channel_hint": "The incident's line to the desk",
+ "dispatch_channel_unavailable": "No dispatch channel has been created for this incident yet.",
+ "dm_failed": "Couldn't open that conversation.",
+ "dm_unavailable": "That contact has no Resgrid account to message.",
+ "due": "Fällig",
+ "edit": "Bearbeiten",
+ "edit_action_plan": "Aktionsplan bearbeiten",
+ "establish": "Einsatzleitung einrichten",
+ "establish_description": "Die Führungsübersicht kann optional aus einer Vorlage erstellt werden.",
+ "establish_error": "Einsatzleitung konnte nicht eingerichtet werden",
+ "establish_success": "Einsatzleitung eingerichtet",
+ "establish_title": "Einsatzleitung einrichten",
+ "established_on": "Eingerichtet",
+ "green": "Grün",
+ "hold_to_talk": "Zum Sprechen gedrückt halten",
+ "incident_channel": "Incident chat",
+ "incident_channel_hint": "Everyone working the incident",
+ "incident_channel_unavailable": "No incident channel has been created for this call yet.",
+ "join": "Beitreten",
+ "lane": "Bereich",
+ "lane_name": "Bereichsname",
+ "lane_type": "Bereichstyp",
+ "marker": "Markierung",
+ "marker_label": "Beschriftung der Markierung",
+ "move": "Verschieben",
+ "move_lane": "Bereich verschieben",
+ "move_resource": "Ressource verschieben",
+ "name_required": "Name ist erforderlich",
+ "no_accountability": "Kein Personal erfasst.",
+ "no_action_plan": "Kein Aktionsplan festgelegt.",
+ "no_active": "Keine aktiven Einsatzleitungen",
+ "no_active_description": "Für Einsätze eingerichtete Einsatzleitungen werden hier angezeigt.",
+ "no_annotations": "Keine Markierungen.",
+ "no_channels": "Keine offenen Kanäle.",
+ "no_command": "Keine Einsatzleitung eingerichtet",
+ "no_command_description": "Richten Sie eine Einsatzleitung ein, um Ressourcen, Rollen, Ziele und die Personalübersicht für diesen Einsatz zu koordinieren.",
+ "no_lanes": "Keine Bereiche definiert.",
+ "no_objectives": "Keine Ziele.",
+ "no_resources": "Keine Ressourcen zugewiesen.",
+ "no_roles": "Keine Rollen zugewiesen.",
+ "no_template": "Keine Vorlage (leere Übersicht)",
+ "no_timeline": "Keine Verlaufseinträge.",
+ "no_timers": "Keine laufenden Timer.",
+ "not_authorized": "Command board unavailable",
+ "not_authorized_description": "Your department hasn't authorized you to work incident command. Ask an administrator for the Command App Login permission.",
+ "objective_name": "Ziel",
+ "objective_type": "Typ",
+ "objectives": "Ziele",
+ "open_chat": "Open",
+ "open_full_board": "Gesamte Übersicht öffnen",
+ "open_tactical_map": "Taktische Karte öffnen",
+ "parent_lane": "Übergeordneter Bereich",
+ "person": "Person",
+ "personnel": "Personal",
+ "release": "Freigeben",
+ "resource": "Ressource",
+ "resource_type": "Ressourcentyp",
+ "role": "Rolle",
+ "roles": "Leitungsrollen",
+ "run_par": "PAR durchführen",
+ "save": "Speichern",
+ "save_error": "Vorgang fehlgeschlagen",
+ "saved": "Gespeichert",
+ "select_lane": "Bereich auswählen",
+ "select_person": "Person auswählen",
+ "select_resource": "Ressource auswählen",
+ "select_role": "Rolle auswählen",
+ "send_message": "Message",
+ "status": "Status",
+ "structure": "Leitungsstruktur",
+ "tab_title": "Leitung",
+ "tactical_map": "Taktische Karte",
+ "talking": "Übertragung...",
+ "tap_to_place": "Tippen Sie auf die Karte, um eine Markierung zu setzen",
+ "template": "Vorlage",
+ "timeline": "Leitungsverlauf",
+ "timers": "Timer",
+ "title": "Einsatzleitung",
+ "top_level": "Oberste Ebene",
+ "transfer": "Übertragen",
+ "transfer_command": "Übertragen",
+ "transfer_notes": "Notizen",
+ "transfer_success": "Einsatzleitung übertragen",
+ "transfer_title": "Einsatzleitung übertragen",
+ "unassigned": "Nicht zugewiesen",
+ "unit": "Einheit",
+ "voice_channels": "Sprachkanäle",
+ "voice_join_error": "Beitritt zum Sprachkanal fehlgeschlagen",
+ "voice_joined": "Sprachkanal beigetreten",
+ "warning": "Warnung"
+ },
+ "livekit": {
+ "audio_devices": "Audiogeräte",
+ "audio_settings": "Audioeinstellungen",
+ "connected_to_room": "Mit Kanal verbunden",
+ "connecting": "Verbindung wird hergestellt...",
+ "disconnect": "Trennen",
+ "join": "Beitreten",
+ "microphone": "Mikrofon",
+ "mute": "Stummschalten",
+ "no_rooms_available": "Keine Sprachkanäle verfügbar",
+ "speaker": "Lautsprecher",
+ "speaking": "Spricht",
+ "title": "Sprachkanäle",
+ "unmute": "Stummschaltung aufheben"
+ },
+ "loading": {
+ "loading": "Laden...",
+ "loadingData": "Daten werden geladen...",
+ "pleaseWait": "Bitte warten",
+ "processingRequest": "Ihre Anfrage wird verarbeitet..."
+ },
+ "lockscreen": {
+ "message": "Geben Sie Ihr Passwort ein, um den Bildschirm zu entsperren",
+ "not_you": "Nicht Sie? Zurück zur Anmeldung",
+ "password": "Passwort",
+ "password_placeholder": "Passwort eingeben",
+ "title": "Bildschirmsperre",
+ "unlock_button": "Entsperren",
+ "unlock_failed": "Entsperren fehlgeschlagen. Bitte versuchen Sie es erneut.",
+ "unlocking": "Wird entsperrt...",
+ "welcome_back": "Willkommen zurück",
+ "relogin_required": "Die Passwortüberprüfung ist für diese Sitzung nicht verfügbar. Bitte melde dich erneut an."
+ },
+ "login": {
+ "branding_subtitle": "Leistungsstarke Leitstellensoftware für Feuerwehren, Rettungsdienste und Organisationen der öffentlichen Sicherheit.",
+ "branding_title": "Einsatzmanagement",
+ "dispatch_not_authorized": "You are not authorized to use the Dispatch app. Contact your department administrator.",
+ "errorModal": {
+ "confirmButton": "OK",
+ "message": "Bitte überprüfen Sie Ihren Benutzernamen und Ihr Passwort und versuchen Sie es erneut.",
+ "title": "Anmeldung fehlgeschlagen"
+ },
+ "feature_dispatch_desc": "Disponieren Sie Einheiten sofort und verwalten Sie Einsätze mit Live-Aktualisierungen auf allen Geräten.",
+ "feature_dispatch_title": "Echtzeit-Disposition",
+ "feature_mapping_desc": "Verfolgen Sie Einheiten in Echtzeit mit detaillierten Karten, Routenführung und Standortverwaltung.",
+ "feature_mapping_title": "Erweiterte Kartendarstellung",
+ "feature_personnel_desc": "Verwalten Sie Ihr Team mit rollenbasiertem Zugriff, Statusverfolgung und Kommunikationswerkzeugen.",
+ "feature_personnel_title": "Personalverwaltung",
+ "footer_text": "Erstellt mit ❤️ in Lake Tahoe",
+ "login": "Anmelden",
+ "login_button": "Anmelden",
+ "login_button_description": "Melden Sie sich bei Ihrem Konto an, um fortzufahren",
+ "login_button_error": "Fehler bei der Anmeldung",
+ "login_button_loading": "Anmeldung läuft...",
+ "login_button_success": "Erfolgreich angemeldet",
+ "no_account": "Noch kein Konto?",
+ "page_subtitle": "Geben Sie Ihre Anmeldedaten ein.",
+ "page_title": "Resgrid Dispatch",
+ "password": "Passwort",
+ "password_incorrect": "Passwort war falsch",
+ "password_placeholder": "Passwort eingeben",
+ "register": "Registrieren",
+ "title": "Anmeldung",
+ "username": "Benutzername",
+ "username_placeholder": "Benutzernamen eingeben",
+ "welcome_title": "Willkommen zurück"
+ },
+ "maintenance": {
+ "downtime_message": "Wir arbeiten daran, die Wartung so schnell wie möglich abzuschließen. Bitte schauen Sie bald wieder vorbei.",
+ "downtime_title": "Wie lange dauert die Ausfallzeit?",
+ "message": "Bitte schauen Sie später wieder vorbei.",
+ "support_message": "Wenn Sie Hilfe benötigen, kontaktieren Sie uns unter",
+ "support_title": "Benötigen Sie Unterstützung?",
+ "title": "Wartungsarbeiten",
+ "why_down_message": "Wir führen geplante Wartungsarbeiten durch, um Ihre Erfahrung zu verbessern. Wir entschuldigen uns für etwaige Unannehmlichkeiten.",
+ "why_down_title": "Warum ist die Seite nicht erreichbar?"
},
"map": {
"view_poi_details": "POI-Details anzeigen",
@@ -690,6 +1009,26 @@
"hide_all": "Alle ausblenden",
"view_call_details": "Einsatzdetails anzeigen"
},
+ "menu": {
+ "scheduled_calls": "Geplante Einsätze",
+ "pois": "POIs",
+ "calls": "Einsätze",
+ "calls_list": "Einsatzliste",
+ "contacts": "Kontakte",
+ "home": "Startseite",
+ "map": "Karte",
+ "menu": "Menü",
+ "messages": "Nachrichten",
+ "new_call": "Neuer Einsatz",
+ "personnel": "Personal",
+ "protocols": "Protokolle",
+ "settings": "Einstellungen",
+ "units": "Einheiten",
+ "weatherAlerts": "Wetterwarnungen",
+ "incident_command": "Einsatzleitung",
+ "chat": "Chat",
+ "assistant": "Assistent"
+ },
"notes": {
"actions": {
"add": "Notiz hinzufügen",
@@ -709,6 +1048,23 @@
"search": "Notizen durchsuchen...",
"title": "Notizen"
},
+ "onboarding": {
+ "screen1": {
+ "title": "Resgrid Dispatch",
+ "description": "Erstellen, disponieren und verwalten Sie Einsätze mit einer leistungsstarken mobilen Leitstelle direkt in Ihrer Hand"
+ },
+ "screen2": {
+ "title": "Echtzeit-Lagebild",
+ "description": "Verfolgen Sie alle Einheiten, Einsatzkräfte und Ressourcen auf einer interaktiven Karte mit Live-Statusaktualisierungen und AVL"
+ },
+ "screen3": {
+ "title": "Nahtlose Koordination",
+ "description": "Kommunizieren Sie sofort mit Einheiten im Feld, aktualisieren Sie den Einsatzstatus und koordinieren Sie den Einsatz von überall aus"
+ },
+ "skip": "Überspringen",
+ "next": "Weiter",
+ "getStarted": "Los geht's"
+ },
"personnel": {
"title": "Personal",
"search": "Personal suchen...",
@@ -741,22 +1097,41 @@
"send_email": "E-Mail",
"custom_fields": "Zusätzliche Informationen"
},
- "onboarding": {
- "screen1": {
- "title": "Resgrid Dispatch",
- "description": "Erstellen, disponieren und verwalten Sie Einsätze mit einer leistungsstarken mobilen Leitstelle direkt in Ihrer Hand"
- },
- "screen2": {
- "title": "Echtzeit-Lagebild",
- "description": "Verfolgen Sie alle Einheiten, Einsatzkräfte und Ressourcen auf einer interaktiven Karte mit Live-Statusaktualisierungen und AVL"
- },
- "screen3": {
- "title": "Nahtlose Koordination",
- "description": "Kommunizieren Sie sofort mit Einheiten im Feld, aktualisieren Sie den Einsatzstatus und koordinieren Sie den Einsatz von überall aus"
+ "pois": {
+ "address": "Adresse",
+ "all_types": "Alle Typen",
+ "destination": "Ziel",
+ "details": "Details",
+ "detail_not_found": "POI nicht gefunden",
+ "detail_not_found_description": "Der angeforderte POI konnte nicht geladen werden.",
+ "detail_title": "POI-Details",
+ "empty": "Keine POIs gefunden",
+ "empty_description": "Für Ihre Abteilung sind noch keine Sonderziele verfügbar.",
+ "empty_filtered": "Keine passenden POIs",
+ "empty_filtered_description": "Löschen Sie die Suche oder wählen Sie einen anderen POI-Typ.",
+ "filter_by_type": "Nach POI-Typ filtern",
+ "invalid_poi": "Ungültiger POI",
+ "invalid_poi_description": "Die ausgewählte POI-Kennung ist ungültig.",
+ "loading": "POIs werden geladen...",
+ "loading_detail": "POI-Details werden geladen...",
+ "map": "Karte",
+ "no_location": "Kein Standort verfügbar",
+ "no_location_description": "Dieser POI besitzt keine verwendbaren Koordinaten.",
+ "no_location_for_routing": "Keine Standortdaten für die Routenplanung verfügbar",
+ "note": "Notiz",
+ "route_error": "Kartenanwendung konnte nicht geöffnet werden",
+ "search": "POIs suchen...",
+ "sort": "Sortieren",
+ "sort_options": {
+ "address-asc": "Adresse",
+ "name-asc": "Name (A–Z)",
+ "name-desc": "Name (Z–A)",
+ "type-asc": "Typ"
},
- "skip": "Überspringen",
- "next": "Weiter",
- "getStarted": "Los geht's"
+ "title": "POIs",
+ "type": "Typ",
+ "unknown_type": "Unbekannter Typ",
+ "unnamed": "Unbenannter POI"
},
"protocols": {
"details": {
@@ -796,6 +1171,20 @@
"tap_to_manage": "Tippen, um Rollen zu verwalten",
"unassigned": "Nicht zugewiesen"
},
+ "scheduled_calls": {
+ "title": "Geplante Einsätze",
+ "loading": "Geplante Einsätze werden geladen...",
+ "no_scheduled_calls": "Keine geplanten Einsätze",
+ "no_scheduled_calls_description": "Derzeit stehen keine geplanten Einsätze aus.",
+ "search": "Geplante Einsätze suchen...",
+ "scheduled_for": "Geplant für",
+ "table_number": "Einsatz-Nr.",
+ "table_name": "Name",
+ "table_type": "Typ",
+ "table_priority": "Priorität",
+ "table_address": "Adresse",
+ "table_scheduled": "Geplant für"
+ },
"settings": {
"about": "Über",
"account": "Konto",
@@ -884,6 +1273,29 @@
"version": "Version",
"website": "Webseite"
},
+ "sso": {
+ "authenticating": "Authentifizierung läuft...",
+ "back_to_login": "Zurück zur Anmeldung",
+ "back_to_lookup": "Benutzer wechseln",
+ "continue_button": "Weiter",
+ "department_id_label": "Abteilungs-ID",
+ "department_id_placeholder": "Abteilungs-ID eingeben",
+ "error_generic": "Anmeldung fehlgeschlagen. Bitte versuchen Sie es erneut.",
+ "error_oidc_cancelled": "Anmeldung wurde abgebrochen.",
+ "error_oidc_not_ready": "SSO-Anbieter wird geladen, bitte warten.",
+ "error_sso_not_enabled": "Single Sign-On ist für diesen Benutzer nicht aktiviert.",
+ "error_token_exchange": "Anmeldung konnte nicht abgeschlossen werden. Bitte versuchen Sie es erneut.",
+ "error_user_not_found": "Benutzer nicht gefunden. Bitte überprüfen und erneut versuchen.",
+ "looking_up": "Wird gesucht...",
+ "optional": "optional",
+ "page_subtitle": "Geben Sie Ihren Benutzernamen ein, um die Anmeldeoptionen Ihrer Organisation zu finden.",
+ "page_title": "Einmalanmeldung",
+ "provider_oidc": "OpenID Connect (OIDC)",
+ "provider_saml": "SAML 2.0",
+ "sign_in_button": "Mit SSO anmelden",
+ "sign_in_title": "Anmelden",
+ "sso_button": "SSO-Anmeldung"
+ },
"status": {
"all_destinations_enabled": "Kann auf Einsätze, Wachen oder POIs reagieren",
"calls_and_pois_destination_enabled": "Kann auf Einsätze oder POIs reagieren",
@@ -916,173 +1328,6 @@
"stations_tab": "Wachen",
"status_saved_successfully": "Status erfolgreich gespeichert!"
},
- "dispatch": {
- "active_calls": "Aktive Einsätze",
- "pending_calls": "Ausstehend",
- "scheduled_calls": "Geplant",
- "units_available": "Verfügbar",
- "personnel_available": "Verfügbar",
- "personnel_on_duty": "Im Dienst",
- "units": "Einheiten",
- "personnel": "Personal",
- "map": "Karte",
- "notes": "Notizen",
- "activity_log": "Aktivitätsprotokoll",
- "communications": "Kommunikation",
- "no_active_calls": "Keine aktiven Einsätze",
- "no_units": "Keine Einheiten verfügbar",
- "no_personnel": "Kein Personal verfügbar",
- "no_notes": "Keine Notizen verfügbar",
- "no_activity": "Keine aktuelle Aktivität",
- "current_channel": "Aktueller Kanal",
- "audio_stream": "Audiostream",
- "no_stream": "Kein Stream aktiv",
- "ptt": "PTT",
- "ptt_start": "PTT Start",
- "ptt_end": "PTT Ende",
- "transmitting_on": "Senden auf {{channel}}",
- "transmission_ended": "Übertragung beendet",
- "voice_disabled": "Sprache deaktiviert",
- "disconnected": "Getrennt",
- "select_channel": "Kanal auswählen",
- "select_channel_description": "Wählen Sie einen Sprachkanal zum Verbinden",
- "change_channel_warning": "Das Auswählen eines neuen Kanals trennt die Verbindung zum aktuellen Kanal",
- "default_channel": "Standard",
- "no_channels_available": "Keine Sprachkanäle verfügbar",
- "system_update": "Systemaktualisierung",
- "data_refreshed": "Daten vom Server aktualisiert",
- "call_selected": "Einsatz ausgewählt",
- "unit_selected": "Einheit ausgewählt",
- "unit_deselected": "Einheit abgewählt",
- "personnel_selected": "Personal ausgewählt",
- "personnel_deselected": "Personal abgewählt",
- "loading_map": "Karte wird geladen...",
- "map_not_available_web": "Karte auf der Webplattform nicht verfügbar",
- "filtering_by_call": "Filterung nach Einsatz",
- "clear_filter": "Filter löschen",
- "call_filter_active": "Einsatzfilter aktiv",
- "call_filter_cleared": "Einsatzfilter gelöscht",
- "showing_all_data": "Alle Daten werden angezeigt",
- "call_notes": "Einsatznotizen",
- "no_call_notes": "Keine Einsatznotizen",
- "add_call_note_placeholder": "Notiz hinzufügen...",
- "note_added": "Notiz hinzugefügt",
- "note_added_to_console": "Eine neue Notiz wurde zur Konsole hinzugefügt",
- "add_note_title": "Neue Notiz hinzufügen",
- "note_title_label": "Titel",
- "note_title_placeholder": "Notiztitel eingeben...",
- "note_category_label": "Kategorie",
- "note_category_placeholder": "Kategorie auswählen",
- "note_no_category": "Keine Kategorie",
- "note_body_label": "Notizinhalt",
- "note_body_placeholder": "Notizinhalt eingeben...",
- "note_save_error": "Notiz konnte nicht gespeichert werden: {{error}}",
- "note_created": "Notiz erstellt",
- "units_on_call": "Einheiten im Einsatz",
- "no_units_on_call": "Keine Einheiten im Einsatz",
- "personnel_on_call": "Personal im Einsatz",
- "no_personnel_on_call": "Kein Personal im Einsatz",
- "call_activity": "Einsatzaktivität",
- "no_call_activity": "Keine Einsatzaktivität",
- "on_call": "Im Einsatz",
- "filtered": "Gefiltert",
- "active_filter": "Aktiver Filter",
- "unit_status_change": "Statusänderung der Einheit",
- "personnel_status_change": "Statusänderung des Personals",
- "view_call_details": "Einsatzdetails anzeigen",
- "dispatched_resources": "Disponiert",
- "unassigned": "Nicht zugewiesen",
- "available": "Verfügbar",
- "unknown": "Unbekannt",
- "search_personnel_placeholder": "Personal durchsuchen...",
- "search_calls_placeholder": "Einsätze durchsuchen...",
- "search_units_placeholder": "Einheiten durchsuchen...",
- "search_notes_placeholder": "Notizen durchsuchen...",
- "signalr_update": "Echtzeit-Aktualisierung",
- "signalr_connected": "Verbunden",
- "realtime_updates_active": "Echtzeit-Aktualisierungen sind jetzt aktiv",
- "personnel_status_updated": "Personalstatus aktualisiert",
- "personnel_staffing_updated": "Personalbesetzung aktualisiert",
- "unit_status_updated": "Einheitenstatus aktualisiert",
- "calls_updated": "Einsätze aktualisiert",
- "call_added": "Neuer Einsatz hinzugefügt",
- "call_closed": "Einsatz geschlossen",
- "check_ins": "Kontrollen",
- "no_check_ins": "Keine Einsätze mit Kontroll-Timern",
- "radio_log": "Funkprotokoll",
- "radio": "Funk",
- "activity": "Aktivität",
- "actions": "Aktionen",
- "no_radio_activity": "Keine Funkübertragungen",
- "live": "LIVE",
- "currently_transmitting": "Sendet gerade...",
- "duration": "Dauer",
- "call_actions": "Einsatzaktionen",
- "unit_actions": "Einheitenaktionen",
- "personnel_actions": {
- "title": "Personalaktionen",
- "status_tab": "Status",
- "staffing_tab": "Besetzung",
- "select_status": "Status auswählen",
- "select_staffing": "Besetzungsstufe auswählen",
- "destination": "Ziel",
- "no_destination": "Kein Ziel",
- "note": "Notiz",
- "note_placeholder": "Optionale Notiz hinzufügen...",
- "update_status": "Status aktualisieren",
- "update_staffing": "Besetzung aktualisieren",
- "no_statuses_available": "Keine Status verfügbar",
- "no_staffings_available": "Keine Besetzungsstufen verfügbar"
- },
- "unit_actions_panel": {
- "status": "Status",
- "select_status": "Status auswählen",
- "destination": "Ziel",
- "no_destination": "Kein Ziel",
- "note": "Notiz",
- "note_placeholder": "Optionale Notiz hinzufügen...",
- "update_status": "Status aktualisieren",
- "no_statuses_available": "Keine Status verfügbar",
- "no_active_calls": "Keine aktiven Einsätze",
- "no_stations_available": "Keine Wachen verfügbar",
- "no_destinations_available": "Keine Ziele verfügbar"
- },
- "call": "Einsatz",
- "station": "Wache",
- "calls": "Einsätze",
- "stations": "Wachen",
- "no_stations_available": "Keine Wachen verfügbar",
- "new_call": "Neuer Einsatz",
- "view_details": "Details",
- "add_note": "Notiz hinzufügen",
- "close_call": "Schließen",
- "set_status": "Status setzen",
- "set_staffing": "Besetzung",
- "dispatch": "Disponieren",
- "select_items_for_actions": "Wählen Sie einen Einsatz, eine Einheit oder Personal aus, um kontextbezogene Aktionen zu aktivieren",
- "weather": {
- "clear": "Klar",
- "mainly_clear": "Überwiegend klar",
- "partly_cloudy": "Teilweise bewölkt",
- "overcast": "Bedeckt",
- "fog": "Nebel",
- "drizzle": "Nieselregen",
- "freezing_drizzle": "Gefrierender Nieselregen",
- "rain": "Regen",
- "freezing_rain": "Gefrierender Regen",
- "snow": "Schnee",
- "rain_showers": "Regenschauer",
- "snow_showers": "Schneeschauer",
- "thunderstorm": "Gewitter",
- "thunderstorm_hail": "Gewitter mit Hagel",
- "unknown": "Unbekannt"
- },
- "available_only": "Nur verfügbare",
- "single_list": "Einzelne Liste",
- "resources": "Ressourcen",
- "search_resources_placeholder": "Ressourcen suchen...",
- "no_resources": "Keine Ressourcen"
- },
"tabs": {
"calls": "Einsätze",
"calendar": "Kalender",
@@ -1096,44 +1341,6 @@
"shifts": "Schichten",
"personnel": "Personal"
},
- "check_in": {
- "tab_title": "Kontrolle",
- "timer_status": "Timer-Status",
- "perform_check_in": "Kontrolle durchführen",
- "check_in_success": "Kontrolle erfolgreich erfasst",
- "check_in_error": "Kontrolle konnte nicht erfasst werden",
- "checked_in_by": "von {{name}}",
- "last_check_in": "Letzte Kontrolle",
- "elapsed": "Vergangen",
- "duration": "Dauer",
- "status_ok": "OK",
- "status_green": "OK",
- "status_warning": "Warnung",
- "status_yellow": "Warnung",
- "status_overdue": "Überfällig",
- "status_red": "Überfällig",
- "status_critical": "Kritisch",
- "history": "Kontrollverlauf",
- "no_timers": "Keine Kontroll-Timer konfiguriert",
- "timers_disabled": "Kontroll-Timer sind für diesen Einsatz deaktiviert",
- "type_personnel": "Personal",
- "type_unit": "Einheit",
- "type_ic": "Einsatzleiter",
- "type_par": "PAR",
- "type_hazmat": "Gefahrstoffexposition",
- "type_sector_rotation": "Abschnittsrotation",
- "type_rehab": "Erholung",
- "add_note": "Notiz hinzufügen (optional)",
- "confirm": "Kontrolle bestätigen",
- "minutes_ago": "vor {{count}} Min.",
- "select_target": "Einheit für Kontrolle auswählen",
- "overdue_count": "{{count}} Überfällig",
- "warning_count": "{{count}} Warnung",
- "enable_timers": "Timer aktivieren",
- "disable_timers": "Timer deaktivieren",
- "summary": "{{overdue}} überfällig, {{warning}} Warnung, {{ok}} ok",
- "par_title": "Personalübersicht (PAR)"
- },
"units": {
"search": "Einheiten suchen...",
"loading": "Einheiten werden geladen...",
@@ -1163,6 +1370,63 @@
"no_destination": "Keines",
"title": "Einheiten"
},
+ "videoFeeds": {
+ "title": "Videoübertragungen",
+ "noFeeds": "Keine Videoübertragungen für diesen Einsatz",
+ "addFeed": "Videoübertragung hinzufügen",
+ "editFeed": "Videoübertragung bearbeiten",
+ "deleteFeed": "Videoübertragung löschen",
+ "deleteConfirm": "Sind Sie sicher, dass Sie diese Videoübertragung entfernen möchten?",
+ "watch": "Ansehen",
+ "goLive": "Live gehen",
+ "stopLive": "Live beenden",
+ "flipCamera": "Kamera wechseln",
+ "feedAdded": "Videoübertragung hinzugefügt",
+ "feedUpdated": "Videoübertragung aktualisiert",
+ "feedDeleted": "Videoübertragung entfernt",
+ "feedError": "Videoübertragung konnte nicht geladen werden",
+ "unsupportedFormat": "Dieses Streamformat wird auf Mobilgeräten nicht unterstützt",
+ "copyUrl": "URL kopieren",
+ "form": {
+ "name": "Name der Übertragung",
+ "namePlaceholder": "z. B. Löschfahrzeug 1 Drohne",
+ "url": "Stream-URL",
+ "urlPlaceholder": "z. B. https://stream.example.com/live.m3u8",
+ "feedType": "Kameratyp",
+ "feedFormat": "Streamformat",
+ "description": "Beschreibung",
+ "descriptionPlaceholder": "Optionale Beschreibung",
+ "status": "Status",
+ "sortOrder": "Sortierreihenfolge",
+ "cameraLocation": "Kamerastandort",
+ "useCurrentLocation": "Aktuellen Standort verwenden"
+ },
+ "type": {
+ "drone": "Drohne",
+ "fixedCamera": "Festkamera",
+ "bodyCam": "Körperkamera",
+ "trafficCam": "Verkehrskamera",
+ "weatherCam": "Wetterkamera",
+ "satelliteFeed": "Satellitenübertragung",
+ "webCam": "Webcam",
+ "other": "Sonstige"
+ },
+ "format": {
+ "rtsp": "RTSP",
+ "hls": "HLS",
+ "mjpeg": "MJPEG",
+ "youtubeLive": "YouTube Live",
+ "webrtc": "WebRTC",
+ "dash": "DASH",
+ "embed": "Einbetten",
+ "other": "Sonstige"
+ },
+ "status": {
+ "active": "Aktiv",
+ "inactive": "Inaktiv",
+ "error": "Fehler"
+ }
+ },
"weatherAlerts": {
"title": "Wetterwarnungen",
"activeAlerts": "Aktive Warnungen",
@@ -1270,251 +1534,5 @@
},
"stats_label": "Wetterwarnungen"
},
- "videoFeeds": {
- "title": "Videoübertragungen",
- "noFeeds": "Keine Videoübertragungen für diesen Einsatz",
- "addFeed": "Videoübertragung hinzufügen",
- "editFeed": "Videoübertragung bearbeiten",
- "deleteFeed": "Videoübertragung löschen",
- "deleteConfirm": "Sind Sie sicher, dass Sie diese Videoübertragung entfernen möchten?",
- "watch": "Ansehen",
- "goLive": "Live gehen",
- "stopLive": "Live beenden",
- "flipCamera": "Kamera wechseln",
- "feedAdded": "Videoübertragung hinzugefügt",
- "feedUpdated": "Videoübertragung aktualisiert",
- "feedDeleted": "Videoübertragung entfernt",
- "feedError": "Videoübertragung konnte nicht geladen werden",
- "unsupportedFormat": "Dieses Streamformat wird auf Mobilgeräten nicht unterstützt",
- "copyUrl": "URL kopieren",
- "form": {
- "name": "Name der Übertragung",
- "namePlaceholder": "z. B. Löschfahrzeug 1 Drohne",
- "url": "Stream-URL",
- "urlPlaceholder": "z. B. https://stream.example.com/live.m3u8",
- "feedType": "Kameratyp",
- "feedFormat": "Streamformat",
- "description": "Beschreibung",
- "descriptionPlaceholder": "Optionale Beschreibung",
- "status": "Status",
- "sortOrder": "Sortierreihenfolge",
- "cameraLocation": "Kamerastandort",
- "useCurrentLocation": "Aktuellen Standort verwenden"
- },
- "type": {
- "drone": "Drohne",
- "fixedCamera": "Festkamera",
- "bodyCam": "Körperkamera",
- "trafficCam": "Verkehrskamera",
- "weatherCam": "Wetterkamera",
- "satelliteFeed": "Satellitenübertragung",
- "webCam": "Webcam",
- "other": "Sonstige"
- },
- "format": {
- "rtsp": "RTSP",
- "hls": "HLS",
- "mjpeg": "MJPEG",
- "youtubeLive": "YouTube Live",
- "webrtc": "WebRTC",
- "dash": "DASH",
- "embed": "Einbetten",
- "other": "Sonstige"
- },
- "status": {
- "active": "Aktiv",
- "inactive": "Inaktiv",
- "error": "Fehler"
- }
- },
- "welcome": "Willkommen bei der obytes App-Seite",
- "incident_command": {
- "tab_title": "Leitung",
- "title": "Einsatzleitung",
- "open_full_board": "Gesamte Übersicht öffnen",
- "no_command": "Keine Einsatzleitung eingerichtet",
- "no_command_description": "Richten Sie eine Einsatzleitung ein, um Ressourcen, Rollen, Ziele und die Personalübersicht für diesen Einsatz zu koordinieren.",
- "establish": "Einsatzleitung einrichten",
- "establish_title": "Einsatzleitung einrichten",
- "establish_description": "Die Führungsübersicht kann optional aus einer Vorlage erstellt werden.",
- "establish_success": "Einsatzleitung eingerichtet",
- "establish_error": "Einsatzleitung konnte nicht eingerichtet werden",
- "template": "Vorlage",
- "no_template": "Keine Vorlage (leere Übersicht)",
- "saved": "Gespeichert",
- "save_error": "Vorgang fehlgeschlagen",
- "edit_action_plan": "Aktionsplan bearbeiten",
- "action_plan": "Aktionsplan",
- "action_plan_placeholder": "Einsatzaktionsplan beschreiben...",
- "save": "Speichern",
- "no_action_plan": "Kein Aktionsplan festgelegt.",
- "add": "Hinzufügen",
- "add_objective": "Ziel hinzufügen",
- "objective_name": "Ziel",
- "objective_type": "Typ",
- "name_required": "Name ist erforderlich",
- "add_lane": "Bereich hinzufügen",
- "lane_name": "Bereichsname",
- "lane_type": "Bereichstyp",
- "assign_resource": "Ressource zuweisen",
- "assign_resource_required": "Bereich und Ressource auswählen",
- "lane": "Bereich",
- "select_lane": "Bereich auswählen",
- "resource_type": "Ressourcentyp",
- "resource": "Ressource",
- "select_resource": "Ressource auswählen",
- "unit": "Einheit",
- "personnel": "Personal",
- "assign": "Zuweisen",
- "assign_role": "Rolle zuweisen",
- "assign_role_required": "Person und Rolle auswählen",
- "person": "Person",
- "select_person": "Person auswählen",
- "role": "Rolle",
- "select_role": "Rolle auswählen",
- "transfer_title": "Einsatzleitung übertragen",
- "transfer_notes": "Notizen",
- "transfer": "Übertragen",
- "transfer_command": "Übertragen",
- "transfer_success": "Einsatzleitung übertragen",
- "close_command": "Einsatzleitung schließen",
- "confirm_close": "Einsatzleitung für diesen Einsatz schließen?",
- "status": "Status",
- "active": "Aktiv",
- "closed": "Geschlossen",
- "commander": "Einsatzleiter",
- "established_on": "Eingerichtet",
- "edit": "Bearbeiten",
- "roles": "Leitungsrollen",
- "no_roles": "Keine Rollen zugewiesen.",
- "structure": "Leitungsstruktur",
- "no_lanes": "Keine Bereiche definiert.",
- "no_resources": "Keine Ressourcen zugewiesen.",
- "release": "Freigeben",
- "objectives": "Ziele",
- "no_objectives": "Keine Ziele.",
- "complete": "Abschließen",
- "completed": "Abgeschlossen",
- "timers": "Timer",
- "no_timers": "Keine laufenden Timer.",
- "acknowledge": "Bestätigen",
- "due": "Fällig",
- "accountability": "Personalübersicht (PAR)",
- "run_par": "PAR durchführen",
- "green": "Grün",
- "warning": "Warnung",
- "critical": "Kritisch",
- "no_accountability": "Kein Personal erfasst.",
- "timeline": "Leitungsverlauf",
- "no_timeline": "Keine Verlaufseinträge.",
- "unassigned": "Nicht zugewiesen",
- "active_title": "Aktive Einsatzleitungen",
- "no_active": "Keine aktiven Einsatzleitungen",
- "no_active_description": "Für Einsätze eingerichtete Einsatzleitungen werden hier angezeigt.",
- "call": "Einsatz",
- "tactical_map": "Taktische Karte",
- "annotations": "Kartenmarkierungen",
- "no_annotations": "Keine Markierungen.",
- "open_tactical_map": "Taktische Karte öffnen",
- "marker": "Markierung",
- "add_marker": "Markierung hinzufügen",
- "tap_to_place": "Tippen Sie auf die Karte, um eine Markierung zu setzen",
- "marker_label": "Beschriftung der Markierung",
- "delete_annotation_confirm": "Diese Markierung entfernen?",
- "voice_channels": "Sprachkanäle",
- "no_channels": "Keine offenen Kanäle.",
- "add_channel": "Kanal hinzufügen",
- "channel_name": "Kanalname",
- "close_all_channels": "Alle Kanäle schließen",
- "join": "Beitreten",
- "hold_to_talk": "Zum Sprechen gedrückt halten",
- "talking": "Übertragung...",
- "voice_joined": "Sprachkanal beigetreten",
- "voice_join_error": "Beitritt zum Sprachkanal fehlgeschlagen",
- "move": "Verschieben",
- "move_lane": "Bereich verschieben",
- "parent_lane": "Übergeordneter Bereich",
- "top_level": "Oberste Ebene",
- "move_resource": "Ressource verschieben"
- },
- "chat": {
- "title": "Chat",
- "assistant": "Assistent",
- "empty": "Noch keine Unterhaltungen. Starte eine Direktnachricht oder erstelle eine Gruppe.",
- "section_assistant": "Assistent",
- "section_direct_messages": "Direktnachrichten",
- "section_channels": "Kanäle",
- "section_incidents": "Vorfälle",
- "new_direct_message": "Neue Direktnachricht",
- "new_group": "Neue Gruppe",
- "open_assistant": "Assistent öffnen",
- "create_conversation_failed": "Unterhaltung konnte nicht gestartet werden",
- "group_name": "Gruppenname",
- "search_people": "Personen suchen",
- "no_people": "Keine Personen gefunden",
- "create_group_with": "Gruppe erstellen ({{count}})",
- "message_deleted": "Diese Nachricht wurde gelöscht",
- "urgent": "Dringend",
- "urgent_will_send": "Diese Nachricht wird als dringend gesendet",
- "shared_location": "Geteilter Standort",
- "thread_replies": "{{count}} Antworten",
- "edited": "(bearbeitet)",
- "failed_tap_retry": "Fehlgeschlagen – zum Wiederholen tippen",
- "type_a_message": "Nachricht eingeben",
- "emoji": "Emoji",
- "add_image": "Bild hinzufügen",
- "add_gif": "GIF hinzufügen",
- "share_location": "Standort teilen",
- "send": "Senden",
- "someone": "Jemand",
- "is_typing": "{{name}} schreibt...",
- "are_typing": "{{count}} Personen schreiben...",
- "permission_photos_denied": "Zugriff auf die Fotomediathek verweigert",
- "permission_location_denied": "Zugriff auf den Standort verweigert",
- "search_gifs": "GIFs suchen",
- "no_gifs": "Keine GIFs gefunden",
- "flag_reason": "Warum meldest du dies?",
- "flag_inappropriate": "Unangemessen",
- "flag_harassment": "Belästigung",
- "flag_spam": "Spam",
- "flag_sensitive": "Sensible Informationen",
- "flag_policy": "Richtlinienverstoß",
- "flag_other": "Sonstiges",
- "reply_in_thread": "Im Thread antworten",
- "copy": "Kopieren",
- "copied": "Kopiert",
- "copy_unavailable": "Kopieren ist auf diesem Gerät nicht verfügbar",
- "edit": "Bearbeiten",
- "edit_message": "Nachricht bearbeiten",
- "save": "Speichern",
- "delete": "Löschen",
- "pin": "Anheften",
- "unpin": "Anheften aufheben",
- "flag": "Melden",
- "moderator_delete": "Entfernen (Moderator)",
- "moderator_removed": "Vom Moderator entfernt",
- "attachment_failed": "Anhang-Upload fehlgeschlagen",
- "ack_required": "Bestätigung erforderlich",
- "ack_pending_one": "Du hast eine dringende Nachricht zu bestätigen",
- "ack_pending_count": "Du hast {{count}} dringende Nachrichten zu bestätigen",
- "acknowledge": "Bestätigen",
- "thread": "Thread",
- "original_message": "Ursprüngliche Nachricht",
- "reply_placeholder": "Antworten...",
- "channel": "Kanal",
- "direct_message": "Direktnachricht",
- "load_people_failed": "Personen konnten nicht geladen werden",
- "reaction_failed": "Reaktion konnte nicht aktualisiert werden",
- "edit_failed": "Nachricht konnte nicht bearbeitet werden",
- "delete_failed": "Nachricht konnte nicht gelöscht werden",
- "pin_failed": "Anheften konnte nicht aktualisiert werden",
- "flag_failed": "Nachricht konnte nicht gemeldet werden"
- },
- "chatbot": {
- "title": "Assistent",
- "subtitle": "KI-Helfer für deine Abteilung",
- "new_session": "Neue Sitzung",
- "empty": "Frag den Assistenten etwas, um zu beginnen.",
- "ask_placeholder": "Frag den Assistenten..."
- }
+ "welcome": "Willkommen bei der obytes App-Seite"
}
diff --git a/src/translations/en.json b/src/translations/en.json
index e82795dc..4852e3ba 100644
--- a/src/translations/en.json
+++ b/src/translations/en.json
@@ -451,6 +451,44 @@
"empty": "Ask the assistant anything to get started.",
"ask_placeholder": "Ask the assistant..."
},
+ "check_in": {
+ "tab_title": "Check-In",
+ "timer_status": "Timer Status",
+ "perform_check_in": "Check In",
+ "check_in_success": "Check-in recorded successfully",
+ "check_in_error": "Failed to record check-in",
+ "checked_in_by": "by {{name}}",
+ "last_check_in": "Last check-in",
+ "elapsed": "Elapsed",
+ "duration": "Duration",
+ "status_ok": "OK",
+ "status_green": "OK",
+ "status_warning": "Warning",
+ "status_yellow": "Warning",
+ "status_overdue": "Overdue",
+ "status_red": "Overdue",
+ "status_critical": "Critical",
+ "history": "Check-In History",
+ "no_timers": "No check-in timers configured",
+ "timers_disabled": "Check-in timers are disabled for this call",
+ "type_personnel": "Personnel",
+ "type_unit": "Unit",
+ "type_ic": "Incident Commander",
+ "type_par": "PAR",
+ "type_hazmat": "Hazmat Exposure",
+ "type_sector_rotation": "Sector Rotation",
+ "type_rehab": "Rehab",
+ "add_note": "Add Note (Optional)",
+ "confirm": "Confirm Check-In",
+ "minutes_ago": "{{count}} min ago",
+ "select_target": "Select Entity to Check In",
+ "overdue_count": "{{count}} Overdue",
+ "warning_count": "{{count}} Warning",
+ "enable_timers": "Enable Timers",
+ "disable_timers": "Disable Timers",
+ "summary": "{{overdue}} overdue, {{warning}} warning, {{ok}} ok",
+ "par_title": "Personnel Accountability (PAR)"
+ },
"common": {
"add": "Add",
"back": "Back",
@@ -582,21 +620,314 @@
"website": "Website",
"zip": "Zip Code"
},
+ "dispatch": {
+ "active_calls": "Active Calls",
+ "pending_calls": "Pending",
+ "scheduled_calls": "Scheduled",
+ "units_available": "Available",
+ "personnel_available": "Available",
+ "personnel_on_duty": "On Duty",
+ "units": "Units",
+ "personnel": "Personnel",
+ "map": "Map",
+ "notes": "Notes",
+ "activity_log": "Activity Log",
+ "communications": "Communications",
+ "no_active_calls": "No active calls",
+ "no_units": "No units available",
+ "no_personnel": "No personnel available",
+ "no_notes": "No notes available",
+ "no_activity": "No recent activity",
+ "current_channel": "Current Channel",
+ "audio_stream": "Audio Stream",
+ "no_stream": "No stream active",
+ "ptt": "PTT",
+ "ptt_start": "PTT Start",
+ "ptt_end": "PTT End",
+ "transmitting_on": "Transmitting on {{channel}}",
+ "transmission_ended": "Transmission ended",
+ "voice_disabled": "Voice disabled",
+ "disconnected": "Disconnected",
+ "select_channel": "Select Channel",
+ "select_channel_description": "Choose a voice channel to connect to",
+ "change_channel_warning": "Selecting a new channel will disconnect from the current one",
+ "default_channel": "Default",
+ "no_channels_available": "No voice channels available",
+ "system_update": "System Update",
+ "data_refreshed": "Data refreshed from server",
+ "call_selected": "Call Selected",
+ "unit_selected": "Unit Selected",
+ "unit_deselected": "Unit Deselected",
+ "personnel_selected": "Personnel Selected",
+ "personnel_deselected": "Personnel Deselected",
+ "loading_map": "Loading map...",
+ "map_not_available_web": "Map not available on web platform",
+ "filtering_by_call": "Filtering by call",
+ "clear_filter": "Clear Filter",
+ "call_filter_active": "Call Filter Active",
+ "call_filter_cleared": "Call Filter Cleared",
+ "showing_all_data": "Showing all data",
+ "call_notes": "Call Notes",
+ "no_call_notes": "No call notes",
+ "add_call_note_placeholder": "Add a note...",
+ "note_added": "Note Added",
+ "note_added_to_console": "A new note has been added to the console",
+ "add_note_title": "Add New Note",
+ "note_title_label": "Title",
+ "note_title_placeholder": "Enter note title...",
+ "note_category_label": "Category",
+ "note_category_placeholder": "Select a category",
+ "note_no_category": "No Category",
+ "note_body_label": "Note Content",
+ "note_body_placeholder": "Enter note content...",
+ "note_save_error": "Failed to save note: {{error}}",
+ "note_created": "Note Created",
+ "units_on_call": "Units on Call",
+ "no_units_on_call": "No units on call",
+ "personnel_on_call": "Personnel on Call",
+ "no_personnel_on_call": "No personnel on call",
+ "call_activity": "Call Activity",
+ "no_call_activity": "No call activity",
+ "on_call": "On Call",
+ "filtered": "Filtered",
+ "active_filter": "Active Filter",
+ "unit_status_change": "Unit Status Change",
+ "personnel_status_change": "Personnel Status Change",
+ "view_call_details": "View Call Details",
+ "dispatched_resources": "Dispatched",
+ "unassigned": "Unassigned",
+ "available": "Available",
+ "unknown": "Unknown",
+ "search_personnel_placeholder": "Search personnel...",
+ "search_calls_placeholder": "Search calls...",
+ "search_units_placeholder": "Search units...",
+ "search_notes_placeholder": "Search notes...",
+ "signalr_update": "Real-time Update",
+ "signalr_connected": "Connected",
+ "realtime_updates_active": "Real-time updates are now active",
+ "personnel_status_updated": "Personnel status updated",
+ "personnel_staffing_updated": "Personnel staffing updated",
+ "unit_status_updated": "Unit status updated",
+ "calls_updated": "Calls updated",
+ "call_added": "New call added",
+ "call_closed": "Call closed",
+ "check_ins": "Check-Ins",
+ "no_check_ins": "No calls with check-in timers",
+ "radio_log": "Radio Log",
+ "radio": "Radio",
+ "activity": "Activity",
+ "actions": "Actions",
+ "no_radio_activity": "No radio transmissions",
+ "live": "LIVE",
+ "currently_transmitting": "Currently transmitting...",
+ "duration": "Duration",
+ "call_actions": "Call Actions",
+ "unit_actions": "Unit Actions",
+ "personnel_actions": {
+ "title": "Personnel Actions",
+ "status_tab": "Status",
+ "staffing_tab": "Staffing",
+ "select_status": "Select Status",
+ "select_staffing": "Select Staffing Level",
+ "destination": "Destination",
+ "no_destination": "No Destination",
+ "note": "Note",
+ "note_placeholder": "Add an optional note...",
+ "update_status": "Update Status",
+ "update_staffing": "Update Staffing",
+ "no_statuses_available": "No statuses available",
+ "no_staffings_available": "No staffing levels available"
+ },
+ "unit_actions_panel": {
+ "status": "Status",
+ "select_status": "Select Status",
+ "destination": "Destination",
+ "no_destination": "No Destination",
+ "note": "Note",
+ "note_placeholder": "Add an optional note...",
+ "update_status": "Update Status",
+ "no_statuses_available": "No statuses available",
+ "no_active_calls": "No active calls",
+ "no_stations_available": "No stations available",
+ "no_destinations_available": "No destinations available"
+ },
+ "call": "Call",
+ "station": "Station",
+ "calls": "Calls",
+ "stations": "Stations",
+ "no_stations_available": "No stations available",
+ "new_call": "New Call",
+ "view_details": "Details",
+ "add_note": "Add Note",
+ "close_call": "Close",
+ "set_status": "Set Status",
+ "set_staffing": "Staffing",
+ "dispatch": "Dispatch",
+ "select_items_for_actions": "Select a call, unit, or personnel to enable context-aware actions",
+ "weather": {
+ "clear": "Clear",
+ "mainly_clear": "Mainly Clear",
+ "partly_cloudy": "Partly Cloudy",
+ "overcast": "Overcast",
+ "fog": "Fog",
+ "drizzle": "Drizzle",
+ "freezing_drizzle": "Freezing Drizzle",
+ "rain": "Rain",
+ "freezing_rain": "Freezing Rain",
+ "snow": "Snow",
+ "rain_showers": "Rain Showers",
+ "snow_showers": "Snow Showers",
+ "thunderstorm": "Thunderstorm",
+ "thunderstorm_hail": "Thunderstorm w/ Hail",
+ "unknown": "Unknown"
+ },
+ "available_only": "Available only",
+ "single_list": "Single list",
+ "resources": "Resources",
+ "search_resources_placeholder": "Search resources...",
+ "no_resources": "No resources"
+ },
"form": {
"invalid_url": "Please enter a valid URL starting with https:// (http:// is only allowed for localhost)",
"required": "This field is required"
},
- "livekit": {
- "audio_devices": "Audio Devices",
- "audio_settings": "Audio Settings",
- "connected_to_room": "Connected to Channel",
- "connecting": "Connecting...",
- "disconnect": "Disconnect",
+ "incident_command": {
+ "accountability": "Accountability (PAR)",
+ "acknowledge": "Acknowledge",
+ "action_plan": "Action Plan",
+ "action_plan_placeholder": "Describe the incident action plan...",
+ "active": "Active",
+ "active_title": "Active Incident Commands",
+ "add": "Add",
+ "add_channel": "Add Channel",
+ "add_lane": "Add Lane",
+ "add_marker": "Add Marker",
+ "add_objective": "Add Objective",
+ "annotations": "Map Annotations",
+ "assign": "Assign",
+ "assign_resource": "Assign Resource",
+ "assign_resource_required": "Select a lane and a resource",
+ "assign_role": "Assign Role",
+ "assign_role_required": "Select a person and a role",
+ "call": "Call",
+ "channel_name": "Channel name",
+ "chat": "Chat",
+ "chat_frozen": "This incident is closed. Conversations are kept as a point-in-time record — no new messages.",
+ "close_all_channels": "Close all channels",
+ "close_command": "Close Command",
+ "closed": "Closed",
+ "command_channel": "Command chat",
+ "command_channel_hint": "Command staff and dispatch",
+ "command_channel_unavailable": "No command channel has been created for this incident yet.",
+ "commander": "Commander",
+ "complete": "Complete",
+ "completed": "Completed",
+ "confirm_close": "Close incident command for this call?",
+ "critical": "Critical",
+ "delete_annotation_confirm": "Remove this annotation?",
+ "dispatch_channel": "Dispatch",
+ "dispatch_channel_hint": "The incident's line to the desk",
+ "dispatch_channel_unavailable": "No dispatch channel has been created for this incident yet.",
+ "dm_failed": "Couldn't open that conversation.",
+ "dm_unavailable": "That contact has no Resgrid account to message.",
+ "due": "Due",
+ "edit": "Edit",
+ "edit_action_plan": "Edit Action Plan",
+ "establish": "Establish Command",
+ "establish_description": "Optionally seed the command board from a template.",
+ "establish_error": "Failed to establish command",
+ "establish_success": "Incident command established",
+ "establish_title": "Establish Incident Command",
+ "established_on": "Established",
+ "green": "Green",
+ "hold_to_talk": "Hold to Talk",
+ "incident_channel": "Incident chat",
+ "incident_channel_hint": "Everyone working the incident",
+ "incident_channel_unavailable": "No incident channel has been created for this call yet.",
"join": "Join",
- "microphone": "Microphone",
- "mute": "Mute",
- "no_rooms_available": "No voice channels available",
- "speaker": "Speaker",
+ "lane": "Lane",
+ "lane_name": "Lane Name",
+ "lane_type": "Lane Type",
+ "marker": "Marker",
+ "marker_label": "Marker label",
+ "move": "Move",
+ "move_lane": "Move Lane",
+ "move_resource": "Move Resource",
+ "name_required": "Name is required",
+ "no_accountability": "No personnel tracked.",
+ "no_action_plan": "No action plan set.",
+ "no_active": "No Active Incident Commands",
+ "no_active_description": "Incident commands established on calls will appear here.",
+ "no_annotations": "No annotations.",
+ "no_channels": "No open channels.",
+ "no_command": "No Incident Command Established",
+ "no_command_description": "Establish incident command to coordinate resources, roles, objectives and accountability for this call.",
+ "no_lanes": "No lanes defined.",
+ "no_objectives": "No objectives.",
+ "no_resources": "No resources assigned.",
+ "no_roles": "No roles assigned.",
+ "no_template": "No template (blank board)",
+ "no_timeline": "No timeline entries.",
+ "no_timers": "No timers running.",
+ "not_authorized": "Command board unavailable",
+ "not_authorized_description": "Your department hasn't authorized you to work incident command. Ask an administrator for the Command App Login permission.",
+ "objective_name": "Objective",
+ "objective_type": "Type",
+ "objectives": "Objectives",
+ "open_chat": "Open",
+ "open_full_board": "Open Full Board",
+ "open_tactical_map": "Open tactical map",
+ "parent_lane": "Parent lane",
+ "person": "Person",
+ "personnel": "Personnel",
+ "release": "Release",
+ "resource": "Resource",
+ "resource_type": "Resource Type",
+ "role": "Role",
+ "roles": "Command Roles",
+ "run_par": "Run PAR",
+ "save": "Save",
+ "save_error": "Operation failed",
+ "saved": "Saved",
+ "select_lane": "Select a lane",
+ "select_person": "Select a person",
+ "select_resource": "Select a resource",
+ "select_role": "Select a role",
+ "send_message": "Message",
+ "status": "Status",
+ "structure": "Command Structure",
+ "tab_title": "Command",
+ "tactical_map": "Tactical Map",
+ "talking": "Transmitting...",
+ "tap_to_place": "Tap the map to place a marker",
+ "template": "Template",
+ "timeline": "Command Timeline",
+ "timers": "Timers",
+ "title": "Incident Command",
+ "top_level": "Top level",
+ "transfer": "Transfer",
+ "transfer_command": "Transfer",
+ "transfer_notes": "Notes",
+ "transfer_success": "Command transferred",
+ "transfer_title": "Transfer Command",
+ "unassigned": "Unassigned",
+ "unit": "Unit",
+ "voice_channels": "Voice Channels",
+ "voice_join_error": "Failed to join voice channel",
+ "voice_joined": "Joined voice channel",
+ "warning": "Warning"
+ },
+ "livekit": {
+ "audio_devices": "Audio Devices",
+ "audio_settings": "Audio Settings",
+ "connected_to_room": "Connected to Channel",
+ "connecting": "Connecting...",
+ "disconnect": "Disconnect",
+ "join": "Join",
+ "microphone": "Microphone",
+ "mute": "Mute",
+ "no_rooms_available": "No voice channels available",
+ "speaker": "Speaker",
"speaking": "Speaking",
"title": "Voice Channels",
"unmute": "Unmute"
@@ -607,32 +938,22 @@
"pleaseWait": "Please wait",
"processingRequest": "Processing your request..."
},
- "sso": {
- "authenticating": "Authenticating...",
- "back_to_login": "Back to Login",
- "back_to_lookup": "Change User",
- "continue_button": "Continue",
- "department_id_label": "Department ID",
- "department_id_placeholder": "Enter department ID",
- "error_generic": "Sign-in failed. Please try again.",
- "error_oidc_cancelled": "Sign-in was cancelled.",
- "error_oidc_not_ready": "SSO provider is loading, please wait.",
- "error_sso_not_enabled": "Single sign-on is not enabled for this user.",
- "error_token_exchange": "Failed to complete sign-in. Please try again.",
- "error_user_not_found": "User not found. Please check and try again.",
- "looking_up": "Looking up...",
- "optional": "optional",
- "page_subtitle": "Enter your username to look up your organization's sign-in options.",
- "page_title": "Single Sign-On",
- "provider_oidc": "OpenID Connect (OIDC)",
- "provider_saml": "SAML 2.0",
- "sign_in_button": "Sign In with SSO",
- "sign_in_title": "Sign In",
- "sso_button": "SSO Login"
+ "lockscreen": {
+ "message": "Enter your password to unlock the screen",
+ "not_you": "Not you? Return to login",
+ "password": "Password",
+ "password_placeholder": "Enter your password",
+ "title": "Lock Screen",
+ "unlock_button": "Unlock",
+ "unlock_failed": "Failed to unlock. Please try again.",
+ "relogin_required": "Password verification is unavailable for this session. Please log in again.",
+ "unlocking": "Unlocking...",
+ "welcome_back": "Welcome Back"
},
"login": {
"branding_subtitle": "Powerful dispatch software for first responders, search & rescue, and public safety organizations.",
"branding_title": "Emergency Response Management",
+ "dispatch_not_authorized": "You are not authorized to use the Dispatch app. Contact your department administrator.",
"errorModal": {
"confirmButton": "OK",
"message": "Please check your username and password and try again.",
@@ -663,18 +984,6 @@
"username_placeholder": "Enter your username",
"welcome_title": "Welcome Back"
},
- "lockscreen": {
- "message": "Enter your password to unlock the screen",
- "not_you": "Not you? Return to login",
- "password": "Password",
- "password_placeholder": "Enter your password",
- "title": "Lock Screen",
- "unlock_button": "Unlock",
- "unlock_failed": "Failed to unlock. Please try again.",
- "relogin_required": "Password verification is unavailable for this session. Please log in again.",
- "unlocking": "Unlocking...",
- "welcome_back": "Welcome Back"
- },
"maintenance": {
"downtime_message": "We are working hard to complete the maintenance as quickly as possible. Please check back soon.",
"downtime_title": "What is the Downtime?",
@@ -685,6 +994,21 @@
"why_down_message": "We are performing scheduled maintenance to improve your experience. We apologize for any inconvenience.",
"why_down_title": "Why is the Site Down?"
},
+ "map": {
+ "call_set_as_current": "Call set as current call",
+ "failed_to_open_maps": "Failed to open maps application",
+ "failed_to_set_current_call": "Failed to set call as current call",
+ "layers": "Map Layers",
+ "no_layers": "No layers available",
+ "no_location_for_routing": "No location data available for routing",
+ "pin_color": "Pin Color",
+ "recenter_map": "Recenter Map",
+ "set_as_current_call": "Set as Current Call",
+ "show_all": "Show All",
+ "hide_all": "Hide All",
+ "view_call_details": "View Call Details",
+ "view_poi_details": "View POI Details"
+ },
"menu": {
"calls": "Calls",
"calls_list": "Calls List",
@@ -705,71 +1029,6 @@
"chat": "Chat",
"assistant": "Assistant"
},
- "pois": {
- "address": "Address",
- "all_types": "All types",
- "destination": "Destination",
- "details": "Details",
- "detail_not_found": "POI not found",
- "detail_not_found_description": "The requested POI could not be loaded.",
- "detail_title": "POI Details",
- "empty": "No POIs found",
- "empty_description": "There are no points of interest available for your department yet.",
- "empty_filtered": "No matching POIs",
- "empty_filtered_description": "Try clearing your search or choosing a different POI type.",
- "filter_by_type": "Filter by POI type",
- "invalid_poi": "Invalid POI",
- "invalid_poi_description": "The selected POI identifier is not valid.",
- "loading": "Loading POIs...",
- "loading_detail": "Loading POI details...",
- "map": "Map",
- "no_location": "No location available",
- "no_location_description": "This POI does not have usable coordinates.",
- "no_location_for_routing": "No location data available for routing",
- "note": "Note",
- "route_error": "Failed to open maps application",
- "search": "Search POIs...",
- "sort": "Sort",
- "sort_options": {
- "address-asc": "Address",
- "name-asc": "Name (A-Z)",
- "name-desc": "Name (Z-A)",
- "type-asc": "Type"
- },
- "title": "POIs",
- "type": "Type",
- "unknown_type": "Unknown type",
- "unnamed": "Unnamed POI"
- },
- "scheduled_calls": {
- "title": "Scheduled Calls",
- "loading": "Loading scheduled calls...",
- "no_scheduled_calls": "No scheduled calls",
- "no_scheduled_calls_description": "There are no pending scheduled calls at this time.",
- "search": "Search scheduled calls...",
- "scheduled_for": "Scheduled for",
- "table_number": "Call #",
- "table_name": "Name",
- "table_type": "Type",
- "table_priority": "Priority",
- "table_address": "Address",
- "table_scheduled": "Scheduled For"
- },
- "map": {
- "call_set_as_current": "Call set as current call",
- "failed_to_open_maps": "Failed to open maps application",
- "failed_to_set_current_call": "Failed to set call as current call",
- "layers": "Map Layers",
- "no_layers": "No layers available",
- "no_location_for_routing": "No location data available for routing",
- "pin_color": "Pin Color",
- "recenter_map": "Recenter Map",
- "set_as_current_call": "Set as Current Call",
- "show_all": "Show All",
- "hide_all": "Hide All",
- "view_call_details": "View Call Details",
- "view_poi_details": "View POI Details"
- },
"notes": {
"actions": {
"add": "Add Note",
@@ -789,6 +1048,23 @@
"search": "Search notes...",
"title": "Notes"
},
+ "onboarding": {
+ "screen1": {
+ "title": "Resgrid Dispatch",
+ "description": "Create, dispatch, and manage emergency calls with a powerful mobile command center at your fingertips"
+ },
+ "screen2": {
+ "title": "Real-Time Situational Awareness",
+ "description": "Track all units, personnel, and resources on an interactive map with live status updates and AVL"
+ },
+ "screen3": {
+ "title": "Seamless Coordination",
+ "description": "Communicate instantly with field units, update call statuses, and coordinate response efforts from anywhere"
+ },
+ "skip": "Skip",
+ "next": "Next",
+ "getStarted": "Let's Get Started"
+ },
"personnel": {
"title": "Personnel",
"search": "Search personnel...",
@@ -821,22 +1097,41 @@
"send_email": "Email",
"custom_fields": "Additional Information"
},
- "onboarding": {
- "screen1": {
- "title": "Resgrid Dispatch",
- "description": "Create, dispatch, and manage emergency calls with a powerful mobile command center at your fingertips"
+ "pois": {
+ "address": "Address",
+ "all_types": "All types",
+ "destination": "Destination",
+ "details": "Details",
+ "detail_not_found": "POI not found",
+ "detail_not_found_description": "The requested POI could not be loaded.",
+ "detail_title": "POI Details",
+ "empty": "No POIs found",
+ "empty_description": "There are no points of interest available for your department yet.",
+ "empty_filtered": "No matching POIs",
+ "empty_filtered_description": "Try clearing your search or choosing a different POI type.",
+ "filter_by_type": "Filter by POI type",
+ "invalid_poi": "Invalid POI",
+ "invalid_poi_description": "The selected POI identifier is not valid.",
+ "loading": "Loading POIs...",
+ "loading_detail": "Loading POI details...",
+ "map": "Map",
+ "no_location": "No location available",
+ "no_location_description": "This POI does not have usable coordinates.",
+ "no_location_for_routing": "No location data available for routing",
+ "note": "Note",
+ "route_error": "Failed to open maps application",
+ "search": "Search POIs...",
+ "sort": "Sort",
+ "sort_options": {
+ "address-asc": "Address",
+ "name-asc": "Name (A-Z)",
+ "name-desc": "Name (Z-A)",
+ "type-asc": "Type"
},
- "screen2": {
- "title": "Real-Time Situational Awareness",
- "description": "Track all units, personnel, and resources on an interactive map with live status updates and AVL"
- },
- "screen3": {
- "title": "Seamless Coordination",
- "description": "Communicate instantly with field units, update call statuses, and coordinate response efforts from anywhere"
- },
- "skip": "Skip",
- "next": "Next",
- "getStarted": "Let's Get Started"
+ "title": "POIs",
+ "type": "Type",
+ "unknown_type": "Unknown type",
+ "unnamed": "Unnamed POI"
},
"protocols": {
"details": {
@@ -876,6 +1171,20 @@
"tap_to_manage": "Tap to manage roles",
"unassigned": "Unassigned"
},
+ "scheduled_calls": {
+ "title": "Scheduled Calls",
+ "loading": "Loading scheduled calls...",
+ "no_scheduled_calls": "No scheduled calls",
+ "no_scheduled_calls_description": "There are no pending scheduled calls at this time.",
+ "search": "Search scheduled calls...",
+ "scheduled_for": "Scheduled for",
+ "table_number": "Call #",
+ "table_name": "Name",
+ "table_type": "Type",
+ "table_priority": "Priority",
+ "table_address": "Address",
+ "table_scheduled": "Scheduled For"
+ },
"settings": {
"about": "About",
"account": "Account",
@@ -964,6 +1273,29 @@
"version": "Version",
"website": "Website"
},
+ "sso": {
+ "authenticating": "Authenticating...",
+ "back_to_login": "Back to Login",
+ "back_to_lookup": "Change User",
+ "continue_button": "Continue",
+ "department_id_label": "Department ID",
+ "department_id_placeholder": "Enter department ID",
+ "error_generic": "Sign-in failed. Please try again.",
+ "error_oidc_cancelled": "Sign-in was cancelled.",
+ "error_oidc_not_ready": "SSO provider is loading, please wait.",
+ "error_sso_not_enabled": "Single sign-on is not enabled for this user.",
+ "error_token_exchange": "Failed to complete sign-in. Please try again.",
+ "error_user_not_found": "User not found. Please check and try again.",
+ "looking_up": "Looking up...",
+ "optional": "optional",
+ "page_subtitle": "Enter your username to look up your organization's sign-in options.",
+ "page_title": "Single Sign-On",
+ "provider_oidc": "OpenID Connect (OIDC)",
+ "provider_saml": "SAML 2.0",
+ "sign_in_button": "Sign In with SSO",
+ "sign_in_title": "Sign In",
+ "sso_button": "SSO Login"
+ },
"status": {
"add_note": "Add Note",
"all_destinations_enabled": "Can respond to calls, stations, or POIs",
@@ -972,247 +1304,42 @@
"calls_and_pois_destination_enabled": "Can respond to calls or POIs",
"calls_tab": "Calls",
"failed_to_save_status": "Failed to save status. Please try again.",
- "general_status": "General status without specific destination",
- "loading_pois": "Loading POIs...",
- "loading_stations": "Loading stations...",
- "no_destination": "No Destination",
- "no_pois_available": "No POIs available",
- "no_stations_available": "No stations available",
- "no_statuses_available": "No statuses available",
- "note": "Note",
- "note_optional": "Add an optional note for this status update",
- "note_required": "Please enter a note for this status update",
- "poi_destination_enabled": "Can respond to POIs",
- "pois_tab": "POIs",
- "select_destination": "Select Destination for {{status}}",
- "select_destination_type": "Where would you like to respond?",
- "select_status": "Select Status",
- "select_status_type": "What status would you like to set?",
- "selected_destination": "Selected Destination",
- "selected_status": "Selected Status",
- "set_status": "Set Status",
- "station_destination_enabled": "Can respond to stations",
- "stations_and_pois_destination_enabled": "Can respond to stations or POIs",
- "stations_tab": "Stations",
- "status_saved_successfully": "Status saved successfully!"
- },
- "dispatch": {
- "active_calls": "Active Calls",
- "pending_calls": "Pending",
- "scheduled_calls": "Scheduled",
- "units_available": "Available",
- "personnel_available": "Available",
- "personnel_on_duty": "On Duty",
- "units": "Units",
- "personnel": "Personnel",
- "map": "Map",
- "notes": "Notes",
- "activity_log": "Activity Log",
- "communications": "Communications",
- "no_active_calls": "No active calls",
- "no_units": "No units available",
- "no_personnel": "No personnel available",
- "no_notes": "No notes available",
- "no_activity": "No recent activity",
- "current_channel": "Current Channel",
- "audio_stream": "Audio Stream",
- "no_stream": "No stream active",
- "ptt": "PTT",
- "ptt_start": "PTT Start",
- "ptt_end": "PTT End",
- "transmitting_on": "Transmitting on {{channel}}",
- "transmission_ended": "Transmission ended",
- "voice_disabled": "Voice disabled",
- "disconnected": "Disconnected",
- "select_channel": "Select Channel",
- "select_channel_description": "Choose a voice channel to connect to",
- "change_channel_warning": "Selecting a new channel will disconnect from the current one",
- "default_channel": "Default",
- "no_channels_available": "No voice channels available",
- "system_update": "System Update",
- "data_refreshed": "Data refreshed from server",
- "call_selected": "Call Selected",
- "unit_selected": "Unit Selected",
- "unit_deselected": "Unit Deselected",
- "personnel_selected": "Personnel Selected",
- "personnel_deselected": "Personnel Deselected",
- "loading_map": "Loading map...",
- "map_not_available_web": "Map not available on web platform",
- "filtering_by_call": "Filtering by call",
- "clear_filter": "Clear Filter",
- "call_filter_active": "Call Filter Active",
- "call_filter_cleared": "Call Filter Cleared",
- "showing_all_data": "Showing all data",
- "call_notes": "Call Notes",
- "no_call_notes": "No call notes",
- "add_call_note_placeholder": "Add a note...",
- "note_added": "Note Added",
- "note_added_to_console": "A new note has been added to the console",
- "add_note_title": "Add New Note",
- "note_title_label": "Title",
- "note_title_placeholder": "Enter note title...",
- "note_category_label": "Category",
- "note_category_placeholder": "Select a category",
- "note_no_category": "No Category",
- "note_body_label": "Note Content",
- "note_body_placeholder": "Enter note content...",
- "note_save_error": "Failed to save note: {{error}}",
- "note_created": "Note Created",
- "units_on_call": "Units on Call",
- "no_units_on_call": "No units on call",
- "personnel_on_call": "Personnel on Call",
- "no_personnel_on_call": "No personnel on call",
- "call_activity": "Call Activity",
- "no_call_activity": "No call activity",
- "on_call": "On Call",
- "filtered": "Filtered",
- "active_filter": "Active Filter",
- "unit_status_change": "Unit Status Change",
- "personnel_status_change": "Personnel Status Change",
- "view_call_details": "View Call Details",
- "dispatched_resources": "Dispatched",
- "unassigned": "Unassigned",
- "available": "Available",
- "unknown": "Unknown",
- "search_personnel_placeholder": "Search personnel...",
- "search_calls_placeholder": "Search calls...",
- "search_units_placeholder": "Search units...",
- "search_notes_placeholder": "Search notes...",
- "signalr_update": "Real-time Update",
- "signalr_connected": "Connected",
- "realtime_updates_active": "Real-time updates are now active",
- "personnel_status_updated": "Personnel status updated",
- "personnel_staffing_updated": "Personnel staffing updated",
- "unit_status_updated": "Unit status updated",
- "calls_updated": "Calls updated",
- "call_added": "New call added",
- "call_closed": "Call closed",
- "check_ins": "Check-Ins",
- "no_check_ins": "No calls with check-in timers",
- "radio_log": "Radio Log",
- "radio": "Radio",
- "activity": "Activity",
- "actions": "Actions",
- "no_radio_activity": "No radio transmissions",
- "live": "LIVE",
- "currently_transmitting": "Currently transmitting...",
- "duration": "Duration",
- "call_actions": "Call Actions",
- "unit_actions": "Unit Actions",
- "personnel_actions": {
- "title": "Personnel Actions",
- "status_tab": "Status",
- "staffing_tab": "Staffing",
- "select_status": "Select Status",
- "select_staffing": "Select Staffing Level",
- "destination": "Destination",
- "no_destination": "No Destination",
- "note": "Note",
- "note_placeholder": "Add an optional note...",
- "update_status": "Update Status",
- "update_staffing": "Update Staffing",
- "no_statuses_available": "No statuses available",
- "no_staffings_available": "No staffing levels available"
- },
- "unit_actions_panel": {
- "status": "Status",
- "select_status": "Select Status",
- "destination": "Destination",
- "no_destination": "No Destination",
- "note": "Note",
- "note_placeholder": "Add an optional note...",
- "update_status": "Update Status",
- "no_statuses_available": "No statuses available",
- "no_active_calls": "No active calls",
- "no_stations_available": "No stations available",
- "no_destinations_available": "No destinations available"
- },
- "call": "Call",
- "station": "Station",
- "calls": "Calls",
- "stations": "Stations",
- "no_stations_available": "No stations available",
- "new_call": "New Call",
- "view_details": "Details",
- "add_note": "Add Note",
- "close_call": "Close",
- "set_status": "Set Status",
- "set_staffing": "Staffing",
- "dispatch": "Dispatch",
- "select_items_for_actions": "Select a call, unit, or personnel to enable context-aware actions",
- "weather": {
- "clear": "Clear",
- "mainly_clear": "Mainly Clear",
- "partly_cloudy": "Partly Cloudy",
- "overcast": "Overcast",
- "fog": "Fog",
- "drizzle": "Drizzle",
- "freezing_drizzle": "Freezing Drizzle",
- "rain": "Rain",
- "freezing_rain": "Freezing Rain",
- "snow": "Snow",
- "rain_showers": "Rain Showers",
- "snow_showers": "Snow Showers",
- "thunderstorm": "Thunderstorm",
- "thunderstorm_hail": "Thunderstorm w/ Hail",
- "unknown": "Unknown"
- },
- "available_only": "Available only",
- "single_list": "Single list",
- "resources": "Resources",
- "search_resources_placeholder": "Search resources...",
- "no_resources": "No resources"
- },
- "tabs": {
- "calls": "Calls",
- "calendar": "Calendar",
- "contacts": "Contacts",
- "home": "Home",
- "map": "Map",
- "messages": "Messages",
- "notes": "Notes",
- "protocols": "Protocols",
- "settings": "Settings",
- "shifts": "Shifts",
- "personnel": "Personnel"
- },
- "check_in": {
- "tab_title": "Check-In",
- "timer_status": "Timer Status",
- "perform_check_in": "Check In",
- "check_in_success": "Check-in recorded successfully",
- "check_in_error": "Failed to record check-in",
- "checked_in_by": "by {{name}}",
- "last_check_in": "Last check-in",
- "elapsed": "Elapsed",
- "duration": "Duration",
- "status_ok": "OK",
- "status_green": "OK",
- "status_warning": "Warning",
- "status_yellow": "Warning",
- "status_overdue": "Overdue",
- "status_red": "Overdue",
- "status_critical": "Critical",
- "history": "Check-In History",
- "no_timers": "No check-in timers configured",
- "timers_disabled": "Check-in timers are disabled for this call",
- "type_personnel": "Personnel",
- "type_unit": "Unit",
- "type_ic": "Incident Commander",
- "type_par": "PAR",
- "type_hazmat": "Hazmat Exposure",
- "type_sector_rotation": "Sector Rotation",
- "type_rehab": "Rehab",
- "add_note": "Add Note (Optional)",
- "confirm": "Confirm Check-In",
- "minutes_ago": "{{count}} min ago",
- "select_target": "Select Entity to Check In",
- "overdue_count": "{{count}} Overdue",
- "warning_count": "{{count}} Warning",
- "enable_timers": "Enable Timers",
- "disable_timers": "Disable Timers",
- "summary": "{{overdue}} overdue, {{warning}} warning, {{ok}} ok",
- "par_title": "Personnel Accountability (PAR)"
+ "general_status": "General status without specific destination",
+ "loading_pois": "Loading POIs...",
+ "loading_stations": "Loading stations...",
+ "no_destination": "No Destination",
+ "no_pois_available": "No POIs available",
+ "no_stations_available": "No stations available",
+ "no_statuses_available": "No statuses available",
+ "note": "Note",
+ "note_optional": "Add an optional note for this status update",
+ "note_required": "Please enter a note for this status update",
+ "poi_destination_enabled": "Can respond to POIs",
+ "pois_tab": "POIs",
+ "select_destination": "Select Destination for {{status}}",
+ "select_destination_type": "Where would you like to respond?",
+ "select_status": "Select Status",
+ "select_status_type": "What status would you like to set?",
+ "selected_destination": "Selected Destination",
+ "selected_status": "Selected Status",
+ "set_status": "Set Status",
+ "station_destination_enabled": "Can respond to stations",
+ "stations_and_pois_destination_enabled": "Can respond to stations or POIs",
+ "stations_tab": "Stations",
+ "status_saved_successfully": "Status saved successfully!"
+ },
+ "tabs": {
+ "calls": "Calls",
+ "calendar": "Calendar",
+ "contacts": "Contacts",
+ "home": "Home",
+ "map": "Map",
+ "messages": "Messages",
+ "notes": "Notes",
+ "protocols": "Protocols",
+ "settings": "Settings",
+ "shifts": "Shifts",
+ "personnel": "Personnel"
},
"units": {
"title": "Units",
@@ -1243,6 +1370,63 @@
"unknown_status": "Unknown",
"no_destination": "None"
},
+ "videoFeeds": {
+ "title": "Video Feeds",
+ "noFeeds": "No video feeds for this call",
+ "addFeed": "Add Video Feed",
+ "editFeed": "Edit Video Feed",
+ "deleteFeed": "Delete Video Feed",
+ "deleteConfirm": "Are you sure you want to remove this video feed?",
+ "watch": "Watch",
+ "goLive": "Go Live",
+ "stopLive": "Stop Live",
+ "flipCamera": "Flip Camera",
+ "feedAdded": "Video feed added",
+ "feedUpdated": "Video feed updated",
+ "feedDeleted": "Video feed removed",
+ "feedError": "Failed to load video feed",
+ "unsupportedFormat": "This stream format is not supported on mobile",
+ "copyUrl": "Copy URL",
+ "form": {
+ "name": "Feed Name",
+ "namePlaceholder": "e.g. Engine 1 Drone",
+ "url": "Stream URL",
+ "urlPlaceholder": "e.g. https://stream.example.com/live.m3u8",
+ "feedType": "Camera Type",
+ "feedFormat": "Stream Format",
+ "description": "Description",
+ "descriptionPlaceholder": "Optional description",
+ "status": "Status",
+ "sortOrder": "Sort Order",
+ "cameraLocation": "Camera Location",
+ "useCurrentLocation": "Use Current Location"
+ },
+ "type": {
+ "drone": "Drone",
+ "fixedCamera": "Fixed Camera",
+ "bodyCam": "Body Cam",
+ "trafficCam": "Traffic Cam",
+ "weatherCam": "Weather Cam",
+ "satelliteFeed": "Satellite Feed",
+ "webCam": "Web Cam",
+ "other": "Other"
+ },
+ "format": {
+ "rtsp": "RTSP",
+ "hls": "HLS",
+ "mjpeg": "MJPEG",
+ "youtubeLive": "YouTube Live",
+ "webrtc": "WebRTC",
+ "dash": "DASH",
+ "embed": "Embed",
+ "other": "Other"
+ },
+ "status": {
+ "active": "Active",
+ "inactive": "Inactive",
+ "error": "Error"
+ }
+ },
"weatherAlerts": {
"title": "Weather Alerts",
"activeAlerts": "Active Alerts",
@@ -1350,171 +1534,5 @@
},
"stats_label": "Weather Alerts"
},
- "videoFeeds": {
- "title": "Video Feeds",
- "noFeeds": "No video feeds for this call",
- "addFeed": "Add Video Feed",
- "editFeed": "Edit Video Feed",
- "deleteFeed": "Delete Video Feed",
- "deleteConfirm": "Are you sure you want to remove this video feed?",
- "watch": "Watch",
- "goLive": "Go Live",
- "stopLive": "Stop Live",
- "flipCamera": "Flip Camera",
- "feedAdded": "Video feed added",
- "feedUpdated": "Video feed updated",
- "feedDeleted": "Video feed removed",
- "feedError": "Failed to load video feed",
- "unsupportedFormat": "This stream format is not supported on mobile",
- "copyUrl": "Copy URL",
- "form": {
- "name": "Feed Name",
- "namePlaceholder": "e.g. Engine 1 Drone",
- "url": "Stream URL",
- "urlPlaceholder": "e.g. https://stream.example.com/live.m3u8",
- "feedType": "Camera Type",
- "feedFormat": "Stream Format",
- "description": "Description",
- "descriptionPlaceholder": "Optional description",
- "status": "Status",
- "sortOrder": "Sort Order",
- "cameraLocation": "Camera Location",
- "useCurrentLocation": "Use Current Location"
- },
- "type": {
- "drone": "Drone",
- "fixedCamera": "Fixed Camera",
- "bodyCam": "Body Cam",
- "trafficCam": "Traffic Cam",
- "weatherCam": "Weather Cam",
- "satelliteFeed": "Satellite Feed",
- "webCam": "Web Cam",
- "other": "Other"
- },
- "format": {
- "rtsp": "RTSP",
- "hls": "HLS",
- "mjpeg": "MJPEG",
- "youtubeLive": "YouTube Live",
- "webrtc": "WebRTC",
- "dash": "DASH",
- "embed": "Embed",
- "other": "Other"
- },
- "status": {
- "active": "Active",
- "inactive": "Inactive",
- "error": "Error"
- }
- },
- "welcome": "Welcome to obytes app site",
- "incident_command": {
- "tab_title": "Command",
- "title": "Incident Command",
- "open_full_board": "Open Full Board",
- "no_command": "No Incident Command Established",
- "no_command_description": "Establish incident command to coordinate resources, roles, objectives and accountability for this call.",
- "establish": "Establish Command",
- "establish_title": "Establish Incident Command",
- "establish_description": "Optionally seed the command board from a template.",
- "establish_success": "Incident command established",
- "establish_error": "Failed to establish command",
- "template": "Template",
- "no_template": "No template (blank board)",
- "saved": "Saved",
- "save_error": "Operation failed",
- "edit_action_plan": "Edit Action Plan",
- "action_plan": "Action Plan",
- "action_plan_placeholder": "Describe the incident action plan...",
- "save": "Save",
- "no_action_plan": "No action plan set.",
- "add": "Add",
- "add_objective": "Add Objective",
- "objective_name": "Objective",
- "objective_type": "Type",
- "name_required": "Name is required",
- "add_lane": "Add Lane",
- "lane_name": "Lane Name",
- "lane_type": "Lane Type",
- "assign_resource": "Assign Resource",
- "assign_resource_required": "Select a lane and a resource",
- "lane": "Lane",
- "select_lane": "Select a lane",
- "resource_type": "Resource Type",
- "resource": "Resource",
- "select_resource": "Select a resource",
- "unit": "Unit",
- "personnel": "Personnel",
- "assign": "Assign",
- "assign_role": "Assign Role",
- "assign_role_required": "Select a person and a role",
- "person": "Person",
- "select_person": "Select a person",
- "role": "Role",
- "select_role": "Select a role",
- "transfer_title": "Transfer Command",
- "transfer_notes": "Notes",
- "transfer": "Transfer",
- "transfer_command": "Transfer",
- "transfer_success": "Command transferred",
- "close_command": "Close Command",
- "confirm_close": "Close incident command for this call?",
- "status": "Status",
- "active": "Active",
- "closed": "Closed",
- "commander": "Commander",
- "established_on": "Established",
- "edit": "Edit",
- "roles": "Command Roles",
- "no_roles": "No roles assigned.",
- "structure": "Command Structure",
- "no_lanes": "No lanes defined.",
- "no_resources": "No resources assigned.",
- "release": "Release",
- "objectives": "Objectives",
- "no_objectives": "No objectives.",
- "complete": "Complete",
- "completed": "Completed",
- "timers": "Timers",
- "no_timers": "No timers running.",
- "acknowledge": "Acknowledge",
- "due": "Due",
- "accountability": "Accountability (PAR)",
- "run_par": "Run PAR",
- "green": "Green",
- "warning": "Warning",
- "critical": "Critical",
- "no_accountability": "No personnel tracked.",
- "timeline": "Command Timeline",
- "no_timeline": "No timeline entries.",
- "unassigned": "Unassigned",
- "active_title": "Active Incident Commands",
- "no_active": "No Active Incident Commands",
- "no_active_description": "Incident commands established on calls will appear here.",
- "call": "Call",
- "tactical_map": "Tactical Map",
- "annotations": "Map Annotations",
- "no_annotations": "No annotations.",
- "open_tactical_map": "Open tactical map",
- "marker": "Marker",
- "add_marker": "Add Marker",
- "tap_to_place": "Tap the map to place a marker",
- "marker_label": "Marker label",
- "delete_annotation_confirm": "Remove this annotation?",
- "voice_channels": "Voice Channels",
- "no_channels": "No open channels.",
- "add_channel": "Add Channel",
- "channel_name": "Channel name",
- "close_all_channels": "Close all channels",
- "join": "Join",
- "hold_to_talk": "Hold to Talk",
- "talking": "Transmitting...",
- "voice_joined": "Joined voice channel",
- "voice_join_error": "Failed to join voice channel",
- "move": "Move",
- "move_lane": "Move Lane",
- "parent_lane": "Parent lane",
- "top_level": "Top level",
- "move_resource": "Move Resource"
- }
+ "welcome": "Welcome to obytes app site"
}
diff --git a/src/translations/es.json b/src/translations/es.json
index 62dd9e9f..ba3bb974 100644
--- a/src/translations/es.json
+++ b/src/translations/es.json
@@ -371,6 +371,124 @@
"audio_name": "Clip de audio"
}
},
+ "chat": {
+ "title": "Chat",
+ "assistant": "Asistente",
+ "empty": "Aún no hay conversaciones. Inicia un mensaje directo o crea un grupo.",
+ "section_assistant": "Asistente",
+ "section_direct_messages": "Mensajes directos",
+ "section_channels": "Canales",
+ "section_incidents": "Incidentes",
+ "new_direct_message": "Nuevo mensaje directo",
+ "new_group": "Nuevo grupo",
+ "open_assistant": "Abrir asistente",
+ "create_conversation_failed": "No se pudo iniciar la conversación",
+ "group_name": "Nombre del grupo",
+ "search_people": "Buscar personas",
+ "no_people": "No se encontraron personas",
+ "create_group_with": "Crear grupo ({{count}})",
+ "message_deleted": "Este mensaje fue eliminado",
+ "urgent": "Urgente",
+ "urgent_will_send": "Este mensaje se enviará como urgente",
+ "shared_location": "Ubicación compartida",
+ "thread_replies": "{{count}} respuestas",
+ "edited": "(editado)",
+ "failed_tap_retry": "Error: toca para reintentar",
+ "type_a_message": "Escribe un mensaje",
+ "emoji": "Emoji",
+ "add_image": "Agregar imagen",
+ "add_gif": "Agregar GIF",
+ "share_location": "Compartir ubicación",
+ "send": "Enviar",
+ "someone": "Alguien",
+ "is_typing": "{{name}} está escribiendo...",
+ "are_typing": "{{count}} personas están escribiendo...",
+ "permission_photos_denied": "Permiso de la biblioteca de fotos denegado",
+ "permission_location_denied": "Permiso de ubicación denegado",
+ "search_gifs": "Buscar GIFs",
+ "no_gifs": "No se encontraron GIFs",
+ "flag_reason": "¿Por qué lo reportas?",
+ "flag_inappropriate": "Inapropiado",
+ "flag_harassment": "Acoso",
+ "flag_spam": "Spam",
+ "flag_sensitive": "Información sensible",
+ "flag_policy": "Violación de la política",
+ "flag_other": "Otro",
+ "reply_in_thread": "Responder en el hilo",
+ "copy": "Copiar",
+ "copied": "Copiado",
+ "copy_unavailable": "Copiar no está disponible en este dispositivo",
+ "edit": "Editar",
+ "edit_message": "Editar mensaje",
+ "save": "Guardar",
+ "delete": "Eliminar",
+ "pin": "Fijar",
+ "unpin": "Dejar de fijar",
+ "flag": "Reportar",
+ "moderator_delete": "Eliminar (moderador)",
+ "moderator_removed": "Eliminado por el moderador",
+ "attachment_failed": "Error al subir el adjunto",
+ "ack_required": "Confirmación requerida",
+ "ack_pending_one": "Tienes un mensaje urgente por confirmar",
+ "ack_pending_count": "Tienes {{count}} mensajes urgentes por confirmar",
+ "acknowledge": "Confirmar",
+ "thread": "Hilo",
+ "original_message": "Mensaje original",
+ "reply_placeholder": "Responder...",
+ "channel": "Canal",
+ "direct_message": "Mensaje directo",
+ "load_people_failed": "No se pudieron cargar las personas",
+ "reaction_failed": "No se pudo actualizar la reacción",
+ "edit_failed": "No se pudo editar el mensaje",
+ "delete_failed": "No se pudo eliminar el mensaje",
+ "pin_failed": "No se pudo actualizar el mensaje fijado",
+ "flag_failed": "No se pudo reportar el mensaje"
+ },
+ "chatbot": {
+ "title": "Asistente",
+ "subtitle": "Asistente de IA para tu departamento",
+ "new_session": "Nueva sesión",
+ "empty": "Pregúntale cualquier cosa al asistente para comenzar.",
+ "ask_placeholder": "Pregúntale al asistente..."
+ },
+ "check_in": {
+ "tab_title": "Registro",
+ "timer_status": "Estado del Temporizador",
+ "perform_check_in": "Registrar",
+ "check_in_success": "Registro realizado exitosamente",
+ "check_in_error": "Error al realizar el registro",
+ "checked_in_by": "por {{name}}",
+ "last_check_in": "Último registro",
+ "elapsed": "Transcurrido",
+ "duration": "Duración",
+ "status_ok": "OK",
+ "status_green": "OK",
+ "status_warning": "Advertencia",
+ "status_yellow": "Advertencia",
+ "status_overdue": "Vencido",
+ "status_red": "Vencido",
+ "status_critical": "Crítico",
+ "history": "Historial de Registros",
+ "no_timers": "No hay temporizadores de registro configurados",
+ "timers_disabled": "Los temporizadores de registro están desactivados para esta llamada",
+ "type_personnel": "Personal",
+ "type_unit": "Unidad",
+ "type_ic": "Comandante de Incidente",
+ "type_par": "PAR",
+ "type_hazmat": "Exposición a Materiales Peligrosos",
+ "type_sector_rotation": "Rotación de Sector",
+ "type_rehab": "Rehabilitación",
+ "add_note": "Agregar Nota (Opcional)",
+ "confirm": "Confirmar Registro",
+ "minutes_ago": "hace {{count}} min",
+ "select_target": "Seleccionar Entidad para Registrar",
+ "overdue_count": "{{count}} Vencidos",
+ "warning_count": "{{count}} Advertencias",
+ "enable_timers": "Activar Temporizadores",
+ "disable_timers": "Desactivar Temporizadores",
+ "summary": "{{overdue}} vencidos, {{warning}} advertencias, {{ok}} ok",
+ "par_title": "Control de personal (PAR)"
+ },
"common": {
"add": "Añadir",
"back": "Atrás",
@@ -502,144 +620,396 @@
"website": "Sitio web",
"zip": "Código postal"
},
- "form": {
- "invalid_url": "Por favor, introduce una URL válida que comience con http:// o https://",
- "required": "Este campo es obligatorio"
- },
- "livekit": {
- "audio_devices": "Dispositivos de audio",
- "audio_settings": "Configuración de audio",
- "connected_to_room": "Conectado al canal",
- "connecting": "Conectando...",
- "disconnect": "Desconectar",
- "join": "Unirse",
- "microphone": "Micrófono",
- "mute": "Silenciar",
- "no_rooms_available": "No hay canales de voz disponibles",
- "speaker": "Altavoz",
- "speaking": "Hablando",
- "title": "Canales de voz",
- "unmute": "Activar sonido"
- },
- "loading": {
- "loading": "Cargando...",
- "loadingData": "Cargando datos...",
- "pleaseWait": "Por favor, espere",
- "processingRequest": "Procesando su solicitud..."
- },
- "sso": {
- "authenticating": "Autenticando...",
- "back_to_login": "Volver al inicio de sesión",
- "back_to_lookup": "Cambiar usuario",
- "continue_button": "Continuar",
- "department_id_label": "ID de departamento",
- "department_id_placeholder": "Ingrese el ID de departamento",
- "error_generic": "Error al iniciar sesión. Por favor intente de nuevo.",
- "error_oidc_cancelled": "El inicio de sesión fue cancelado.",
- "error_oidc_not_ready": "El proveedor SSO se está cargando, por favor espere.",
- "error_sso_not_enabled": "El inicio de sesión único no está habilitado para este usuario.",
- "error_token_exchange": "Error al completar el inicio de sesión. Por favor intente de nuevo.",
- "error_user_not_found": "Usuario no encontrado. Por favor verifique e intente de nuevo.",
- "looking_up": "Buscando...",
- "optional": "opcional",
- "page_subtitle": "Ingrese su nombre de usuario para ver las opciones de inicio de sesión de su organización.",
- "page_title": "Inicio de sesión único",
- "provider_oidc": "OpenID Connect (OIDC)",
- "provider_saml": "SAML 2.0",
- "sign_in_button": "Iniciar sesión con SSO",
- "sign_in_title": "Iniciar sesión",
- "sso_button": "Acceso SSO"
- },
- "login": {
- "branding_subtitle": "Software de despacho potente para socorristas, búsqueda y rescate, y organizaciones de seguridad pública.",
- "branding_title": "Gestión de Respuesta de Emergencia",
- "errorModal": {
- "confirmButton": "Aceptar",
- "message": "Por favor, comprueba tu nombre de usuario y contraseña e inténtalo de nuevo.",
- "title": "Inicio de sesión fallido"
- },
- "feature_dispatch_desc": "Despacha unidades y gestiona llamadas al instante con actualizaciones en vivo en todos los dispositivos.",
- "feature_dispatch_title": "Despacho en Tiempo Real",
- "feature_mapping_desc": "Rastrea unidades en tiempo real con mapas detallados, enrutamiento y gestión de ubicación.",
- "feature_mapping_title": "Mapeo Avanzado",
- "feature_personnel_desc": "Gestiona tu equipo con acceso basado en roles, seguimiento de estado y herramientas de comunicación.",
- "feature_personnel_title": "Gestión de Personal",
- "footer_text": "Creado con ❤️ en Lake Tahoe",
- "login": "Iniciar sesión",
- "login_button": "Iniciar sesión",
- "login_button_description": "Inicia sesión en tu cuenta para continuar",
- "login_button_error": "Error al iniciar sesión",
- "login_button_loading": "Iniciando sesión...",
- "login_button_success": "Sesión iniciada con éxito",
- "no_account": "¿No tienes cuenta?",
- "page_subtitle": "Introduce tus credenciales para iniciar sesión.",
- "page_title": "Resgrid Dispatch",
- "password": "Contraseña",
- "password_incorrect": "La contraseña era incorrecta",
- "password_placeholder": "Introduce tu contraseña",
- "register": "Registrarse",
- "title": "Iniciar sesión",
- "username": "Nombre de usuario",
- "username_placeholder": "Introduce tu nombre de usuario",
- "welcome_title": "Bienvenido de Nuevo"
- },
- "lockscreen": {
- "message": "Ingrese su contraseña para desbloquear la pantalla",
- "not_you": "¿No eres tú? Volver al inicio de sesión",
- "password": "Contraseña",
- "password_placeholder": "Ingrese su contraseña",
- "title": "Pantalla de Bloqueo",
- "unlock_button": "Desbloquear",
- "unlock_failed": "Error al desbloquear. Por favor, inténtelo de nuevo.",
- "unlocking": "Desbloqueando...",
- "welcome_back": "Bienvenido de Nuevo",
- "relogin_required": "La verificación de contraseña no está disponible para esta sesión. Por favor, inicia sesión de nuevo."
- },
- "maintenance": {
- "downtime_message": "Estamos trabajando arduamente para completar el mantenimiento lo antes posible. Por favor, vuelva pronto.",
- "downtime_title": "¿Cuál es el tiempo de inactividad?",
- "message": "Por favor, vuelva en algún momento.",
- "support_message": "Si necesita asistencia, contáctenos en",
- "support_title": "¿Necesita soporte?",
- "title": "Sitio en Mantenimiento",
- "why_down_message": "Estamos realizando mantenimiento programado para mejorar su experiencia. Nos disculpamos por cualquier inconveniente.",
- "why_down_title": "¿Por qué está el sitio inactivo?"
- },
- "map": {
- "call_set_as_current": "Llamada establecida como llamada actual",
- "failed_to_open_maps": "Error al abrir la aplicación de mapas",
- "failed_to_set_current_call": "Error al establecer la llamada como llamada actual",
- "no_location_for_routing": "No hay datos de ubicación disponibles para el enrutamiento",
- "pin_color": "Color del pin",
- "recenter_map": "Recentrar mapa",
- "set_as_current_call": "Establecer como llamada actual",
- "view_call_details": "Ver detalles de la llamada",
- "view_poi_details": "Ver detalles del PDI",
- "layers": "Capas del Mapa",
- "no_layers": "No hay capas disponibles",
- "show_all": "Mostrar Todo",
- "hide_all": "Ocultar Todo"
- },
- "notes": {
- "actions": {
- "add": "Añadir nota",
- "delete_confirm": "¿Estás seguro de que quieres eliminar esta nota?"
- },
- "details": {
- "close": "Cerrar",
- "created": "Creado",
- "delete": "Eliminar",
- "edit": "Editar",
- "tags": "Etiquetas",
- "title": "Detalles de la nota",
- "updated": "Actualizado"
- },
- "empty": "No se encontraron notas",
- "emptyDescription": "No se han creado notas para tu departamento todavía.",
- "search": "Buscar notas...",
- "title": "Notas"
- },
- "menu": {
+ "dispatch": {
+ "active_calls": "Llamadas Activas",
+ "pending_calls": "Pendientes",
+ "scheduled_calls": "Programadas",
+ "units_available": "Disponibles",
+ "personnel_available": "Disponibles",
+ "personnel_on_duty": "De Servicio",
+ "units": "Unidades",
+ "personnel": "Personal",
+ "map": "Mapa",
+ "notes": "Notas",
+ "activity_log": "Registro de Actividad",
+ "communications": "Comunicaciones",
+ "no_active_calls": "Sin llamadas activas",
+ "no_units": "Sin unidades disponibles",
+ "no_personnel": "Sin personal disponible",
+ "no_notes": "Sin notas disponibles",
+ "no_activity": "Sin actividad reciente",
+ "current_channel": "Canal Actual",
+ "audio_stream": "Transmisión de Audio",
+ "no_stream": "Sin transmisión activa",
+ "ptt": "PTT",
+ "ptt_start": "Inicio PTT",
+ "ptt_end": "Fin PTT",
+ "transmitting_on": "Transmitiendo en {{channel}}",
+ "transmission_ended": "Transmisión finalizada",
+ "voice_disabled": "Voz deshabilitada",
+ "disconnected": "Desconectado",
+ "select_channel": "Seleccionar Canal",
+ "select_channel_description": "Elija un canal de voz para conectarse",
+ "change_channel_warning": "Seleccionar un nuevo canal desconectará del actual",
+ "default_channel": "Predeterminado",
+ "no_channels_available": "No hay canales de voz disponibles",
+ "system_update": "Actualización del Sistema",
+ "data_refreshed": "Datos actualizados desde el servidor",
+ "call_selected": "Llamada Seleccionada",
+ "unit_selected": "Unidad Seleccionada",
+ "unit_deselected": "Unidad Deseleccionada",
+ "personnel_selected": "Personal Seleccionado",
+ "personnel_deselected": "Personal Deseleccionado",
+ "loading_map": "Cargando mapa...",
+ "map_not_available_web": "Mapa no disponible en plataforma web",
+ "filtering_by_call": "Filtrando por llamada",
+ "clear_filter": "Limpiar Filtro",
+ "call_filter_active": "Filtro de Llamada Activo",
+ "call_filter_cleared": "Filtro de Llamada Limpiado",
+ "showing_all_data": "Mostrando todos los datos",
+ "call_notes": "Notas de Llamada",
+ "no_call_notes": "Sin notas de llamada",
+ "add_call_note_placeholder": "Añadir una nota...",
+ "note_added": "Nota Añadida",
+ "note_added_to_console": "Se ha añadido una nueva nota a la consola",
+ "add_note_title": "Añadir Nueva Nota",
+ "note_title_label": "Título",
+ "note_title_placeholder": "Ingrese el título de la nota...",
+ "note_category_label": "Categoría",
+ "note_category_placeholder": "Seleccione una categoría",
+ "note_no_category": "Sin Categoría",
+ "note_body_label": "Contenido de la Nota",
+ "note_body_placeholder": "Ingrese el contenido de la nota...",
+ "note_save_error": "Error al guardar la nota: {{error}}",
+ "note_created": "Nota Creada",
+ "units_on_call": "Unidades en Llamada",
+ "no_units_on_call": "Sin unidades en llamada",
+ "personnel_on_call": "Personal en Llamada",
+ "no_personnel_on_call": "Sin personal en llamada",
+ "call_activity": "Actividad de Llamada",
+ "no_call_activity": "Sin actividad de llamada",
+ "on_call": "En Llamada",
+ "filtered": "Filtrado",
+ "active_filter": "Filtro Activo",
+ "unit_status_change": "Cambio de Estado de Unidad",
+ "personnel_status_change": "Cambio de Estado de Personal",
+ "view_call_details": "Ver Detalles de Llamada",
+ "dispatched_resources": "Despachados",
+ "unassigned": "Sin Asignar",
+ "available": "Disponible",
+ "unknown": "Desconocido",
+ "search_personnel_placeholder": "Buscar personal...",
+ "search_calls_placeholder": "Buscar llamadas...",
+ "search_units_placeholder": "Buscar unidades...",
+ "search_notes_placeholder": "Buscar notas...",
+ "signalr_update": "Actualización en Tiempo Real",
+ "signalr_connected": "Conectado",
+ "realtime_updates_active": "Las actualizaciones en tiempo real están activas",
+ "personnel_status_updated": "Estado de personal actualizado",
+ "personnel_staffing_updated": "Dotación de personal actualizada",
+ "unit_status_updated": "Estado de unidad actualizado",
+ "calls_updated": "Llamadas actualizadas",
+ "call_added": "Nueva llamada añadida",
+ "call_closed": "Llamada cerrada",
+ "check_ins": "Registros",
+ "no_check_ins": "No hay llamadas con temporizadores de registro",
+ "radio_log": "Registro de Radio",
+ "radio": "Radio",
+ "activity": "Actividad",
+ "actions": "Acciones",
+ "no_radio_activity": "Sin transmisiones de radio",
+ "live": "EN VIVO",
+ "currently_transmitting": "Transmitiendo actualmente...",
+ "duration": "Duración",
+ "call_actions": "Acciones de Llamada",
+ "unit_actions": "Acciones de Unidad",
+ "personnel_actions": {
+ "title": "Acciones de Personal",
+ "status_tab": "Estado",
+ "staffing_tab": "Dotación",
+ "select_status": "Seleccionar Estado",
+ "select_staffing": "Seleccionar Nivel de Dotación",
+ "destination": "Destino",
+ "no_destination": "Sin Destino",
+ "note": "Nota",
+ "note_placeholder": "Añadir una nota opcional...",
+ "update_status": "Actualizar Estado",
+ "update_staffing": "Actualizar Dotación",
+ "no_statuses_available": "No hay estados disponibles",
+ "no_staffings_available": "No hay niveles de dotación disponibles"
+ },
+ "unit_actions_panel": {
+ "status": "Estado",
+ "select_status": "Seleccionar Estado",
+ "destination": "Destino",
+ "no_destination": "Sin Destino",
+ "note": "Nota",
+ "note_placeholder": "Añadir una nota opcional...",
+ "update_status": "Actualizar Estado",
+ "no_statuses_available": "No hay estados disponibles",
+ "no_active_calls": "No hay llamadas activas",
+ "no_stations_available": "No hay estaciones disponibles",
+ "no_destinations_available": "No hay destinos disponibles"
+ },
+ "call": "Llamada",
+ "station": "Estación",
+ "calls": "Llamadas",
+ "stations": "Estaciones",
+ "no_stations_available": "No hay estaciones disponibles",
+ "new_call": "Nueva Llamada",
+ "view_details": "Detalles",
+ "add_note": "Añadir Nota",
+ "close_call": "Cerrar",
+ "set_status": "Estado",
+ "set_staffing": "Dotación",
+ "dispatch": "Despachar",
+ "select_items_for_actions": "Selecciona una llamada, unidad o personal para habilitar acciones contextuales",
+ "weather": {
+ "clear": "Despejado",
+ "mainly_clear": "Mayormente Despejado",
+ "partly_cloudy": "Parcialmente Nublado",
+ "overcast": "Nublado",
+ "fog": "Niebla",
+ "drizzle": "Llovizna",
+ "freezing_drizzle": "Llovizna Helada",
+ "rain": "Lluvia",
+ "freezing_rain": "Lluvia Helada",
+ "snow": "Nieve",
+ "rain_showers": "Chubascos",
+ "snow_showers": "Nevadas",
+ "thunderstorm": "Tormenta Eléctrica",
+ "thunderstorm_hail": "Tormenta con Granizo",
+ "unknown": "Desconocido"
+ },
+ "available_only": "Solo disponibles",
+ "single_list": "Lista única",
+ "resources": "Recursos",
+ "search_resources_placeholder": "Buscar recursos...",
+ "no_resources": "No hay recursos"
+ },
+ "form": {
+ "invalid_url": "Por favor, introduce una URL válida que comience con http:// o https://",
+ "required": "Este campo es obligatorio"
+ },
+ "incident_command": {
+ "accountability": "Control de personal (PAR)",
+ "acknowledge": "Confirmar",
+ "action_plan": "Plan de acción",
+ "action_plan_placeholder": "Describa el plan de acción del incidente...",
+ "active": "Activo",
+ "active_title": "Mandos de incidentes activos",
+ "add": "Añadir",
+ "add_channel": "Añadir canal",
+ "add_lane": "Añadir sección",
+ "add_marker": "Añadir marcador",
+ "add_objective": "Añadir objetivo",
+ "annotations": "Anotaciones del mapa",
+ "assign": "Asignar",
+ "assign_resource": "Asignar recurso",
+ "assign_resource_required": "Seleccione una sección y un recurso",
+ "assign_role": "Asignar función",
+ "assign_role_required": "Seleccione una persona y una función",
+ "call": "Llamada",
+ "channel_name": "Nombre del canal",
+ "chat": "Chat",
+ "chat_frozen": "This incident is closed. Conversations are kept as a point-in-time record — no new messages.",
+ "close_all_channels": "Cerrar todos los canales",
+ "close_command": "Cerrar mando",
+ "closed": "Cerrado",
+ "command_channel": "Command chat",
+ "command_channel_hint": "Command staff and dispatch",
+ "command_channel_unavailable": "No command channel has been created for this incident yet.",
+ "commander": "Comandante",
+ "complete": "Completar",
+ "completed": "Completado",
+ "confirm_close": "¿Cerrar el mando del incidente para esta llamada?",
+ "critical": "Crítico",
+ "delete_annotation_confirm": "¿Eliminar esta anotación?",
+ "dispatch_channel": "Dispatch",
+ "dispatch_channel_hint": "The incident's line to the desk",
+ "dispatch_channel_unavailable": "No dispatch channel has been created for this incident yet.",
+ "dm_failed": "Couldn't open that conversation.",
+ "dm_unavailable": "That contact has no Resgrid account to message.",
+ "due": "Vence",
+ "edit": "Editar",
+ "edit_action_plan": "Editar plan de acción",
+ "establish": "Establecer mando",
+ "establish_description": "Opcionalmente, inicialice el panel de mando a partir de una plantilla.",
+ "establish_error": "No se pudo establecer el mando",
+ "establish_success": "Mando del incidente establecido",
+ "establish_title": "Establecer mando del incidente",
+ "established_on": "Establecido",
+ "green": "Verde",
+ "hold_to_talk": "Mantener pulsado para hablar",
+ "incident_channel": "Incident chat",
+ "incident_channel_hint": "Everyone working the incident",
+ "incident_channel_unavailable": "No incident channel has been created for this call yet.",
+ "join": "Unirse",
+ "lane": "Sección",
+ "lane_name": "Nombre de la sección",
+ "lane_type": "Tipo de sección",
+ "marker": "Marcador",
+ "marker_label": "Etiqueta del marcador",
+ "move": "Mover",
+ "move_lane": "Mover sección",
+ "move_resource": "Mover recurso",
+ "name_required": "El nombre es obligatorio",
+ "no_accountability": "No hay personal registrado.",
+ "no_action_plan": "No se ha establecido ningún plan de acción.",
+ "no_active": "No hay mandos de incidentes activos",
+ "no_active_description": "Los mandos establecidos en llamadas aparecerán aquí.",
+ "no_annotations": "No hay anotaciones.",
+ "no_channels": "No hay canales abiertos.",
+ "no_command": "No se ha establecido el mando del incidente",
+ "no_command_description": "Establezca el mando del incidente para coordinar recursos, funciones, objetivos y control de personal para esta llamada.",
+ "no_lanes": "No hay secciones definidas.",
+ "no_objectives": "No hay objetivos.",
+ "no_resources": "No hay recursos asignados.",
+ "no_roles": "No hay funciones asignadas.",
+ "no_template": "Sin plantilla (panel vacío)",
+ "no_timeline": "No hay entradas en la cronología.",
+ "no_timers": "No hay temporizadores activos.",
+ "not_authorized": "Command board unavailable",
+ "not_authorized_description": "Your department hasn't authorized you to work incident command. Ask an administrator for the Command App Login permission.",
+ "objective_name": "Objetivo",
+ "objective_type": "Tipo",
+ "objectives": "Objetivos",
+ "open_chat": "Open",
+ "open_full_board": "Abrir panel completo",
+ "open_tactical_map": "Abrir mapa táctico",
+ "parent_lane": "Sección superior",
+ "person": "Persona",
+ "personnel": "Personal",
+ "release": "Liberar",
+ "resource": "Recurso",
+ "resource_type": "Tipo de recurso",
+ "role": "Función",
+ "roles": "Funciones de mando",
+ "run_par": "Ejecutar PAR",
+ "save": "Guardar",
+ "save_error": "La operación falló",
+ "saved": "Guardado",
+ "select_lane": "Seleccione una sección",
+ "select_person": "Seleccione una persona",
+ "select_resource": "Seleccione un recurso",
+ "select_role": "Seleccione una función",
+ "send_message": "Message",
+ "status": "Estado",
+ "structure": "Estructura de mando",
+ "tab_title": "Mando",
+ "tactical_map": "Mapa táctico",
+ "talking": "Transmitiendo...",
+ "tap_to_place": "Toque el mapa para colocar un marcador",
+ "template": "Plantilla",
+ "timeline": "Cronología del mando",
+ "timers": "Temporizadores",
+ "title": "Mando del incidente",
+ "top_level": "Nivel superior",
+ "transfer": "Transferir",
+ "transfer_command": "Transferir",
+ "transfer_notes": "Notas",
+ "transfer_success": "Mando transferido",
+ "transfer_title": "Transferir el mando",
+ "unassigned": "Sin asignar",
+ "unit": "Unidad",
+ "voice_channels": "Canales de voz",
+ "voice_join_error": "No se pudo unir al canal de voz",
+ "voice_joined": "Se unió al canal de voz",
+ "warning": "Advertencia"
+ },
+ "livekit": {
+ "audio_devices": "Dispositivos de audio",
+ "audio_settings": "Configuración de audio",
+ "connected_to_room": "Conectado al canal",
+ "connecting": "Conectando...",
+ "disconnect": "Desconectar",
+ "join": "Unirse",
+ "microphone": "Micrófono",
+ "mute": "Silenciar",
+ "no_rooms_available": "No hay canales de voz disponibles",
+ "speaker": "Altavoz",
+ "speaking": "Hablando",
+ "title": "Canales de voz",
+ "unmute": "Activar sonido"
+ },
+ "loading": {
+ "loading": "Cargando...",
+ "loadingData": "Cargando datos...",
+ "pleaseWait": "Por favor, espere",
+ "processingRequest": "Procesando su solicitud..."
+ },
+ "lockscreen": {
+ "message": "Ingrese su contraseña para desbloquear la pantalla",
+ "not_you": "¿No eres tú? Volver al inicio de sesión",
+ "password": "Contraseña",
+ "password_placeholder": "Ingrese su contraseña",
+ "title": "Pantalla de Bloqueo",
+ "unlock_button": "Desbloquear",
+ "unlock_failed": "Error al desbloquear. Por favor, inténtelo de nuevo.",
+ "unlocking": "Desbloqueando...",
+ "welcome_back": "Bienvenido de Nuevo",
+ "relogin_required": "La verificación de contraseña no está disponible para esta sesión. Por favor, inicia sesión de nuevo."
+ },
+ "login": {
+ "branding_subtitle": "Software de despacho potente para socorristas, búsqueda y rescate, y organizaciones de seguridad pública.",
+ "branding_title": "Gestión de Respuesta de Emergencia",
+ "dispatch_not_authorized": "You are not authorized to use the Dispatch app. Contact your department administrator.",
+ "errorModal": {
+ "confirmButton": "Aceptar",
+ "message": "Por favor, comprueba tu nombre de usuario y contraseña e inténtalo de nuevo.",
+ "title": "Inicio de sesión fallido"
+ },
+ "feature_dispatch_desc": "Despacha unidades y gestiona llamadas al instante con actualizaciones en vivo en todos los dispositivos.",
+ "feature_dispatch_title": "Despacho en Tiempo Real",
+ "feature_mapping_desc": "Rastrea unidades en tiempo real con mapas detallados, enrutamiento y gestión de ubicación.",
+ "feature_mapping_title": "Mapeo Avanzado",
+ "feature_personnel_desc": "Gestiona tu equipo con acceso basado en roles, seguimiento de estado y herramientas de comunicación.",
+ "feature_personnel_title": "Gestión de Personal",
+ "footer_text": "Creado con ❤️ en Lake Tahoe",
+ "login": "Iniciar sesión",
+ "login_button": "Iniciar sesión",
+ "login_button_description": "Inicia sesión en tu cuenta para continuar",
+ "login_button_error": "Error al iniciar sesión",
+ "login_button_loading": "Iniciando sesión...",
+ "login_button_success": "Sesión iniciada con éxito",
+ "no_account": "¿No tienes cuenta?",
+ "page_subtitle": "Introduce tus credenciales para iniciar sesión.",
+ "page_title": "Resgrid Dispatch",
+ "password": "Contraseña",
+ "password_incorrect": "La contraseña era incorrecta",
+ "password_placeholder": "Introduce tu contraseña",
+ "register": "Registrarse",
+ "title": "Iniciar sesión",
+ "username": "Nombre de usuario",
+ "username_placeholder": "Introduce tu nombre de usuario",
+ "welcome_title": "Bienvenido de Nuevo"
+ },
+ "maintenance": {
+ "downtime_message": "Estamos trabajando arduamente para completar el mantenimiento lo antes posible. Por favor, vuelva pronto.",
+ "downtime_title": "¿Cuál es el tiempo de inactividad?",
+ "message": "Por favor, vuelva en algún momento.",
+ "support_message": "Si necesita asistencia, contáctenos en",
+ "support_title": "¿Necesita soporte?",
+ "title": "Sitio en Mantenimiento",
+ "why_down_message": "Estamos realizando mantenimiento programado para mejorar su experiencia. Nos disculpamos por cualquier inconveniente.",
+ "why_down_title": "¿Por qué está el sitio inactivo?"
+ },
+ "map": {
+ "call_set_as_current": "Llamada establecida como llamada actual",
+ "failed_to_open_maps": "Error al abrir la aplicación de mapas",
+ "failed_to_set_current_call": "Error al establecer la llamada como llamada actual",
+ "no_location_for_routing": "No hay datos de ubicación disponibles para el enrutamiento",
+ "pin_color": "Color del pin",
+ "recenter_map": "Recentrar mapa",
+ "set_as_current_call": "Establecer como llamada actual",
+ "view_call_details": "Ver detalles de la llamada",
+ "view_poi_details": "Ver detalles del PDI",
+ "layers": "Capas del Mapa",
+ "no_layers": "No hay capas disponibles",
+ "show_all": "Mostrar Todo",
+ "hide_all": "Ocultar Todo"
+ },
+ "menu": {
"calls": "Llamadas",
"calls_list": "Lista de Llamadas",
"scheduled_calls": "Llamadas Programadas",
@@ -659,6 +1029,42 @@
"chat": "Chat",
"assistant": "Asistente"
},
+ "notes": {
+ "actions": {
+ "add": "Añadir nota",
+ "delete_confirm": "¿Estás seguro de que quieres eliminar esta nota?"
+ },
+ "details": {
+ "close": "Cerrar",
+ "created": "Creado",
+ "delete": "Eliminar",
+ "edit": "Editar",
+ "tags": "Etiquetas",
+ "title": "Detalles de la nota",
+ "updated": "Actualizado"
+ },
+ "empty": "No se encontraron notas",
+ "emptyDescription": "No se han creado notas para tu departamento todavía.",
+ "search": "Buscar notas...",
+ "title": "Notas"
+ },
+ "onboarding": {
+ "screen1": {
+ "title": "Resgrid Dispatch",
+ "description": "Cree, despache y gestione llamadas de emergencia con un potente centro de comando móvil al alcance de su mano"
+ },
+ "screen2": {
+ "title": "Conciencia Situacional en Tiempo Real",
+ "description": "Rastree todas las unidades, personal y recursos en un mapa interactivo con actualizaciones de estado en vivo y AVL"
+ },
+ "screen3": {
+ "title": "Coordinación Fluida",
+ "description": "Comuníquese instantáneamente con las unidades de campo, actualice los estados de llamadas y coordine los esfuerzos de respuesta desde cualquier lugar"
+ },
+ "skip": "Omitir",
+ "next": "Siguiente",
+ "getStarted": "Empecemos"
+ },
"personnel": {
"title": "Personal",
"search": "Buscar personal...",
@@ -727,37 +1133,6 @@
"unknown_type": "Tipo desconocido",
"unnamed": "PDI sin nombre"
},
- "scheduled_calls": {
- "title": "Llamadas Programadas",
- "loading": "Cargando llamadas programadas...",
- "no_scheduled_calls": "No hay llamadas programadas",
- "no_scheduled_calls_description": "No hay llamadas programadas pendientes en este momento.",
- "search": "Buscar llamadas programadas...",
- "scheduled_for": "Programada para",
- "table_number": "N.º de llamada",
- "table_name": "Nombre",
- "table_type": "Tipo",
- "table_priority": "Prioridad",
- "table_address": "Dirección",
- "table_scheduled": "Programada para"
- },
- "onboarding": {
- "screen1": {
- "title": "Resgrid Dispatch",
- "description": "Cree, despache y gestione llamadas de emergencia con un potente centro de comando móvil al alcance de su mano"
- },
- "screen2": {
- "title": "Conciencia Situacional en Tiempo Real",
- "description": "Rastree todas las unidades, personal y recursos en un mapa interactivo con actualizaciones de estado en vivo y AVL"
- },
- "screen3": {
- "title": "Coordinación Fluida",
- "description": "Comuníquese instantáneamente con las unidades de campo, actualice los estados de llamadas y coordine los esfuerzos de respuesta desde cualquier lugar"
- },
- "skip": "Omitir",
- "next": "Siguiente",
- "getStarted": "Empecemos"
- },
"protocols": {
"details": {
"close": "Cerrar",
@@ -796,6 +1171,20 @@
"tap_to_manage": "Toca para gestionar roles",
"unassigned": "Sin asignar"
},
+ "scheduled_calls": {
+ "title": "Llamadas Programadas",
+ "loading": "Cargando llamadas programadas...",
+ "no_scheduled_calls": "No hay llamadas programadas",
+ "no_scheduled_calls_description": "No hay llamadas programadas pendientes en este momento.",
+ "search": "Buscar llamadas programadas...",
+ "scheduled_for": "Programada para",
+ "table_number": "N.º de llamada",
+ "table_name": "Nombre",
+ "table_type": "Tipo",
+ "table_priority": "Prioridad",
+ "table_address": "Dirección",
+ "table_scheduled": "Programada para"
+ },
"settings": {
"about": "Acerca de",
"account": "Cuenta",
@@ -884,6 +1273,29 @@
"version": "Versión",
"website": "Sitio web"
},
+ "sso": {
+ "authenticating": "Autenticando...",
+ "back_to_login": "Volver al inicio de sesión",
+ "back_to_lookup": "Cambiar usuario",
+ "continue_button": "Continuar",
+ "department_id_label": "ID de departamento",
+ "department_id_placeholder": "Ingrese el ID de departamento",
+ "error_generic": "Error al iniciar sesión. Por favor intente de nuevo.",
+ "error_oidc_cancelled": "El inicio de sesión fue cancelado.",
+ "error_oidc_not_ready": "El proveedor SSO se está cargando, por favor espere.",
+ "error_sso_not_enabled": "El inicio de sesión único no está habilitado para este usuario.",
+ "error_token_exchange": "Error al completar el inicio de sesión. Por favor intente de nuevo.",
+ "error_user_not_found": "Usuario no encontrado. Por favor verifique e intente de nuevo.",
+ "looking_up": "Buscando...",
+ "optional": "opcional",
+ "page_subtitle": "Ingrese su nombre de usuario para ver las opciones de inicio de sesión de su organización.",
+ "page_title": "Inicio de sesión único",
+ "provider_oidc": "OpenID Connect (OIDC)",
+ "provider_saml": "SAML 2.0",
+ "sign_in_button": "Iniciar sesión con SSO",
+ "sign_in_title": "Iniciar sesión",
+ "sso_button": "Acceso SSO"
+ },
"status": {
"add_note": "Añadir Nota",
"all_destinations_enabled": "Puede responder a llamadas, estaciones o PDI",
@@ -929,211 +1341,6 @@
"shifts": "Turnos",
"personnel": "Personal"
},
- "dispatch": {
- "active_calls": "Llamadas Activas",
- "pending_calls": "Pendientes",
- "scheduled_calls": "Programadas",
- "units_available": "Disponibles",
- "personnel_available": "Disponibles",
- "personnel_on_duty": "De Servicio",
- "units": "Unidades",
- "personnel": "Personal",
- "map": "Mapa",
- "notes": "Notas",
- "activity_log": "Registro de Actividad",
- "communications": "Comunicaciones",
- "no_active_calls": "Sin llamadas activas",
- "no_units": "Sin unidades disponibles",
- "no_personnel": "Sin personal disponible",
- "no_notes": "Sin notas disponibles",
- "no_activity": "Sin actividad reciente",
- "current_channel": "Canal Actual",
- "audio_stream": "Transmisión de Audio",
- "no_stream": "Sin transmisión activa",
- "ptt": "PTT",
- "ptt_start": "Inicio PTT",
- "ptt_end": "Fin PTT",
- "transmitting_on": "Transmitiendo en {{channel}}",
- "transmission_ended": "Transmisión finalizada",
- "voice_disabled": "Voz deshabilitada",
- "disconnected": "Desconectado",
- "select_channel": "Seleccionar Canal",
- "select_channel_description": "Elija un canal de voz para conectarse",
- "change_channel_warning": "Seleccionar un nuevo canal desconectará del actual",
- "default_channel": "Predeterminado",
- "no_channels_available": "No hay canales de voz disponibles",
- "system_update": "Actualización del Sistema",
- "data_refreshed": "Datos actualizados desde el servidor",
- "call_selected": "Llamada Seleccionada",
- "unit_selected": "Unidad Seleccionada",
- "unit_deselected": "Unidad Deseleccionada",
- "personnel_selected": "Personal Seleccionado",
- "personnel_deselected": "Personal Deseleccionado",
- "loading_map": "Cargando mapa...",
- "map_not_available_web": "Mapa no disponible en plataforma web",
- "filtering_by_call": "Filtrando por llamada",
- "clear_filter": "Limpiar Filtro",
- "call_filter_active": "Filtro de Llamada Activo",
- "call_filter_cleared": "Filtro de Llamada Limpiado",
- "showing_all_data": "Mostrando todos los datos",
- "call_notes": "Notas de Llamada",
- "no_call_notes": "Sin notas de llamada",
- "add_call_note_placeholder": "Añadir una nota...",
- "note_added": "Nota Añadida",
- "note_added_to_console": "Se ha añadido una nueva nota a la consola",
- "add_note_title": "Añadir Nueva Nota",
- "note_title_label": "Título",
- "note_title_placeholder": "Ingrese el título de la nota...",
- "note_category_label": "Categoría",
- "note_category_placeholder": "Seleccione una categoría",
- "note_no_category": "Sin Categoría",
- "note_body_label": "Contenido de la Nota",
- "note_body_placeholder": "Ingrese el contenido de la nota...",
- "note_save_error": "Error al guardar la nota: {{error}}",
- "note_created": "Nota Creada",
- "units_on_call": "Unidades en Llamada",
- "no_units_on_call": "Sin unidades en llamada",
- "personnel_on_call": "Personal en Llamada",
- "no_personnel_on_call": "Sin personal en llamada",
- "call_activity": "Actividad de Llamada",
- "no_call_activity": "Sin actividad de llamada",
- "on_call": "En Llamada",
- "filtered": "Filtrado",
- "active_filter": "Filtro Activo",
- "unit_status_change": "Cambio de Estado de Unidad",
- "personnel_status_change": "Cambio de Estado de Personal",
- "view_call_details": "Ver Detalles de Llamada",
- "dispatched_resources": "Despachados",
- "unassigned": "Sin Asignar",
- "available": "Disponible",
- "unknown": "Desconocido",
- "search_personnel_placeholder": "Buscar personal...",
- "search_calls_placeholder": "Buscar llamadas...",
- "search_units_placeholder": "Buscar unidades...",
- "search_notes_placeholder": "Buscar notas...",
- "signalr_update": "Actualización en Tiempo Real",
- "signalr_connected": "Conectado",
- "realtime_updates_active": "Las actualizaciones en tiempo real están activas",
- "personnel_status_updated": "Estado de personal actualizado",
- "personnel_staffing_updated": "Dotación de personal actualizada",
- "unit_status_updated": "Estado de unidad actualizado",
- "calls_updated": "Llamadas actualizadas",
- "call_added": "Nueva llamada añadida",
- "call_closed": "Llamada cerrada",
- "check_ins": "Registros",
- "no_check_ins": "No hay llamadas con temporizadores de registro",
- "radio_log": "Registro de Radio",
- "radio": "Radio",
- "activity": "Actividad",
- "actions": "Acciones",
- "no_radio_activity": "Sin transmisiones de radio",
- "live": "EN VIVO",
- "currently_transmitting": "Transmitiendo actualmente...",
- "duration": "Duración",
- "call_actions": "Acciones de Llamada",
- "unit_actions": "Acciones de Unidad",
- "personnel_actions": {
- "title": "Acciones de Personal",
- "status_tab": "Estado",
- "staffing_tab": "Dotación",
- "select_status": "Seleccionar Estado",
- "select_staffing": "Seleccionar Nivel de Dotación",
- "destination": "Destino",
- "no_destination": "Sin Destino",
- "note": "Nota",
- "note_placeholder": "Añadir una nota opcional...",
- "update_status": "Actualizar Estado",
- "update_staffing": "Actualizar Dotación",
- "no_statuses_available": "No hay estados disponibles",
- "no_staffings_available": "No hay niveles de dotación disponibles"
- },
- "unit_actions_panel": {
- "status": "Estado",
- "select_status": "Seleccionar Estado",
- "destination": "Destino",
- "no_destination": "Sin Destino",
- "note": "Nota",
- "note_placeholder": "Añadir una nota opcional...",
- "update_status": "Actualizar Estado",
- "no_statuses_available": "No hay estados disponibles",
- "no_active_calls": "No hay llamadas activas",
- "no_stations_available": "No hay estaciones disponibles",
- "no_destinations_available": "No hay destinos disponibles"
- },
- "call": "Llamada",
- "station": "Estación",
- "calls": "Llamadas",
- "stations": "Estaciones",
- "no_stations_available": "No hay estaciones disponibles",
- "new_call": "Nueva Llamada",
- "view_details": "Detalles",
- "add_note": "Añadir Nota",
- "close_call": "Cerrar",
- "set_status": "Estado",
- "set_staffing": "Dotación",
- "dispatch": "Despachar",
- "select_items_for_actions": "Selecciona una llamada, unidad o personal para habilitar acciones contextuales",
- "weather": {
- "clear": "Despejado",
- "mainly_clear": "Mayormente Despejado",
- "partly_cloudy": "Parcialmente Nublado",
- "overcast": "Nublado",
- "fog": "Niebla",
- "drizzle": "Llovizna",
- "freezing_drizzle": "Llovizna Helada",
- "rain": "Lluvia",
- "freezing_rain": "Lluvia Helada",
- "snow": "Nieve",
- "rain_showers": "Chubascos",
- "snow_showers": "Nevadas",
- "thunderstorm": "Tormenta Eléctrica",
- "thunderstorm_hail": "Tormenta con Granizo",
- "unknown": "Desconocido"
- },
- "available_only": "Solo disponibles",
- "single_list": "Lista única",
- "resources": "Recursos",
- "search_resources_placeholder": "Buscar recursos...",
- "no_resources": "No hay recursos"
- },
- "check_in": {
- "tab_title": "Registro",
- "timer_status": "Estado del Temporizador",
- "perform_check_in": "Registrar",
- "check_in_success": "Registro realizado exitosamente",
- "check_in_error": "Error al realizar el registro",
- "checked_in_by": "por {{name}}",
- "last_check_in": "Último registro",
- "elapsed": "Transcurrido",
- "duration": "Duración",
- "status_ok": "OK",
- "status_green": "OK",
- "status_warning": "Advertencia",
- "status_yellow": "Advertencia",
- "status_overdue": "Vencido",
- "status_red": "Vencido",
- "status_critical": "Crítico",
- "history": "Historial de Registros",
- "no_timers": "No hay temporizadores de registro configurados",
- "timers_disabled": "Los temporizadores de registro están desactivados para esta llamada",
- "type_personnel": "Personal",
- "type_unit": "Unidad",
- "type_ic": "Comandante de Incidente",
- "type_par": "PAR",
- "type_hazmat": "Exposición a Materiales Peligrosos",
- "type_sector_rotation": "Rotación de Sector",
- "type_rehab": "Rehabilitación",
- "add_note": "Agregar Nota (Opcional)",
- "confirm": "Confirmar Registro",
- "minutes_ago": "hace {{count}} min",
- "select_target": "Seleccionar Entidad para Registrar",
- "overdue_count": "{{count}} Vencidos",
- "warning_count": "{{count}} Advertencias",
- "enable_timers": "Activar Temporizadores",
- "disable_timers": "Desactivar Temporizadores",
- "summary": "{{overdue}} vencidos, {{warning}} advertencias, {{ok}} ok",
- "par_title": "Control de personal (PAR)"
- },
"units": {
"search": "Buscar unidades...",
"loading": "Cargando unidades...",
@@ -1163,6 +1370,63 @@
"no_destination": "Ninguno",
"title": "Unidades"
},
+ "videoFeeds": {
+ "title": "Transmisiones de Video",
+ "noFeeds": "No hay transmisiones de video para esta llamada",
+ "addFeed": "Agregar Transmisión",
+ "editFeed": "Editar Transmisión",
+ "deleteFeed": "Eliminar Transmisión",
+ "deleteConfirm": "¿Está seguro de que desea eliminar esta transmisión de video?",
+ "watch": "Ver",
+ "goLive": "Transmitir",
+ "stopLive": "Detener",
+ "flipCamera": "Voltear Cámara",
+ "feedAdded": "Transmisión de video agregada",
+ "feedUpdated": "Transmisión de video actualizada",
+ "feedDeleted": "Transmisión de video eliminada",
+ "feedError": "Error al cargar la transmisión de video",
+ "unsupportedFormat": "Este formato de transmisión no es compatible en dispositivos móviles",
+ "copyUrl": "Copiar URL",
+ "form": {
+ "name": "Nombre de la Transmisión",
+ "namePlaceholder": "ej. Dron Motor 1",
+ "url": "URL de Transmisión",
+ "urlPlaceholder": "ej. https://stream.example.com/live.m3u8",
+ "feedType": "Tipo de Cámara",
+ "feedFormat": "Formato de Transmisión",
+ "description": "Descripción",
+ "descriptionPlaceholder": "Descripción opcional",
+ "status": "Estado",
+ "sortOrder": "Orden",
+ "cameraLocation": "Ubicación de la Cámara",
+ "useCurrentLocation": "Usar Ubicación Actual"
+ },
+ "type": {
+ "drone": "Dron",
+ "fixedCamera": "Cámara Fija",
+ "bodyCam": "Cámara Corporal",
+ "trafficCam": "Cámara de Tráfico",
+ "weatherCam": "Cámara Meteorológica",
+ "satelliteFeed": "Transmisión Satelital",
+ "webCam": "Cámara Web",
+ "other": "Otro"
+ },
+ "format": {
+ "rtsp": "RTSP",
+ "hls": "HLS",
+ "mjpeg": "MJPEG",
+ "youtubeLive": "YouTube Live",
+ "webrtc": "WebRTC",
+ "dash": "DASH",
+ "embed": "Embed",
+ "other": "Otro"
+ },
+ "status": {
+ "active": "Activo",
+ "inactive": "Inactivo",
+ "error": "Error"
+ }
+ },
"weatherAlerts": {
"title": "Alertas Meteorológicas",
"activeAlerts": "Alertas Activas",
@@ -1270,251 +1534,5 @@
},
"stats_label": "Alertas Meteorológicas"
},
- "videoFeeds": {
- "title": "Transmisiones de Video",
- "noFeeds": "No hay transmisiones de video para esta llamada",
- "addFeed": "Agregar Transmisión",
- "editFeed": "Editar Transmisión",
- "deleteFeed": "Eliminar Transmisión",
- "deleteConfirm": "¿Está seguro de que desea eliminar esta transmisión de video?",
- "watch": "Ver",
- "goLive": "Transmitir",
- "stopLive": "Detener",
- "flipCamera": "Voltear Cámara",
- "feedAdded": "Transmisión de video agregada",
- "feedUpdated": "Transmisión de video actualizada",
- "feedDeleted": "Transmisión de video eliminada",
- "feedError": "Error al cargar la transmisión de video",
- "unsupportedFormat": "Este formato de transmisión no es compatible en dispositivos móviles",
- "copyUrl": "Copiar URL",
- "form": {
- "name": "Nombre de la Transmisión",
- "namePlaceholder": "ej. Dron Motor 1",
- "url": "URL de Transmisión",
- "urlPlaceholder": "ej. https://stream.example.com/live.m3u8",
- "feedType": "Tipo de Cámara",
- "feedFormat": "Formato de Transmisión",
- "description": "Descripción",
- "descriptionPlaceholder": "Descripción opcional",
- "status": "Estado",
- "sortOrder": "Orden",
- "cameraLocation": "Ubicación de la Cámara",
- "useCurrentLocation": "Usar Ubicación Actual"
- },
- "type": {
- "drone": "Dron",
- "fixedCamera": "Cámara Fija",
- "bodyCam": "Cámara Corporal",
- "trafficCam": "Cámara de Tráfico",
- "weatherCam": "Cámara Meteorológica",
- "satelliteFeed": "Transmisión Satelital",
- "webCam": "Cámara Web",
- "other": "Otro"
- },
- "format": {
- "rtsp": "RTSP",
- "hls": "HLS",
- "mjpeg": "MJPEG",
- "youtubeLive": "YouTube Live",
- "webrtc": "WebRTC",
- "dash": "DASH",
- "embed": "Embed",
- "other": "Otro"
- },
- "status": {
- "active": "Activo",
- "inactive": "Inactivo",
- "error": "Error"
- }
- },
- "welcome": "Bienvenido al sitio de la aplicación obytes",
- "incident_command": {
- "tab_title": "Mando",
- "title": "Mando del incidente",
- "open_full_board": "Abrir panel completo",
- "no_command": "No se ha establecido el mando del incidente",
- "no_command_description": "Establezca el mando del incidente para coordinar recursos, funciones, objetivos y control de personal para esta llamada.",
- "establish": "Establecer mando",
- "establish_title": "Establecer mando del incidente",
- "establish_description": "Opcionalmente, inicialice el panel de mando a partir de una plantilla.",
- "establish_success": "Mando del incidente establecido",
- "establish_error": "No se pudo establecer el mando",
- "template": "Plantilla",
- "no_template": "Sin plantilla (panel vacío)",
- "saved": "Guardado",
- "save_error": "La operación falló",
- "edit_action_plan": "Editar plan de acción",
- "action_plan": "Plan de acción",
- "action_plan_placeholder": "Describa el plan de acción del incidente...",
- "save": "Guardar",
- "no_action_plan": "No se ha establecido ningún plan de acción.",
- "add": "Añadir",
- "add_objective": "Añadir objetivo",
- "objective_name": "Objetivo",
- "objective_type": "Tipo",
- "name_required": "El nombre es obligatorio",
- "add_lane": "Añadir sección",
- "lane_name": "Nombre de la sección",
- "lane_type": "Tipo de sección",
- "assign_resource": "Asignar recurso",
- "assign_resource_required": "Seleccione una sección y un recurso",
- "lane": "Sección",
- "select_lane": "Seleccione una sección",
- "resource_type": "Tipo de recurso",
- "resource": "Recurso",
- "select_resource": "Seleccione un recurso",
- "unit": "Unidad",
- "personnel": "Personal",
- "assign": "Asignar",
- "assign_role": "Asignar función",
- "assign_role_required": "Seleccione una persona y una función",
- "person": "Persona",
- "select_person": "Seleccione una persona",
- "role": "Función",
- "select_role": "Seleccione una función",
- "transfer_title": "Transferir el mando",
- "transfer_notes": "Notas",
- "transfer": "Transferir",
- "transfer_command": "Transferir",
- "transfer_success": "Mando transferido",
- "close_command": "Cerrar mando",
- "confirm_close": "¿Cerrar el mando del incidente para esta llamada?",
- "status": "Estado",
- "active": "Activo",
- "closed": "Cerrado",
- "commander": "Comandante",
- "established_on": "Establecido",
- "edit": "Editar",
- "roles": "Funciones de mando",
- "no_roles": "No hay funciones asignadas.",
- "structure": "Estructura de mando",
- "no_lanes": "No hay secciones definidas.",
- "no_resources": "No hay recursos asignados.",
- "release": "Liberar",
- "objectives": "Objetivos",
- "no_objectives": "No hay objetivos.",
- "complete": "Completar",
- "completed": "Completado",
- "timers": "Temporizadores",
- "no_timers": "No hay temporizadores activos.",
- "acknowledge": "Confirmar",
- "due": "Vence",
- "accountability": "Control de personal (PAR)",
- "run_par": "Ejecutar PAR",
- "green": "Verde",
- "warning": "Advertencia",
- "critical": "Crítico",
- "no_accountability": "No hay personal registrado.",
- "timeline": "Cronología del mando",
- "no_timeline": "No hay entradas en la cronología.",
- "unassigned": "Sin asignar",
- "active_title": "Mandos de incidentes activos",
- "no_active": "No hay mandos de incidentes activos",
- "no_active_description": "Los mandos establecidos en llamadas aparecerán aquí.",
- "call": "Llamada",
- "tactical_map": "Mapa táctico",
- "annotations": "Anotaciones del mapa",
- "no_annotations": "No hay anotaciones.",
- "open_tactical_map": "Abrir mapa táctico",
- "marker": "Marcador",
- "add_marker": "Añadir marcador",
- "tap_to_place": "Toque el mapa para colocar un marcador",
- "marker_label": "Etiqueta del marcador",
- "delete_annotation_confirm": "¿Eliminar esta anotación?",
- "voice_channels": "Canales de voz",
- "no_channels": "No hay canales abiertos.",
- "add_channel": "Añadir canal",
- "channel_name": "Nombre del canal",
- "close_all_channels": "Cerrar todos los canales",
- "join": "Unirse",
- "hold_to_talk": "Mantener pulsado para hablar",
- "talking": "Transmitiendo...",
- "voice_joined": "Se unió al canal de voz",
- "voice_join_error": "No se pudo unir al canal de voz",
- "move": "Mover",
- "move_lane": "Mover sección",
- "parent_lane": "Sección superior",
- "top_level": "Nivel superior",
- "move_resource": "Mover recurso"
- },
- "chat": {
- "title": "Chat",
- "assistant": "Asistente",
- "empty": "Aún no hay conversaciones. Inicia un mensaje directo o crea un grupo.",
- "section_assistant": "Asistente",
- "section_direct_messages": "Mensajes directos",
- "section_channels": "Canales",
- "section_incidents": "Incidentes",
- "new_direct_message": "Nuevo mensaje directo",
- "new_group": "Nuevo grupo",
- "open_assistant": "Abrir asistente",
- "create_conversation_failed": "No se pudo iniciar la conversación",
- "group_name": "Nombre del grupo",
- "search_people": "Buscar personas",
- "no_people": "No se encontraron personas",
- "create_group_with": "Crear grupo ({{count}})",
- "message_deleted": "Este mensaje fue eliminado",
- "urgent": "Urgente",
- "urgent_will_send": "Este mensaje se enviará como urgente",
- "shared_location": "Ubicación compartida",
- "thread_replies": "{{count}} respuestas",
- "edited": "(editado)",
- "failed_tap_retry": "Error: toca para reintentar",
- "type_a_message": "Escribe un mensaje",
- "emoji": "Emoji",
- "add_image": "Agregar imagen",
- "add_gif": "Agregar GIF",
- "share_location": "Compartir ubicación",
- "send": "Enviar",
- "someone": "Alguien",
- "is_typing": "{{name}} está escribiendo...",
- "are_typing": "{{count}} personas están escribiendo...",
- "permission_photos_denied": "Permiso de la biblioteca de fotos denegado",
- "permission_location_denied": "Permiso de ubicación denegado",
- "search_gifs": "Buscar GIFs",
- "no_gifs": "No se encontraron GIFs",
- "flag_reason": "¿Por qué lo reportas?",
- "flag_inappropriate": "Inapropiado",
- "flag_harassment": "Acoso",
- "flag_spam": "Spam",
- "flag_sensitive": "Información sensible",
- "flag_policy": "Violación de la política",
- "flag_other": "Otro",
- "reply_in_thread": "Responder en el hilo",
- "copy": "Copiar",
- "copied": "Copiado",
- "copy_unavailable": "Copiar no está disponible en este dispositivo",
- "edit": "Editar",
- "edit_message": "Editar mensaje",
- "save": "Guardar",
- "delete": "Eliminar",
- "pin": "Fijar",
- "unpin": "Dejar de fijar",
- "flag": "Reportar",
- "moderator_delete": "Eliminar (moderador)",
- "moderator_removed": "Eliminado por el moderador",
- "attachment_failed": "Error al subir el adjunto",
- "ack_required": "Confirmación requerida",
- "ack_pending_one": "Tienes un mensaje urgente por confirmar",
- "ack_pending_count": "Tienes {{count}} mensajes urgentes por confirmar",
- "acknowledge": "Confirmar",
- "thread": "Hilo",
- "original_message": "Mensaje original",
- "reply_placeholder": "Responder...",
- "channel": "Canal",
- "direct_message": "Mensaje directo",
- "load_people_failed": "No se pudieron cargar las personas",
- "reaction_failed": "No se pudo actualizar la reacción",
- "edit_failed": "No se pudo editar el mensaje",
- "delete_failed": "No se pudo eliminar el mensaje",
- "pin_failed": "No se pudo actualizar el mensaje fijado",
- "flag_failed": "No se pudo reportar el mensaje"
- },
- "chatbot": {
- "title": "Asistente",
- "subtitle": "Asistente de IA para tu departamento",
- "new_session": "Nueva sesión",
- "empty": "Pregúntale cualquier cosa al asistente para comenzar.",
- "ask_placeholder": "Pregúntale al asistente..."
- }
+ "welcome": "Bienvenido al sitio de la aplicación obytes"
}
diff --git a/src/translations/fr.json b/src/translations/fr.json
index 81bddce4..b8c5bad8 100644
--- a/src/translations/fr.json
+++ b/src/translations/fr.json
@@ -371,6 +371,124 @@
"audio_name": "Extrait audio"
}
},
+ "chat": {
+ "title": "Chat",
+ "assistant": "Assistant",
+ "empty": "Aucune conversation pour le moment. Démarrez un message direct ou créez un groupe.",
+ "section_assistant": "Assistant",
+ "section_direct_messages": "Messages directs",
+ "section_channels": "Canaux",
+ "section_incidents": "Incidents",
+ "new_direct_message": "Nouveau message direct",
+ "new_group": "Nouveau groupe",
+ "open_assistant": "Ouvrir l'assistant",
+ "create_conversation_failed": "Impossible de démarrer la conversation",
+ "group_name": "Nom du groupe",
+ "search_people": "Rechercher des personnes",
+ "no_people": "Aucune personne trouvée",
+ "create_group_with": "Créer un groupe ({{count}})",
+ "message_deleted": "Ce message a été supprimé",
+ "urgent": "Urgent",
+ "urgent_will_send": "Ce message sera envoyé comme urgent",
+ "shared_location": "Position partagée",
+ "thread_replies": "{{count}} réponses",
+ "edited": "(modifié)",
+ "failed_tap_retry": "Échec – appuyez pour réessayer",
+ "type_a_message": "Saisir un message",
+ "emoji": "Emoji",
+ "add_image": "Ajouter une image",
+ "add_gif": "Ajouter un GIF",
+ "share_location": "Partager la position",
+ "send": "Envoyer",
+ "someone": "Quelqu'un",
+ "is_typing": "{{name}} est en train d'écrire...",
+ "are_typing": "{{count}} personnes sont en train d'écrire...",
+ "permission_photos_denied": "Autorisation d'accès à la photothèque refusée",
+ "permission_location_denied": "Autorisation de localisation refusée",
+ "search_gifs": "Rechercher des GIF",
+ "no_gifs": "Aucun GIF trouvé",
+ "flag_reason": "Pourquoi le signalez-vous ?",
+ "flag_inappropriate": "Inapproprié",
+ "flag_harassment": "Harcèlement",
+ "flag_spam": "Spam",
+ "flag_sensitive": "Informations sensibles",
+ "flag_policy": "Violation de la politique",
+ "flag_other": "Autre",
+ "reply_in_thread": "Répondre dans le fil",
+ "copy": "Copier",
+ "copied": "Copié",
+ "copy_unavailable": "La copie n'est pas disponible sur cet appareil",
+ "edit": "Modifier",
+ "edit_message": "Modifier le message",
+ "save": "Enregistrer",
+ "delete": "Supprimer",
+ "pin": "Épingler",
+ "unpin": "Désépingler",
+ "flag": "Signaler",
+ "moderator_delete": "Supprimer (modérateur)",
+ "moderator_removed": "Supprimé par le modérateur",
+ "attachment_failed": "Échec de l'envoi de la pièce jointe",
+ "ack_required": "Accusé de réception requis",
+ "ack_pending_one": "Vous avez un message urgent à accuser réception",
+ "ack_pending_count": "Vous avez {{count}} messages urgents à accuser réception",
+ "acknowledge": "Accuser réception",
+ "thread": "Fil",
+ "original_message": "Message d'origine",
+ "reply_placeholder": "Répondre...",
+ "channel": "Canal",
+ "direct_message": "Message direct",
+ "load_people_failed": "Impossible de charger les personnes",
+ "reaction_failed": "Impossible de mettre à jour la réaction",
+ "edit_failed": "Impossible de modifier le message",
+ "delete_failed": "Impossible de supprimer le message",
+ "pin_failed": "Impossible de mettre à jour l'épinglage",
+ "flag_failed": "Impossible de signaler le message"
+ },
+ "chatbot": {
+ "title": "Assistant",
+ "subtitle": "Assistant IA pour votre service",
+ "new_session": "Nouvelle session",
+ "empty": "Posez n'importe quelle question à l'assistant pour commencer.",
+ "ask_placeholder": "Demandez à l'assistant..."
+ },
+ "check_in": {
+ "tab_title": "Pointage",
+ "timer_status": "Statut de la minuterie",
+ "perform_check_in": "Pointer",
+ "check_in_success": "Pointage enregistré avec succès",
+ "check_in_error": "Échec de l'enregistrement du pointage",
+ "checked_in_by": "par {{name}}",
+ "last_check_in": "Dernier pointage",
+ "elapsed": "Écoulé",
+ "duration": "Durée",
+ "status_ok": "OK",
+ "status_green": "OK",
+ "status_warning": "Avertissement",
+ "status_yellow": "Avertissement",
+ "status_overdue": "En retard",
+ "status_red": "En retard",
+ "status_critical": "Critique",
+ "history": "Historique des pointages",
+ "no_timers": "Aucune minuterie de pointage configurée",
+ "timers_disabled": "Les minuteries de pointage sont désactivées pour cet appel",
+ "type_personnel": "Personnel",
+ "type_unit": "Unité",
+ "type_ic": "Commandant des opérations",
+ "type_par": "PAR",
+ "type_hazmat": "Exposition Hazmat",
+ "type_sector_rotation": "Rotation de secteur",
+ "type_rehab": "Réhabilitation",
+ "add_note": "Ajouter une note (optionnel)",
+ "confirm": "Confirmer le pointage",
+ "minutes_ago": "il y a {{count}} min",
+ "select_target": "Sélectionner l'entité à pointer",
+ "overdue_count": "{{count}} en retard",
+ "warning_count": "{{count}} avertissement",
+ "enable_timers": "Activer les minuteries",
+ "disable_timers": "Désactiver les minuteries",
+ "summary": "{{overdue}} en retard, {{warning}} avertissement, {{ok}} ok",
+ "par_title": "Suivi du personnel (PAR)"
+ },
"common": {
"add": "Ajouter",
"back": "Retour",
@@ -502,178 +620,379 @@
"website": "Site web",
"zip": "Code postal"
},
- "form": {
- "invalid_url": "Veuillez saisir une URL valide commençant par http:// ou https://",
- "required": "Ce champ est obligatoire"
- },
- "livekit": {
- "audio_devices": "Appareils audio",
- "audio_settings": "Paramètres audio",
- "connected_to_room": "Connecté au canal",
- "connecting": "Connexion en cours...",
- "disconnect": "Déconnecter",
- "join": "Rejoindre",
- "microphone": "Microphone",
- "mute": "Couper le son",
- "no_rooms_available": "Aucun canal vocal disponible",
- "speaker": "Haut-parleur",
- "speaking": "En train de parler",
- "title": "Canaux vocaux",
- "unmute": "Réactiver le son"
- },
- "loading": {
- "loading": "Chargement...",
- "loadingData": "Chargement des données...",
- "pleaseWait": "Veuillez patienter",
- "processingRequest": "Traitement de votre demande..."
- },
- "sso": {
- "authenticating": "Authentification en cours...",
- "back_to_login": "Retour à la connexion",
- "back_to_lookup": "Changer d'utilisateur",
- "continue_button": "Continuer",
- "department_id_label": "ID du service",
- "department_id_placeholder": "Saisissez l'ID du service",
- "error_generic": "La connexion a échoué. Veuillez réessayer.",
- "error_oidc_cancelled": "La connexion a été annulée.",
- "error_oidc_not_ready": "Le fournisseur SSO est en cours de chargement, veuillez patienter.",
- "error_sso_not_enabled": "L'authentification unique n'est pas activée pour cet utilisateur.",
- "error_token_exchange": "Échec de la connexion. Veuillez réessayer.",
- "error_user_not_found": "Utilisateur introuvable. Veuillez vérifier et réessayer.",
- "looking_up": "Recherche en cours...",
- "optional": "optionnel",
- "page_subtitle": "Saisissez votre nom d'utilisateur pour rechercher les options de connexion de votre organisation.",
- "page_title": "Authentification unique",
- "provider_oidc": "OpenID Connect (OIDC)",
- "provider_saml": "SAML 2.0",
- "sign_in_button": "Se connecter avec SSO",
- "sign_in_title": "Connexion",
- "sso_button": "Connexion SSO"
- },
- "login": {
- "branding_subtitle": "Logiciel de dispatch puissant pour les premiers intervenants, les équipes de recherche et de sauvetage et les organisations de sécurité publique.",
- "branding_title": "Gestion des interventions d'urgence",
- "errorModal": {
- "confirmButton": "OK",
- "message": "Veuillez vérifier votre nom d'utilisateur et votre mot de passe, puis réessayer.",
- "title": "Échec de la connexion"
- },
- "feature_dispatch_desc": "Dépêchez instantanément des unités et gérez les appels avec des mises à jour en temps réel sur tous les appareils.",
- "feature_dispatch_title": "Dispatch en temps réel",
- "feature_mapping_desc": "Suivez les unités en temps réel avec des cartes détaillées, le routage et la gestion de la localisation.",
- "feature_mapping_title": "Cartographie avancée",
- "feature_personnel_desc": "Gérez votre équipe avec un accès basé sur les rôles, le suivi des statuts et des outils de communication.",
- "feature_personnel_title": "Gestion du personnel",
- "footer_text": "Créé avec ❤️ à Lake Tahoe",
- "login": "Connexion",
- "login_button": "Se connecter",
- "login_button_description": "Connectez-vous à votre compte pour continuer",
- "login_button_error": "Erreur lors de la connexion",
- "login_button_loading": "Connexion en cours...",
- "login_button_success": "Connecté avec succès",
- "no_account": "Vous n'avez pas de compte ?",
- "page_subtitle": "Saisissez vos identifiants pour vous connecter.",
- "page_title": "Resgrid Dispatch",
- "password": "Mot de passe",
- "password_incorrect": "Le mot de passe est incorrect",
- "password_placeholder": "Saisissez votre mot de passe",
- "register": "S'inscrire",
- "title": "Connexion",
- "username": "Nom d'utilisateur",
- "username_placeholder": "Saisissez votre nom d'utilisateur",
- "welcome_title": "Bon retour"
- },
- "lockscreen": {
- "message": "Saisissez votre mot de passe pour déverrouiller l'écran",
- "not_you": "Ce n'est pas vous ? Retour à la connexion",
- "password": "Mot de passe",
- "password_placeholder": "Saisissez votre mot de passe",
- "title": "Écran de verrouillage",
- "unlock_button": "Déverrouiller",
- "unlock_failed": "Échec du déverrouillage. Veuillez réessayer.",
- "unlocking": "Déverrouillage en cours...",
- "welcome_back": "Bon retour",
- "relogin_required": "La vérification du mot de passe n'est pas disponible pour cette session. Veuillez vous reconnecter."
- },
- "maintenance": {
- "downtime_message": "Nous travaillons activement pour terminer la maintenance le plus rapidement possible. Veuillez revenir bientôt.",
- "downtime_title": "Quelle est la durée de l'interruption ?",
- "message": "Veuillez revenir dans quelques instants.",
- "support_message": "Si vous avez besoin d'aide, veuillez nous contacter à",
- "support_title": "Avez-vous besoin d'aide ?",
- "title": "Le site est en maintenance",
- "why_down_message": "Nous effectuons une maintenance planifiée pour améliorer votre expérience. Nous nous excusons pour tout inconvénient.",
- "why_down_title": "Pourquoi le site est-il indisponible ?"
- },
- "menu": {
- "scheduled_calls": "Interventions planifiées",
- "pois": "POI",
- "calls": "Appels",
- "calls_list": "Liste des appels",
- "contacts": "Contacts",
- "home": "Accueil",
- "map": "Carte",
- "menu": "Menu",
- "messages": "Messages",
- "new_call": "Nouvel appel",
- "personnel": "Personnel",
- "protocols": "Protocoles",
- "settings": "Paramètres",
+ "dispatch": {
+ "active_calls": "Appels actifs",
+ "pending_calls": "En attente",
+ "scheduled_calls": "Planifiés",
+ "units_available": "Disponibles",
+ "personnel_available": "Disponibles",
+ "personnel_on_duty": "En service",
"units": "Unités",
- "weatherAlerts": "Alertes météo",
- "incident_command": "Commandement d’intervention",
- "chat": "Chat",
- "assistant": "Assistant"
- },
- "pois": {
- "address": "Adresse",
- "all_types": "Tous les types",
- "destination": "Destination",
- "details": "Détails",
- "detail_not_found": "POI introuvable",
- "detail_not_found_description": "Le POI demandé n’a pas pu être chargé.",
- "detail_title": "Détails du POI",
- "empty": "Aucun POI trouvé",
- "empty_description": "Aucun point d’intérêt n’est encore disponible pour votre service.",
- "empty_filtered": "Aucun POI correspondant",
- "empty_filtered_description": "Effacez votre recherche ou choisissez un autre type de POI.",
- "filter_by_type": "Filtrer par type de POI",
- "invalid_poi": "POI non valide",
- "invalid_poi_description": "L’identifiant du POI sélectionné n’est pas valide.",
- "loading": "Chargement des POI...",
- "loading_detail": "Chargement des détails du POI...",
+ "personnel": "Personnel",
"map": "Carte",
- "no_location": "Aucun emplacement disponible",
- "no_location_description": "Ce POI ne possède pas de coordonnées utilisables.",
- "no_location_for_routing": "Aucune donnée d’emplacement disponible pour l’itinéraire",
- "note": "Note",
- "route_error": "Impossible d’ouvrir l’application de cartographie",
- "search": "Rechercher des POI...",
- "sort": "Trier",
- "sort_options": {
- "address-asc": "Adresse",
- "name-asc": "Nom (A–Z)",
- "name-desc": "Nom (Z–A)",
- "type-asc": "Type"
+ "notes": "Notes",
+ "activity_log": "Journal d'activité",
+ "communications": "Communications",
+ "no_active_calls": "Aucun appel actif",
+ "no_units": "Aucune unité disponible",
+ "no_personnel": "Aucun personnel disponible",
+ "no_notes": "Aucune note disponible",
+ "no_activity": "Aucune activité récente",
+ "current_channel": "Canal actuel",
+ "audio_stream": "Flux audio",
+ "no_stream": "Aucun flux actif",
+ "ptt": "PTT",
+ "ptt_start": "Début PTT",
+ "ptt_end": "Fin PTT",
+ "transmitting_on": "Transmission sur {{channel}}",
+ "transmission_ended": "Transmission terminée",
+ "voice_disabled": "Voix désactivée",
+ "disconnected": "Déconnecté",
+ "select_channel": "Sélectionner un canal",
+ "select_channel_description": "Choisissez un canal vocal auquel vous connecter",
+ "change_channel_warning": "Sélectionner un nouveau canal déconnectera le canal actuel",
+ "default_channel": "Par défaut",
+ "no_channels_available": "Aucun canal vocal disponible",
+ "system_update": "Mise à jour système",
+ "data_refreshed": "Données actualisées depuis le serveur",
+ "call_selected": "Appel sélectionné",
+ "unit_selected": "Unité sélectionnée",
+ "unit_deselected": "Unité désélectionnée",
+ "personnel_selected": "Personnel sélectionné",
+ "personnel_deselected": "Personnel désélectionné",
+ "loading_map": "Chargement de la carte...",
+ "map_not_available_web": "Carte non disponible sur la plateforme web",
+ "filtering_by_call": "Filtrage par appel",
+ "clear_filter": "Effacer le filtre",
+ "call_filter_active": "Filtre d'appel actif",
+ "call_filter_cleared": "Filtre d'appel effacé",
+ "showing_all_data": "Affichage de toutes les données",
+ "call_notes": "Notes de l'appel",
+ "no_call_notes": "Aucune note d'appel",
+ "add_call_note_placeholder": "Ajouter une note...",
+ "note_added": "Note ajoutée",
+ "note_added_to_console": "Une nouvelle note a été ajoutée à la console",
+ "add_note_title": "Ajouter une nouvelle note",
+ "note_title_label": "Titre",
+ "note_title_placeholder": "Saisissez le titre de la note...",
+ "note_category_label": "Catégorie",
+ "note_category_placeholder": "Sélectionner une catégorie",
+ "note_no_category": "Sans catégorie",
+ "note_body_label": "Contenu de la note",
+ "note_body_placeholder": "Saisissez le contenu de la note...",
+ "note_save_error": "Échec de la sauvegarde de la note : {{error}}",
+ "note_created": "Note créée",
+ "units_on_call": "Unités en intervention",
+ "no_units_on_call": "Aucune unité en intervention",
+ "personnel_on_call": "Personnel en intervention",
+ "no_personnel_on_call": "Aucun personnel en intervention",
+ "call_activity": "Activité de l'appel",
+ "no_call_activity": "Aucune activité d'appel",
+ "on_call": "En intervention",
+ "filtered": "Filtré",
+ "active_filter": "Filtre actif",
+ "unit_status_change": "Changement de statut d'unité",
+ "personnel_status_change": "Changement de statut du personnel",
+ "view_call_details": "Voir les détails de l'appel",
+ "dispatched_resources": "Dépêchés",
+ "unassigned": "Non affecté",
+ "available": "Disponible",
+ "unknown": "Inconnu",
+ "search_personnel_placeholder": "Rechercher du personnel...",
+ "search_calls_placeholder": "Rechercher des appels...",
+ "search_units_placeholder": "Rechercher des unités...",
+ "search_notes_placeholder": "Rechercher des notes...",
+ "signalr_update": "Mise à jour en temps réel",
+ "signalr_connected": "Connecté",
+ "realtime_updates_active": "Les mises à jour en temps réel sont maintenant actives",
+ "personnel_status_updated": "Statut du personnel mis à jour",
+ "personnel_staffing_updated": "Effectifs du personnel mis à jour",
+ "unit_status_updated": "Statut de l'unité mis à jour",
+ "calls_updated": "Appels mis à jour",
+ "call_added": "Nouvel appel ajouté",
+ "call_closed": "Appel clôturé",
+ "check_ins": "Pointages",
+ "no_check_ins": "Aucun appel avec minuterie de pointage",
+ "radio_log": "Journal radio",
+ "radio": "Radio",
+ "activity": "Activité",
+ "actions": "Actions",
+ "no_radio_activity": "Aucune transmission radio",
+ "live": "EN DIRECT",
+ "currently_transmitting": "Transmission en cours...",
+ "duration": "Durée",
+ "call_actions": "Actions de l'appel",
+ "unit_actions": "Actions de l'unité",
+ "personnel_actions": {
+ "title": "Actions du personnel",
+ "status_tab": "Statut",
+ "staffing_tab": "Effectifs",
+ "select_status": "Sélectionner le statut",
+ "select_staffing": "Sélectionner le niveau d'effectifs",
+ "destination": "Destination",
+ "no_destination": "Aucune destination",
+ "note": "Note",
+ "note_placeholder": "Ajouter une note optionnelle...",
+ "update_status": "Mettre à jour le statut",
+ "update_staffing": "Mettre à jour les effectifs",
+ "no_statuses_available": "Aucun statut disponible",
+ "no_staffings_available": "Aucun niveau d'effectifs disponible"
},
- "title": "POI",
- "type": "Type",
- "unknown_type": "Type inconnu",
- "unnamed": "POI sans nom"
+ "unit_actions_panel": {
+ "status": "Statut",
+ "select_status": "Sélectionner le statut",
+ "destination": "Destination",
+ "no_destination": "Aucune destination",
+ "note": "Note",
+ "note_placeholder": "Ajouter une note optionnelle...",
+ "update_status": "Mettre à jour le statut",
+ "no_statuses_available": "Aucun statut disponible",
+ "no_active_calls": "Aucun appel actif",
+ "no_stations_available": "Aucune caserne disponible",
+ "no_destinations_available": "Aucune destination disponible"
+ },
+ "call": "Appel",
+ "station": "Caserne",
+ "calls": "Appels",
+ "stations": "Casernes",
+ "no_stations_available": "Aucune caserne disponible",
+ "new_call": "Nouvel appel",
+ "view_details": "Détails",
+ "add_note": "Ajouter une note",
+ "close_call": "Clôturer",
+ "set_status": "Définir le statut",
+ "set_staffing": "Effectifs",
+ "dispatch": "Dispatch",
+ "select_items_for_actions": "Sélectionnez un appel, une unité ou du personnel pour activer les actions contextuelles",
+ "weather": {
+ "clear": "Dégagé",
+ "mainly_clear": "Principalement dégagé",
+ "partly_cloudy": "Partiellement nuageux",
+ "overcast": "Couvert",
+ "fog": "Brouillard",
+ "drizzle": "Bruine",
+ "freezing_drizzle": "Bruine verglaçante",
+ "rain": "Pluie",
+ "freezing_rain": "Pluie verglaçante",
+ "snow": "Neige",
+ "rain_showers": "Averses de pluie",
+ "snow_showers": "Averses de neige",
+ "thunderstorm": "Orage",
+ "thunderstorm_hail": "Orage avec grêle",
+ "unknown": "Inconnu"
+ },
+ "available_only": "Disponibles uniquement",
+ "single_list": "Liste unique",
+ "resources": "Ressources",
+ "search_resources_placeholder": "Rechercher des ressources...",
+ "no_resources": "Aucune ressource"
},
- "scheduled_calls": {
- "title": "Interventions planifiées",
- "loading": "Chargement des interventions planifiées...",
- "no_scheduled_calls": "Aucune intervention planifiée",
- "no_scheduled_calls_description": "Aucune intervention planifiée n’est en attente actuellement.",
- "search": "Rechercher des interventions planifiées...",
- "scheduled_for": "Planifiée pour",
- "table_number": "Intervention n°",
- "table_name": "Nom",
- "table_type": "Type",
- "table_priority": "Priorité",
- "table_address": "Adresse",
- "table_scheduled": "Planifiée pour"
+ "form": {
+ "invalid_url": "Veuillez saisir une URL valide commençant par http:// ou https://",
+ "required": "Ce champ est obligatoire"
+ },
+ "incident_command": {
+ "accountability": "Suivi du personnel (PAR)",
+ "acknowledge": "Confirmer",
+ "action_plan": "Plan d’action",
+ "action_plan_placeholder": "Décrivez le plan d’action de l’intervention...",
+ "active": "Actif",
+ "active_title": "Commandements d’intervention actifs",
+ "add": "Ajouter",
+ "add_channel": "Ajouter un canal",
+ "add_lane": "Ajouter un secteur",
+ "add_marker": "Ajouter un repère",
+ "add_objective": "Ajouter un objectif",
+ "annotations": "Annotations de la carte",
+ "assign": "Affecter",
+ "assign_resource": "Affecter une ressource",
+ "assign_resource_required": "Sélectionnez un secteur et une ressource",
+ "assign_role": "Affecter un rôle",
+ "assign_role_required": "Sélectionnez une personne et un rôle",
+ "call": "Intervention",
+ "channel_name": "Nom du canal",
+ "chat": "Chat",
+ "chat_frozen": "This incident is closed. Conversations are kept as a point-in-time record — no new messages.",
+ "close_all_channels": "Fermer tous les canaux",
+ "close_command": "Clore le commandement",
+ "closed": "Clos",
+ "command_channel": "Command chat",
+ "command_channel_hint": "Command staff and dispatch",
+ "command_channel_unavailable": "No command channel has been created for this incident yet.",
+ "commander": "Commandant",
+ "complete": "Terminer",
+ "completed": "Terminé",
+ "confirm_close": "Clore le commandement de cette intervention ?",
+ "critical": "Critique",
+ "delete_annotation_confirm": "Supprimer cette annotation ?",
+ "dispatch_channel": "Dispatch",
+ "dispatch_channel_hint": "The incident's line to the desk",
+ "dispatch_channel_unavailable": "No dispatch channel has been created for this incident yet.",
+ "dm_failed": "Couldn't open that conversation.",
+ "dm_unavailable": "That contact has no Resgrid account to message.",
+ "due": "Échéance",
+ "edit": "Modifier",
+ "edit_action_plan": "Modifier le plan d’action",
+ "establish": "Établir le commandement",
+ "establish_description": "Initialisez éventuellement le tableau de commandement à partir d’un modèle.",
+ "establish_error": "Échec de l’établissement du commandement",
+ "establish_success": "Commandement de l’intervention établi",
+ "establish_title": "Établir le commandement de l’intervention",
+ "established_on": "Établi",
+ "green": "Vert",
+ "hold_to_talk": "Maintenir pour parler",
+ "incident_channel": "Incident chat",
+ "incident_channel_hint": "Everyone working the incident",
+ "incident_channel_unavailable": "No incident channel has been created for this call yet.",
+ "join": "Rejoindre",
+ "lane": "Secteur",
+ "lane_name": "Nom du secteur",
+ "lane_type": "Type de secteur",
+ "marker": "Repère",
+ "marker_label": "Libellé du repère",
+ "move": "Déplacer",
+ "move_lane": "Déplacer le secteur",
+ "move_resource": "Déplacer la ressource",
+ "name_required": "Le nom est obligatoire",
+ "no_accountability": "Aucun personnel suivi.",
+ "no_action_plan": "Aucun plan d’action défini.",
+ "no_active": "Aucun commandement d’intervention actif",
+ "no_active_description": "Les commandements établis pour les interventions apparaîtront ici.",
+ "no_annotations": "Aucune annotation.",
+ "no_channels": "Aucun canal ouvert.",
+ "no_command": "Aucun commandement d’intervention établi",
+ "no_command_description": "Établissez un commandement d’intervention pour coordonner les ressources, les rôles, les objectifs et le suivi du personnel pour cet appel.",
+ "no_lanes": "Aucun secteur défini.",
+ "no_objectives": "Aucun objectif.",
+ "no_resources": "Aucune ressource affectée.",
+ "no_roles": "Aucun rôle attribué.",
+ "no_template": "Aucun modèle (tableau vide)",
+ "no_timeline": "Aucune entrée dans la chronologie.",
+ "no_timers": "Aucun minuteur en cours.",
+ "not_authorized": "Command board unavailable",
+ "not_authorized_description": "Your department hasn't authorized you to work incident command. Ask an administrator for the Command App Login permission.",
+ "objective_name": "Objectif",
+ "objective_type": "Type",
+ "objectives": "Objectifs",
+ "open_chat": "Open",
+ "open_full_board": "Ouvrir le tableau complet",
+ "open_tactical_map": "Ouvrir la carte tactique",
+ "parent_lane": "Secteur parent",
+ "person": "Personne",
+ "personnel": "Personnel",
+ "release": "Libérer",
+ "resource": "Ressource",
+ "resource_type": "Type de ressource",
+ "role": "Rôle",
+ "roles": "Rôles de commandement",
+ "run_par": "Lancer le PAR",
+ "save": "Enregistrer",
+ "save_error": "Échec de l’opération",
+ "saved": "Enregistré",
+ "select_lane": "Sélectionnez un secteur",
+ "select_person": "Sélectionnez une personne",
+ "select_resource": "Sélectionnez une ressource",
+ "select_role": "Sélectionnez un rôle",
+ "send_message": "Message",
+ "status": "État",
+ "structure": "Structure de commandement",
+ "tab_title": "Commandement",
+ "tactical_map": "Carte tactique",
+ "talking": "Transmission...",
+ "tap_to_place": "Touchez la carte pour placer un repère",
+ "template": "Modèle",
+ "timeline": "Chronologie du commandement",
+ "timers": "Minuteurs",
+ "title": "Commandement de l’intervention",
+ "top_level": "Niveau supérieur",
+ "transfer": "Transférer",
+ "transfer_command": "Transférer",
+ "transfer_notes": "Notes",
+ "transfer_success": "Commandement transféré",
+ "transfer_title": "Transférer le commandement",
+ "unassigned": "Non affecté",
+ "unit": "Unité",
+ "voice_channels": "Canaux vocaux",
+ "voice_join_error": "Impossible de rejoindre le canal vocal",
+ "voice_joined": "Canal vocal rejoint",
+ "warning": "Avertissement"
+ },
+ "livekit": {
+ "audio_devices": "Appareils audio",
+ "audio_settings": "Paramètres audio",
+ "connected_to_room": "Connecté au canal",
+ "connecting": "Connexion en cours...",
+ "disconnect": "Déconnecter",
+ "join": "Rejoindre",
+ "microphone": "Microphone",
+ "mute": "Couper le son",
+ "no_rooms_available": "Aucun canal vocal disponible",
+ "speaker": "Haut-parleur",
+ "speaking": "En train de parler",
+ "title": "Canaux vocaux",
+ "unmute": "Réactiver le son"
+ },
+ "loading": {
+ "loading": "Chargement...",
+ "loadingData": "Chargement des données...",
+ "pleaseWait": "Veuillez patienter",
+ "processingRequest": "Traitement de votre demande..."
+ },
+ "lockscreen": {
+ "message": "Saisissez votre mot de passe pour déverrouiller l'écran",
+ "not_you": "Ce n'est pas vous ? Retour à la connexion",
+ "password": "Mot de passe",
+ "password_placeholder": "Saisissez votre mot de passe",
+ "title": "Écran de verrouillage",
+ "unlock_button": "Déverrouiller",
+ "unlock_failed": "Échec du déverrouillage. Veuillez réessayer.",
+ "unlocking": "Déverrouillage en cours...",
+ "welcome_back": "Bon retour",
+ "relogin_required": "La vérification du mot de passe n'est pas disponible pour cette session. Veuillez vous reconnecter."
+ },
+ "login": {
+ "branding_subtitle": "Logiciel de dispatch puissant pour les premiers intervenants, les équipes de recherche et de sauvetage et les organisations de sécurité publique.",
+ "branding_title": "Gestion des interventions d'urgence",
+ "dispatch_not_authorized": "You are not authorized to use the Dispatch app. Contact your department administrator.",
+ "errorModal": {
+ "confirmButton": "OK",
+ "message": "Veuillez vérifier votre nom d'utilisateur et votre mot de passe, puis réessayer.",
+ "title": "Échec de la connexion"
+ },
+ "feature_dispatch_desc": "Dépêchez instantanément des unités et gérez les appels avec des mises à jour en temps réel sur tous les appareils.",
+ "feature_dispatch_title": "Dispatch en temps réel",
+ "feature_mapping_desc": "Suivez les unités en temps réel avec des cartes détaillées, le routage et la gestion de la localisation.",
+ "feature_mapping_title": "Cartographie avancée",
+ "feature_personnel_desc": "Gérez votre équipe avec un accès basé sur les rôles, le suivi des statuts et des outils de communication.",
+ "feature_personnel_title": "Gestion du personnel",
+ "footer_text": "Créé avec ❤️ à Lake Tahoe",
+ "login": "Connexion",
+ "login_button": "Se connecter",
+ "login_button_description": "Connectez-vous à votre compte pour continuer",
+ "login_button_error": "Erreur lors de la connexion",
+ "login_button_loading": "Connexion en cours...",
+ "login_button_success": "Connecté avec succès",
+ "no_account": "Vous n'avez pas de compte ?",
+ "page_subtitle": "Saisissez vos identifiants pour vous connecter.",
+ "page_title": "Resgrid Dispatch",
+ "password": "Mot de passe",
+ "password_incorrect": "Le mot de passe est incorrect",
+ "password_placeholder": "Saisissez votre mot de passe",
+ "register": "S'inscrire",
+ "title": "Connexion",
+ "username": "Nom d'utilisateur",
+ "username_placeholder": "Saisissez votre nom d'utilisateur",
+ "welcome_title": "Bon retour"
+ },
+ "maintenance": {
+ "downtime_message": "Nous travaillons activement pour terminer la maintenance le plus rapidement possible. Veuillez revenir bientôt.",
+ "downtime_title": "Quelle est la durée de l'interruption ?",
+ "message": "Veuillez revenir dans quelques instants.",
+ "support_message": "Si vous avez besoin d'aide, veuillez nous contacter à",
+ "support_title": "Avez-vous besoin d'aide ?",
+ "title": "Le site est en maintenance",
+ "why_down_message": "Nous effectuons une maintenance planifiée pour améliorer votre expérience. Nous nous excusons pour tout inconvénient.",
+ "why_down_title": "Pourquoi le site est-il indisponible ?"
},
"map": {
"view_poi_details": "Afficher les détails du POI",
@@ -690,6 +1009,26 @@
"hide_all": "Tout masquer",
"view_call_details": "Voir les détails de l'appel"
},
+ "menu": {
+ "scheduled_calls": "Interventions planifiées",
+ "pois": "POI",
+ "calls": "Appels",
+ "calls_list": "Liste des appels",
+ "contacts": "Contacts",
+ "home": "Accueil",
+ "map": "Carte",
+ "menu": "Menu",
+ "messages": "Messages",
+ "new_call": "Nouvel appel",
+ "personnel": "Personnel",
+ "protocols": "Protocoles",
+ "settings": "Paramètres",
+ "units": "Unités",
+ "weatherAlerts": "Alertes météo",
+ "incident_command": "Commandement d’intervention",
+ "chat": "Chat",
+ "assistant": "Assistant"
+ },
"notes": {
"actions": {
"add": "Ajouter une note",
@@ -709,6 +1048,23 @@
"search": "Rechercher dans les notes...",
"title": "Notes"
},
+ "onboarding": {
+ "screen1": {
+ "title": "Resgrid Dispatch",
+ "description": "Créez, dépêchez et gérez les appels d'urgence avec un puissant centre de commandement mobile à portée de main"
+ },
+ "screen2": {
+ "title": "Connaissance situationnelle en temps réel",
+ "description": "Suivez toutes les unités, le personnel et les ressources sur une carte interactive avec des mises à jour de statut en direct et l'AVL"
+ },
+ "screen3": {
+ "title": "Coordination fluide",
+ "description": "Communiquez instantanément avec les unités sur le terrain, mettez à jour les statuts des appels et coordonnez les efforts d'intervention depuis n'importe où"
+ },
+ "skip": "Passer",
+ "next": "Suivant",
+ "getStarted": "C'est parti"
+ },
"personnel": {
"title": "Personnel",
"search": "Rechercher du personnel...",
@@ -741,22 +1097,41 @@
"send_email": "E-mail",
"custom_fields": "Informations supplémentaires"
},
- "onboarding": {
- "screen1": {
- "title": "Resgrid Dispatch",
- "description": "Créez, dépêchez et gérez les appels d'urgence avec un puissant centre de commandement mobile à portée de main"
- },
- "screen2": {
- "title": "Connaissance situationnelle en temps réel",
- "description": "Suivez toutes les unités, le personnel et les ressources sur une carte interactive avec des mises à jour de statut en direct et l'AVL"
- },
- "screen3": {
- "title": "Coordination fluide",
- "description": "Communiquez instantanément avec les unités sur le terrain, mettez à jour les statuts des appels et coordonnez les efforts d'intervention depuis n'importe où"
+ "pois": {
+ "address": "Adresse",
+ "all_types": "Tous les types",
+ "destination": "Destination",
+ "details": "Détails",
+ "detail_not_found": "POI introuvable",
+ "detail_not_found_description": "Le POI demandé n’a pas pu être chargé.",
+ "detail_title": "Détails du POI",
+ "empty": "Aucun POI trouvé",
+ "empty_description": "Aucun point d’intérêt n’est encore disponible pour votre service.",
+ "empty_filtered": "Aucun POI correspondant",
+ "empty_filtered_description": "Effacez votre recherche ou choisissez un autre type de POI.",
+ "filter_by_type": "Filtrer par type de POI",
+ "invalid_poi": "POI non valide",
+ "invalid_poi_description": "L’identifiant du POI sélectionné n’est pas valide.",
+ "loading": "Chargement des POI...",
+ "loading_detail": "Chargement des détails du POI...",
+ "map": "Carte",
+ "no_location": "Aucun emplacement disponible",
+ "no_location_description": "Ce POI ne possède pas de coordonnées utilisables.",
+ "no_location_for_routing": "Aucune donnée d’emplacement disponible pour l’itinéraire",
+ "note": "Note",
+ "route_error": "Impossible d’ouvrir l’application de cartographie",
+ "search": "Rechercher des POI...",
+ "sort": "Trier",
+ "sort_options": {
+ "address-asc": "Adresse",
+ "name-asc": "Nom (A–Z)",
+ "name-desc": "Nom (Z–A)",
+ "type-asc": "Type"
},
- "skip": "Passer",
- "next": "Suivant",
- "getStarted": "C'est parti"
+ "title": "POI",
+ "type": "Type",
+ "unknown_type": "Type inconnu",
+ "unnamed": "POI sans nom"
},
"protocols": {
"details": {
@@ -796,6 +1171,20 @@
"tap_to_manage": "Appuyez pour gérer les rôles",
"unassigned": "Non affecté"
},
+ "scheduled_calls": {
+ "title": "Interventions planifiées",
+ "loading": "Chargement des interventions planifiées...",
+ "no_scheduled_calls": "Aucune intervention planifiée",
+ "no_scheduled_calls_description": "Aucune intervention planifiée n’est en attente actuellement.",
+ "search": "Rechercher des interventions planifiées...",
+ "scheduled_for": "Planifiée pour",
+ "table_number": "Intervention n°",
+ "table_name": "Nom",
+ "table_type": "Type",
+ "table_priority": "Priorité",
+ "table_address": "Adresse",
+ "table_scheduled": "Planifiée pour"
+ },
"settings": {
"about": "À propos",
"account": "Compte",
@@ -884,6 +1273,29 @@
"version": "Version",
"website": "Site web"
},
+ "sso": {
+ "authenticating": "Authentification en cours...",
+ "back_to_login": "Retour à la connexion",
+ "back_to_lookup": "Changer d'utilisateur",
+ "continue_button": "Continuer",
+ "department_id_label": "ID du service",
+ "department_id_placeholder": "Saisissez l'ID du service",
+ "error_generic": "La connexion a échoué. Veuillez réessayer.",
+ "error_oidc_cancelled": "La connexion a été annulée.",
+ "error_oidc_not_ready": "Le fournisseur SSO est en cours de chargement, veuillez patienter.",
+ "error_sso_not_enabled": "L'authentification unique n'est pas activée pour cet utilisateur.",
+ "error_token_exchange": "Échec de la connexion. Veuillez réessayer.",
+ "error_user_not_found": "Utilisateur introuvable. Veuillez vérifier et réessayer.",
+ "looking_up": "Recherche en cours...",
+ "optional": "optionnel",
+ "page_subtitle": "Saisissez votre nom d'utilisateur pour rechercher les options de connexion de votre organisation.",
+ "page_title": "Authentification unique",
+ "provider_oidc": "OpenID Connect (OIDC)",
+ "provider_saml": "SAML 2.0",
+ "sign_in_button": "Se connecter avec SSO",
+ "sign_in_title": "Connexion",
+ "sso_button": "Connexion SSO"
+ },
"status": {
"all_destinations_enabled": "Peut répondre aux interventions, casernes ou POI",
"calls_and_pois_destination_enabled": "Peut répondre aux interventions ou POI",
@@ -916,173 +1328,6 @@
"stations_tab": "Casernes",
"status_saved_successfully": "Statut sauvegardé avec succès !"
},
- "dispatch": {
- "active_calls": "Appels actifs",
- "pending_calls": "En attente",
- "scheduled_calls": "Planifiés",
- "units_available": "Disponibles",
- "personnel_available": "Disponibles",
- "personnel_on_duty": "En service",
- "units": "Unités",
- "personnel": "Personnel",
- "map": "Carte",
- "notes": "Notes",
- "activity_log": "Journal d'activité",
- "communications": "Communications",
- "no_active_calls": "Aucun appel actif",
- "no_units": "Aucune unité disponible",
- "no_personnel": "Aucun personnel disponible",
- "no_notes": "Aucune note disponible",
- "no_activity": "Aucune activité récente",
- "current_channel": "Canal actuel",
- "audio_stream": "Flux audio",
- "no_stream": "Aucun flux actif",
- "ptt": "PTT",
- "ptt_start": "Début PTT",
- "ptt_end": "Fin PTT",
- "transmitting_on": "Transmission sur {{channel}}",
- "transmission_ended": "Transmission terminée",
- "voice_disabled": "Voix désactivée",
- "disconnected": "Déconnecté",
- "select_channel": "Sélectionner un canal",
- "select_channel_description": "Choisissez un canal vocal auquel vous connecter",
- "change_channel_warning": "Sélectionner un nouveau canal déconnectera le canal actuel",
- "default_channel": "Par défaut",
- "no_channels_available": "Aucun canal vocal disponible",
- "system_update": "Mise à jour système",
- "data_refreshed": "Données actualisées depuis le serveur",
- "call_selected": "Appel sélectionné",
- "unit_selected": "Unité sélectionnée",
- "unit_deselected": "Unité désélectionnée",
- "personnel_selected": "Personnel sélectionné",
- "personnel_deselected": "Personnel désélectionné",
- "loading_map": "Chargement de la carte...",
- "map_not_available_web": "Carte non disponible sur la plateforme web",
- "filtering_by_call": "Filtrage par appel",
- "clear_filter": "Effacer le filtre",
- "call_filter_active": "Filtre d'appel actif",
- "call_filter_cleared": "Filtre d'appel effacé",
- "showing_all_data": "Affichage de toutes les données",
- "call_notes": "Notes de l'appel",
- "no_call_notes": "Aucune note d'appel",
- "add_call_note_placeholder": "Ajouter une note...",
- "note_added": "Note ajoutée",
- "note_added_to_console": "Une nouvelle note a été ajoutée à la console",
- "add_note_title": "Ajouter une nouvelle note",
- "note_title_label": "Titre",
- "note_title_placeholder": "Saisissez le titre de la note...",
- "note_category_label": "Catégorie",
- "note_category_placeholder": "Sélectionner une catégorie",
- "note_no_category": "Sans catégorie",
- "note_body_label": "Contenu de la note",
- "note_body_placeholder": "Saisissez le contenu de la note...",
- "note_save_error": "Échec de la sauvegarde de la note : {{error}}",
- "note_created": "Note créée",
- "units_on_call": "Unités en intervention",
- "no_units_on_call": "Aucune unité en intervention",
- "personnel_on_call": "Personnel en intervention",
- "no_personnel_on_call": "Aucun personnel en intervention",
- "call_activity": "Activité de l'appel",
- "no_call_activity": "Aucune activité d'appel",
- "on_call": "En intervention",
- "filtered": "Filtré",
- "active_filter": "Filtre actif",
- "unit_status_change": "Changement de statut d'unité",
- "personnel_status_change": "Changement de statut du personnel",
- "view_call_details": "Voir les détails de l'appel",
- "dispatched_resources": "Dépêchés",
- "unassigned": "Non affecté",
- "available": "Disponible",
- "unknown": "Inconnu",
- "search_personnel_placeholder": "Rechercher du personnel...",
- "search_calls_placeholder": "Rechercher des appels...",
- "search_units_placeholder": "Rechercher des unités...",
- "search_notes_placeholder": "Rechercher des notes...",
- "signalr_update": "Mise à jour en temps réel",
- "signalr_connected": "Connecté",
- "realtime_updates_active": "Les mises à jour en temps réel sont maintenant actives",
- "personnel_status_updated": "Statut du personnel mis à jour",
- "personnel_staffing_updated": "Effectifs du personnel mis à jour",
- "unit_status_updated": "Statut de l'unité mis à jour",
- "calls_updated": "Appels mis à jour",
- "call_added": "Nouvel appel ajouté",
- "call_closed": "Appel clôturé",
- "check_ins": "Pointages",
- "no_check_ins": "Aucun appel avec minuterie de pointage",
- "radio_log": "Journal radio",
- "radio": "Radio",
- "activity": "Activité",
- "actions": "Actions",
- "no_radio_activity": "Aucune transmission radio",
- "live": "EN DIRECT",
- "currently_transmitting": "Transmission en cours...",
- "duration": "Durée",
- "call_actions": "Actions de l'appel",
- "unit_actions": "Actions de l'unité",
- "personnel_actions": {
- "title": "Actions du personnel",
- "status_tab": "Statut",
- "staffing_tab": "Effectifs",
- "select_status": "Sélectionner le statut",
- "select_staffing": "Sélectionner le niveau d'effectifs",
- "destination": "Destination",
- "no_destination": "Aucune destination",
- "note": "Note",
- "note_placeholder": "Ajouter une note optionnelle...",
- "update_status": "Mettre à jour le statut",
- "update_staffing": "Mettre à jour les effectifs",
- "no_statuses_available": "Aucun statut disponible",
- "no_staffings_available": "Aucun niveau d'effectifs disponible"
- },
- "unit_actions_panel": {
- "status": "Statut",
- "select_status": "Sélectionner le statut",
- "destination": "Destination",
- "no_destination": "Aucune destination",
- "note": "Note",
- "note_placeholder": "Ajouter une note optionnelle...",
- "update_status": "Mettre à jour le statut",
- "no_statuses_available": "Aucun statut disponible",
- "no_active_calls": "Aucun appel actif",
- "no_stations_available": "Aucune caserne disponible",
- "no_destinations_available": "Aucune destination disponible"
- },
- "call": "Appel",
- "station": "Caserne",
- "calls": "Appels",
- "stations": "Casernes",
- "no_stations_available": "Aucune caserne disponible",
- "new_call": "Nouvel appel",
- "view_details": "Détails",
- "add_note": "Ajouter une note",
- "close_call": "Clôturer",
- "set_status": "Définir le statut",
- "set_staffing": "Effectifs",
- "dispatch": "Dispatch",
- "select_items_for_actions": "Sélectionnez un appel, une unité ou du personnel pour activer les actions contextuelles",
- "weather": {
- "clear": "Dégagé",
- "mainly_clear": "Principalement dégagé",
- "partly_cloudy": "Partiellement nuageux",
- "overcast": "Couvert",
- "fog": "Brouillard",
- "drizzle": "Bruine",
- "freezing_drizzle": "Bruine verglaçante",
- "rain": "Pluie",
- "freezing_rain": "Pluie verglaçante",
- "snow": "Neige",
- "rain_showers": "Averses de pluie",
- "snow_showers": "Averses de neige",
- "thunderstorm": "Orage",
- "thunderstorm_hail": "Orage avec grêle",
- "unknown": "Inconnu"
- },
- "available_only": "Disponibles uniquement",
- "single_list": "Liste unique",
- "resources": "Ressources",
- "search_resources_placeholder": "Rechercher des ressources...",
- "no_resources": "Aucune ressource"
- },
"tabs": {
"calls": "Appels",
"calendar": "Calendrier",
@@ -1096,44 +1341,6 @@
"shifts": "Quarts",
"personnel": "Personnel"
},
- "check_in": {
- "tab_title": "Pointage",
- "timer_status": "Statut de la minuterie",
- "perform_check_in": "Pointer",
- "check_in_success": "Pointage enregistré avec succès",
- "check_in_error": "Échec de l'enregistrement du pointage",
- "checked_in_by": "par {{name}}",
- "last_check_in": "Dernier pointage",
- "elapsed": "Écoulé",
- "duration": "Durée",
- "status_ok": "OK",
- "status_green": "OK",
- "status_warning": "Avertissement",
- "status_yellow": "Avertissement",
- "status_overdue": "En retard",
- "status_red": "En retard",
- "status_critical": "Critique",
- "history": "Historique des pointages",
- "no_timers": "Aucune minuterie de pointage configurée",
- "timers_disabled": "Les minuteries de pointage sont désactivées pour cet appel",
- "type_personnel": "Personnel",
- "type_unit": "Unité",
- "type_ic": "Commandant des opérations",
- "type_par": "PAR",
- "type_hazmat": "Exposition Hazmat",
- "type_sector_rotation": "Rotation de secteur",
- "type_rehab": "Réhabilitation",
- "add_note": "Ajouter une note (optionnel)",
- "confirm": "Confirmer le pointage",
- "minutes_ago": "il y a {{count}} min",
- "select_target": "Sélectionner l'entité à pointer",
- "overdue_count": "{{count}} en retard",
- "warning_count": "{{count}} avertissement",
- "enable_timers": "Activer les minuteries",
- "disable_timers": "Désactiver les minuteries",
- "summary": "{{overdue}} en retard, {{warning}} avertissement, {{ok}} ok",
- "par_title": "Suivi du personnel (PAR)"
- },
"units": {
"search": "Rechercher des unités...",
"loading": "Chargement des unités...",
@@ -1163,6 +1370,63 @@
"no_destination": "Aucune",
"title": "Unités"
},
+ "videoFeeds": {
+ "title": "Flux vidéo",
+ "noFeeds": "Aucun flux vidéo pour cet appel",
+ "addFeed": "Ajouter un flux vidéo",
+ "editFeed": "Modifier le flux vidéo",
+ "deleteFeed": "Supprimer le flux vidéo",
+ "deleteConfirm": "Êtes-vous sûr de vouloir supprimer ce flux vidéo ?",
+ "watch": "Regarder",
+ "goLive": "Diffuser en direct",
+ "stopLive": "Arrêter la diffusion",
+ "flipCamera": "Inverser la caméra",
+ "feedAdded": "Flux vidéo ajouté",
+ "feedUpdated": "Flux vidéo mis à jour",
+ "feedDeleted": "Flux vidéo supprimé",
+ "feedError": "Échec du chargement du flux vidéo",
+ "unsupportedFormat": "Ce format de flux n'est pas pris en charge sur mobile",
+ "copyUrl": "Copier l'URL",
+ "form": {
+ "name": "Nom du flux",
+ "namePlaceholder": "ex. : Drone Véhicule 1",
+ "url": "URL du flux",
+ "urlPlaceholder": "ex. : https://stream.example.com/live.m3u8",
+ "feedType": "Type de caméra",
+ "feedFormat": "Format du flux",
+ "description": "Description",
+ "descriptionPlaceholder": "Description optionnelle",
+ "status": "Statut",
+ "sortOrder": "Ordre de tri",
+ "cameraLocation": "Emplacement de la caméra",
+ "useCurrentLocation": "Utiliser la position actuelle"
+ },
+ "type": {
+ "drone": "Drone",
+ "fixedCamera": "Caméra fixe",
+ "bodyCam": "Caméra corporelle",
+ "trafficCam": "Caméra de circulation",
+ "weatherCam": "Caméra météo",
+ "satelliteFeed": "Flux satellite",
+ "webCam": "Webcam",
+ "other": "Autre"
+ },
+ "format": {
+ "rtsp": "RTSP",
+ "hls": "HLS",
+ "mjpeg": "MJPEG",
+ "youtubeLive": "YouTube Live",
+ "webrtc": "WebRTC",
+ "dash": "DASH",
+ "embed": "Intégré",
+ "other": "Autre"
+ },
+ "status": {
+ "active": "Actif",
+ "inactive": "Inactif",
+ "error": "Erreur"
+ }
+ },
"weatherAlerts": {
"title": "Alertes météo",
"activeAlerts": "Alertes actives",
@@ -1270,251 +1534,5 @@
},
"stats_label": "Alertes météo"
},
- "videoFeeds": {
- "title": "Flux vidéo",
- "noFeeds": "Aucun flux vidéo pour cet appel",
- "addFeed": "Ajouter un flux vidéo",
- "editFeed": "Modifier le flux vidéo",
- "deleteFeed": "Supprimer le flux vidéo",
- "deleteConfirm": "Êtes-vous sûr de vouloir supprimer ce flux vidéo ?",
- "watch": "Regarder",
- "goLive": "Diffuser en direct",
- "stopLive": "Arrêter la diffusion",
- "flipCamera": "Inverser la caméra",
- "feedAdded": "Flux vidéo ajouté",
- "feedUpdated": "Flux vidéo mis à jour",
- "feedDeleted": "Flux vidéo supprimé",
- "feedError": "Échec du chargement du flux vidéo",
- "unsupportedFormat": "Ce format de flux n'est pas pris en charge sur mobile",
- "copyUrl": "Copier l'URL",
- "form": {
- "name": "Nom du flux",
- "namePlaceholder": "ex. : Drone Véhicule 1",
- "url": "URL du flux",
- "urlPlaceholder": "ex. : https://stream.example.com/live.m3u8",
- "feedType": "Type de caméra",
- "feedFormat": "Format du flux",
- "description": "Description",
- "descriptionPlaceholder": "Description optionnelle",
- "status": "Statut",
- "sortOrder": "Ordre de tri",
- "cameraLocation": "Emplacement de la caméra",
- "useCurrentLocation": "Utiliser la position actuelle"
- },
- "type": {
- "drone": "Drone",
- "fixedCamera": "Caméra fixe",
- "bodyCam": "Caméra corporelle",
- "trafficCam": "Caméra de circulation",
- "weatherCam": "Caméra météo",
- "satelliteFeed": "Flux satellite",
- "webCam": "Webcam",
- "other": "Autre"
- },
- "format": {
- "rtsp": "RTSP",
- "hls": "HLS",
- "mjpeg": "MJPEG",
- "youtubeLive": "YouTube Live",
- "webrtc": "WebRTC",
- "dash": "DASH",
- "embed": "Intégré",
- "other": "Autre"
- },
- "status": {
- "active": "Actif",
- "inactive": "Inactif",
- "error": "Erreur"
- }
- },
- "welcome": "Bienvenue sur le site de l'application obytes",
- "incident_command": {
- "tab_title": "Commandement",
- "title": "Commandement de l’intervention",
- "open_full_board": "Ouvrir le tableau complet",
- "no_command": "Aucun commandement d’intervention établi",
- "no_command_description": "Établissez un commandement d’intervention pour coordonner les ressources, les rôles, les objectifs et le suivi du personnel pour cet appel.",
- "establish": "Établir le commandement",
- "establish_title": "Établir le commandement de l’intervention",
- "establish_description": "Initialisez éventuellement le tableau de commandement à partir d’un modèle.",
- "establish_success": "Commandement de l’intervention établi",
- "establish_error": "Échec de l’établissement du commandement",
- "template": "Modèle",
- "no_template": "Aucun modèle (tableau vide)",
- "saved": "Enregistré",
- "save_error": "Échec de l’opération",
- "edit_action_plan": "Modifier le plan d’action",
- "action_plan": "Plan d’action",
- "action_plan_placeholder": "Décrivez le plan d’action de l’intervention...",
- "save": "Enregistrer",
- "no_action_plan": "Aucun plan d’action défini.",
- "add": "Ajouter",
- "add_objective": "Ajouter un objectif",
- "objective_name": "Objectif",
- "objective_type": "Type",
- "name_required": "Le nom est obligatoire",
- "add_lane": "Ajouter un secteur",
- "lane_name": "Nom du secteur",
- "lane_type": "Type de secteur",
- "assign_resource": "Affecter une ressource",
- "assign_resource_required": "Sélectionnez un secteur et une ressource",
- "lane": "Secteur",
- "select_lane": "Sélectionnez un secteur",
- "resource_type": "Type de ressource",
- "resource": "Ressource",
- "select_resource": "Sélectionnez une ressource",
- "unit": "Unité",
- "personnel": "Personnel",
- "assign": "Affecter",
- "assign_role": "Affecter un rôle",
- "assign_role_required": "Sélectionnez une personne et un rôle",
- "person": "Personne",
- "select_person": "Sélectionnez une personne",
- "role": "Rôle",
- "select_role": "Sélectionnez un rôle",
- "transfer_title": "Transférer le commandement",
- "transfer_notes": "Notes",
- "transfer": "Transférer",
- "transfer_command": "Transférer",
- "transfer_success": "Commandement transféré",
- "close_command": "Clore le commandement",
- "confirm_close": "Clore le commandement de cette intervention ?",
- "status": "État",
- "active": "Actif",
- "closed": "Clos",
- "commander": "Commandant",
- "established_on": "Établi",
- "edit": "Modifier",
- "roles": "Rôles de commandement",
- "no_roles": "Aucun rôle attribué.",
- "structure": "Structure de commandement",
- "no_lanes": "Aucun secteur défini.",
- "no_resources": "Aucune ressource affectée.",
- "release": "Libérer",
- "objectives": "Objectifs",
- "no_objectives": "Aucun objectif.",
- "complete": "Terminer",
- "completed": "Terminé",
- "timers": "Minuteurs",
- "no_timers": "Aucun minuteur en cours.",
- "acknowledge": "Confirmer",
- "due": "Échéance",
- "accountability": "Suivi du personnel (PAR)",
- "run_par": "Lancer le PAR",
- "green": "Vert",
- "warning": "Avertissement",
- "critical": "Critique",
- "no_accountability": "Aucun personnel suivi.",
- "timeline": "Chronologie du commandement",
- "no_timeline": "Aucune entrée dans la chronologie.",
- "unassigned": "Non affecté",
- "active_title": "Commandements d’intervention actifs",
- "no_active": "Aucun commandement d’intervention actif",
- "no_active_description": "Les commandements établis pour les interventions apparaîtront ici.",
- "call": "Intervention",
- "tactical_map": "Carte tactique",
- "annotations": "Annotations de la carte",
- "no_annotations": "Aucune annotation.",
- "open_tactical_map": "Ouvrir la carte tactique",
- "marker": "Repère",
- "add_marker": "Ajouter un repère",
- "tap_to_place": "Touchez la carte pour placer un repère",
- "marker_label": "Libellé du repère",
- "delete_annotation_confirm": "Supprimer cette annotation ?",
- "voice_channels": "Canaux vocaux",
- "no_channels": "Aucun canal ouvert.",
- "add_channel": "Ajouter un canal",
- "channel_name": "Nom du canal",
- "close_all_channels": "Fermer tous les canaux",
- "join": "Rejoindre",
- "hold_to_talk": "Maintenir pour parler",
- "talking": "Transmission...",
- "voice_joined": "Canal vocal rejoint",
- "voice_join_error": "Impossible de rejoindre le canal vocal",
- "move": "Déplacer",
- "move_lane": "Déplacer le secteur",
- "parent_lane": "Secteur parent",
- "top_level": "Niveau supérieur",
- "move_resource": "Déplacer la ressource"
- },
- "chat": {
- "title": "Chat",
- "assistant": "Assistant",
- "empty": "Aucune conversation pour le moment. Démarrez un message direct ou créez un groupe.",
- "section_assistant": "Assistant",
- "section_direct_messages": "Messages directs",
- "section_channels": "Canaux",
- "section_incidents": "Incidents",
- "new_direct_message": "Nouveau message direct",
- "new_group": "Nouveau groupe",
- "open_assistant": "Ouvrir l'assistant",
- "create_conversation_failed": "Impossible de démarrer la conversation",
- "group_name": "Nom du groupe",
- "search_people": "Rechercher des personnes",
- "no_people": "Aucune personne trouvée",
- "create_group_with": "Créer un groupe ({{count}})",
- "message_deleted": "Ce message a été supprimé",
- "urgent": "Urgent",
- "urgent_will_send": "Ce message sera envoyé comme urgent",
- "shared_location": "Position partagée",
- "thread_replies": "{{count}} réponses",
- "edited": "(modifié)",
- "failed_tap_retry": "Échec – appuyez pour réessayer",
- "type_a_message": "Saisir un message",
- "emoji": "Emoji",
- "add_image": "Ajouter une image",
- "add_gif": "Ajouter un GIF",
- "share_location": "Partager la position",
- "send": "Envoyer",
- "someone": "Quelqu'un",
- "is_typing": "{{name}} est en train d'écrire...",
- "are_typing": "{{count}} personnes sont en train d'écrire...",
- "permission_photos_denied": "Autorisation d'accès à la photothèque refusée",
- "permission_location_denied": "Autorisation de localisation refusée",
- "search_gifs": "Rechercher des GIF",
- "no_gifs": "Aucun GIF trouvé",
- "flag_reason": "Pourquoi le signalez-vous ?",
- "flag_inappropriate": "Inapproprié",
- "flag_harassment": "Harcèlement",
- "flag_spam": "Spam",
- "flag_sensitive": "Informations sensibles",
- "flag_policy": "Violation de la politique",
- "flag_other": "Autre",
- "reply_in_thread": "Répondre dans le fil",
- "copy": "Copier",
- "copied": "Copié",
- "copy_unavailable": "La copie n'est pas disponible sur cet appareil",
- "edit": "Modifier",
- "edit_message": "Modifier le message",
- "save": "Enregistrer",
- "delete": "Supprimer",
- "pin": "Épingler",
- "unpin": "Désépingler",
- "flag": "Signaler",
- "moderator_delete": "Supprimer (modérateur)",
- "moderator_removed": "Supprimé par le modérateur",
- "attachment_failed": "Échec de l'envoi de la pièce jointe",
- "ack_required": "Accusé de réception requis",
- "ack_pending_one": "Vous avez un message urgent à accuser réception",
- "ack_pending_count": "Vous avez {{count}} messages urgents à accuser réception",
- "acknowledge": "Accuser réception",
- "thread": "Fil",
- "original_message": "Message d'origine",
- "reply_placeholder": "Répondre...",
- "channel": "Canal",
- "direct_message": "Message direct",
- "load_people_failed": "Impossible de charger les personnes",
- "reaction_failed": "Impossible de mettre à jour la réaction",
- "edit_failed": "Impossible de modifier le message",
- "delete_failed": "Impossible de supprimer le message",
- "pin_failed": "Impossible de mettre à jour l'épinglage",
- "flag_failed": "Impossible de signaler le message"
- },
- "chatbot": {
- "title": "Assistant",
- "subtitle": "Assistant IA pour votre service",
- "new_session": "Nouvelle session",
- "empty": "Posez n'importe quelle question à l'assistant pour commencer.",
- "ask_placeholder": "Demandez à l'assistant..."
- }
+ "welcome": "Bienvenue sur le site de l'application obytes"
}
diff --git a/src/translations/it.json b/src/translations/it.json
index 8c55a960..50c048a5 100644
--- a/src/translations/it.json
+++ b/src/translations/it.json
@@ -371,6 +371,124 @@
"audio_name": "Clip audio"
}
},
+ "chat": {
+ "title": "Chat",
+ "assistant": "Assistente",
+ "empty": "Ancora nessuna conversazione. Avvia un messaggio diretto o crea un gruppo.",
+ "section_assistant": "Assistente",
+ "section_direct_messages": "Messaggi diretti",
+ "section_channels": "Canali",
+ "section_incidents": "Incidenti",
+ "new_direct_message": "Nuovo messaggio diretto",
+ "new_group": "Nuovo gruppo",
+ "open_assistant": "Apri assistente",
+ "create_conversation_failed": "Impossibile avviare la conversazione",
+ "group_name": "Nome del gruppo",
+ "search_people": "Cerca persone",
+ "no_people": "Nessuna persona trovata",
+ "create_group_with": "Crea gruppo ({{count}})",
+ "message_deleted": "Questo messaggio è stato eliminato",
+ "urgent": "Urgente",
+ "urgent_will_send": "Questo messaggio verrà inviato come urgente",
+ "shared_location": "Posizione condivisa",
+ "thread_replies": "{{count}} risposte",
+ "edited": "(modificato)",
+ "failed_tap_retry": "Non riuscito - tocca per riprovare",
+ "type_a_message": "Scrivi un messaggio",
+ "emoji": "Emoji",
+ "add_image": "Aggiungi immagine",
+ "add_gif": "Aggiungi GIF",
+ "share_location": "Condividi posizione",
+ "send": "Invia",
+ "someone": "Qualcuno",
+ "is_typing": "{{name}} sta scrivendo...",
+ "are_typing": "{{count}} persone stanno scrivendo...",
+ "permission_photos_denied": "Autorizzazione alla libreria foto negata",
+ "permission_location_denied": "Autorizzazione alla posizione negata",
+ "search_gifs": "Cerca GIF",
+ "no_gifs": "Nessun GIF trovato",
+ "flag_reason": "Perché lo segnali?",
+ "flag_inappropriate": "Inappropriato",
+ "flag_harassment": "Molestie",
+ "flag_spam": "Spam",
+ "flag_sensitive": "Informazioni sensibili",
+ "flag_policy": "Violazione delle regole",
+ "flag_other": "Altro",
+ "reply_in_thread": "Rispondi nel thread",
+ "copy": "Copia",
+ "copied": "Copiato",
+ "copy_unavailable": "La copia non è disponibile su questo dispositivo",
+ "edit": "Modifica",
+ "edit_message": "Modifica messaggio",
+ "save": "Salva",
+ "delete": "Elimina",
+ "pin": "Fissa",
+ "unpin": "Rimuovi fissaggio",
+ "flag": "Segnala",
+ "moderator_delete": "Rimuovi (moderatore)",
+ "moderator_removed": "Rimosso dal moderatore",
+ "attachment_failed": "Caricamento allegato non riuscito",
+ "ack_required": "Conferma richiesta",
+ "ack_pending_one": "Hai un messaggio urgente da confermare",
+ "ack_pending_count": "Hai {{count}} messaggi urgenti da confermare",
+ "acknowledge": "Conferma",
+ "thread": "Thread",
+ "original_message": "Messaggio originale",
+ "reply_placeholder": "Rispondi...",
+ "channel": "Canale",
+ "direct_message": "Messaggio diretto",
+ "load_people_failed": "Impossibile caricare le persone",
+ "reaction_failed": "Impossibile aggiornare la reazione",
+ "edit_failed": "Impossibile modificare il messaggio",
+ "delete_failed": "Impossibile eliminare il messaggio",
+ "pin_failed": "Impossibile aggiornare il messaggio fissato",
+ "flag_failed": "Impossibile segnalare il messaggio"
+ },
+ "chatbot": {
+ "title": "Assistente",
+ "subtitle": "Assistente IA per il tuo reparto",
+ "new_session": "Nuova sessione",
+ "empty": "Chiedi qualsiasi cosa all'assistente per iniziare.",
+ "ask_placeholder": "Chiedi all'assistente..."
+ },
+ "check_in": {
+ "tab_title": "Check-in",
+ "timer_status": "Stato timer",
+ "perform_check_in": "Check-in",
+ "check_in_success": "Check-in registrato con successo",
+ "check_in_error": "Impossibile registrare il check-in",
+ "checked_in_by": "da {{name}}",
+ "last_check_in": "Ultimo check-in",
+ "elapsed": "Trascorso",
+ "duration": "Durata",
+ "status_ok": "OK",
+ "status_green": "OK",
+ "status_warning": "Attenzione",
+ "status_yellow": "Attenzione",
+ "status_overdue": "Scaduto",
+ "status_red": "Scaduto",
+ "status_critical": "Critico",
+ "history": "Cronologia check-in",
+ "no_timers": "Nessun timer di check-in configurato",
+ "timers_disabled": "I timer di check-in sono disabilitati per questo intervento",
+ "type_personnel": "Personale",
+ "type_unit": "Unità",
+ "type_ic": "Comandante dell'incidente",
+ "type_par": "PAR",
+ "type_hazmat": "Esposizione Hazmat",
+ "type_sector_rotation": "Rotazione settore",
+ "type_rehab": "Riabilitazione",
+ "add_note": "Aggiungi nota (facoltativo)",
+ "confirm": "Conferma check-in",
+ "minutes_ago": "{{count}} min fa",
+ "select_target": "Seleziona entità per il check-in",
+ "overdue_count": "{{count}} scaduti",
+ "warning_count": "{{count}} in attenzione",
+ "enable_timers": "Abilita timer",
+ "disable_timers": "Disabilita timer",
+ "summary": "{{overdue}} scaduti, {{warning}} in attenzione, {{ok}} ok",
+ "par_title": "Controllo del personale (PAR)"
+ },
"common": {
"add": "Aggiungi",
"back": "Indietro",
@@ -502,178 +620,379 @@
"website": "Sito web",
"zip": "CAP"
},
- "form": {
- "invalid_url": "Inserisci un URL valido che inizi con http:// o https://",
- "required": "Questo campo è obbligatorio"
- },
- "livekit": {
- "audio_devices": "Dispositivi audio",
- "audio_settings": "Impostazioni audio",
- "connected_to_room": "Connesso al canale",
- "connecting": "Connessione...",
- "disconnect": "Disconnetti",
- "join": "Entra",
- "microphone": "Microfono",
- "mute": "Silenzia",
- "no_rooms_available": "Nessun canale vocale disponibile",
- "speaker": "Altoparlante",
- "speaking": "In conversazione",
- "title": "Canali vocali",
- "unmute": "Riattiva audio"
- },
- "loading": {
- "loading": "Caricamento...",
- "loadingData": "Caricamento dati...",
- "pleaseWait": "Attendere prego",
- "processingRequest": "Elaborazione della richiesta..."
- },
- "sso": {
- "authenticating": "Autenticazione...",
- "back_to_login": "Torna al login",
- "back_to_lookup": "Cambia utente",
- "continue_button": "Continua",
- "department_id_label": "ID dipartimento",
- "department_id_placeholder": "Inserisci l'ID del dipartimento",
- "error_generic": "Accesso fallito. Riprova.",
- "error_oidc_cancelled": "L'accesso è stato annullato.",
- "error_oidc_not_ready": "Il provider SSO si sta caricando, attendere prego.",
- "error_sso_not_enabled": "L'accesso unico non è abilitato per questo utente.",
- "error_token_exchange": "Impossibile completare l'accesso. Riprova.",
- "error_user_not_found": "Utente non trovato. Verifica e riprova.",
- "looking_up": "Ricerca in corso...",
- "optional": "facoltativo",
- "page_subtitle": "Inserisci il tuo nome utente per cercare le opzioni di accesso della tua organizzazione.",
- "page_title": "Accesso singolo",
- "provider_oidc": "OpenID Connect (OIDC)",
- "provider_saml": "SAML 2.0",
- "sign_in_button": "Accedi con SSO",
- "sign_in_title": "Accedi",
- "sso_button": "Accesso SSO"
- },
- "login": {
- "branding_subtitle": "Software di centrale operativa potente per vigili del fuoco, soccorso e organizzazioni di sicurezza pubblica.",
- "branding_title": "Gestione delle emergenze",
- "errorModal": {
- "confirmButton": "OK",
- "message": "Verifica nome utente e password e riprova.",
- "title": "Accesso fallito"
- },
- "feature_dispatch_desc": "Invia istantaneamente le unità e gestisci gli interventi con aggiornamenti in tempo reale su tutti i dispositivi.",
- "feature_dispatch_title": "Centrale operativa in tempo reale",
- "feature_mapping_desc": "Traccia le unità in tempo reale con mappe dettagliate, percorsi e gestione della posizione.",
- "feature_mapping_title": "Cartografia avanzata",
- "feature_personnel_desc": "Gestisci il tuo team con accesso basato sui ruoli, monitoraggio dello stato e strumenti di comunicazione.",
- "feature_personnel_title": "Gestione personale",
- "footer_text": "Creato con ❤️ a Lake Tahoe",
- "login": "Accedi",
- "login_button": "Accedi",
- "login_button_description": "Accedi al tuo account per continuare",
- "login_button_error": "Errore durante l'accesso",
- "login_button_loading": "Accesso in corso...",
- "login_button_success": "Accesso effettuato con successo",
- "no_account": "Non hai un account?",
- "page_subtitle": "Inserisci le tue credenziali per accedere.",
- "page_title": "Resgrid Dispatch",
- "password": "Password",
- "password_incorrect": "Password errata",
- "password_placeholder": "Inserisci la tua password",
- "register": "Registrati",
- "title": "Accesso",
- "username": "Nome utente",
- "username_placeholder": "Inserisci il tuo nome utente",
- "welcome_title": "Bentornato"
- },
- "lockscreen": {
- "message": "Inserisci la tua password per sbloccare lo schermo",
- "not_you": "Non sei tu? Torna al login",
- "password": "Password",
- "password_placeholder": "Inserisci la tua password",
- "title": "Schermata di blocco",
- "unlock_button": "Sblocca",
- "unlock_failed": "Sblocco fallito. Riprova.",
- "unlocking": "Sblocco in corso...",
- "welcome_back": "Bentornato",
- "relogin_required": "La verifica della password non è disponibile per questa sessione. Effettua nuovamente l'accesso."
- },
- "maintenance": {
- "downtime_message": "Stiamo lavorando per completare la manutenzione il prima possibile. Ricontrolla presto.",
- "downtime_title": "Qual è il tempo di inattività?",
- "message": "Ricontrolla tra qualche momento.",
- "support_message": "Se hai bisogno di assistenza, contattaci a",
- "support_title": "Hai bisogno di supporto?",
- "title": "Sito in manutenzione",
- "why_down_message": "Stiamo effettuando una manutenzione programmata per migliorare la tua esperienza. Ci scusiamo per l'inconveniente.",
- "why_down_title": "Perché il sito è offline?"
- },
- "menu": {
- "scheduled_calls": "Chiamate programmate",
- "pois": "POI",
- "calls": "Interventi",
- "calls_list": "Lista interventi",
- "contacts": "Contatti",
- "home": "Home",
- "map": "Mappa",
- "menu": "Menu",
- "messages": "Messaggi",
- "new_call": "Nuovo intervento",
- "personnel": "Personale",
- "protocols": "Protocolli",
- "settings": "Impostazioni",
+ "dispatch": {
+ "active_calls": "Interventi attivi",
+ "pending_calls": "In attesa",
+ "scheduled_calls": "Programmati",
+ "units_available": "Disponibili",
+ "personnel_available": "Disponibili",
+ "personnel_on_duty": "In servizio",
"units": "Unità",
- "weatherAlerts": "Allerte meteo",
- "incident_command": "Comando dell’intervento",
- "chat": "Chat",
- "assistant": "Assistente"
- },
- "pois": {
- "address": "Indirizzo",
- "all_types": "Tutti i tipi",
- "destination": "Destinazione",
- "details": "Dettagli",
- "detail_not_found": "POI non trovato",
- "detail_not_found_description": "Impossibile caricare il POI richiesto.",
- "detail_title": "Dettagli del POI",
- "empty": "Nessun POI trovato",
- "empty_description": "Non sono ancora disponibili punti di interesse per il reparto.",
- "empty_filtered": "Nessun POI corrispondente",
- "empty_filtered_description": "Prova a cancellare la ricerca o a scegliere un altro tipo di POI.",
- "filter_by_type": "Filtra per tipo di POI",
- "invalid_poi": "POI non valido",
- "invalid_poi_description": "L’identificativo del POI selezionato non è valido.",
- "loading": "Caricamento dei POI...",
- "loading_detail": "Caricamento dei dettagli del POI...",
+ "personnel": "Personale",
"map": "Mappa",
- "no_location": "Nessuna posizione disponibile",
- "no_location_description": "Questo POI non dispone di coordinate utilizzabili.",
- "no_location_for_routing": "Nessun dato di posizione disponibile per il percorso",
- "note": "Nota",
- "route_error": "Impossibile aprire l’applicazione delle mappe",
- "search": "Cerca POI...",
- "sort": "Ordina",
- "sort_options": {
- "address-asc": "Indirizzo",
- "name-asc": "Nome (A–Z)",
- "name-desc": "Nome (Z–A)",
- "type-asc": "Tipo"
+ "notes": "Note",
+ "activity_log": "Registro attività",
+ "communications": "Comunicazioni",
+ "no_active_calls": "Nessun intervento attivo",
+ "no_units": "Nessuna unità disponibile",
+ "no_personnel": "Nessun personale disponibile",
+ "no_notes": "Nessuna nota disponibile",
+ "no_activity": "Nessuna attività recente",
+ "current_channel": "Canale attuale",
+ "audio_stream": "Flusso audio",
+ "no_stream": "Nessun flusso attivo",
+ "ptt": "PTT",
+ "ptt_start": "Inizio PTT",
+ "ptt_end": "Fine PTT",
+ "transmitting_on": "Trasmissione su {{channel}}",
+ "transmission_ended": "Trasmissione terminata",
+ "voice_disabled": "Voce disabilitata",
+ "disconnected": "Disconnesso",
+ "select_channel": "Seleziona canale",
+ "select_channel_description": "Scegli un canale vocale a cui connettersi",
+ "change_channel_warning": "Selezionare un nuovo canale disconnetterà dal canale attuale",
+ "default_channel": "Predefinito",
+ "no_channels_available": "Nessun canale vocale disponibile",
+ "system_update": "Aggiornamento di sistema",
+ "data_refreshed": "Dati aggiornati dal server",
+ "call_selected": "Intervento selezionato",
+ "unit_selected": "Unità selezionata",
+ "unit_deselected": "Unità deselezionata",
+ "personnel_selected": "Personale selezionato",
+ "personnel_deselected": "Personale deselezionato",
+ "loading_map": "Caricamento mappa...",
+ "map_not_available_web": "Mappa non disponibile sulla piattaforma web",
+ "filtering_by_call": "Filtro per intervento",
+ "clear_filter": "Cancella filtro",
+ "call_filter_active": "Filtro intervento attivo",
+ "call_filter_cleared": "Filtro intervento cancellato",
+ "showing_all_data": "Visualizzazione di tutti i dati",
+ "call_notes": "Note intervento",
+ "no_call_notes": "Nessuna nota intervento",
+ "add_call_note_placeholder": "Aggiungi una nota...",
+ "note_added": "Nota aggiunta",
+ "note_added_to_console": "Una nuova nota è stata aggiunta alla console",
+ "add_note_title": "Aggiungi nuova nota",
+ "note_title_label": "Titolo",
+ "note_title_placeholder": "Inserisci il titolo della nota...",
+ "note_category_label": "Categoria",
+ "note_category_placeholder": "Seleziona una categoria",
+ "note_no_category": "Nessuna categoria",
+ "note_body_label": "Contenuto nota",
+ "note_body_placeholder": "Inserisci il contenuto della nota...",
+ "note_save_error": "Impossibile salvare la nota: {{error}}",
+ "note_created": "Nota creata",
+ "units_on_call": "Unità in servizio",
+ "no_units_on_call": "Nessuna unità in servizio",
+ "personnel_on_call": "Personale in servizio",
+ "no_personnel_on_call": "Nessun personale in servizio",
+ "call_activity": "Attività intervento",
+ "no_call_activity": "Nessuna attività intervento",
+ "on_call": "In servizio",
+ "filtered": "Filtrato",
+ "active_filter": "Filtro attivo",
+ "unit_status_change": "Cambio stato unità",
+ "personnel_status_change": "Cambio stato personale",
+ "view_call_details": "Visualizza dettagli intervento",
+ "dispatched_resources": "Inviati",
+ "unassigned": "Non assegnato",
+ "available": "Disponibile",
+ "unknown": "Sconosciuto",
+ "search_personnel_placeholder": "Cerca personale...",
+ "search_calls_placeholder": "Cerca interventi...",
+ "search_units_placeholder": "Cerca unità...",
+ "search_notes_placeholder": "Cerca note...",
+ "signalr_update": "Aggiornamento in tempo reale",
+ "signalr_connected": "Connesso",
+ "realtime_updates_active": "Aggiornamenti in tempo reale attivi",
+ "personnel_status_updated": "Stato personale aggiornato",
+ "personnel_staffing_updated": "Organico personale aggiornato",
+ "unit_status_updated": "Stato unità aggiornato",
+ "calls_updated": "Interventi aggiornati",
+ "call_added": "Nuovo intervento aggiunto",
+ "call_closed": "Intervento chiuso",
+ "check_ins": "Check-in",
+ "no_check_ins": "Nessun intervento con timer di check-in",
+ "radio_log": "Registro radio",
+ "radio": "Radio",
+ "activity": "Attività",
+ "actions": "Azioni",
+ "no_radio_activity": "Nessuna trasmissione radio",
+ "live": "LIVE",
+ "currently_transmitting": "Trasmissione in corso...",
+ "duration": "Durata",
+ "call_actions": "Azioni intervento",
+ "unit_actions": "Azioni unità",
+ "personnel_actions": {
+ "title": "Azioni personale",
+ "status_tab": "Stato",
+ "staffing_tab": "Organico",
+ "select_status": "Seleziona stato",
+ "select_staffing": "Seleziona livello organico",
+ "destination": "Destinazione",
+ "no_destination": "Nessuna destinazione",
+ "note": "Nota",
+ "note_placeholder": "Aggiungi una nota facoltativa...",
+ "update_status": "Aggiorna stato",
+ "update_staffing": "Aggiorna organico",
+ "no_statuses_available": "Nessuno stato disponibile",
+ "no_staffings_available": "Nessun livello di organico disponibile"
},
- "title": "POI",
- "type": "Tipo",
- "unknown_type": "Tipo sconosciuto",
- "unnamed": "POI senza nome"
+ "unit_actions_panel": {
+ "status": "Stato",
+ "select_status": "Seleziona stato",
+ "destination": "Destinazione",
+ "no_destination": "Nessuna destinazione",
+ "note": "Nota",
+ "note_placeholder": "Aggiungi una nota facoltativa...",
+ "update_status": "Aggiorna stato",
+ "no_statuses_available": "Nessuno stato disponibile",
+ "no_active_calls": "Nessun intervento attivo",
+ "no_stations_available": "Nessuna stazione disponibile",
+ "no_destinations_available": "Nessuna destinazione disponibile"
+ },
+ "call": "Intervento",
+ "station": "Stazione",
+ "calls": "Interventi",
+ "stations": "Stazioni",
+ "no_stations_available": "Nessuna stazione disponibile",
+ "new_call": "Nuovo intervento",
+ "view_details": "Dettagli",
+ "add_note": "Aggiungi nota",
+ "close_call": "Chiudi",
+ "set_status": "Imposta stato",
+ "set_staffing": "Organico",
+ "dispatch": "Invia",
+ "select_items_for_actions": "Seleziona un intervento, un'unità o personale per abilitare le azioni contestuali",
+ "weather": {
+ "clear": "Sereno",
+ "mainly_clear": "Prevalentemente sereno",
+ "partly_cloudy": "Parzialmente nuvoloso",
+ "overcast": "Coperto",
+ "fog": "Nebbia",
+ "drizzle": "Pioggerella",
+ "freezing_drizzle": "Pioggerella gelata",
+ "rain": "Pioggia",
+ "freezing_rain": "Pioggia gelata",
+ "snow": "Neve",
+ "rain_showers": "Rovesci di pioggia",
+ "snow_showers": "Rovesci di neve",
+ "thunderstorm": "Temporale",
+ "thunderstorm_hail": "Temporale con grandine",
+ "unknown": "Sconosciuto"
+ },
+ "available_only": "Solo disponibili",
+ "single_list": "Elenco singolo",
+ "resources": "Risorse",
+ "search_resources_placeholder": "Cerca risorse...",
+ "no_resources": "Nessuna risorsa"
},
- "scheduled_calls": {
- "title": "Chiamate programmate",
- "loading": "Caricamento delle chiamate programmate...",
- "no_scheduled_calls": "Nessuna chiamata programmata",
- "no_scheduled_calls_description": "Al momento non ci sono chiamate programmate in attesa.",
- "search": "Cerca chiamate programmate...",
- "scheduled_for": "Programmata per",
- "table_number": "Chiamata n.",
- "table_name": "Nome",
- "table_type": "Tipo",
- "table_priority": "Priorità",
- "table_address": "Indirizzo",
- "table_scheduled": "Programmata per"
+ "form": {
+ "invalid_url": "Inserisci un URL valido che inizi con http:// o https://",
+ "required": "Questo campo è obbligatorio"
+ },
+ "incident_command": {
+ "accountability": "Controllo del personale (PAR)",
+ "acknowledge": "Conferma",
+ "action_plan": "Piano d’azione",
+ "action_plan_placeholder": "Descrivi il piano d’azione dell’intervento...",
+ "active": "Attivo",
+ "active_title": "Comandi degli interventi attivi",
+ "add": "Aggiungi",
+ "add_channel": "Aggiungi canale",
+ "add_lane": "Aggiungi settore",
+ "add_marker": "Aggiungi indicatore",
+ "add_objective": "Aggiungi obiettivo",
+ "annotations": "Annotazioni della mappa",
+ "assign": "Assegna",
+ "assign_resource": "Assegna risorsa",
+ "assign_resource_required": "Seleziona un settore e una risorsa",
+ "assign_role": "Assegna ruolo",
+ "assign_role_required": "Seleziona una persona e un ruolo",
+ "call": "Chiamata",
+ "channel_name": "Nome del canale",
+ "chat": "Chat",
+ "chat_frozen": "This incident is closed. Conversations are kept as a point-in-time record — no new messages.",
+ "close_all_channels": "Chiudi tutti i canali",
+ "close_command": "Chiudi comando",
+ "closed": "Chiuso",
+ "command_channel": "Command chat",
+ "command_channel_hint": "Command staff and dispatch",
+ "command_channel_unavailable": "No command channel has been created for this incident yet.",
+ "commander": "Comandante",
+ "complete": "Completa",
+ "completed": "Completato",
+ "confirm_close": "Chiudere il comando dell’intervento per questa chiamata?",
+ "critical": "Critico",
+ "delete_annotation_confirm": "Rimuovere questa annotazione?",
+ "dispatch_channel": "Dispatch",
+ "dispatch_channel_hint": "The incident's line to the desk",
+ "dispatch_channel_unavailable": "No dispatch channel has been created for this incident yet.",
+ "dm_failed": "Couldn't open that conversation.",
+ "dm_unavailable": "That contact has no Resgrid account to message.",
+ "due": "Scadenza",
+ "edit": "Modifica",
+ "edit_action_plan": "Modifica piano d’azione",
+ "establish": "Stabilisci comando",
+ "establish_description": "Facoltativamente, inizializza il pannello di comando da un modello.",
+ "establish_error": "Impossibile stabilire il comando",
+ "establish_success": "Comando dell’intervento stabilito",
+ "establish_title": "Stabilisci comando dell’intervento",
+ "established_on": "Stabilito",
+ "green": "Verde",
+ "hold_to_talk": "Tieni premuto per parlare",
+ "incident_channel": "Incident chat",
+ "incident_channel_hint": "Everyone working the incident",
+ "incident_channel_unavailable": "No incident channel has been created for this call yet.",
+ "join": "Partecipa",
+ "lane": "Settore",
+ "lane_name": "Nome del settore",
+ "lane_type": "Tipo di settore",
+ "marker": "Indicatore",
+ "marker_label": "Etichetta dell’indicatore",
+ "move": "Sposta",
+ "move_lane": "Sposta settore",
+ "move_resource": "Sposta risorsa",
+ "name_required": "Il nome è obbligatorio",
+ "no_accountability": "Nessun personale monitorato.",
+ "no_action_plan": "Nessun piano d’azione impostato.",
+ "no_active": "Nessun comando dell’intervento attivo",
+ "no_active_description": "I comandi stabiliti per le chiamate verranno visualizzati qui.",
+ "no_annotations": "Nessuna annotazione.",
+ "no_channels": "Nessun canale aperto.",
+ "no_command": "Nessun comando dell’intervento stabilito",
+ "no_command_description": "Stabilisci il comando dell’intervento per coordinare risorse, ruoli, obiettivi e controllo del personale per questa chiamata.",
+ "no_lanes": "Nessun settore definito.",
+ "no_objectives": "Nessun obiettivo.",
+ "no_resources": "Nessuna risorsa assegnata.",
+ "no_roles": "Nessun ruolo assegnato.",
+ "no_template": "Nessun modello (pannello vuoto)",
+ "no_timeline": "Nessuna voce nella cronologia.",
+ "no_timers": "Nessun timer in esecuzione.",
+ "not_authorized": "Command board unavailable",
+ "not_authorized_description": "Your department hasn't authorized you to work incident command. Ask an administrator for the Command App Login permission.",
+ "objective_name": "Obiettivo",
+ "objective_type": "Tipo",
+ "objectives": "Obiettivi",
+ "open_chat": "Open",
+ "open_full_board": "Apri pannello completo",
+ "open_tactical_map": "Apri mappa tattica",
+ "parent_lane": "Settore principale",
+ "person": "Persona",
+ "personnel": "Personale",
+ "release": "Rilascia",
+ "resource": "Risorsa",
+ "resource_type": "Tipo di risorsa",
+ "role": "Ruolo",
+ "roles": "Ruoli di comando",
+ "run_par": "Esegui PAR",
+ "save": "Salva",
+ "save_error": "Operazione non riuscita",
+ "saved": "Salvato",
+ "select_lane": "Seleziona un settore",
+ "select_person": "Seleziona una persona",
+ "select_resource": "Seleziona una risorsa",
+ "select_role": "Seleziona un ruolo",
+ "send_message": "Message",
+ "status": "Stato",
+ "structure": "Struttura di comando",
+ "tab_title": "Comando",
+ "tactical_map": "Mappa tattica",
+ "talking": "Trasmissione...",
+ "tap_to_place": "Tocca la mappa per posizionare un indicatore",
+ "template": "Modello",
+ "timeline": "Cronologia del comando",
+ "timers": "Timer",
+ "title": "Comando dell’intervento",
+ "top_level": "Livello superiore",
+ "transfer": "Trasferisci",
+ "transfer_command": "Trasferisci",
+ "transfer_notes": "Note",
+ "transfer_success": "Comando trasferito",
+ "transfer_title": "Trasferisci comando",
+ "unassigned": "Non assegnato",
+ "unit": "Unità",
+ "voice_channels": "Canali vocali",
+ "voice_join_error": "Impossibile collegarsi al canale vocale",
+ "voice_joined": "Canale vocale collegato",
+ "warning": "Avviso"
+ },
+ "livekit": {
+ "audio_devices": "Dispositivi audio",
+ "audio_settings": "Impostazioni audio",
+ "connected_to_room": "Connesso al canale",
+ "connecting": "Connessione...",
+ "disconnect": "Disconnetti",
+ "join": "Entra",
+ "microphone": "Microfono",
+ "mute": "Silenzia",
+ "no_rooms_available": "Nessun canale vocale disponibile",
+ "speaker": "Altoparlante",
+ "speaking": "In conversazione",
+ "title": "Canali vocali",
+ "unmute": "Riattiva audio"
+ },
+ "loading": {
+ "loading": "Caricamento...",
+ "loadingData": "Caricamento dati...",
+ "pleaseWait": "Attendere prego",
+ "processingRequest": "Elaborazione della richiesta..."
+ },
+ "lockscreen": {
+ "message": "Inserisci la tua password per sbloccare lo schermo",
+ "not_you": "Non sei tu? Torna al login",
+ "password": "Password",
+ "password_placeholder": "Inserisci la tua password",
+ "title": "Schermata di blocco",
+ "unlock_button": "Sblocca",
+ "unlock_failed": "Sblocco fallito. Riprova.",
+ "unlocking": "Sblocco in corso...",
+ "welcome_back": "Bentornato",
+ "relogin_required": "La verifica della password non è disponibile per questa sessione. Effettua nuovamente l'accesso."
+ },
+ "login": {
+ "branding_subtitle": "Software di centrale operativa potente per vigili del fuoco, soccorso e organizzazioni di sicurezza pubblica.",
+ "branding_title": "Gestione delle emergenze",
+ "dispatch_not_authorized": "You are not authorized to use the Dispatch app. Contact your department administrator.",
+ "errorModal": {
+ "confirmButton": "OK",
+ "message": "Verifica nome utente e password e riprova.",
+ "title": "Accesso fallito"
+ },
+ "feature_dispatch_desc": "Invia istantaneamente le unità e gestisci gli interventi con aggiornamenti in tempo reale su tutti i dispositivi.",
+ "feature_dispatch_title": "Centrale operativa in tempo reale",
+ "feature_mapping_desc": "Traccia le unità in tempo reale con mappe dettagliate, percorsi e gestione della posizione.",
+ "feature_mapping_title": "Cartografia avanzata",
+ "feature_personnel_desc": "Gestisci il tuo team con accesso basato sui ruoli, monitoraggio dello stato e strumenti di comunicazione.",
+ "feature_personnel_title": "Gestione personale",
+ "footer_text": "Creato con ❤️ a Lake Tahoe",
+ "login": "Accedi",
+ "login_button": "Accedi",
+ "login_button_description": "Accedi al tuo account per continuare",
+ "login_button_error": "Errore durante l'accesso",
+ "login_button_loading": "Accesso in corso...",
+ "login_button_success": "Accesso effettuato con successo",
+ "no_account": "Non hai un account?",
+ "page_subtitle": "Inserisci le tue credenziali per accedere.",
+ "page_title": "Resgrid Dispatch",
+ "password": "Password",
+ "password_incorrect": "Password errata",
+ "password_placeholder": "Inserisci la tua password",
+ "register": "Registrati",
+ "title": "Accesso",
+ "username": "Nome utente",
+ "username_placeholder": "Inserisci il tuo nome utente",
+ "welcome_title": "Bentornato"
+ },
+ "maintenance": {
+ "downtime_message": "Stiamo lavorando per completare la manutenzione il prima possibile. Ricontrolla presto.",
+ "downtime_title": "Qual è il tempo di inattività?",
+ "message": "Ricontrolla tra qualche momento.",
+ "support_message": "Se hai bisogno di assistenza, contattaci a",
+ "support_title": "Hai bisogno di supporto?",
+ "title": "Sito in manutenzione",
+ "why_down_message": "Stiamo effettuando una manutenzione programmata per migliorare la tua esperienza. Ci scusiamo per l'inconveniente.",
+ "why_down_title": "Perché il sito è offline?"
},
"map": {
"view_poi_details": "Visualizza dettagli del POI",
@@ -690,6 +1009,26 @@
"hide_all": "Nascondi tutto",
"view_call_details": "Visualizza dettagli intervento"
},
+ "menu": {
+ "scheduled_calls": "Chiamate programmate",
+ "pois": "POI",
+ "calls": "Interventi",
+ "calls_list": "Lista interventi",
+ "contacts": "Contatti",
+ "home": "Home",
+ "map": "Mappa",
+ "menu": "Menu",
+ "messages": "Messaggi",
+ "new_call": "Nuovo intervento",
+ "personnel": "Personale",
+ "protocols": "Protocolli",
+ "settings": "Impostazioni",
+ "units": "Unità",
+ "weatherAlerts": "Allerte meteo",
+ "incident_command": "Comando dell’intervento",
+ "chat": "Chat",
+ "assistant": "Assistente"
+ },
"notes": {
"actions": {
"add": "Aggiungi nota",
@@ -709,6 +1048,23 @@
"search": "Cerca note...",
"title": "Note"
},
+ "onboarding": {
+ "screen1": {
+ "title": "Resgrid Dispatch",
+ "description": "Crea, invia e gestisci gli interventi di emergenza con un potente centro di comando mobile a portata di mano"
+ },
+ "screen2": {
+ "title": "Consapevolezza situazionale in tempo reale",
+ "description": "Traccia tutte le unità, il personale e le risorse su una mappa interattiva con aggiornamenti di stato in tempo reale e AVL"
+ },
+ "screen3": {
+ "title": "Coordinamento senza interruzioni",
+ "description": "Comunica istantaneamente con le unità sul campo, aggiorna lo stato degli interventi e coordina gli sforzi di risposta da qualsiasi luogo"
+ },
+ "skip": "Salta",
+ "next": "Avanti",
+ "getStarted": "Iniziamo"
+ },
"personnel": {
"title": "Personale",
"search": "Cerca personale...",
@@ -741,22 +1097,41 @@
"send_email": "E-mail",
"custom_fields": "Informazioni aggiuntive"
},
- "onboarding": {
- "screen1": {
- "title": "Resgrid Dispatch",
- "description": "Crea, invia e gestisci gli interventi di emergenza con un potente centro di comando mobile a portata di mano"
- },
- "screen2": {
- "title": "Consapevolezza situazionale in tempo reale",
- "description": "Traccia tutte le unità, il personale e le risorse su una mappa interattiva con aggiornamenti di stato in tempo reale e AVL"
- },
- "screen3": {
- "title": "Coordinamento senza interruzioni",
- "description": "Comunica istantaneamente con le unità sul campo, aggiorna lo stato degli interventi e coordina gli sforzi di risposta da qualsiasi luogo"
+ "pois": {
+ "address": "Indirizzo",
+ "all_types": "Tutti i tipi",
+ "destination": "Destinazione",
+ "details": "Dettagli",
+ "detail_not_found": "POI non trovato",
+ "detail_not_found_description": "Impossibile caricare il POI richiesto.",
+ "detail_title": "Dettagli del POI",
+ "empty": "Nessun POI trovato",
+ "empty_description": "Non sono ancora disponibili punti di interesse per il reparto.",
+ "empty_filtered": "Nessun POI corrispondente",
+ "empty_filtered_description": "Prova a cancellare la ricerca o a scegliere un altro tipo di POI.",
+ "filter_by_type": "Filtra per tipo di POI",
+ "invalid_poi": "POI non valido",
+ "invalid_poi_description": "L’identificativo del POI selezionato non è valido.",
+ "loading": "Caricamento dei POI...",
+ "loading_detail": "Caricamento dei dettagli del POI...",
+ "map": "Mappa",
+ "no_location": "Nessuna posizione disponibile",
+ "no_location_description": "Questo POI non dispone di coordinate utilizzabili.",
+ "no_location_for_routing": "Nessun dato di posizione disponibile per il percorso",
+ "note": "Nota",
+ "route_error": "Impossibile aprire l’applicazione delle mappe",
+ "search": "Cerca POI...",
+ "sort": "Ordina",
+ "sort_options": {
+ "address-asc": "Indirizzo",
+ "name-asc": "Nome (A–Z)",
+ "name-desc": "Nome (Z–A)",
+ "type-asc": "Tipo"
},
- "skip": "Salta",
- "next": "Avanti",
- "getStarted": "Iniziamo"
+ "title": "POI",
+ "type": "Tipo",
+ "unknown_type": "Tipo sconosciuto",
+ "unnamed": "POI senza nome"
},
"protocols": {
"details": {
@@ -796,6 +1171,20 @@
"tap_to_manage": "Tocca per gestire i ruoli",
"unassigned": "Non assegnato"
},
+ "scheduled_calls": {
+ "title": "Chiamate programmate",
+ "loading": "Caricamento delle chiamate programmate...",
+ "no_scheduled_calls": "Nessuna chiamata programmata",
+ "no_scheduled_calls_description": "Al momento non ci sono chiamate programmate in attesa.",
+ "search": "Cerca chiamate programmate...",
+ "scheduled_for": "Programmata per",
+ "table_number": "Chiamata n.",
+ "table_name": "Nome",
+ "table_type": "Tipo",
+ "table_priority": "Priorità",
+ "table_address": "Indirizzo",
+ "table_scheduled": "Programmata per"
+ },
"settings": {
"about": "Informazioni",
"account": "Account",
@@ -884,6 +1273,29 @@
"version": "Versione",
"website": "Sito web"
},
+ "sso": {
+ "authenticating": "Autenticazione...",
+ "back_to_login": "Torna al login",
+ "back_to_lookup": "Cambia utente",
+ "continue_button": "Continua",
+ "department_id_label": "ID dipartimento",
+ "department_id_placeholder": "Inserisci l'ID del dipartimento",
+ "error_generic": "Accesso fallito. Riprova.",
+ "error_oidc_cancelled": "L'accesso è stato annullato.",
+ "error_oidc_not_ready": "Il provider SSO si sta caricando, attendere prego.",
+ "error_sso_not_enabled": "L'accesso unico non è abilitato per questo utente.",
+ "error_token_exchange": "Impossibile completare l'accesso. Riprova.",
+ "error_user_not_found": "Utente non trovato. Verifica e riprova.",
+ "looking_up": "Ricerca in corso...",
+ "optional": "facoltativo",
+ "page_subtitle": "Inserisci il tuo nome utente per cercare le opzioni di accesso della tua organizzazione.",
+ "page_title": "Accesso singolo",
+ "provider_oidc": "OpenID Connect (OIDC)",
+ "provider_saml": "SAML 2.0",
+ "sign_in_button": "Accedi con SSO",
+ "sign_in_title": "Accedi",
+ "sso_button": "Accesso SSO"
+ },
"status": {
"all_destinations_enabled": "Può rispondere a chiamate, stazioni o POI",
"calls_and_pois_destination_enabled": "Può rispondere a chiamate o POI",
@@ -916,173 +1328,6 @@
"stations_tab": "Stazioni",
"status_saved_successfully": "Stato salvato con successo!"
},
- "dispatch": {
- "active_calls": "Interventi attivi",
- "pending_calls": "In attesa",
- "scheduled_calls": "Programmati",
- "units_available": "Disponibili",
- "personnel_available": "Disponibili",
- "personnel_on_duty": "In servizio",
- "units": "Unità",
- "personnel": "Personale",
- "map": "Mappa",
- "notes": "Note",
- "activity_log": "Registro attività",
- "communications": "Comunicazioni",
- "no_active_calls": "Nessun intervento attivo",
- "no_units": "Nessuna unità disponibile",
- "no_personnel": "Nessun personale disponibile",
- "no_notes": "Nessuna nota disponibile",
- "no_activity": "Nessuna attività recente",
- "current_channel": "Canale attuale",
- "audio_stream": "Flusso audio",
- "no_stream": "Nessun flusso attivo",
- "ptt": "PTT",
- "ptt_start": "Inizio PTT",
- "ptt_end": "Fine PTT",
- "transmitting_on": "Trasmissione su {{channel}}",
- "transmission_ended": "Trasmissione terminata",
- "voice_disabled": "Voce disabilitata",
- "disconnected": "Disconnesso",
- "select_channel": "Seleziona canale",
- "select_channel_description": "Scegli un canale vocale a cui connettersi",
- "change_channel_warning": "Selezionare un nuovo canale disconnetterà dal canale attuale",
- "default_channel": "Predefinito",
- "no_channels_available": "Nessun canale vocale disponibile",
- "system_update": "Aggiornamento di sistema",
- "data_refreshed": "Dati aggiornati dal server",
- "call_selected": "Intervento selezionato",
- "unit_selected": "Unità selezionata",
- "unit_deselected": "Unità deselezionata",
- "personnel_selected": "Personale selezionato",
- "personnel_deselected": "Personale deselezionato",
- "loading_map": "Caricamento mappa...",
- "map_not_available_web": "Mappa non disponibile sulla piattaforma web",
- "filtering_by_call": "Filtro per intervento",
- "clear_filter": "Cancella filtro",
- "call_filter_active": "Filtro intervento attivo",
- "call_filter_cleared": "Filtro intervento cancellato",
- "showing_all_data": "Visualizzazione di tutti i dati",
- "call_notes": "Note intervento",
- "no_call_notes": "Nessuna nota intervento",
- "add_call_note_placeholder": "Aggiungi una nota...",
- "note_added": "Nota aggiunta",
- "note_added_to_console": "Una nuova nota è stata aggiunta alla console",
- "add_note_title": "Aggiungi nuova nota",
- "note_title_label": "Titolo",
- "note_title_placeholder": "Inserisci il titolo della nota...",
- "note_category_label": "Categoria",
- "note_category_placeholder": "Seleziona una categoria",
- "note_no_category": "Nessuna categoria",
- "note_body_label": "Contenuto nota",
- "note_body_placeholder": "Inserisci il contenuto della nota...",
- "note_save_error": "Impossibile salvare la nota: {{error}}",
- "note_created": "Nota creata",
- "units_on_call": "Unità in servizio",
- "no_units_on_call": "Nessuna unità in servizio",
- "personnel_on_call": "Personale in servizio",
- "no_personnel_on_call": "Nessun personale in servizio",
- "call_activity": "Attività intervento",
- "no_call_activity": "Nessuna attività intervento",
- "on_call": "In servizio",
- "filtered": "Filtrato",
- "active_filter": "Filtro attivo",
- "unit_status_change": "Cambio stato unità",
- "personnel_status_change": "Cambio stato personale",
- "view_call_details": "Visualizza dettagli intervento",
- "dispatched_resources": "Inviati",
- "unassigned": "Non assegnato",
- "available": "Disponibile",
- "unknown": "Sconosciuto",
- "search_personnel_placeholder": "Cerca personale...",
- "search_calls_placeholder": "Cerca interventi...",
- "search_units_placeholder": "Cerca unità...",
- "search_notes_placeholder": "Cerca note...",
- "signalr_update": "Aggiornamento in tempo reale",
- "signalr_connected": "Connesso",
- "realtime_updates_active": "Aggiornamenti in tempo reale attivi",
- "personnel_status_updated": "Stato personale aggiornato",
- "personnel_staffing_updated": "Organico personale aggiornato",
- "unit_status_updated": "Stato unità aggiornato",
- "calls_updated": "Interventi aggiornati",
- "call_added": "Nuovo intervento aggiunto",
- "call_closed": "Intervento chiuso",
- "check_ins": "Check-in",
- "no_check_ins": "Nessun intervento con timer di check-in",
- "radio_log": "Registro radio",
- "radio": "Radio",
- "activity": "Attività",
- "actions": "Azioni",
- "no_radio_activity": "Nessuna trasmissione radio",
- "live": "LIVE",
- "currently_transmitting": "Trasmissione in corso...",
- "duration": "Durata",
- "call_actions": "Azioni intervento",
- "unit_actions": "Azioni unità",
- "personnel_actions": {
- "title": "Azioni personale",
- "status_tab": "Stato",
- "staffing_tab": "Organico",
- "select_status": "Seleziona stato",
- "select_staffing": "Seleziona livello organico",
- "destination": "Destinazione",
- "no_destination": "Nessuna destinazione",
- "note": "Nota",
- "note_placeholder": "Aggiungi una nota facoltativa...",
- "update_status": "Aggiorna stato",
- "update_staffing": "Aggiorna organico",
- "no_statuses_available": "Nessuno stato disponibile",
- "no_staffings_available": "Nessun livello di organico disponibile"
- },
- "unit_actions_panel": {
- "status": "Stato",
- "select_status": "Seleziona stato",
- "destination": "Destinazione",
- "no_destination": "Nessuna destinazione",
- "note": "Nota",
- "note_placeholder": "Aggiungi una nota facoltativa...",
- "update_status": "Aggiorna stato",
- "no_statuses_available": "Nessuno stato disponibile",
- "no_active_calls": "Nessun intervento attivo",
- "no_stations_available": "Nessuna stazione disponibile",
- "no_destinations_available": "Nessuna destinazione disponibile"
- },
- "call": "Intervento",
- "station": "Stazione",
- "calls": "Interventi",
- "stations": "Stazioni",
- "no_stations_available": "Nessuna stazione disponibile",
- "new_call": "Nuovo intervento",
- "view_details": "Dettagli",
- "add_note": "Aggiungi nota",
- "close_call": "Chiudi",
- "set_status": "Imposta stato",
- "set_staffing": "Organico",
- "dispatch": "Invia",
- "select_items_for_actions": "Seleziona un intervento, un'unità o personale per abilitare le azioni contestuali",
- "weather": {
- "clear": "Sereno",
- "mainly_clear": "Prevalentemente sereno",
- "partly_cloudy": "Parzialmente nuvoloso",
- "overcast": "Coperto",
- "fog": "Nebbia",
- "drizzle": "Pioggerella",
- "freezing_drizzle": "Pioggerella gelata",
- "rain": "Pioggia",
- "freezing_rain": "Pioggia gelata",
- "snow": "Neve",
- "rain_showers": "Rovesci di pioggia",
- "snow_showers": "Rovesci di neve",
- "thunderstorm": "Temporale",
- "thunderstorm_hail": "Temporale con grandine",
- "unknown": "Sconosciuto"
- },
- "available_only": "Solo disponibili",
- "single_list": "Elenco singolo",
- "resources": "Risorse",
- "search_resources_placeholder": "Cerca risorse...",
- "no_resources": "Nessuna risorsa"
- },
"tabs": {
"calls": "Interventi",
"calendar": "Calendario",
@@ -1096,44 +1341,6 @@
"shifts": "Turni",
"personnel": "Personale"
},
- "check_in": {
- "tab_title": "Check-in",
- "timer_status": "Stato timer",
- "perform_check_in": "Check-in",
- "check_in_success": "Check-in registrato con successo",
- "check_in_error": "Impossibile registrare il check-in",
- "checked_in_by": "da {{name}}",
- "last_check_in": "Ultimo check-in",
- "elapsed": "Trascorso",
- "duration": "Durata",
- "status_ok": "OK",
- "status_green": "OK",
- "status_warning": "Attenzione",
- "status_yellow": "Attenzione",
- "status_overdue": "Scaduto",
- "status_red": "Scaduto",
- "status_critical": "Critico",
- "history": "Cronologia check-in",
- "no_timers": "Nessun timer di check-in configurato",
- "timers_disabled": "I timer di check-in sono disabilitati per questo intervento",
- "type_personnel": "Personale",
- "type_unit": "Unità",
- "type_ic": "Comandante dell'incidente",
- "type_par": "PAR",
- "type_hazmat": "Esposizione Hazmat",
- "type_sector_rotation": "Rotazione settore",
- "type_rehab": "Riabilitazione",
- "add_note": "Aggiungi nota (facoltativo)",
- "confirm": "Conferma check-in",
- "minutes_ago": "{{count}} min fa",
- "select_target": "Seleziona entità per il check-in",
- "overdue_count": "{{count}} scaduti",
- "warning_count": "{{count}} in attenzione",
- "enable_timers": "Abilita timer",
- "disable_timers": "Disabilita timer",
- "summary": "{{overdue}} scaduti, {{warning}} in attenzione, {{ok}} ok",
- "par_title": "Controllo del personale (PAR)"
- },
"units": {
"search": "Cerca unità...",
"loading": "Caricamento delle unità...",
@@ -1163,6 +1370,63 @@
"no_destination": "Nessuna",
"title": "Unità"
},
+ "videoFeeds": {
+ "title": "Feed video",
+ "noFeeds": "Nessun feed video per questo intervento",
+ "addFeed": "Aggiungi feed video",
+ "editFeed": "Modifica feed video",
+ "deleteFeed": "Elimina feed video",
+ "deleteConfirm": "Sei sicuro di voler rimuovere questo feed video?",
+ "watch": "Guarda",
+ "goLive": "Vai in diretta",
+ "stopLive": "Interrompi diretta",
+ "flipCamera": "Inverti fotocamera",
+ "feedAdded": "Feed video aggiunto",
+ "feedUpdated": "Feed video aggiornato",
+ "feedDeleted": "Feed video rimosso",
+ "feedError": "Impossibile caricare il feed video",
+ "unsupportedFormat": "Questo formato di streaming non è supportato su dispositivi mobili",
+ "copyUrl": "Copia URL",
+ "form": {
+ "name": "Nome feed",
+ "namePlaceholder": "es. Drone Autopompa 1",
+ "url": "URL dello stream",
+ "urlPlaceholder": "es. https://stream.example.com/live.m3u8",
+ "feedType": "Tipo di telecamera",
+ "feedFormat": "Formato stream",
+ "description": "Descrizione",
+ "descriptionPlaceholder": "Descrizione facoltativa",
+ "status": "Stato",
+ "sortOrder": "Ordine",
+ "cameraLocation": "Posizione telecamera",
+ "useCurrentLocation": "Usa posizione attuale"
+ },
+ "type": {
+ "drone": "Drone",
+ "fixedCamera": "Telecamera fissa",
+ "bodyCam": "Body cam",
+ "trafficCam": "Telecamera traffico",
+ "weatherCam": "Telecamera meteo",
+ "satelliteFeed": "Feed satellitare",
+ "webCam": "Webcam",
+ "other": "Altro"
+ },
+ "format": {
+ "rtsp": "RTSP",
+ "hls": "HLS",
+ "mjpeg": "MJPEG",
+ "youtubeLive": "YouTube Live",
+ "webrtc": "WebRTC",
+ "dash": "DASH",
+ "embed": "Incorporato",
+ "other": "Altro"
+ },
+ "status": {
+ "active": "Attivo",
+ "inactive": "Inattivo",
+ "error": "Errore"
+ }
+ },
"weatherAlerts": {
"title": "Allerte meteo",
"activeAlerts": "Allerte attive",
@@ -1270,251 +1534,5 @@
},
"stats_label": "Allerte meteo"
},
- "videoFeeds": {
- "title": "Feed video",
- "noFeeds": "Nessun feed video per questo intervento",
- "addFeed": "Aggiungi feed video",
- "editFeed": "Modifica feed video",
- "deleteFeed": "Elimina feed video",
- "deleteConfirm": "Sei sicuro di voler rimuovere questo feed video?",
- "watch": "Guarda",
- "goLive": "Vai in diretta",
- "stopLive": "Interrompi diretta",
- "flipCamera": "Inverti fotocamera",
- "feedAdded": "Feed video aggiunto",
- "feedUpdated": "Feed video aggiornato",
- "feedDeleted": "Feed video rimosso",
- "feedError": "Impossibile caricare il feed video",
- "unsupportedFormat": "Questo formato di streaming non è supportato su dispositivi mobili",
- "copyUrl": "Copia URL",
- "form": {
- "name": "Nome feed",
- "namePlaceholder": "es. Drone Autopompa 1",
- "url": "URL dello stream",
- "urlPlaceholder": "es. https://stream.example.com/live.m3u8",
- "feedType": "Tipo di telecamera",
- "feedFormat": "Formato stream",
- "description": "Descrizione",
- "descriptionPlaceholder": "Descrizione facoltativa",
- "status": "Stato",
- "sortOrder": "Ordine",
- "cameraLocation": "Posizione telecamera",
- "useCurrentLocation": "Usa posizione attuale"
- },
- "type": {
- "drone": "Drone",
- "fixedCamera": "Telecamera fissa",
- "bodyCam": "Body cam",
- "trafficCam": "Telecamera traffico",
- "weatherCam": "Telecamera meteo",
- "satelliteFeed": "Feed satellitare",
- "webCam": "Webcam",
- "other": "Altro"
- },
- "format": {
- "rtsp": "RTSP",
- "hls": "HLS",
- "mjpeg": "MJPEG",
- "youtubeLive": "YouTube Live",
- "webrtc": "WebRTC",
- "dash": "DASH",
- "embed": "Incorporato",
- "other": "Altro"
- },
- "status": {
- "active": "Attivo",
- "inactive": "Inattivo",
- "error": "Errore"
- }
- },
- "welcome": "Benvenuto nel sito dell'app obytes",
- "incident_command": {
- "tab_title": "Comando",
- "title": "Comando dell’intervento",
- "open_full_board": "Apri pannello completo",
- "no_command": "Nessun comando dell’intervento stabilito",
- "no_command_description": "Stabilisci il comando dell’intervento per coordinare risorse, ruoli, obiettivi e controllo del personale per questa chiamata.",
- "establish": "Stabilisci comando",
- "establish_title": "Stabilisci comando dell’intervento",
- "establish_description": "Facoltativamente, inizializza il pannello di comando da un modello.",
- "establish_success": "Comando dell’intervento stabilito",
- "establish_error": "Impossibile stabilire il comando",
- "template": "Modello",
- "no_template": "Nessun modello (pannello vuoto)",
- "saved": "Salvato",
- "save_error": "Operazione non riuscita",
- "edit_action_plan": "Modifica piano d’azione",
- "action_plan": "Piano d’azione",
- "action_plan_placeholder": "Descrivi il piano d’azione dell’intervento...",
- "save": "Salva",
- "no_action_plan": "Nessun piano d’azione impostato.",
- "add": "Aggiungi",
- "add_objective": "Aggiungi obiettivo",
- "objective_name": "Obiettivo",
- "objective_type": "Tipo",
- "name_required": "Il nome è obbligatorio",
- "add_lane": "Aggiungi settore",
- "lane_name": "Nome del settore",
- "lane_type": "Tipo di settore",
- "assign_resource": "Assegna risorsa",
- "assign_resource_required": "Seleziona un settore e una risorsa",
- "lane": "Settore",
- "select_lane": "Seleziona un settore",
- "resource_type": "Tipo di risorsa",
- "resource": "Risorsa",
- "select_resource": "Seleziona una risorsa",
- "unit": "Unità",
- "personnel": "Personale",
- "assign": "Assegna",
- "assign_role": "Assegna ruolo",
- "assign_role_required": "Seleziona una persona e un ruolo",
- "person": "Persona",
- "select_person": "Seleziona una persona",
- "role": "Ruolo",
- "select_role": "Seleziona un ruolo",
- "transfer_title": "Trasferisci comando",
- "transfer_notes": "Note",
- "transfer": "Trasferisci",
- "transfer_command": "Trasferisci",
- "transfer_success": "Comando trasferito",
- "close_command": "Chiudi comando",
- "confirm_close": "Chiudere il comando dell’intervento per questa chiamata?",
- "status": "Stato",
- "active": "Attivo",
- "closed": "Chiuso",
- "commander": "Comandante",
- "established_on": "Stabilito",
- "edit": "Modifica",
- "roles": "Ruoli di comando",
- "no_roles": "Nessun ruolo assegnato.",
- "structure": "Struttura di comando",
- "no_lanes": "Nessun settore definito.",
- "no_resources": "Nessuna risorsa assegnata.",
- "release": "Rilascia",
- "objectives": "Obiettivi",
- "no_objectives": "Nessun obiettivo.",
- "complete": "Completa",
- "completed": "Completato",
- "timers": "Timer",
- "no_timers": "Nessun timer in esecuzione.",
- "acknowledge": "Conferma",
- "due": "Scadenza",
- "accountability": "Controllo del personale (PAR)",
- "run_par": "Esegui PAR",
- "green": "Verde",
- "warning": "Avviso",
- "critical": "Critico",
- "no_accountability": "Nessun personale monitorato.",
- "timeline": "Cronologia del comando",
- "no_timeline": "Nessuna voce nella cronologia.",
- "unassigned": "Non assegnato",
- "active_title": "Comandi degli interventi attivi",
- "no_active": "Nessun comando dell’intervento attivo",
- "no_active_description": "I comandi stabiliti per le chiamate verranno visualizzati qui.",
- "call": "Chiamata",
- "tactical_map": "Mappa tattica",
- "annotations": "Annotazioni della mappa",
- "no_annotations": "Nessuna annotazione.",
- "open_tactical_map": "Apri mappa tattica",
- "marker": "Indicatore",
- "add_marker": "Aggiungi indicatore",
- "tap_to_place": "Tocca la mappa per posizionare un indicatore",
- "marker_label": "Etichetta dell’indicatore",
- "delete_annotation_confirm": "Rimuovere questa annotazione?",
- "voice_channels": "Canali vocali",
- "no_channels": "Nessun canale aperto.",
- "add_channel": "Aggiungi canale",
- "channel_name": "Nome del canale",
- "close_all_channels": "Chiudi tutti i canali",
- "join": "Partecipa",
- "hold_to_talk": "Tieni premuto per parlare",
- "talking": "Trasmissione...",
- "voice_joined": "Canale vocale collegato",
- "voice_join_error": "Impossibile collegarsi al canale vocale",
- "move": "Sposta",
- "move_lane": "Sposta settore",
- "parent_lane": "Settore principale",
- "top_level": "Livello superiore",
- "move_resource": "Sposta risorsa"
- },
- "chat": {
- "title": "Chat",
- "assistant": "Assistente",
- "empty": "Ancora nessuna conversazione. Avvia un messaggio diretto o crea un gruppo.",
- "section_assistant": "Assistente",
- "section_direct_messages": "Messaggi diretti",
- "section_channels": "Canali",
- "section_incidents": "Incidenti",
- "new_direct_message": "Nuovo messaggio diretto",
- "new_group": "Nuovo gruppo",
- "open_assistant": "Apri assistente",
- "create_conversation_failed": "Impossibile avviare la conversazione",
- "group_name": "Nome del gruppo",
- "search_people": "Cerca persone",
- "no_people": "Nessuna persona trovata",
- "create_group_with": "Crea gruppo ({{count}})",
- "message_deleted": "Questo messaggio è stato eliminato",
- "urgent": "Urgente",
- "urgent_will_send": "Questo messaggio verrà inviato come urgente",
- "shared_location": "Posizione condivisa",
- "thread_replies": "{{count}} risposte",
- "edited": "(modificato)",
- "failed_tap_retry": "Non riuscito - tocca per riprovare",
- "type_a_message": "Scrivi un messaggio",
- "emoji": "Emoji",
- "add_image": "Aggiungi immagine",
- "add_gif": "Aggiungi GIF",
- "share_location": "Condividi posizione",
- "send": "Invia",
- "someone": "Qualcuno",
- "is_typing": "{{name}} sta scrivendo...",
- "are_typing": "{{count}} persone stanno scrivendo...",
- "permission_photos_denied": "Autorizzazione alla libreria foto negata",
- "permission_location_denied": "Autorizzazione alla posizione negata",
- "search_gifs": "Cerca GIF",
- "no_gifs": "Nessun GIF trovato",
- "flag_reason": "Perché lo segnali?",
- "flag_inappropriate": "Inappropriato",
- "flag_harassment": "Molestie",
- "flag_spam": "Spam",
- "flag_sensitive": "Informazioni sensibili",
- "flag_policy": "Violazione delle regole",
- "flag_other": "Altro",
- "reply_in_thread": "Rispondi nel thread",
- "copy": "Copia",
- "copied": "Copiato",
- "copy_unavailable": "La copia non è disponibile su questo dispositivo",
- "edit": "Modifica",
- "edit_message": "Modifica messaggio",
- "save": "Salva",
- "delete": "Elimina",
- "pin": "Fissa",
- "unpin": "Rimuovi fissaggio",
- "flag": "Segnala",
- "moderator_delete": "Rimuovi (moderatore)",
- "moderator_removed": "Rimosso dal moderatore",
- "attachment_failed": "Caricamento allegato non riuscito",
- "ack_required": "Conferma richiesta",
- "ack_pending_one": "Hai un messaggio urgente da confermare",
- "ack_pending_count": "Hai {{count}} messaggi urgenti da confermare",
- "acknowledge": "Conferma",
- "thread": "Thread",
- "original_message": "Messaggio originale",
- "reply_placeholder": "Rispondi...",
- "channel": "Canale",
- "direct_message": "Messaggio diretto",
- "load_people_failed": "Impossibile caricare le persone",
- "reaction_failed": "Impossibile aggiornare la reazione",
- "edit_failed": "Impossibile modificare il messaggio",
- "delete_failed": "Impossibile eliminare il messaggio",
- "pin_failed": "Impossibile aggiornare il messaggio fissato",
- "flag_failed": "Impossibile segnalare il messaggio"
- },
- "chatbot": {
- "title": "Assistente",
- "subtitle": "Assistente IA per il tuo reparto",
- "new_session": "Nuova sessione",
- "empty": "Chiedi qualsiasi cosa all'assistente per iniziare.",
- "ask_placeholder": "Chiedi all'assistente..."
- }
+ "welcome": "Benvenuto nel sito dell'app obytes"
}
diff --git a/src/translations/pl.json b/src/translations/pl.json
index 7c79c181..36f8a1b2 100644
--- a/src/translations/pl.json
+++ b/src/translations/pl.json
@@ -371,6 +371,124 @@
"audio_name": "Klip dźwiękowy"
}
},
+ "chat": {
+ "title": "Czat",
+ "assistant": "Asystent",
+ "empty": "Brak rozmów. Rozpocznij wiadomość bezpośrednią lub utwórz grupę.",
+ "section_assistant": "Asystent",
+ "section_direct_messages": "Wiadomości bezpośrednie",
+ "section_channels": "Kanały",
+ "section_incidents": "Zdarzenia",
+ "new_direct_message": "Nowa wiadomość bezpośrednia",
+ "new_group": "Nowa grupa",
+ "open_assistant": "Otwórz asystenta",
+ "create_conversation_failed": "Nie można rozpocząć rozmowy",
+ "group_name": "Nazwa grupy",
+ "search_people": "Szukaj osób",
+ "no_people": "Nie znaleziono osób",
+ "create_group_with": "Utwórz grupę ({{count}})",
+ "message_deleted": "Ta wiadomość została usunięta",
+ "urgent": "Pilne",
+ "urgent_will_send": "Ta wiadomość zostanie wysłana jako pilna",
+ "shared_location": "Udostępniona lokalizacja",
+ "thread_replies": "{{count}} odpowiedzi",
+ "edited": "(edytowano)",
+ "failed_tap_retry": "Niepowodzenie – dotknij, aby ponowić",
+ "type_a_message": "Napisz wiadomość",
+ "emoji": "Emoji",
+ "add_image": "Dodaj obraz",
+ "add_gif": "Dodaj GIF",
+ "share_location": "Udostępnij lokalizację",
+ "send": "Wyślij",
+ "someone": "Ktoś",
+ "is_typing": "{{name}} pisze...",
+ "are_typing": "{{count}} osób pisze...",
+ "permission_photos_denied": "Odmówiono dostępu do biblioteki zdjęć",
+ "permission_location_denied": "Odmówiono dostępu do lokalizacji",
+ "search_gifs": "Szukaj GIF-ów",
+ "no_gifs": "Nie znaleziono GIF-ów",
+ "flag_reason": "Dlaczego to zgłaszasz?",
+ "flag_inappropriate": "Nieodpowiednie",
+ "flag_harassment": "Nękanie",
+ "flag_spam": "Spam",
+ "flag_sensitive": "Informacje poufne",
+ "flag_policy": "Naruszenie zasad",
+ "flag_other": "Inne",
+ "reply_in_thread": "Odpowiedz w wątku",
+ "copy": "Kopiuj",
+ "copied": "Skopiowano",
+ "copy_unavailable": "Kopiowanie jest niedostępne na tym urządzeniu",
+ "edit": "Edytuj",
+ "edit_message": "Edytuj wiadomość",
+ "save": "Zapisz",
+ "delete": "Usuń",
+ "pin": "Przypnij",
+ "unpin": "Odepnij",
+ "flag": "Zgłoś",
+ "moderator_delete": "Usuń (moderator)",
+ "moderator_removed": "Usunięto przez moderatora",
+ "attachment_failed": "Przesyłanie załącznika nie powiodło się",
+ "ack_required": "Wymagane potwierdzenie",
+ "ack_pending_one": "Masz pilną wiadomość do potwierdzenia",
+ "ack_pending_count": "Masz {{count}} pilnych wiadomości do potwierdzenia",
+ "acknowledge": "Potwierdź",
+ "thread": "Wątek",
+ "original_message": "Oryginalna wiadomość",
+ "reply_placeholder": "Odpowiedz...",
+ "channel": "Kanał",
+ "direct_message": "Wiadomość bezpośrednia",
+ "load_people_failed": "Nie można załadować osób",
+ "reaction_failed": "Nie można zaktualizować reakcji",
+ "edit_failed": "Nie można edytować wiadomości",
+ "delete_failed": "Nie można usunąć wiadomości",
+ "pin_failed": "Nie można zaktualizować przypięcia",
+ "flag_failed": "Nie można zgłosić wiadomości"
+ },
+ "chatbot": {
+ "title": "Asystent",
+ "subtitle": "Asystent AI dla Twojej jednostki",
+ "new_session": "Nowa sesja",
+ "empty": "Zapytaj asystenta o cokolwiek, aby rozpocząć.",
+ "ask_placeholder": "Zapytaj asystenta..."
+ },
+ "check_in": {
+ "tab_title": "Meldunek",
+ "timer_status": "Status licznika",
+ "perform_check_in": "Zamelduj się",
+ "check_in_success": "Meldunek zarejestrowany pomyślnie",
+ "check_in_error": "Nie udało się zarejestrować meldunku",
+ "checked_in_by": "przez {{name}}",
+ "last_check_in": "Ostatni meldunek",
+ "elapsed": "Upłynęło",
+ "duration": "Czas trwania",
+ "status_ok": "OK",
+ "status_green": "OK",
+ "status_warning": "Ostrzeżenie",
+ "status_yellow": "Ostrzeżenie",
+ "status_overdue": "Zaległy",
+ "status_red": "Zaległy",
+ "status_critical": "Krytyczny",
+ "history": "Historia meldunków",
+ "no_timers": "Brak skonfigurowanych liczników meldunków",
+ "timers_disabled": "Liczniki meldunków są wyłączone dla tego zgłoszenia",
+ "type_personnel": "Personel",
+ "type_unit": "Jednostka",
+ "type_ic": "Kierujący działaniem ratowniczym",
+ "type_par": "PAR",
+ "type_hazmat": "Narażenie na materiały niebezpieczne",
+ "type_sector_rotation": "Rotacja sektora",
+ "type_rehab": "Rehabilitacja",
+ "add_note": "Dodaj notatkę (opcjonalnie)",
+ "confirm": "Potwierdź meldunek",
+ "minutes_ago": "{{count}} min temu",
+ "select_target": "Wybierz podmiot do meldunku",
+ "overdue_count": "{{count}} zaległych",
+ "warning_count": "{{count}} ostrzeżeń",
+ "enable_timers": "Włącz liczniki",
+ "disable_timers": "Wyłącz liczniki",
+ "summary": "{{overdue}} zaległych, {{warning}} ostrzeżeń, {{ok}} ok",
+ "par_title": "Ewidencja personelu (PAR)"
+ },
"common": {
"add": "Dodaj",
"back": "Wstecz",
@@ -502,178 +620,379 @@
"website": "Strona internetowa",
"zip": "Kod pocztowy"
},
- "form": {
- "invalid_url": "Proszę podać prawidłowy adres URL zaczynający się od http:// lub https://",
- "required": "To pole jest wymagane"
- },
- "livekit": {
- "audio_devices": "Urządzenia audio",
- "audio_settings": "Ustawienia audio",
- "connected_to_room": "Połączono z kanałem",
- "connecting": "Łączenie...",
- "disconnect": "Rozłącz",
- "join": "Dołącz",
- "microphone": "Mikrofon",
- "mute": "Wycisz",
- "no_rooms_available": "Brak dostępnych kanałów głosowych",
- "speaker": "Głośnik",
- "speaking": "Mówi",
- "title": "Kanały głosowe",
- "unmute": "Wyłącz wyciszenie"
- },
- "loading": {
- "loading": "Ładowanie...",
- "loadingData": "Ładowanie danych...",
- "pleaseWait": "Proszę czekać",
- "processingRequest": "Przetwarzanie żądania..."
- },
- "sso": {
- "authenticating": "Uwierzytelnianie...",
- "back_to_login": "Powrót do logowania",
- "back_to_lookup": "Zmień użytkownika",
- "continue_button": "Kontynuuj",
- "department_id_label": "ID wydziału",
- "department_id_placeholder": "Wprowadź ID wydziału",
- "error_generic": "Logowanie nie powiodło się. Proszę spróbować ponownie.",
- "error_oidc_cancelled": "Logowanie zostało anulowane.",
- "error_oidc_not_ready": "Dostawca SSO się ładuje, proszę czekać.",
- "error_sso_not_enabled": "Logowanie jednokrotne nie jest włączone dla tego użytkownika.",
- "error_token_exchange": "Nie udało się zakończyć logowania. Proszę spróbować ponownie.",
- "error_user_not_found": "Nie znaleziono użytkownika. Proszę sprawdzić i spróbować ponownie.",
- "looking_up": "Wyszukiwanie...",
- "optional": "opcjonalne",
- "page_subtitle": "Wprowadź swoją nazwę użytkownika, aby wyszukać opcje logowania Twojej organizacji.",
- "page_title": "Logowanie jednokrotne",
- "provider_oidc": "OpenID Connect (OIDC)",
- "provider_saml": "SAML 2.0",
- "sign_in_button": "Zaloguj się przez SSO",
- "sign_in_title": "Logowanie",
- "sso_button": "Logowanie SSO"
- },
- "login": {
- "branding_subtitle": "Zaawansowane oprogramowanie dyspozytorskie dla służb ratowniczych, poszukiwawczo-ratowniczych i organizacji bezpieczeństwa publicznego.",
- "branding_title": "Zarządzanie reagowaniem kryzysowym",
- "errorModal": {
- "confirmButton": "OK",
- "message": "Proszę sprawdzić nazwę użytkownika i hasło i spróbować ponownie.",
- "title": "Logowanie nie powiodło się"
- },
- "feature_dispatch_desc": "Natychmiastowe dysponowanie jednostek i zarządzanie zgłoszeniami z aktualizacjami na żywo na wszystkich urządzeniach.",
- "feature_dispatch_title": "Dyspozycja w czasie rzeczywistym",
- "feature_mapping_desc": "Śledzenie jednostek w czasie rzeczywistym ze szczegółowymi mapami, trasami i zarządzaniem lokalizacją.",
- "feature_mapping_title": "Zaawansowane mapowanie",
- "feature_personnel_desc": "Zarządzanie zespołem z kontrolą dostępu opartą na rolach, śledzeniem statusu i narzędziami komunikacyjnymi.",
- "feature_personnel_title": "Zarządzanie personelem",
- "footer_text": "Stworzone z ❤️ w Lake Tahoe",
- "login": "Zaloguj się",
- "login_button": "Zaloguj się",
- "login_button_description": "Zaloguj się na swoje konto, aby kontynuować",
- "login_button_error": "Błąd logowania",
- "login_button_loading": "Logowanie...",
- "login_button_success": "Zalogowano pomyślnie",
- "no_account": "Nie masz konta?",
- "page_subtitle": "Wprowadź swoje dane uwierzytelniające, aby się zalogować.",
- "page_title": "Resgrid Dispatch",
- "password": "Hasło",
- "password_incorrect": "Hasło jest nieprawidłowe",
- "password_placeholder": "Wprowadź swoje hasło",
- "register": "Zarejestruj się",
- "title": "Logowanie",
- "username": "Nazwa użytkownika",
- "username_placeholder": "Wprowadź swoją nazwę użytkownika",
- "welcome_title": "Witaj ponownie"
- },
- "lockscreen": {
- "message": "Wprowadź hasło, aby odblokować ekran",
- "not_you": "To nie Ty? Wróć do logowania",
- "password": "Hasło",
- "password_placeholder": "Wprowadź swoje hasło",
- "title": "Ekran blokady",
- "unlock_button": "Odblokuj",
- "unlock_failed": "Nie udało się odblokować. Proszę spróbować ponownie.",
- "unlocking": "Odblokowywanie...",
- "welcome_back": "Witaj ponownie",
- "relogin_required": "Weryfikacja hasła jest niedostępna w tej sesji. Zaloguj się ponownie."
- },
- "maintenance": {
- "downtime_message": "Pracujemy nad jak najszybszym zakończeniem konserwacji. Proszę sprawdzić ponownie wkrótce.",
- "downtime_title": "Jaki jest czas przestoju?",
- "message": "Proszę sprawdzić ponownie za jakiś czas.",
- "support_message": "Jeśli potrzebujesz pomocy, skontaktuj się z nami pod adresem",
- "support_title": "Potrzebujesz wsparcia?",
- "title": "Strona jest w trakcie konserwacji",
- "why_down_message": "Przeprowadzamy zaplanowaną konserwację w celu poprawy jakości usług. Przepraszamy za wszelkie niedogodności.",
- "why_down_title": "Dlaczego strona jest niedostępna?"
- },
- "menu": {
- "scheduled_calls": "Zaplanowane zgłoszenia",
- "pois": "POI",
- "calls": "Zgłoszenia",
- "calls_list": "Lista zgłoszeń",
- "contacts": "Kontakty",
- "home": "Strona główna",
- "map": "Mapa",
- "menu": "Menu",
- "messages": "Wiadomości",
- "new_call": "Nowe zgłoszenie",
- "personnel": "Personel",
- "protocols": "Protokoły",
- "settings": "Ustawienia",
+ "dispatch": {
+ "active_calls": "Aktywne zgłoszenia",
+ "pending_calls": "Oczekujące",
+ "scheduled_calls": "Zaplanowane",
+ "units_available": "Dostępne",
+ "personnel_available": "Dostępni",
+ "personnel_on_duty": "Na służbie",
"units": "Jednostki",
- "weatherAlerts": "Alerty pogodowe",
- "incident_command": "Dowodzenie zdarzeniem",
- "chat": "Czat",
- "assistant": "Asystent"
- },
- "pois": {
- "address": "Adres",
- "all_types": "Wszystkie typy",
- "destination": "Miejsce docelowe",
- "details": "Szczegóły",
- "detail_not_found": "Nie znaleziono POI",
- "detail_not_found_description": "Nie udało się załadować żądanego POI.",
- "detail_title": "Szczegóły POI",
- "empty": "Nie znaleziono POI",
- "empty_description": "Dla Twojego działu nie ma jeszcze dostępnych punktów zainteresowania.",
- "empty_filtered": "Brak pasujących POI",
- "empty_filtered_description": "Wyczyść wyszukiwanie lub wybierz inny typ POI.",
- "filter_by_type": "Filtruj według typu POI",
- "invalid_poi": "Nieprawidłowy POI",
- "invalid_poi_description": "Wybrany identyfikator POI jest nieprawidłowy.",
- "loading": "Ładowanie POI...",
- "loading_detail": "Ładowanie szczegółów POI...",
+ "personnel": "Personel",
"map": "Mapa",
- "no_location": "Brak dostępnej lokalizacji",
- "no_location_description": "Ten POI nie ma użytecznych współrzędnych.",
- "no_location_for_routing": "Brak danych lokalizacji do wyznaczenia trasy",
- "note": "Notatka",
- "route_error": "Nie udało się otworzyć aplikacji map",
- "search": "Szukaj POI...",
- "sort": "Sortuj",
- "sort_options": {
- "address-asc": "Adres",
- "name-asc": "Nazwa (A–Z)",
- "name-desc": "Nazwa (Z–A)",
- "type-asc": "Typ"
+ "notes": "Notatki",
+ "activity_log": "Dziennik aktywności",
+ "communications": "Komunikacja",
+ "no_active_calls": "Brak aktywnych zgłoszeń",
+ "no_units": "Brak dostępnych jednostek",
+ "no_personnel": "Brak dostępnego personelu",
+ "no_notes": "Brak dostępnych notatek",
+ "no_activity": "Brak ostatniej aktywności",
+ "current_channel": "Bieżący kanał",
+ "audio_stream": "Strumień audio",
+ "no_stream": "Brak aktywnego strumienia",
+ "ptt": "PTT",
+ "ptt_start": "PTT Start",
+ "ptt_end": "PTT Koniec",
+ "transmitting_on": "Transmisja na {{channel}}",
+ "transmission_ended": "Transmisja zakończona",
+ "voice_disabled": "Głos wyłączony",
+ "disconnected": "Rozłączono",
+ "select_channel": "Wybierz kanał",
+ "select_channel_description": "Wybierz kanał głosowy, aby się połączyć",
+ "change_channel_warning": "Wybranie nowego kanału spowoduje rozłączenie z bieżącym",
+ "default_channel": "Domyślny",
+ "no_channels_available": "Brak dostępnych kanałów głosowych",
+ "system_update": "Aktualizacja systemu",
+ "data_refreshed": "Dane odświeżone z serwera",
+ "call_selected": "Zgłoszenie wybrane",
+ "unit_selected": "Jednostka wybrana",
+ "unit_deselected": "Jednostka odznaczona",
+ "personnel_selected": "Personel wybrany",
+ "personnel_deselected": "Personel odznaczony",
+ "loading_map": "Ładowanie mapy...",
+ "map_not_available_web": "Mapa niedostępna na platformie webowej",
+ "filtering_by_call": "Filtrowanie wg zgłoszenia",
+ "clear_filter": "Wyczyść filtr",
+ "call_filter_active": "Filtr zgłoszenia aktywny",
+ "call_filter_cleared": "Filtr zgłoszenia wyczyszczony",
+ "showing_all_data": "Wyświetlanie wszystkich danych",
+ "call_notes": "Notatki zgłoszenia",
+ "no_call_notes": "Brak notatek zgłoszenia",
+ "add_call_note_placeholder": "Dodaj notatkę...",
+ "note_added": "Notatka dodana",
+ "note_added_to_console": "Nowa notatka została dodana do konsoli",
+ "add_note_title": "Dodaj nową notatkę",
+ "note_title_label": "Tytuł",
+ "note_title_placeholder": "Wprowadź tytuł notatki...",
+ "note_category_label": "Kategoria",
+ "note_category_placeholder": "Wybierz kategorię",
+ "note_no_category": "Bez kategorii",
+ "note_body_label": "Treść notatki",
+ "note_body_placeholder": "Wprowadź treść notatki...",
+ "note_save_error": "Nie udało się zapisać notatki: {{error}}",
+ "note_created": "Notatka utworzona",
+ "units_on_call": "Jednostki na zgłoszeniu",
+ "no_units_on_call": "Brak jednostek na zgłoszeniu",
+ "personnel_on_call": "Personel na zgłoszeniu",
+ "no_personnel_on_call": "Brak personelu na zgłoszeniu",
+ "call_activity": "Aktywność zgłoszenia",
+ "no_call_activity": "Brak aktywności zgłoszenia",
+ "on_call": "Na zgłoszeniu",
+ "filtered": "Przefiltrowane",
+ "active_filter": "Aktywny filtr",
+ "unit_status_change": "Zmiana statusu jednostki",
+ "personnel_status_change": "Zmiana statusu personelu",
+ "view_call_details": "Zobacz szczegóły zgłoszenia",
+ "dispatched_resources": "Zadysponowane",
+ "unassigned": "Nieprzypisane",
+ "available": "Dostępne",
+ "unknown": "Nieznane",
+ "search_personnel_placeholder": "Szukaj personelu...",
+ "search_calls_placeholder": "Szukaj zgłoszeń...",
+ "search_units_placeholder": "Szukaj jednostek...",
+ "search_notes_placeholder": "Szukaj notatek...",
+ "signalr_update": "Aktualizacja w czasie rzeczywistym",
+ "signalr_connected": "Połączono",
+ "realtime_updates_active": "Aktualizacje w czasie rzeczywistym są teraz aktywne",
+ "personnel_status_updated": "Status personelu zaktualizowany",
+ "personnel_staffing_updated": "Obsada personelu zaktualizowana",
+ "unit_status_updated": "Status jednostki zaktualizowany",
+ "calls_updated": "Zgłoszenia zaktualizowane",
+ "call_added": "Dodano nowe zgłoszenie",
+ "call_closed": "Zgłoszenie zamknięte",
+ "check_ins": "Meldunki",
+ "no_check_ins": "Brak zgłoszeń z licznikami meldunków",
+ "radio_log": "Dziennik radiowy",
+ "radio": "Radio",
+ "activity": "Aktywność",
+ "actions": "Akcje",
+ "no_radio_activity": "Brak transmisji radiowych",
+ "live": "NA ŻYWO",
+ "currently_transmitting": "Trwa transmisja...",
+ "duration": "Czas trwania",
+ "call_actions": "Akcje zgłoszenia",
+ "unit_actions": "Akcje jednostki",
+ "personnel_actions": {
+ "title": "Akcje personelu",
+ "status_tab": "Status",
+ "staffing_tab": "Obsada",
+ "select_status": "Wybierz status",
+ "select_staffing": "Wybierz poziom obsady",
+ "destination": "Cel",
+ "no_destination": "Brak celu",
+ "note": "Notatka",
+ "note_placeholder": "Dodaj opcjonalną notatkę...",
+ "update_status": "Zaktualizuj status",
+ "update_staffing": "Zaktualizuj obsadę",
+ "no_statuses_available": "Brak dostępnych statusów",
+ "no_staffings_available": "Brak dostępnych poziomów obsady"
},
- "title": "POI",
- "type": "Typ",
- "unknown_type": "Nieznany typ",
- "unnamed": "POI bez nazwy"
+ "unit_actions_panel": {
+ "status": "Status",
+ "select_status": "Wybierz status",
+ "destination": "Cel",
+ "no_destination": "Brak celu",
+ "note": "Notatka",
+ "note_placeholder": "Dodaj opcjonalną notatkę...",
+ "update_status": "Zaktualizuj status",
+ "no_statuses_available": "Brak dostępnych statusów",
+ "no_active_calls": "Brak aktywnych zgłoszeń",
+ "no_stations_available": "Brak dostępnych stacji",
+ "no_destinations_available": "Brak dostępnych celów"
+ },
+ "call": "Zgłoszenie",
+ "station": "Stacja",
+ "calls": "Zgłoszenia",
+ "stations": "Stacje",
+ "no_stations_available": "Brak dostępnych stacji",
+ "new_call": "Nowe zgłoszenie",
+ "view_details": "Szczegóły",
+ "add_note": "Dodaj notatkę",
+ "close_call": "Zamknij",
+ "set_status": "Ustaw status",
+ "set_staffing": "Obsada",
+ "dispatch": "Dysponuj",
+ "select_items_for_actions": "Wybierz zgłoszenie, jednostkę lub personel, aby włączyć akcje kontekstowe",
+ "weather": {
+ "clear": "Bezchmurnie",
+ "mainly_clear": "Przeważnie bezchmurnie",
+ "partly_cloudy": "Częściowo zachmurzenie",
+ "overcast": "Pochmurno",
+ "fog": "Mgła",
+ "drizzle": "Mżawka",
+ "freezing_drizzle": "Marznąca mżawka",
+ "rain": "Deszcz",
+ "freezing_rain": "Marznący deszcz",
+ "snow": "Śnieg",
+ "rain_showers": "Przelotny deszcz",
+ "snow_showers": "Przelotny śnieg",
+ "thunderstorm": "Burza",
+ "thunderstorm_hail": "Burza z gradem",
+ "unknown": "Nieznane"
+ },
+ "available_only": "Tylko dostępne",
+ "single_list": "Jedna lista",
+ "resources": "Zasoby",
+ "search_resources_placeholder": "Szukaj zasobów...",
+ "no_resources": "Brak zasobów"
},
- "scheduled_calls": {
- "title": "Zaplanowane zgłoszenia",
- "loading": "Ładowanie zaplanowanych zgłoszeń...",
- "no_scheduled_calls": "Brak zaplanowanych zgłoszeń",
- "no_scheduled_calls_description": "Obecnie nie ma oczekujących zaplanowanych zgłoszeń.",
- "search": "Szukaj zaplanowanych zgłoszeń...",
- "scheduled_for": "Zaplanowano na",
- "table_number": "Nr zgłoszenia",
- "table_name": "Nazwa",
- "table_type": "Typ",
- "table_priority": "Priorytet",
- "table_address": "Adres",
- "table_scheduled": "Zaplanowano na"
+ "form": {
+ "invalid_url": "Proszę podać prawidłowy adres URL zaczynający się od http:// lub https://",
+ "required": "To pole jest wymagane"
+ },
+ "incident_command": {
+ "accountability": "Ewidencja personelu (PAR)",
+ "acknowledge": "Potwierdź",
+ "action_plan": "Plan działania",
+ "action_plan_placeholder": "Opisz plan działania dla zdarzenia...",
+ "active": "Aktywne",
+ "active_title": "Aktywne dowodzenia zdarzeniami",
+ "add": "Dodaj",
+ "add_channel": "Dodaj kanał",
+ "add_lane": "Dodaj sekcję",
+ "add_marker": "Dodaj znacznik",
+ "add_objective": "Dodaj cel",
+ "annotations": "Adnotacje na mapie",
+ "assign": "Przypisz",
+ "assign_resource": "Przypisz zasób",
+ "assign_resource_required": "Wybierz sekcję i zasób",
+ "assign_role": "Przypisz rolę",
+ "assign_role_required": "Wybierz osobę i rolę",
+ "call": "Zgłoszenie",
+ "channel_name": "Nazwa kanału",
+ "chat": "Chat",
+ "chat_frozen": "This incident is closed. Conversations are kept as a point-in-time record — no new messages.",
+ "close_all_channels": "Zamknij wszystkie kanały",
+ "close_command": "Zamknij dowodzenie",
+ "closed": "Zamknięte",
+ "command_channel": "Command chat",
+ "command_channel_hint": "Command staff and dispatch",
+ "command_channel_unavailable": "No command channel has been created for this incident yet.",
+ "commander": "Dowódca",
+ "complete": "Zakończ",
+ "completed": "Zakończono",
+ "confirm_close": "Zamknąć dowodzenie zdarzeniem dla tego zgłoszenia?",
+ "critical": "Krytyczny",
+ "delete_annotation_confirm": "Usunąć tę adnotację?",
+ "dispatch_channel": "Dispatch",
+ "dispatch_channel_hint": "The incident's line to the desk",
+ "dispatch_channel_unavailable": "No dispatch channel has been created for this incident yet.",
+ "dm_failed": "Couldn't open that conversation.",
+ "dm_unavailable": "That contact has no Resgrid account to message.",
+ "due": "Termin",
+ "edit": "Edytuj",
+ "edit_action_plan": "Edytuj plan działania",
+ "establish": "Ustanów dowodzenie",
+ "establish_description": "Opcjonalnie utwórz tablicę dowodzenia na podstawie szablonu.",
+ "establish_error": "Nie udało się ustanowić dowodzenia",
+ "establish_success": "Ustanowiono dowodzenie zdarzeniem",
+ "establish_title": "Ustanów dowodzenie zdarzeniem",
+ "established_on": "Ustanowiono",
+ "green": "Zielony",
+ "hold_to_talk": "Przytrzymaj, aby mówić",
+ "incident_channel": "Incident chat",
+ "incident_channel_hint": "Everyone working the incident",
+ "incident_channel_unavailable": "No incident channel has been created for this call yet.",
+ "join": "Dołącz",
+ "lane": "Sekcja",
+ "lane_name": "Nazwa sekcji",
+ "lane_type": "Typ sekcji",
+ "marker": "Znacznik",
+ "marker_label": "Etykieta znacznika",
+ "move": "Przenieś",
+ "move_lane": "Przenieś sekcję",
+ "move_resource": "Przenieś zasób",
+ "name_required": "Nazwa jest wymagana",
+ "no_accountability": "Nie zarejestrowano personelu.",
+ "no_action_plan": "Nie ustawiono planu działania.",
+ "no_active": "Brak aktywnych dowodzeń zdarzeniami",
+ "no_active_description": "Ustanowione dowodzenia dla zgłoszeń pojawią się tutaj.",
+ "no_annotations": "Brak adnotacji.",
+ "no_channels": "Brak otwartych kanałów.",
+ "no_command": "Nie ustanowiono dowodzenia zdarzeniem",
+ "no_command_description": "Ustanów dowodzenie zdarzeniem, aby koordynować zasoby, role, cele i ewidencję personelu dla tego zgłoszenia.",
+ "no_lanes": "Nie zdefiniowano sekcji.",
+ "no_objectives": "Brak celów.",
+ "no_resources": "Nie przypisano zasobów.",
+ "no_roles": "Nie przypisano ról.",
+ "no_template": "Bez szablonu (pusta tablica)",
+ "no_timeline": "Brak wpisów na osi czasu.",
+ "no_timers": "Brak uruchomionych liczników.",
+ "not_authorized": "Command board unavailable",
+ "not_authorized_description": "Your department hasn't authorized you to work incident command. Ask an administrator for the Command App Login permission.",
+ "objective_name": "Cel",
+ "objective_type": "Typ",
+ "objectives": "Cele",
+ "open_chat": "Open",
+ "open_full_board": "Otwórz pełną tablicę",
+ "open_tactical_map": "Otwórz mapę taktyczną",
+ "parent_lane": "Sekcja nadrzędna",
+ "person": "Osoba",
+ "personnel": "Personel",
+ "release": "Zwolnij",
+ "resource": "Zasób",
+ "resource_type": "Typ zasobu",
+ "role": "Rola",
+ "roles": "Role dowodzenia",
+ "run_par": "Uruchom PAR",
+ "save": "Zapisz",
+ "save_error": "Operacja nie powiodła się",
+ "saved": "Zapisano",
+ "select_lane": "Wybierz sekcję",
+ "select_person": "Wybierz osobę",
+ "select_resource": "Wybierz zasób",
+ "select_role": "Wybierz rolę",
+ "send_message": "Message",
+ "status": "Stan",
+ "structure": "Struktura dowodzenia",
+ "tab_title": "Dowodzenie",
+ "tactical_map": "Mapa taktyczna",
+ "talking": "Nadawanie...",
+ "tap_to_place": "Dotknij mapy, aby umieścić znacznik",
+ "template": "Szablon",
+ "timeline": "Oś czasu dowodzenia",
+ "timers": "Liczniki czasu",
+ "title": "Dowodzenie zdarzeniem",
+ "top_level": "Poziom główny",
+ "transfer": "Przekaż",
+ "transfer_command": "Przekaż",
+ "transfer_notes": "Notatki",
+ "transfer_success": "Przekazano dowodzenie",
+ "transfer_title": "Przekaż dowodzenie",
+ "unassigned": "Nieprzypisane",
+ "unit": "Jednostka",
+ "voice_channels": "Kanały głosowe",
+ "voice_join_error": "Nie udało się dołączyć do kanału głosowego",
+ "voice_joined": "Dołączono do kanału głosowego",
+ "warning": "Ostrzeżenie"
+ },
+ "livekit": {
+ "audio_devices": "Urządzenia audio",
+ "audio_settings": "Ustawienia audio",
+ "connected_to_room": "Połączono z kanałem",
+ "connecting": "Łączenie...",
+ "disconnect": "Rozłącz",
+ "join": "Dołącz",
+ "microphone": "Mikrofon",
+ "mute": "Wycisz",
+ "no_rooms_available": "Brak dostępnych kanałów głosowych",
+ "speaker": "Głośnik",
+ "speaking": "Mówi",
+ "title": "Kanały głosowe",
+ "unmute": "Wyłącz wyciszenie"
+ },
+ "loading": {
+ "loading": "Ładowanie...",
+ "loadingData": "Ładowanie danych...",
+ "pleaseWait": "Proszę czekać",
+ "processingRequest": "Przetwarzanie żądania..."
+ },
+ "lockscreen": {
+ "message": "Wprowadź hasło, aby odblokować ekran",
+ "not_you": "To nie Ty? Wróć do logowania",
+ "password": "Hasło",
+ "password_placeholder": "Wprowadź swoje hasło",
+ "title": "Ekran blokady",
+ "unlock_button": "Odblokuj",
+ "unlock_failed": "Nie udało się odblokować. Proszę spróbować ponownie.",
+ "unlocking": "Odblokowywanie...",
+ "welcome_back": "Witaj ponownie",
+ "relogin_required": "Weryfikacja hasła jest niedostępna w tej sesji. Zaloguj się ponownie."
+ },
+ "login": {
+ "branding_subtitle": "Zaawansowane oprogramowanie dyspozytorskie dla służb ratowniczych, poszukiwawczo-ratowniczych i organizacji bezpieczeństwa publicznego.",
+ "branding_title": "Zarządzanie reagowaniem kryzysowym",
+ "dispatch_not_authorized": "You are not authorized to use the Dispatch app. Contact your department administrator.",
+ "errorModal": {
+ "confirmButton": "OK",
+ "message": "Proszę sprawdzić nazwę użytkownika i hasło i spróbować ponownie.",
+ "title": "Logowanie nie powiodło się"
+ },
+ "feature_dispatch_desc": "Natychmiastowe dysponowanie jednostek i zarządzanie zgłoszeniami z aktualizacjami na żywo na wszystkich urządzeniach.",
+ "feature_dispatch_title": "Dyspozycja w czasie rzeczywistym",
+ "feature_mapping_desc": "Śledzenie jednostek w czasie rzeczywistym ze szczegółowymi mapami, trasami i zarządzaniem lokalizacją.",
+ "feature_mapping_title": "Zaawansowane mapowanie",
+ "feature_personnel_desc": "Zarządzanie zespołem z kontrolą dostępu opartą na rolach, śledzeniem statusu i narzędziami komunikacyjnymi.",
+ "feature_personnel_title": "Zarządzanie personelem",
+ "footer_text": "Stworzone z ❤️ w Lake Tahoe",
+ "login": "Zaloguj się",
+ "login_button": "Zaloguj się",
+ "login_button_description": "Zaloguj się na swoje konto, aby kontynuować",
+ "login_button_error": "Błąd logowania",
+ "login_button_loading": "Logowanie...",
+ "login_button_success": "Zalogowano pomyślnie",
+ "no_account": "Nie masz konta?",
+ "page_subtitle": "Wprowadź swoje dane uwierzytelniające, aby się zalogować.",
+ "page_title": "Resgrid Dispatch",
+ "password": "Hasło",
+ "password_incorrect": "Hasło jest nieprawidłowe",
+ "password_placeholder": "Wprowadź swoje hasło",
+ "register": "Zarejestruj się",
+ "title": "Logowanie",
+ "username": "Nazwa użytkownika",
+ "username_placeholder": "Wprowadź swoją nazwę użytkownika",
+ "welcome_title": "Witaj ponownie"
+ },
+ "maintenance": {
+ "downtime_message": "Pracujemy nad jak najszybszym zakończeniem konserwacji. Proszę sprawdzić ponownie wkrótce.",
+ "downtime_title": "Jaki jest czas przestoju?",
+ "message": "Proszę sprawdzić ponownie za jakiś czas.",
+ "support_message": "Jeśli potrzebujesz pomocy, skontaktuj się z nami pod adresem",
+ "support_title": "Potrzebujesz wsparcia?",
+ "title": "Strona jest w trakcie konserwacji",
+ "why_down_message": "Przeprowadzamy zaplanowaną konserwację w celu poprawy jakości usług. Przepraszamy za wszelkie niedogodności.",
+ "why_down_title": "Dlaczego strona jest niedostępna?"
},
"map": {
"view_poi_details": "Wyświetl szczegóły POI",
@@ -690,6 +1009,26 @@
"hide_all": "Ukryj wszystko",
"view_call_details": "Zobacz szczegóły zgłoszenia"
},
+ "menu": {
+ "scheduled_calls": "Zaplanowane zgłoszenia",
+ "pois": "POI",
+ "calls": "Zgłoszenia",
+ "calls_list": "Lista zgłoszeń",
+ "contacts": "Kontakty",
+ "home": "Strona główna",
+ "map": "Mapa",
+ "menu": "Menu",
+ "messages": "Wiadomości",
+ "new_call": "Nowe zgłoszenie",
+ "personnel": "Personel",
+ "protocols": "Protokoły",
+ "settings": "Ustawienia",
+ "units": "Jednostki",
+ "weatherAlerts": "Alerty pogodowe",
+ "incident_command": "Dowodzenie zdarzeniem",
+ "chat": "Czat",
+ "assistant": "Asystent"
+ },
"notes": {
"actions": {
"add": "Dodaj notatkę",
@@ -709,6 +1048,23 @@
"search": "Szukaj notatek...",
"title": "Notatki"
},
+ "onboarding": {
+ "screen1": {
+ "title": "Resgrid Dispatch",
+ "description": "Twórz, dysponuj i zarządzaj zgłoszeniami alarmowymi dzięki mobilnemu centrum dowodzenia na wyciągnięcie ręki"
+ },
+ "screen2": {
+ "title": "Świadomość sytuacyjna w czasie rzeczywistym",
+ "description": "Śledź wszystkie jednostki, personel i zasoby na interaktywnej mapie z aktualizacjami statusów na żywo i AVL"
+ },
+ "screen3": {
+ "title": "Płynna koordynacja",
+ "description": "Komunikuj się natychmiast z jednostkami w terenie, aktualizuj statusy zgłoszeń i koordynuj działania ratownicze z dowolnego miejsca"
+ },
+ "skip": "Pomiń",
+ "next": "Dalej",
+ "getStarted": "Zaczynajmy"
+ },
"personnel": {
"title": "Personel",
"search": "Szukaj personelu...",
@@ -741,22 +1097,41 @@
"send_email": "E-mail",
"custom_fields": "Dodatkowe informacje"
},
- "onboarding": {
- "screen1": {
- "title": "Resgrid Dispatch",
- "description": "Twórz, dysponuj i zarządzaj zgłoszeniami alarmowymi dzięki mobilnemu centrum dowodzenia na wyciągnięcie ręki"
- },
- "screen2": {
- "title": "Świadomość sytuacyjna w czasie rzeczywistym",
- "description": "Śledź wszystkie jednostki, personel i zasoby na interaktywnej mapie z aktualizacjami statusów na żywo i AVL"
- },
- "screen3": {
- "title": "Płynna koordynacja",
- "description": "Komunikuj się natychmiast z jednostkami w terenie, aktualizuj statusy zgłoszeń i koordynuj działania ratownicze z dowolnego miejsca"
+ "pois": {
+ "address": "Adres",
+ "all_types": "Wszystkie typy",
+ "destination": "Miejsce docelowe",
+ "details": "Szczegóły",
+ "detail_not_found": "Nie znaleziono POI",
+ "detail_not_found_description": "Nie udało się załadować żądanego POI.",
+ "detail_title": "Szczegóły POI",
+ "empty": "Nie znaleziono POI",
+ "empty_description": "Dla Twojego działu nie ma jeszcze dostępnych punktów zainteresowania.",
+ "empty_filtered": "Brak pasujących POI",
+ "empty_filtered_description": "Wyczyść wyszukiwanie lub wybierz inny typ POI.",
+ "filter_by_type": "Filtruj według typu POI",
+ "invalid_poi": "Nieprawidłowy POI",
+ "invalid_poi_description": "Wybrany identyfikator POI jest nieprawidłowy.",
+ "loading": "Ładowanie POI...",
+ "loading_detail": "Ładowanie szczegółów POI...",
+ "map": "Mapa",
+ "no_location": "Brak dostępnej lokalizacji",
+ "no_location_description": "Ten POI nie ma użytecznych współrzędnych.",
+ "no_location_for_routing": "Brak danych lokalizacji do wyznaczenia trasy",
+ "note": "Notatka",
+ "route_error": "Nie udało się otworzyć aplikacji map",
+ "search": "Szukaj POI...",
+ "sort": "Sortuj",
+ "sort_options": {
+ "address-asc": "Adres",
+ "name-asc": "Nazwa (A–Z)",
+ "name-desc": "Nazwa (Z–A)",
+ "type-asc": "Typ"
},
- "skip": "Pomiń",
- "next": "Dalej",
- "getStarted": "Zaczynajmy"
+ "title": "POI",
+ "type": "Typ",
+ "unknown_type": "Nieznany typ",
+ "unnamed": "POI bez nazwy"
},
"protocols": {
"details": {
@@ -796,6 +1171,20 @@
"tap_to_manage": "Dotknij, aby zarządzać rolami",
"unassigned": "Nieprzypisane"
},
+ "scheduled_calls": {
+ "title": "Zaplanowane zgłoszenia",
+ "loading": "Ładowanie zaplanowanych zgłoszeń...",
+ "no_scheduled_calls": "Brak zaplanowanych zgłoszeń",
+ "no_scheduled_calls_description": "Obecnie nie ma oczekujących zaplanowanych zgłoszeń.",
+ "search": "Szukaj zaplanowanych zgłoszeń...",
+ "scheduled_for": "Zaplanowano na",
+ "table_number": "Nr zgłoszenia",
+ "table_name": "Nazwa",
+ "table_type": "Typ",
+ "table_priority": "Priorytet",
+ "table_address": "Adres",
+ "table_scheduled": "Zaplanowano na"
+ },
"settings": {
"about": "O aplikacji",
"account": "Konto",
@@ -884,6 +1273,29 @@
"version": "Wersja",
"website": "Strona internetowa"
},
+ "sso": {
+ "authenticating": "Uwierzytelnianie...",
+ "back_to_login": "Powrót do logowania",
+ "back_to_lookup": "Zmień użytkownika",
+ "continue_button": "Kontynuuj",
+ "department_id_label": "ID wydziału",
+ "department_id_placeholder": "Wprowadź ID wydziału",
+ "error_generic": "Logowanie nie powiodło się. Proszę spróbować ponownie.",
+ "error_oidc_cancelled": "Logowanie zostało anulowane.",
+ "error_oidc_not_ready": "Dostawca SSO się ładuje, proszę czekać.",
+ "error_sso_not_enabled": "Logowanie jednokrotne nie jest włączone dla tego użytkownika.",
+ "error_token_exchange": "Nie udało się zakończyć logowania. Proszę spróbować ponownie.",
+ "error_user_not_found": "Nie znaleziono użytkownika. Proszę sprawdzić i spróbować ponownie.",
+ "looking_up": "Wyszukiwanie...",
+ "optional": "opcjonalne",
+ "page_subtitle": "Wprowadź swoją nazwę użytkownika, aby wyszukać opcje logowania Twojej organizacji.",
+ "page_title": "Logowanie jednokrotne",
+ "provider_oidc": "OpenID Connect (OIDC)",
+ "provider_saml": "SAML 2.0",
+ "sign_in_button": "Zaloguj się przez SSO",
+ "sign_in_title": "Logowanie",
+ "sso_button": "Logowanie SSO"
+ },
"status": {
"all_destinations_enabled": "Może odpowiadać na zgłoszenia, stacje lub POI",
"calls_and_pois_destination_enabled": "Może odpowiadać na zgłoszenia lub POI",
@@ -916,173 +1328,6 @@
"stations_tab": "Stacje",
"status_saved_successfully": "Status zapisany pomyślnie!"
},
- "dispatch": {
- "active_calls": "Aktywne zgłoszenia",
- "pending_calls": "Oczekujące",
- "scheduled_calls": "Zaplanowane",
- "units_available": "Dostępne",
- "personnel_available": "Dostępni",
- "personnel_on_duty": "Na służbie",
- "units": "Jednostki",
- "personnel": "Personel",
- "map": "Mapa",
- "notes": "Notatki",
- "activity_log": "Dziennik aktywności",
- "communications": "Komunikacja",
- "no_active_calls": "Brak aktywnych zgłoszeń",
- "no_units": "Brak dostępnych jednostek",
- "no_personnel": "Brak dostępnego personelu",
- "no_notes": "Brak dostępnych notatek",
- "no_activity": "Brak ostatniej aktywności",
- "current_channel": "Bieżący kanał",
- "audio_stream": "Strumień audio",
- "no_stream": "Brak aktywnego strumienia",
- "ptt": "PTT",
- "ptt_start": "PTT Start",
- "ptt_end": "PTT Koniec",
- "transmitting_on": "Transmisja na {{channel}}",
- "transmission_ended": "Transmisja zakończona",
- "voice_disabled": "Głos wyłączony",
- "disconnected": "Rozłączono",
- "select_channel": "Wybierz kanał",
- "select_channel_description": "Wybierz kanał głosowy, aby się połączyć",
- "change_channel_warning": "Wybranie nowego kanału spowoduje rozłączenie z bieżącym",
- "default_channel": "Domyślny",
- "no_channels_available": "Brak dostępnych kanałów głosowych",
- "system_update": "Aktualizacja systemu",
- "data_refreshed": "Dane odświeżone z serwera",
- "call_selected": "Zgłoszenie wybrane",
- "unit_selected": "Jednostka wybrana",
- "unit_deselected": "Jednostka odznaczona",
- "personnel_selected": "Personel wybrany",
- "personnel_deselected": "Personel odznaczony",
- "loading_map": "Ładowanie mapy...",
- "map_not_available_web": "Mapa niedostępna na platformie webowej",
- "filtering_by_call": "Filtrowanie wg zgłoszenia",
- "clear_filter": "Wyczyść filtr",
- "call_filter_active": "Filtr zgłoszenia aktywny",
- "call_filter_cleared": "Filtr zgłoszenia wyczyszczony",
- "showing_all_data": "Wyświetlanie wszystkich danych",
- "call_notes": "Notatki zgłoszenia",
- "no_call_notes": "Brak notatek zgłoszenia",
- "add_call_note_placeholder": "Dodaj notatkę...",
- "note_added": "Notatka dodana",
- "note_added_to_console": "Nowa notatka została dodana do konsoli",
- "add_note_title": "Dodaj nową notatkę",
- "note_title_label": "Tytuł",
- "note_title_placeholder": "Wprowadź tytuł notatki...",
- "note_category_label": "Kategoria",
- "note_category_placeholder": "Wybierz kategorię",
- "note_no_category": "Bez kategorii",
- "note_body_label": "Treść notatki",
- "note_body_placeholder": "Wprowadź treść notatki...",
- "note_save_error": "Nie udało się zapisać notatki: {{error}}",
- "note_created": "Notatka utworzona",
- "units_on_call": "Jednostki na zgłoszeniu",
- "no_units_on_call": "Brak jednostek na zgłoszeniu",
- "personnel_on_call": "Personel na zgłoszeniu",
- "no_personnel_on_call": "Brak personelu na zgłoszeniu",
- "call_activity": "Aktywność zgłoszenia",
- "no_call_activity": "Brak aktywności zgłoszenia",
- "on_call": "Na zgłoszeniu",
- "filtered": "Przefiltrowane",
- "active_filter": "Aktywny filtr",
- "unit_status_change": "Zmiana statusu jednostki",
- "personnel_status_change": "Zmiana statusu personelu",
- "view_call_details": "Zobacz szczegóły zgłoszenia",
- "dispatched_resources": "Zadysponowane",
- "unassigned": "Nieprzypisane",
- "available": "Dostępne",
- "unknown": "Nieznane",
- "search_personnel_placeholder": "Szukaj personelu...",
- "search_calls_placeholder": "Szukaj zgłoszeń...",
- "search_units_placeholder": "Szukaj jednostek...",
- "search_notes_placeholder": "Szukaj notatek...",
- "signalr_update": "Aktualizacja w czasie rzeczywistym",
- "signalr_connected": "Połączono",
- "realtime_updates_active": "Aktualizacje w czasie rzeczywistym są teraz aktywne",
- "personnel_status_updated": "Status personelu zaktualizowany",
- "personnel_staffing_updated": "Obsada personelu zaktualizowana",
- "unit_status_updated": "Status jednostki zaktualizowany",
- "calls_updated": "Zgłoszenia zaktualizowane",
- "call_added": "Dodano nowe zgłoszenie",
- "call_closed": "Zgłoszenie zamknięte",
- "check_ins": "Meldunki",
- "no_check_ins": "Brak zgłoszeń z licznikami meldunków",
- "radio_log": "Dziennik radiowy",
- "radio": "Radio",
- "activity": "Aktywność",
- "actions": "Akcje",
- "no_radio_activity": "Brak transmisji radiowych",
- "live": "NA ŻYWO",
- "currently_transmitting": "Trwa transmisja...",
- "duration": "Czas trwania",
- "call_actions": "Akcje zgłoszenia",
- "unit_actions": "Akcje jednostki",
- "personnel_actions": {
- "title": "Akcje personelu",
- "status_tab": "Status",
- "staffing_tab": "Obsada",
- "select_status": "Wybierz status",
- "select_staffing": "Wybierz poziom obsady",
- "destination": "Cel",
- "no_destination": "Brak celu",
- "note": "Notatka",
- "note_placeholder": "Dodaj opcjonalną notatkę...",
- "update_status": "Zaktualizuj status",
- "update_staffing": "Zaktualizuj obsadę",
- "no_statuses_available": "Brak dostępnych statusów",
- "no_staffings_available": "Brak dostępnych poziomów obsady"
- },
- "unit_actions_panel": {
- "status": "Status",
- "select_status": "Wybierz status",
- "destination": "Cel",
- "no_destination": "Brak celu",
- "note": "Notatka",
- "note_placeholder": "Dodaj opcjonalną notatkę...",
- "update_status": "Zaktualizuj status",
- "no_statuses_available": "Brak dostępnych statusów",
- "no_active_calls": "Brak aktywnych zgłoszeń",
- "no_stations_available": "Brak dostępnych stacji",
- "no_destinations_available": "Brak dostępnych celów"
- },
- "call": "Zgłoszenie",
- "station": "Stacja",
- "calls": "Zgłoszenia",
- "stations": "Stacje",
- "no_stations_available": "Brak dostępnych stacji",
- "new_call": "Nowe zgłoszenie",
- "view_details": "Szczegóły",
- "add_note": "Dodaj notatkę",
- "close_call": "Zamknij",
- "set_status": "Ustaw status",
- "set_staffing": "Obsada",
- "dispatch": "Dysponuj",
- "select_items_for_actions": "Wybierz zgłoszenie, jednostkę lub personel, aby włączyć akcje kontekstowe",
- "weather": {
- "clear": "Bezchmurnie",
- "mainly_clear": "Przeważnie bezchmurnie",
- "partly_cloudy": "Częściowo zachmurzenie",
- "overcast": "Pochmurno",
- "fog": "Mgła",
- "drizzle": "Mżawka",
- "freezing_drizzle": "Marznąca mżawka",
- "rain": "Deszcz",
- "freezing_rain": "Marznący deszcz",
- "snow": "Śnieg",
- "rain_showers": "Przelotny deszcz",
- "snow_showers": "Przelotny śnieg",
- "thunderstorm": "Burza",
- "thunderstorm_hail": "Burza z gradem",
- "unknown": "Nieznane"
- },
- "available_only": "Tylko dostępne",
- "single_list": "Jedna lista",
- "resources": "Zasoby",
- "search_resources_placeholder": "Szukaj zasobów...",
- "no_resources": "Brak zasobów"
- },
"tabs": {
"calls": "Zgłoszenia",
"calendar": "Kalendarz",
@@ -1096,44 +1341,6 @@
"shifts": "Zmiany",
"personnel": "Personel"
},
- "check_in": {
- "tab_title": "Meldunek",
- "timer_status": "Status licznika",
- "perform_check_in": "Zamelduj się",
- "check_in_success": "Meldunek zarejestrowany pomyślnie",
- "check_in_error": "Nie udało się zarejestrować meldunku",
- "checked_in_by": "przez {{name}}",
- "last_check_in": "Ostatni meldunek",
- "elapsed": "Upłynęło",
- "duration": "Czas trwania",
- "status_ok": "OK",
- "status_green": "OK",
- "status_warning": "Ostrzeżenie",
- "status_yellow": "Ostrzeżenie",
- "status_overdue": "Zaległy",
- "status_red": "Zaległy",
- "status_critical": "Krytyczny",
- "history": "Historia meldunków",
- "no_timers": "Brak skonfigurowanych liczników meldunków",
- "timers_disabled": "Liczniki meldunków są wyłączone dla tego zgłoszenia",
- "type_personnel": "Personel",
- "type_unit": "Jednostka",
- "type_ic": "Kierujący działaniem ratowniczym",
- "type_par": "PAR",
- "type_hazmat": "Narażenie na materiały niebezpieczne",
- "type_sector_rotation": "Rotacja sektora",
- "type_rehab": "Rehabilitacja",
- "add_note": "Dodaj notatkę (opcjonalnie)",
- "confirm": "Potwierdź meldunek",
- "minutes_ago": "{{count}} min temu",
- "select_target": "Wybierz podmiot do meldunku",
- "overdue_count": "{{count}} zaległych",
- "warning_count": "{{count}} ostrzeżeń",
- "enable_timers": "Włącz liczniki",
- "disable_timers": "Wyłącz liczniki",
- "summary": "{{overdue}} zaległych, {{warning}} ostrzeżeń, {{ok}} ok",
- "par_title": "Ewidencja personelu (PAR)"
- },
"units": {
"search": "Szukaj jednostek...",
"loading": "Ładowanie jednostek...",
@@ -1163,6 +1370,63 @@
"no_destination": "Brak",
"title": "Jednostki"
},
+ "videoFeeds": {
+ "title": "Transmisje wideo",
+ "noFeeds": "Brak transmisji wideo dla tego zgłoszenia",
+ "addFeed": "Dodaj transmisję wideo",
+ "editFeed": "Edytuj transmisję wideo",
+ "deleteFeed": "Usuń transmisję wideo",
+ "deleteConfirm": "Czy na pewno chcesz usunąć tę transmisję wideo?",
+ "watch": "Oglądaj",
+ "goLive": "Rozpocznij transmisję",
+ "stopLive": "Zatrzymaj transmisję",
+ "flipCamera": "Odwróć kamerę",
+ "feedAdded": "Transmisja wideo dodana",
+ "feedUpdated": "Transmisja wideo zaktualizowana",
+ "feedDeleted": "Transmisja wideo usunięta",
+ "feedError": "Nie udało się załadować transmisji wideo",
+ "unsupportedFormat": "Ten format strumienia nie jest obsługiwany na urządzeniach mobilnych",
+ "copyUrl": "Kopiuj URL",
+ "form": {
+ "name": "Nazwa transmisji",
+ "namePlaceholder": "np. Dron Wóz 1",
+ "url": "URL strumienia",
+ "urlPlaceholder": "np. https://stream.example.com/live.m3u8",
+ "feedType": "Typ kamery",
+ "feedFormat": "Format strumienia",
+ "description": "Opis",
+ "descriptionPlaceholder": "Opcjonalny opis",
+ "status": "Status",
+ "sortOrder": "Kolejność sortowania",
+ "cameraLocation": "Lokalizacja kamery",
+ "useCurrentLocation": "Użyj bieżącej lokalizacji"
+ },
+ "type": {
+ "drone": "Dron",
+ "fixedCamera": "Kamera stała",
+ "bodyCam": "Kamera osobista",
+ "trafficCam": "Kamera drogowa",
+ "weatherCam": "Kamera pogodowa",
+ "satelliteFeed": "Transmisja satelitarna",
+ "webCam": "Kamera internetowa",
+ "other": "Inna"
+ },
+ "format": {
+ "rtsp": "RTSP",
+ "hls": "HLS",
+ "mjpeg": "MJPEG",
+ "youtubeLive": "YouTube Live",
+ "webrtc": "WebRTC",
+ "dash": "DASH",
+ "embed": "Osadzenie",
+ "other": "Inny"
+ },
+ "status": {
+ "active": "Aktywna",
+ "inactive": "Nieaktywna",
+ "error": "Błąd"
+ }
+ },
"weatherAlerts": {
"title": "Alerty pogodowe",
"activeAlerts": "Aktywne alerty",
@@ -1270,251 +1534,5 @@
},
"stats_label": "Alerty pogodowe"
},
- "videoFeeds": {
- "title": "Transmisje wideo",
- "noFeeds": "Brak transmisji wideo dla tego zgłoszenia",
- "addFeed": "Dodaj transmisję wideo",
- "editFeed": "Edytuj transmisję wideo",
- "deleteFeed": "Usuń transmisję wideo",
- "deleteConfirm": "Czy na pewno chcesz usunąć tę transmisję wideo?",
- "watch": "Oglądaj",
- "goLive": "Rozpocznij transmisję",
- "stopLive": "Zatrzymaj transmisję",
- "flipCamera": "Odwróć kamerę",
- "feedAdded": "Transmisja wideo dodana",
- "feedUpdated": "Transmisja wideo zaktualizowana",
- "feedDeleted": "Transmisja wideo usunięta",
- "feedError": "Nie udało się załadować transmisji wideo",
- "unsupportedFormat": "Ten format strumienia nie jest obsługiwany na urządzeniach mobilnych",
- "copyUrl": "Kopiuj URL",
- "form": {
- "name": "Nazwa transmisji",
- "namePlaceholder": "np. Dron Wóz 1",
- "url": "URL strumienia",
- "urlPlaceholder": "np. https://stream.example.com/live.m3u8",
- "feedType": "Typ kamery",
- "feedFormat": "Format strumienia",
- "description": "Opis",
- "descriptionPlaceholder": "Opcjonalny opis",
- "status": "Status",
- "sortOrder": "Kolejność sortowania",
- "cameraLocation": "Lokalizacja kamery",
- "useCurrentLocation": "Użyj bieżącej lokalizacji"
- },
- "type": {
- "drone": "Dron",
- "fixedCamera": "Kamera stała",
- "bodyCam": "Kamera osobista",
- "trafficCam": "Kamera drogowa",
- "weatherCam": "Kamera pogodowa",
- "satelliteFeed": "Transmisja satelitarna",
- "webCam": "Kamera internetowa",
- "other": "Inna"
- },
- "format": {
- "rtsp": "RTSP",
- "hls": "HLS",
- "mjpeg": "MJPEG",
- "youtubeLive": "YouTube Live",
- "webrtc": "WebRTC",
- "dash": "DASH",
- "embed": "Osadzenie",
- "other": "Inny"
- },
- "status": {
- "active": "Aktywna",
- "inactive": "Nieaktywna",
- "error": "Błąd"
- }
- },
- "welcome": "Witaj w aplikacji obytes",
- "incident_command": {
- "tab_title": "Dowodzenie",
- "title": "Dowodzenie zdarzeniem",
- "open_full_board": "Otwórz pełną tablicę",
- "no_command": "Nie ustanowiono dowodzenia zdarzeniem",
- "no_command_description": "Ustanów dowodzenie zdarzeniem, aby koordynować zasoby, role, cele i ewidencję personelu dla tego zgłoszenia.",
- "establish": "Ustanów dowodzenie",
- "establish_title": "Ustanów dowodzenie zdarzeniem",
- "establish_description": "Opcjonalnie utwórz tablicę dowodzenia na podstawie szablonu.",
- "establish_success": "Ustanowiono dowodzenie zdarzeniem",
- "establish_error": "Nie udało się ustanowić dowodzenia",
- "template": "Szablon",
- "no_template": "Bez szablonu (pusta tablica)",
- "saved": "Zapisano",
- "save_error": "Operacja nie powiodła się",
- "edit_action_plan": "Edytuj plan działania",
- "action_plan": "Plan działania",
- "action_plan_placeholder": "Opisz plan działania dla zdarzenia...",
- "save": "Zapisz",
- "no_action_plan": "Nie ustawiono planu działania.",
- "add": "Dodaj",
- "add_objective": "Dodaj cel",
- "objective_name": "Cel",
- "objective_type": "Typ",
- "name_required": "Nazwa jest wymagana",
- "add_lane": "Dodaj sekcję",
- "lane_name": "Nazwa sekcji",
- "lane_type": "Typ sekcji",
- "assign_resource": "Przypisz zasób",
- "assign_resource_required": "Wybierz sekcję i zasób",
- "lane": "Sekcja",
- "select_lane": "Wybierz sekcję",
- "resource_type": "Typ zasobu",
- "resource": "Zasób",
- "select_resource": "Wybierz zasób",
- "unit": "Jednostka",
- "personnel": "Personel",
- "assign": "Przypisz",
- "assign_role": "Przypisz rolę",
- "assign_role_required": "Wybierz osobę i rolę",
- "person": "Osoba",
- "select_person": "Wybierz osobę",
- "role": "Rola",
- "select_role": "Wybierz rolę",
- "transfer_title": "Przekaż dowodzenie",
- "transfer_notes": "Notatki",
- "transfer": "Przekaż",
- "transfer_command": "Przekaż",
- "transfer_success": "Przekazano dowodzenie",
- "close_command": "Zamknij dowodzenie",
- "confirm_close": "Zamknąć dowodzenie zdarzeniem dla tego zgłoszenia?",
- "status": "Stan",
- "active": "Aktywne",
- "closed": "Zamknięte",
- "commander": "Dowódca",
- "established_on": "Ustanowiono",
- "edit": "Edytuj",
- "roles": "Role dowodzenia",
- "no_roles": "Nie przypisano ról.",
- "structure": "Struktura dowodzenia",
- "no_lanes": "Nie zdefiniowano sekcji.",
- "no_resources": "Nie przypisano zasobów.",
- "release": "Zwolnij",
- "objectives": "Cele",
- "no_objectives": "Brak celów.",
- "complete": "Zakończ",
- "completed": "Zakończono",
- "timers": "Liczniki czasu",
- "no_timers": "Brak uruchomionych liczników.",
- "acknowledge": "Potwierdź",
- "due": "Termin",
- "accountability": "Ewidencja personelu (PAR)",
- "run_par": "Uruchom PAR",
- "green": "Zielony",
- "warning": "Ostrzeżenie",
- "critical": "Krytyczny",
- "no_accountability": "Nie zarejestrowano personelu.",
- "timeline": "Oś czasu dowodzenia",
- "no_timeline": "Brak wpisów na osi czasu.",
- "unassigned": "Nieprzypisane",
- "active_title": "Aktywne dowodzenia zdarzeniami",
- "no_active": "Brak aktywnych dowodzeń zdarzeniami",
- "no_active_description": "Ustanowione dowodzenia dla zgłoszeń pojawią się tutaj.",
- "call": "Zgłoszenie",
- "tactical_map": "Mapa taktyczna",
- "annotations": "Adnotacje na mapie",
- "no_annotations": "Brak adnotacji.",
- "open_tactical_map": "Otwórz mapę taktyczną",
- "marker": "Znacznik",
- "add_marker": "Dodaj znacznik",
- "tap_to_place": "Dotknij mapy, aby umieścić znacznik",
- "marker_label": "Etykieta znacznika",
- "delete_annotation_confirm": "Usunąć tę adnotację?",
- "voice_channels": "Kanały głosowe",
- "no_channels": "Brak otwartych kanałów.",
- "add_channel": "Dodaj kanał",
- "channel_name": "Nazwa kanału",
- "close_all_channels": "Zamknij wszystkie kanały",
- "join": "Dołącz",
- "hold_to_talk": "Przytrzymaj, aby mówić",
- "talking": "Nadawanie...",
- "voice_joined": "Dołączono do kanału głosowego",
- "voice_join_error": "Nie udało się dołączyć do kanału głosowego",
- "move": "Przenieś",
- "move_lane": "Przenieś sekcję",
- "parent_lane": "Sekcja nadrzędna",
- "top_level": "Poziom główny",
- "move_resource": "Przenieś zasób"
- },
- "chat": {
- "title": "Czat",
- "assistant": "Asystent",
- "empty": "Brak rozmów. Rozpocznij wiadomość bezpośrednią lub utwórz grupę.",
- "section_assistant": "Asystent",
- "section_direct_messages": "Wiadomości bezpośrednie",
- "section_channels": "Kanały",
- "section_incidents": "Zdarzenia",
- "new_direct_message": "Nowa wiadomość bezpośrednia",
- "new_group": "Nowa grupa",
- "open_assistant": "Otwórz asystenta",
- "create_conversation_failed": "Nie można rozpocząć rozmowy",
- "group_name": "Nazwa grupy",
- "search_people": "Szukaj osób",
- "no_people": "Nie znaleziono osób",
- "create_group_with": "Utwórz grupę ({{count}})",
- "message_deleted": "Ta wiadomość została usunięta",
- "urgent": "Pilne",
- "urgent_will_send": "Ta wiadomość zostanie wysłana jako pilna",
- "shared_location": "Udostępniona lokalizacja",
- "thread_replies": "{{count}} odpowiedzi",
- "edited": "(edytowano)",
- "failed_tap_retry": "Niepowodzenie – dotknij, aby ponowić",
- "type_a_message": "Napisz wiadomość",
- "emoji": "Emoji",
- "add_image": "Dodaj obraz",
- "add_gif": "Dodaj GIF",
- "share_location": "Udostępnij lokalizację",
- "send": "Wyślij",
- "someone": "Ktoś",
- "is_typing": "{{name}} pisze...",
- "are_typing": "{{count}} osób pisze...",
- "permission_photos_denied": "Odmówiono dostępu do biblioteki zdjęć",
- "permission_location_denied": "Odmówiono dostępu do lokalizacji",
- "search_gifs": "Szukaj GIF-ów",
- "no_gifs": "Nie znaleziono GIF-ów",
- "flag_reason": "Dlaczego to zgłaszasz?",
- "flag_inappropriate": "Nieodpowiednie",
- "flag_harassment": "Nękanie",
- "flag_spam": "Spam",
- "flag_sensitive": "Informacje poufne",
- "flag_policy": "Naruszenie zasad",
- "flag_other": "Inne",
- "reply_in_thread": "Odpowiedz w wątku",
- "copy": "Kopiuj",
- "copied": "Skopiowano",
- "copy_unavailable": "Kopiowanie jest niedostępne na tym urządzeniu",
- "edit": "Edytuj",
- "edit_message": "Edytuj wiadomość",
- "save": "Zapisz",
- "delete": "Usuń",
- "pin": "Przypnij",
- "unpin": "Odepnij",
- "flag": "Zgłoś",
- "moderator_delete": "Usuń (moderator)",
- "moderator_removed": "Usunięto przez moderatora",
- "attachment_failed": "Przesyłanie załącznika nie powiodło się",
- "ack_required": "Wymagane potwierdzenie",
- "ack_pending_one": "Masz pilną wiadomość do potwierdzenia",
- "ack_pending_count": "Masz {{count}} pilnych wiadomości do potwierdzenia",
- "acknowledge": "Potwierdź",
- "thread": "Wątek",
- "original_message": "Oryginalna wiadomość",
- "reply_placeholder": "Odpowiedz...",
- "channel": "Kanał",
- "direct_message": "Wiadomość bezpośrednia",
- "load_people_failed": "Nie można załadować osób",
- "reaction_failed": "Nie można zaktualizować reakcji",
- "edit_failed": "Nie można edytować wiadomości",
- "delete_failed": "Nie można usunąć wiadomości",
- "pin_failed": "Nie można zaktualizować przypięcia",
- "flag_failed": "Nie można zgłosić wiadomości"
- },
- "chatbot": {
- "title": "Asystent",
- "subtitle": "Asystent AI dla Twojej jednostki",
- "new_session": "Nowa sesja",
- "empty": "Zapytaj asystenta o cokolwiek, aby rozpocząć.",
- "ask_placeholder": "Zapytaj asystenta..."
- }
+ "welcome": "Witaj w aplikacji obytes"
}
diff --git a/src/translations/sv.json b/src/translations/sv.json
index 58568b26..e036cc3d 100644
--- a/src/translations/sv.json
+++ b/src/translations/sv.json
@@ -371,6 +371,124 @@
"audio_name": "Ljudklipp"
}
},
+ "chat": {
+ "title": "Chatt",
+ "assistant": "Assistent",
+ "empty": "Inga konversationer ännu. Starta ett direktmeddelande eller skapa en grupp.",
+ "section_assistant": "Assistent",
+ "section_direct_messages": "Direktmeddelanden",
+ "section_channels": "Kanaler",
+ "section_incidents": "Händelser",
+ "new_direct_message": "Nytt direktmeddelande",
+ "new_group": "Ny grupp",
+ "open_assistant": "Öppna assistent",
+ "create_conversation_failed": "Det gick inte att starta konversationen",
+ "group_name": "Gruppnamn",
+ "search_people": "Sök personer",
+ "no_people": "Inga personer hittades",
+ "create_group_with": "Skapa grupp ({{count}})",
+ "message_deleted": "Det här meddelandet har raderats",
+ "urgent": "Brådskande",
+ "urgent_will_send": "Det här meddelandet skickas som brådskande",
+ "shared_location": "Delad plats",
+ "thread_replies": "{{count}} svar",
+ "edited": "(redigerad)",
+ "failed_tap_retry": "Misslyckades – tryck för att försöka igen",
+ "type_a_message": "Skriv ett meddelande",
+ "emoji": "Emoji",
+ "add_image": "Lägg till bild",
+ "add_gif": "Lägg till GIF",
+ "share_location": "Dela plats",
+ "send": "Skicka",
+ "someone": "Någon",
+ "is_typing": "{{name}} skriver...",
+ "are_typing": "{{count}} personer skriver...",
+ "permission_photos_denied": "Åtkomst till fotobiblioteket nekades",
+ "permission_location_denied": "Platsåtkomst nekades",
+ "search_gifs": "Sök GIF-filer",
+ "no_gifs": "Inga GIF-filer hittades",
+ "flag_reason": "Varför rapporterar du detta?",
+ "flag_inappropriate": "Olämpligt",
+ "flag_harassment": "Trakasserier",
+ "flag_spam": "Skräppost",
+ "flag_sensitive": "Känslig information",
+ "flag_policy": "Policyöverträdelse",
+ "flag_other": "Annat",
+ "reply_in_thread": "Svara i tråden",
+ "copy": "Kopiera",
+ "copied": "Kopierad",
+ "copy_unavailable": "Kopiering är inte tillgänglig på den här enheten",
+ "edit": "Redigera",
+ "edit_message": "Redigera meddelande",
+ "save": "Spara",
+ "delete": "Radera",
+ "pin": "Fäst",
+ "unpin": "Ta bort fästning",
+ "flag": "Rapportera",
+ "moderator_delete": "Ta bort (moderator)",
+ "moderator_removed": "Borttagen av moderator",
+ "attachment_failed": "Uppladdning av bilaga misslyckades",
+ "ack_required": "Bekräftelse krävs",
+ "ack_pending_one": "Du har ett brådskande meddelande att bekräfta",
+ "ack_pending_count": "Du har {{count}} brådskande meddelanden att bekräfta",
+ "acknowledge": "Bekräfta",
+ "thread": "Tråd",
+ "original_message": "Ursprungligt meddelande",
+ "reply_placeholder": "Svara...",
+ "channel": "Kanal",
+ "direct_message": "Direktmeddelande",
+ "load_people_failed": "Det gick inte att läsa in personer",
+ "reaction_failed": "Det gick inte att uppdatera reaktionen",
+ "edit_failed": "Det gick inte att redigera meddelandet",
+ "delete_failed": "Det gick inte att radera meddelandet",
+ "pin_failed": "Det gick inte att uppdatera fästningen",
+ "flag_failed": "Det gick inte att rapportera meddelandet"
+ },
+ "chatbot": {
+ "title": "Assistent",
+ "subtitle": "AI-hjälp för din avdelning",
+ "new_session": "Ny session",
+ "empty": "Fråga assistenten om vad som helst för att komma igång.",
+ "ask_placeholder": "Fråga assistenten..."
+ },
+ "check_in": {
+ "tab_title": "Incheckning",
+ "timer_status": "Timerstatus",
+ "perform_check_in": "Checka in",
+ "check_in_success": "Incheckning registrerad",
+ "check_in_error": "Kunde inte registrera incheckning",
+ "checked_in_by": "av {{name}}",
+ "last_check_in": "Senaste incheckning",
+ "elapsed": "Förfluten tid",
+ "duration": "Varaktighet",
+ "status_ok": "OK",
+ "status_green": "OK",
+ "status_warning": "Varning",
+ "status_yellow": "Varning",
+ "status_overdue": "Försenad",
+ "status_red": "Försenad",
+ "status_critical": "Kritisk",
+ "history": "Incheckningshistorik",
+ "no_timers": "Inga incheckningstimrar konfigurerade",
+ "timers_disabled": "Incheckningstimrar är inaktiverade för detta ärende",
+ "type_personnel": "Personal",
+ "type_unit": "Enhet",
+ "type_ic": "Insatsledare",
+ "type_par": "PAR",
+ "type_hazmat": "Farligt gods-exponering",
+ "type_sector_rotation": "Sektorsrotation",
+ "type_rehab": "Rehab",
+ "add_note": "Lägg till anteckning (valfritt)",
+ "confirm": "Bekräfta incheckning",
+ "minutes_ago": "{{count}} min sedan",
+ "select_target": "Välj enhet att checka in",
+ "overdue_count": "{{count}} försenade",
+ "warning_count": "{{count}} varning",
+ "enable_timers": "Aktivera timrar",
+ "disable_timers": "Inaktivera timrar",
+ "summary": "{{overdue}} försenade, {{warning}} varning, {{ok}} ok",
+ "par_title": "Personalkontroll (PAR)"
+ },
"common": {
"add": "Lägg till",
"back": "Tillbaka",
@@ -502,178 +620,379 @@
"website": "Webbplats",
"zip": "Postnummer"
},
- "form": {
- "invalid_url": "Vänligen ange en giltig URL som börjar med http:// eller https://",
- "required": "Detta fält är obligatoriskt"
- },
- "livekit": {
- "audio_devices": "Ljudenheter",
- "audio_settings": "Ljudinställningar",
- "connected_to_room": "Ansluten till kanal",
- "connecting": "Ansluter...",
- "disconnect": "Koppla från",
- "join": "Anslut",
- "microphone": "Mikrofon",
- "mute": "Tysta",
- "no_rooms_available": "Inga röstkanaler tillgängliga",
- "speaker": "Högtalare",
- "speaking": "Talar",
- "title": "Röstkanaler",
- "unmute": "Slå på ljud"
- },
- "loading": {
- "loading": "Laddar...",
- "loadingData": "Laddar data...",
- "pleaseWait": "Vänligen vänta",
- "processingRequest": "Bearbetar din förfrågan..."
- },
- "sso": {
- "authenticating": "Autentiserar...",
- "back_to_login": "Tillbaka till inloggning",
- "back_to_lookup": "Byt användare",
- "continue_button": "Fortsätt",
- "department_id_label": "Avdelnings-ID",
- "department_id_placeholder": "Ange avdelnings-ID",
- "error_generic": "Inloggning misslyckades. Vänligen försök igen.",
- "error_oidc_cancelled": "Inloggningen avbröts.",
- "error_oidc_not_ready": "SSO-leverantören laddas, vänligen vänta.",
- "error_sso_not_enabled": "Enkel inloggning är inte aktiverad för denna användare.",
- "error_token_exchange": "Kunde inte slutföra inloggningen. Vänligen försök igen.",
- "error_user_not_found": "Användare hittades inte. Vänligen kontrollera och försök igen.",
- "looking_up": "Söker...",
- "optional": "valfritt",
- "page_subtitle": "Ange ditt användarnamn för att söka efter din organisations inloggningsalternativ.",
- "page_title": "Enkel inloggning",
- "provider_oidc": "OpenID Connect (OIDC)",
- "provider_saml": "SAML 2.0",
- "sign_in_button": "Logga in med SSO",
- "sign_in_title": "Logga in",
- "sso_button": "SSO-inloggning"
- },
- "login": {
- "branding_subtitle": "Kraftfull larmbehandlingsprogramvara för räddningstjänst, eftersök och räddning samt samhällsskydd.",
- "branding_title": "Hantering av räddningsinsatser",
- "errorModal": {
- "confirmButton": "OK",
- "message": "Vänligen kontrollera ditt användarnamn och lösenord och försök igen.",
- "title": "Inloggning misslyckades"
- },
- "feature_dispatch_desc": "Larma enheter omedelbart och hantera ärenden med realtidsuppdateringar på alla enheter.",
- "feature_dispatch_title": "Realtidsutlarmning",
- "feature_mapping_desc": "Spåra enheter i realtid med detaljerade kartor, vägbeskrivningar och platshantering.",
- "feature_mapping_title": "Avancerade kartor",
- "feature_personnel_desc": "Hantera ditt team med rollbaserad åtkomst, statusspårning och kommunikationsverktyg.",
- "feature_personnel_title": "Personalhantering",
- "footer_text": "Skapat med ❤️ i Lake Tahoe",
- "login": "Logga in",
- "login_button": "Logga in",
- "login_button_description": "Logga in på ditt konto för att fortsätta",
- "login_button_error": "Fel vid inloggning",
- "login_button_loading": "Loggar in...",
- "login_button_success": "Inloggning lyckades",
- "no_account": "Har du inget konto?",
- "page_subtitle": "Ange dina uppgifter för att logga in.",
- "page_title": "Resgrid Dispatch",
- "password": "Lösenord",
- "password_incorrect": "Lösenordet var felaktigt",
- "password_placeholder": "Ange ditt lösenord",
- "register": "Registrera",
- "title": "Logga in",
- "username": "Användarnamn",
- "username_placeholder": "Ange ditt användarnamn",
- "welcome_title": "Välkommen tillbaka"
- },
- "lockscreen": {
- "message": "Ange ditt lösenord för att låsa upp skärmen",
- "not_you": "Inte du? Tillbaka till inloggning",
- "password": "Lösenord",
- "password_placeholder": "Ange ditt lösenord",
- "title": "Låsskärm",
- "unlock_button": "Lås upp",
- "unlock_failed": "Upplåsning misslyckades. Vänligen försök igen.",
- "unlocking": "Låser upp...",
- "welcome_back": "Välkommen tillbaka",
- "relogin_required": "Lösenordsverifiering är inte tillgänglig för den här sessionen. Logga in igen."
- },
- "maintenance": {
- "downtime_message": "Vi arbetar hårt för att slutföra underhållet så snabbt som möjligt. Vänligen kontrollera igen snart.",
- "downtime_title": "Vad är driftstoppet?",
- "message": "Vänligen kontrollera igen om en stund.",
- "support_message": "Om du behöver hjälp, vänligen kontakta oss på",
- "support_title": "Behöver du support?",
- "title": "Webbplatsen är under underhåll",
- "why_down_message": "Vi utför planerat underhåll för att förbättra din upplevelse. Vi ber om ursäkt för eventuella besvär.",
- "why_down_title": "Varför är webbplatsen nere?"
- },
- "menu": {
- "scheduled_calls": "Schemalagda larm",
- "pois": "POI:er",
- "calls": "Ärenden",
- "calls_list": "Ärendelista",
- "contacts": "Kontakter",
- "home": "Hem",
- "map": "Karta",
- "menu": "Meny",
- "messages": "Meddelanden",
- "new_call": "Nytt ärende",
- "personnel": "Personal",
- "protocols": "Protokoll",
- "settings": "Inställningar",
+ "dispatch": {
+ "active_calls": "Aktiva ärenden",
+ "pending_calls": "Väntande",
+ "scheduled_calls": "Schemalagda",
+ "units_available": "Tillgängliga",
+ "personnel_available": "Tillgängliga",
+ "personnel_on_duty": "I tjänst",
"units": "Enheter",
- "weatherAlerts": "Vädervarningar",
- "incident_command": "Insatsledning",
- "chat": "Chatt",
- "assistant": "Assistent"
- },
- "pois": {
- "address": "Adress",
- "all_types": "Alla typer",
- "destination": "Destination",
- "details": "Detaljer",
- "detail_not_found": "POI hittades inte",
- "detail_not_found_description": "Det gick inte att läsa in begärd POI.",
- "detail_title": "POI-detaljer",
- "empty": "Inga POI:er hittades",
- "empty_description": "Det finns ännu inga intressepunkter tillgängliga för din avdelning.",
- "empty_filtered": "Inga matchande POI:er",
- "empty_filtered_description": "Rensa sökningen eller välj en annan POI-typ.",
- "filter_by_type": "Filtrera efter POI-typ",
- "invalid_poi": "Ogiltig POI",
- "invalid_poi_description": "Den valda POI-identifieraren är inte giltig.",
- "loading": "Läser in POI:er...",
- "loading_detail": "Läser in POI-detaljer...",
+ "personnel": "Personal",
"map": "Karta",
- "no_location": "Ingen plats tillgänglig",
- "no_location_description": "Den här POI:n saknar användbara koordinater.",
- "no_location_for_routing": "Inga platsdata tillgängliga för ruttplanering",
- "note": "Anteckning",
- "route_error": "Det gick inte att öppna kartappen",
- "search": "Sök POI:er...",
- "sort": "Sortera",
- "sort_options": {
- "address-asc": "Adress",
- "name-asc": "Namn (A–Ö)",
- "name-desc": "Namn (Ö–A)",
- "type-asc": "Typ"
+ "notes": "Anteckningar",
+ "activity_log": "Aktivitetslogg",
+ "communications": "Kommunikation",
+ "no_active_calls": "Inga aktiva ärenden",
+ "no_units": "Inga enheter tillgängliga",
+ "no_personnel": "Ingen personal tillgänglig",
+ "no_notes": "Inga anteckningar tillgängliga",
+ "no_activity": "Ingen senaste aktivitet",
+ "current_channel": "Aktuell kanal",
+ "audio_stream": "Ljudström",
+ "no_stream": "Ingen ström aktiv",
+ "ptt": "PTT",
+ "ptt_start": "PTT start",
+ "ptt_end": "PTT slut",
+ "transmitting_on": "Sänder på {{channel}}",
+ "transmission_ended": "Sändning avslutad",
+ "voice_disabled": "Röst inaktiverad",
+ "disconnected": "Frånkopplad",
+ "select_channel": "Välj kanal",
+ "select_channel_description": "Välj en röstkanal att ansluta till",
+ "change_channel_warning": "Att välja en ny kanal kopplar från den nuvarande",
+ "default_channel": "Standard",
+ "no_channels_available": "Inga röstkanaler tillgängliga",
+ "system_update": "Systemuppdatering",
+ "data_refreshed": "Data uppdaterad från servern",
+ "call_selected": "Ärende valt",
+ "unit_selected": "Enhet vald",
+ "unit_deselected": "Enhet avmarkerad",
+ "personnel_selected": "Personal vald",
+ "personnel_deselected": "Personal avmarkerad",
+ "loading_map": "Laddar karta...",
+ "map_not_available_web": "Karta ej tillgänglig på webbplattform",
+ "filtering_by_call": "Filtrerar efter ärende",
+ "clear_filter": "Rensa filter",
+ "call_filter_active": "Ärendefilter aktivt",
+ "call_filter_cleared": "Ärendefilter rensat",
+ "showing_all_data": "Visar all data",
+ "call_notes": "Ärendeanteckningar",
+ "no_call_notes": "Inga ärendeanteckningar",
+ "add_call_note_placeholder": "Lägg till en anteckning...",
+ "note_added": "Anteckning tillagd",
+ "note_added_to_console": "En ny anteckning har lagts till i konsolen",
+ "add_note_title": "Lägg till ny anteckning",
+ "note_title_label": "Rubrik",
+ "note_title_placeholder": "Ange anteckningsrubrik...",
+ "note_category_label": "Kategori",
+ "note_category_placeholder": "Välj en kategori",
+ "note_no_category": "Ingen kategori",
+ "note_body_label": "Anteckningsinnehåll",
+ "note_body_placeholder": "Ange anteckningsinnehåll...",
+ "note_save_error": "Kunde inte spara anteckning: {{error}}",
+ "note_created": "Anteckning skapad",
+ "units_on_call": "Enheter på ärende",
+ "no_units_on_call": "Inga enheter på ärende",
+ "personnel_on_call": "Personal på ärende",
+ "no_personnel_on_call": "Ingen personal på ärende",
+ "call_activity": "Ärendeaktivitet",
+ "no_call_activity": "Ingen ärendeaktivitet",
+ "on_call": "På ärende",
+ "filtered": "Filtrerad",
+ "active_filter": "Aktivt filter",
+ "unit_status_change": "Enhetsstatusändring",
+ "personnel_status_change": "Personalstatusändring",
+ "view_call_details": "Visa ärendedetaljer",
+ "dispatched_resources": "Utlarmade",
+ "unassigned": "Ej tilldelad",
+ "available": "Tillgänglig",
+ "unknown": "Okänd",
+ "search_personnel_placeholder": "Sök personal...",
+ "search_calls_placeholder": "Sök ärenden...",
+ "search_units_placeholder": "Sök enheter...",
+ "search_notes_placeholder": "Sök anteckningar...",
+ "signalr_update": "Realtidsuppdatering",
+ "signalr_connected": "Ansluten",
+ "realtime_updates_active": "Realtidsuppdateringar är nu aktiva",
+ "personnel_status_updated": "Personalstatus uppdaterad",
+ "personnel_staffing_updated": "Personalbemanning uppdaterad",
+ "unit_status_updated": "Enhetsstatus uppdaterad",
+ "calls_updated": "Ärenden uppdaterade",
+ "call_added": "Nytt ärende tillagt",
+ "call_closed": "Ärende stängt",
+ "check_ins": "Incheckningar",
+ "no_check_ins": "Inga ärenden med incheckningstimer",
+ "radio_log": "Radiologg",
+ "radio": "Radio",
+ "activity": "Aktivitet",
+ "actions": "Åtgärder",
+ "no_radio_activity": "Inga radiosändningar",
+ "live": "LIVE",
+ "currently_transmitting": "Sänder för närvarande...",
+ "duration": "Varaktighet",
+ "call_actions": "Ärendeåtgärder",
+ "unit_actions": "Enhetsåtgärder",
+ "personnel_actions": {
+ "title": "Personalåtgärder",
+ "status_tab": "Status",
+ "staffing_tab": "Bemanning",
+ "select_status": "Välj status",
+ "select_staffing": "Välj bemanningsnivå",
+ "destination": "Destination",
+ "no_destination": "Ingen destination",
+ "note": "Anteckning",
+ "note_placeholder": "Lägg till en valfri anteckning...",
+ "update_status": "Uppdatera status",
+ "update_staffing": "Uppdatera bemanning",
+ "no_statuses_available": "Inga statusar tillgängliga",
+ "no_staffings_available": "Inga bemanningsnivåer tillgängliga"
},
- "title": "POI:er",
- "type": "Typ",
- "unknown_type": "Okänd typ",
- "unnamed": "Namnlös POI"
+ "unit_actions_panel": {
+ "status": "Status",
+ "select_status": "Välj status",
+ "destination": "Destination",
+ "no_destination": "Ingen destination",
+ "note": "Anteckning",
+ "note_placeholder": "Lägg till en valfri anteckning...",
+ "update_status": "Uppdatera status",
+ "no_statuses_available": "Inga statusar tillgängliga",
+ "no_active_calls": "Inga aktiva ärenden",
+ "no_stations_available": "Inga stationer tillgängliga",
+ "no_destinations_available": "Inga destinationer tillgängliga"
+ },
+ "call": "Ärende",
+ "station": "Station",
+ "calls": "Ärenden",
+ "stations": "Stationer",
+ "no_stations_available": "Inga stationer tillgängliga",
+ "new_call": "Nytt ärende",
+ "view_details": "Detaljer",
+ "add_note": "Lägg till anteckning",
+ "close_call": "Stäng",
+ "set_status": "Ange status",
+ "set_staffing": "Bemanning",
+ "dispatch": "Larma",
+ "select_items_for_actions": "Välj ett ärende, enhet eller personal för att aktivera kontextmedvetna åtgärder",
+ "weather": {
+ "clear": "Klart",
+ "mainly_clear": "Mestadels klart",
+ "partly_cloudy": "Delvis molnigt",
+ "overcast": "Mulet",
+ "fog": "Dimma",
+ "drizzle": "Duggregn",
+ "freezing_drizzle": "Underkylt duggregn",
+ "rain": "Regn",
+ "freezing_rain": "Underkylt regn",
+ "snow": "Snö",
+ "rain_showers": "Regnskurar",
+ "snow_showers": "Snöbyar",
+ "thunderstorm": "Åskväder",
+ "thunderstorm_hail": "Åskväder med hagel",
+ "unknown": "Okänt"
+ },
+ "available_only": "Endast tillgängliga",
+ "single_list": "En lista",
+ "resources": "Resurser",
+ "search_resources_placeholder": "Sök resurser...",
+ "no_resources": "Inga resurser"
},
- "scheduled_calls": {
- "title": "Schemalagda larm",
- "loading": "Läser in schemalagda larm...",
- "no_scheduled_calls": "Inga schemalagda larm",
- "no_scheduled_calls_description": "Det finns inga väntande schemalagda larm just nu.",
- "search": "Sök schemalagda larm...",
- "scheduled_for": "Schemalagt till",
- "table_number": "Larmnummer",
- "table_name": "Namn",
- "table_type": "Typ",
- "table_priority": "Prioritet",
- "table_address": "Adress",
- "table_scheduled": "Schemalagt till"
+ "form": {
+ "invalid_url": "Vänligen ange en giltig URL som börjar med http:// eller https://",
+ "required": "Detta fält är obligatoriskt"
+ },
+ "incident_command": {
+ "accountability": "Personalkontroll (PAR)",
+ "acknowledge": "Bekräfta",
+ "action_plan": "Insatsplan",
+ "action_plan_placeholder": "Beskriv insatsplanen...",
+ "active": "Aktiv",
+ "active_title": "Aktiva insatsledningar",
+ "add": "Lägg till",
+ "add_channel": "Lägg till kanal",
+ "add_lane": "Lägg till sektor",
+ "add_marker": "Lägg till markör",
+ "add_objective": "Lägg till mål",
+ "annotations": "Kartanteckningar",
+ "assign": "Tilldela",
+ "assign_resource": "Tilldela resurs",
+ "assign_resource_required": "Välj en sektor och en resurs",
+ "assign_role": "Tilldela roll",
+ "assign_role_required": "Välj en person och en roll",
+ "call": "Larm",
+ "channel_name": "Kanalnamn",
+ "chat": "Chat",
+ "chat_frozen": "This incident is closed. Conversations are kept as a point-in-time record — no new messages.",
+ "close_all_channels": "Stäng alla kanaler",
+ "close_command": "Avsluta ledning",
+ "closed": "Avslutad",
+ "command_channel": "Command chat",
+ "command_channel_hint": "Command staff and dispatch",
+ "command_channel_unavailable": "No command channel has been created for this incident yet.",
+ "commander": "Insatsledare",
+ "complete": "Slutför",
+ "completed": "Slutförd",
+ "confirm_close": "Avsluta insatsledningen för det här larmet?",
+ "critical": "Kritisk",
+ "delete_annotation_confirm": "Ta bort den här anteckningen?",
+ "dispatch_channel": "Dispatch",
+ "dispatch_channel_hint": "The incident's line to the desk",
+ "dispatch_channel_unavailable": "No dispatch channel has been created for this incident yet.",
+ "dm_failed": "Couldn't open that conversation.",
+ "dm_unavailable": "That contact has no Resgrid account to message.",
+ "due": "Förfaller",
+ "edit": "Redigera",
+ "edit_action_plan": "Redigera insatsplan",
+ "establish": "Upprätta ledning",
+ "establish_description": "Skapa valfritt ledningstavlan från en mall.",
+ "establish_error": "Det gick inte att upprätta ledningen",
+ "establish_success": "Insatsledningen har upprättats",
+ "establish_title": "Upprätta insatsledning",
+ "established_on": "Upprättad",
+ "green": "Grön",
+ "hold_to_talk": "Håll in för att tala",
+ "incident_channel": "Incident chat",
+ "incident_channel_hint": "Everyone working the incident",
+ "incident_channel_unavailable": "No incident channel has been created for this call yet.",
+ "join": "Anslut",
+ "lane": "Sektor",
+ "lane_name": "Sektornamn",
+ "lane_type": "Sektortyp",
+ "marker": "Markör",
+ "marker_label": "Markörens etikett",
+ "move": "Flytta",
+ "move_lane": "Flytta sektor",
+ "move_resource": "Flytta resurs",
+ "name_required": "Namn krävs",
+ "no_accountability": "Ingen personal registrerad.",
+ "no_action_plan": "Ingen insatsplan har angetts.",
+ "no_active": "Inga aktiva insatsledningar",
+ "no_active_description": "Insatsledningar som upprättats för larm visas här.",
+ "no_annotations": "Inga anteckningar.",
+ "no_channels": "Inga öppna kanaler.",
+ "no_command": "Ingen insatsledning upprättad",
+ "no_command_description": "Upprätta en insatsledning för att samordna resurser, roller, mål och personalkontroll för det här larmet.",
+ "no_lanes": "Inga sektorer har definierats.",
+ "no_objectives": "Inga mål.",
+ "no_resources": "Inga resurser har tilldelats.",
+ "no_roles": "Inga roller har tilldelats.",
+ "no_template": "Ingen mall (tom tavla)",
+ "no_timeline": "Inga poster i tidslinjen.",
+ "no_timers": "Inga tidtagare körs.",
+ "not_authorized": "Command board unavailable",
+ "not_authorized_description": "Your department hasn't authorized you to work incident command. Ask an administrator for the Command App Login permission.",
+ "objective_name": "Mål",
+ "objective_type": "Typ",
+ "objectives": "Mål",
+ "open_chat": "Open",
+ "open_full_board": "Öppna hela ledningstavlan",
+ "open_tactical_map": "Öppna taktisk karta",
+ "parent_lane": "Överordnad sektor",
+ "person": "Person",
+ "personnel": "Personal",
+ "release": "Frigör",
+ "resource": "Resurs",
+ "resource_type": "Resurstyp",
+ "role": "Roll",
+ "roles": "Ledningsroller",
+ "run_par": "Genomför PAR",
+ "save": "Spara",
+ "save_error": "Åtgärden misslyckades",
+ "saved": "Sparat",
+ "select_lane": "Välj en sektor",
+ "select_person": "Välj en person",
+ "select_resource": "Välj en resurs",
+ "select_role": "Välj en roll",
+ "send_message": "Message",
+ "status": "Status",
+ "structure": "Ledningsstruktur",
+ "tab_title": "Ledning",
+ "tactical_map": "Taktisk karta",
+ "talking": "Sänder...",
+ "tap_to_place": "Tryck på kartan för att placera en markör",
+ "template": "Mall",
+ "timeline": "Ledningens tidslinje",
+ "timers": "Tidtagare",
+ "title": "Insatsledning",
+ "top_level": "Översta nivån",
+ "transfer": "Överför",
+ "transfer_command": "Överför",
+ "transfer_notes": "Anteckningar",
+ "transfer_success": "Ledningen har överförts",
+ "transfer_title": "Överför ledning",
+ "unassigned": "Ej tilldelad",
+ "unit": "Enhet",
+ "voice_channels": "Röstkanaler",
+ "voice_join_error": "Det gick inte att ansluta till röstkanalen",
+ "voice_joined": "Ansluten till röstkanalen",
+ "warning": "Varning"
+ },
+ "livekit": {
+ "audio_devices": "Ljudenheter",
+ "audio_settings": "Ljudinställningar",
+ "connected_to_room": "Ansluten till kanal",
+ "connecting": "Ansluter...",
+ "disconnect": "Koppla från",
+ "join": "Anslut",
+ "microphone": "Mikrofon",
+ "mute": "Tysta",
+ "no_rooms_available": "Inga röstkanaler tillgängliga",
+ "speaker": "Högtalare",
+ "speaking": "Talar",
+ "title": "Röstkanaler",
+ "unmute": "Slå på ljud"
+ },
+ "loading": {
+ "loading": "Laddar...",
+ "loadingData": "Laddar data...",
+ "pleaseWait": "Vänligen vänta",
+ "processingRequest": "Bearbetar din förfrågan..."
+ },
+ "lockscreen": {
+ "message": "Ange ditt lösenord för att låsa upp skärmen",
+ "not_you": "Inte du? Tillbaka till inloggning",
+ "password": "Lösenord",
+ "password_placeholder": "Ange ditt lösenord",
+ "title": "Låsskärm",
+ "unlock_button": "Lås upp",
+ "unlock_failed": "Upplåsning misslyckades. Vänligen försök igen.",
+ "unlocking": "Låser upp...",
+ "welcome_back": "Välkommen tillbaka",
+ "relogin_required": "Lösenordsverifiering är inte tillgänglig för den här sessionen. Logga in igen."
+ },
+ "login": {
+ "branding_subtitle": "Kraftfull larmbehandlingsprogramvara för räddningstjänst, eftersök och räddning samt samhällsskydd.",
+ "branding_title": "Hantering av räddningsinsatser",
+ "dispatch_not_authorized": "You are not authorized to use the Dispatch app. Contact your department administrator.",
+ "errorModal": {
+ "confirmButton": "OK",
+ "message": "Vänligen kontrollera ditt användarnamn och lösenord och försök igen.",
+ "title": "Inloggning misslyckades"
+ },
+ "feature_dispatch_desc": "Larma enheter omedelbart och hantera ärenden med realtidsuppdateringar på alla enheter.",
+ "feature_dispatch_title": "Realtidsutlarmning",
+ "feature_mapping_desc": "Spåra enheter i realtid med detaljerade kartor, vägbeskrivningar och platshantering.",
+ "feature_mapping_title": "Avancerade kartor",
+ "feature_personnel_desc": "Hantera ditt team med rollbaserad åtkomst, statusspårning och kommunikationsverktyg.",
+ "feature_personnel_title": "Personalhantering",
+ "footer_text": "Skapat med ❤️ i Lake Tahoe",
+ "login": "Logga in",
+ "login_button": "Logga in",
+ "login_button_description": "Logga in på ditt konto för att fortsätta",
+ "login_button_error": "Fel vid inloggning",
+ "login_button_loading": "Loggar in...",
+ "login_button_success": "Inloggning lyckades",
+ "no_account": "Har du inget konto?",
+ "page_subtitle": "Ange dina uppgifter för att logga in.",
+ "page_title": "Resgrid Dispatch",
+ "password": "Lösenord",
+ "password_incorrect": "Lösenordet var felaktigt",
+ "password_placeholder": "Ange ditt lösenord",
+ "register": "Registrera",
+ "title": "Logga in",
+ "username": "Användarnamn",
+ "username_placeholder": "Ange ditt användarnamn",
+ "welcome_title": "Välkommen tillbaka"
+ },
+ "maintenance": {
+ "downtime_message": "Vi arbetar hårt för att slutföra underhållet så snabbt som möjligt. Vänligen kontrollera igen snart.",
+ "downtime_title": "Vad är driftstoppet?",
+ "message": "Vänligen kontrollera igen om en stund.",
+ "support_message": "Om du behöver hjälp, vänligen kontakta oss på",
+ "support_title": "Behöver du support?",
+ "title": "Webbplatsen är under underhåll",
+ "why_down_message": "Vi utför planerat underhåll för att förbättra din upplevelse. Vi ber om ursäkt för eventuella besvär.",
+ "why_down_title": "Varför är webbplatsen nere?"
},
"map": {
"view_poi_details": "Visa POI-detaljer",
@@ -690,6 +1009,26 @@
"hide_all": "Dölj alla",
"view_call_details": "Visa ärendedetaljer"
},
+ "menu": {
+ "scheduled_calls": "Schemalagda larm",
+ "pois": "POI:er",
+ "calls": "Ärenden",
+ "calls_list": "Ärendelista",
+ "contacts": "Kontakter",
+ "home": "Hem",
+ "map": "Karta",
+ "menu": "Meny",
+ "messages": "Meddelanden",
+ "new_call": "Nytt ärende",
+ "personnel": "Personal",
+ "protocols": "Protokoll",
+ "settings": "Inställningar",
+ "units": "Enheter",
+ "weatherAlerts": "Vädervarningar",
+ "incident_command": "Insatsledning",
+ "chat": "Chatt",
+ "assistant": "Assistent"
+ },
"notes": {
"actions": {
"add": "Lägg till anteckning",
@@ -709,6 +1048,23 @@
"search": "Sök anteckningar...",
"title": "Anteckningar"
},
+ "onboarding": {
+ "screen1": {
+ "title": "Resgrid Dispatch",
+ "description": "Skapa, larma och hantera nödsamtal med ett kraftfullt mobilt ledningscentrum i din hand"
+ },
+ "screen2": {
+ "title": "Realtidslägesbild",
+ "description": "Spåra alla enheter, personal och resurser på en interaktiv karta med realtidsuppdateringar och AVL"
+ },
+ "screen3": {
+ "title": "Sömlös samordning",
+ "description": "Kommunicera direkt med fältenheter, uppdatera ärendestatus och samordna insatser var som helst"
+ },
+ "skip": "Hoppa över",
+ "next": "Nästa",
+ "getStarted": "Kom igång"
+ },
"personnel": {
"title": "Personal",
"search": "Sök personal...",
@@ -741,22 +1097,41 @@
"send_email": "E-post",
"custom_fields": "Ytterligare information"
},
- "onboarding": {
- "screen1": {
- "title": "Resgrid Dispatch",
- "description": "Skapa, larma och hantera nödsamtal med ett kraftfullt mobilt ledningscentrum i din hand"
- },
- "screen2": {
- "title": "Realtidslägesbild",
- "description": "Spåra alla enheter, personal och resurser på en interaktiv karta med realtidsuppdateringar och AVL"
- },
- "screen3": {
- "title": "Sömlös samordning",
- "description": "Kommunicera direkt med fältenheter, uppdatera ärendestatus och samordna insatser var som helst"
+ "pois": {
+ "address": "Adress",
+ "all_types": "Alla typer",
+ "destination": "Destination",
+ "details": "Detaljer",
+ "detail_not_found": "POI hittades inte",
+ "detail_not_found_description": "Det gick inte att läsa in begärd POI.",
+ "detail_title": "POI-detaljer",
+ "empty": "Inga POI:er hittades",
+ "empty_description": "Det finns ännu inga intressepunkter tillgängliga för din avdelning.",
+ "empty_filtered": "Inga matchande POI:er",
+ "empty_filtered_description": "Rensa sökningen eller välj en annan POI-typ.",
+ "filter_by_type": "Filtrera efter POI-typ",
+ "invalid_poi": "Ogiltig POI",
+ "invalid_poi_description": "Den valda POI-identifieraren är inte giltig.",
+ "loading": "Läser in POI:er...",
+ "loading_detail": "Läser in POI-detaljer...",
+ "map": "Karta",
+ "no_location": "Ingen plats tillgänglig",
+ "no_location_description": "Den här POI:n saknar användbara koordinater.",
+ "no_location_for_routing": "Inga platsdata tillgängliga för ruttplanering",
+ "note": "Anteckning",
+ "route_error": "Det gick inte att öppna kartappen",
+ "search": "Sök POI:er...",
+ "sort": "Sortera",
+ "sort_options": {
+ "address-asc": "Adress",
+ "name-asc": "Namn (A–Ö)",
+ "name-desc": "Namn (Ö–A)",
+ "type-asc": "Typ"
},
- "skip": "Hoppa över",
- "next": "Nästa",
- "getStarted": "Kom igång"
+ "title": "POI:er",
+ "type": "Typ",
+ "unknown_type": "Okänd typ",
+ "unnamed": "Namnlös POI"
},
"protocols": {
"details": {
@@ -796,6 +1171,20 @@
"tap_to_manage": "Tryck för att hantera roller",
"unassigned": "Ej tilldelad"
},
+ "scheduled_calls": {
+ "title": "Schemalagda larm",
+ "loading": "Läser in schemalagda larm...",
+ "no_scheduled_calls": "Inga schemalagda larm",
+ "no_scheduled_calls_description": "Det finns inga väntande schemalagda larm just nu.",
+ "search": "Sök schemalagda larm...",
+ "scheduled_for": "Schemalagt till",
+ "table_number": "Larmnummer",
+ "table_name": "Namn",
+ "table_type": "Typ",
+ "table_priority": "Prioritet",
+ "table_address": "Adress",
+ "table_scheduled": "Schemalagt till"
+ },
"settings": {
"about": "Om",
"account": "Konto",
@@ -884,6 +1273,29 @@
"version": "Version",
"website": "Webbplats"
},
+ "sso": {
+ "authenticating": "Autentiserar...",
+ "back_to_login": "Tillbaka till inloggning",
+ "back_to_lookup": "Byt användare",
+ "continue_button": "Fortsätt",
+ "department_id_label": "Avdelnings-ID",
+ "department_id_placeholder": "Ange avdelnings-ID",
+ "error_generic": "Inloggning misslyckades. Vänligen försök igen.",
+ "error_oidc_cancelled": "Inloggningen avbröts.",
+ "error_oidc_not_ready": "SSO-leverantören laddas, vänligen vänta.",
+ "error_sso_not_enabled": "Enkel inloggning är inte aktiverad för denna användare.",
+ "error_token_exchange": "Kunde inte slutföra inloggningen. Vänligen försök igen.",
+ "error_user_not_found": "Användare hittades inte. Vänligen kontrollera och försök igen.",
+ "looking_up": "Söker...",
+ "optional": "valfritt",
+ "page_subtitle": "Ange ditt användarnamn för att söka efter din organisations inloggningsalternativ.",
+ "page_title": "Enkel inloggning",
+ "provider_oidc": "OpenID Connect (OIDC)",
+ "provider_saml": "SAML 2.0",
+ "sign_in_button": "Logga in med SSO",
+ "sign_in_title": "Logga in",
+ "sso_button": "SSO-inloggning"
+ },
"status": {
"all_destinations_enabled": "Kan svara på larm, stationer eller POI:er",
"calls_and_pois_destination_enabled": "Kan svara på larm eller POI:er",
@@ -916,173 +1328,6 @@
"stations_tab": "Stationer",
"status_saved_successfully": "Status sparad!"
},
- "dispatch": {
- "active_calls": "Aktiva ärenden",
- "pending_calls": "Väntande",
- "scheduled_calls": "Schemalagda",
- "units_available": "Tillgängliga",
- "personnel_available": "Tillgängliga",
- "personnel_on_duty": "I tjänst",
- "units": "Enheter",
- "personnel": "Personal",
- "map": "Karta",
- "notes": "Anteckningar",
- "activity_log": "Aktivitetslogg",
- "communications": "Kommunikation",
- "no_active_calls": "Inga aktiva ärenden",
- "no_units": "Inga enheter tillgängliga",
- "no_personnel": "Ingen personal tillgänglig",
- "no_notes": "Inga anteckningar tillgängliga",
- "no_activity": "Ingen senaste aktivitet",
- "current_channel": "Aktuell kanal",
- "audio_stream": "Ljudström",
- "no_stream": "Ingen ström aktiv",
- "ptt": "PTT",
- "ptt_start": "PTT start",
- "ptt_end": "PTT slut",
- "transmitting_on": "Sänder på {{channel}}",
- "transmission_ended": "Sändning avslutad",
- "voice_disabled": "Röst inaktiverad",
- "disconnected": "Frånkopplad",
- "select_channel": "Välj kanal",
- "select_channel_description": "Välj en röstkanal att ansluta till",
- "change_channel_warning": "Att välja en ny kanal kopplar från den nuvarande",
- "default_channel": "Standard",
- "no_channels_available": "Inga röstkanaler tillgängliga",
- "system_update": "Systemuppdatering",
- "data_refreshed": "Data uppdaterad från servern",
- "call_selected": "Ärende valt",
- "unit_selected": "Enhet vald",
- "unit_deselected": "Enhet avmarkerad",
- "personnel_selected": "Personal vald",
- "personnel_deselected": "Personal avmarkerad",
- "loading_map": "Laddar karta...",
- "map_not_available_web": "Karta ej tillgänglig på webbplattform",
- "filtering_by_call": "Filtrerar efter ärende",
- "clear_filter": "Rensa filter",
- "call_filter_active": "Ärendefilter aktivt",
- "call_filter_cleared": "Ärendefilter rensat",
- "showing_all_data": "Visar all data",
- "call_notes": "Ärendeanteckningar",
- "no_call_notes": "Inga ärendeanteckningar",
- "add_call_note_placeholder": "Lägg till en anteckning...",
- "note_added": "Anteckning tillagd",
- "note_added_to_console": "En ny anteckning har lagts till i konsolen",
- "add_note_title": "Lägg till ny anteckning",
- "note_title_label": "Rubrik",
- "note_title_placeholder": "Ange anteckningsrubrik...",
- "note_category_label": "Kategori",
- "note_category_placeholder": "Välj en kategori",
- "note_no_category": "Ingen kategori",
- "note_body_label": "Anteckningsinnehåll",
- "note_body_placeholder": "Ange anteckningsinnehåll...",
- "note_save_error": "Kunde inte spara anteckning: {{error}}",
- "note_created": "Anteckning skapad",
- "units_on_call": "Enheter på ärende",
- "no_units_on_call": "Inga enheter på ärende",
- "personnel_on_call": "Personal på ärende",
- "no_personnel_on_call": "Ingen personal på ärende",
- "call_activity": "Ärendeaktivitet",
- "no_call_activity": "Ingen ärendeaktivitet",
- "on_call": "På ärende",
- "filtered": "Filtrerad",
- "active_filter": "Aktivt filter",
- "unit_status_change": "Enhetsstatusändring",
- "personnel_status_change": "Personalstatusändring",
- "view_call_details": "Visa ärendedetaljer",
- "dispatched_resources": "Utlarmade",
- "unassigned": "Ej tilldelad",
- "available": "Tillgänglig",
- "unknown": "Okänd",
- "search_personnel_placeholder": "Sök personal...",
- "search_calls_placeholder": "Sök ärenden...",
- "search_units_placeholder": "Sök enheter...",
- "search_notes_placeholder": "Sök anteckningar...",
- "signalr_update": "Realtidsuppdatering",
- "signalr_connected": "Ansluten",
- "realtime_updates_active": "Realtidsuppdateringar är nu aktiva",
- "personnel_status_updated": "Personalstatus uppdaterad",
- "personnel_staffing_updated": "Personalbemanning uppdaterad",
- "unit_status_updated": "Enhetsstatus uppdaterad",
- "calls_updated": "Ärenden uppdaterade",
- "call_added": "Nytt ärende tillagt",
- "call_closed": "Ärende stängt",
- "check_ins": "Incheckningar",
- "no_check_ins": "Inga ärenden med incheckningstimer",
- "radio_log": "Radiologg",
- "radio": "Radio",
- "activity": "Aktivitet",
- "actions": "Åtgärder",
- "no_radio_activity": "Inga radiosändningar",
- "live": "LIVE",
- "currently_transmitting": "Sänder för närvarande...",
- "duration": "Varaktighet",
- "call_actions": "Ärendeåtgärder",
- "unit_actions": "Enhetsåtgärder",
- "personnel_actions": {
- "title": "Personalåtgärder",
- "status_tab": "Status",
- "staffing_tab": "Bemanning",
- "select_status": "Välj status",
- "select_staffing": "Välj bemanningsnivå",
- "destination": "Destination",
- "no_destination": "Ingen destination",
- "note": "Anteckning",
- "note_placeholder": "Lägg till en valfri anteckning...",
- "update_status": "Uppdatera status",
- "update_staffing": "Uppdatera bemanning",
- "no_statuses_available": "Inga statusar tillgängliga",
- "no_staffings_available": "Inga bemanningsnivåer tillgängliga"
- },
- "unit_actions_panel": {
- "status": "Status",
- "select_status": "Välj status",
- "destination": "Destination",
- "no_destination": "Ingen destination",
- "note": "Anteckning",
- "note_placeholder": "Lägg till en valfri anteckning...",
- "update_status": "Uppdatera status",
- "no_statuses_available": "Inga statusar tillgängliga",
- "no_active_calls": "Inga aktiva ärenden",
- "no_stations_available": "Inga stationer tillgängliga",
- "no_destinations_available": "Inga destinationer tillgängliga"
- },
- "call": "Ärende",
- "station": "Station",
- "calls": "Ärenden",
- "stations": "Stationer",
- "no_stations_available": "Inga stationer tillgängliga",
- "new_call": "Nytt ärende",
- "view_details": "Detaljer",
- "add_note": "Lägg till anteckning",
- "close_call": "Stäng",
- "set_status": "Ange status",
- "set_staffing": "Bemanning",
- "dispatch": "Larma",
- "select_items_for_actions": "Välj ett ärende, enhet eller personal för att aktivera kontextmedvetna åtgärder",
- "weather": {
- "clear": "Klart",
- "mainly_clear": "Mestadels klart",
- "partly_cloudy": "Delvis molnigt",
- "overcast": "Mulet",
- "fog": "Dimma",
- "drizzle": "Duggregn",
- "freezing_drizzle": "Underkylt duggregn",
- "rain": "Regn",
- "freezing_rain": "Underkylt regn",
- "snow": "Snö",
- "rain_showers": "Regnskurar",
- "snow_showers": "Snöbyar",
- "thunderstorm": "Åskväder",
- "thunderstorm_hail": "Åskväder med hagel",
- "unknown": "Okänt"
- },
- "available_only": "Endast tillgängliga",
- "single_list": "En lista",
- "resources": "Resurser",
- "search_resources_placeholder": "Sök resurser...",
- "no_resources": "Inga resurser"
- },
"tabs": {
"calls": "Ärenden",
"calendar": "Kalender",
@@ -1096,44 +1341,6 @@
"shifts": "Skift",
"personnel": "Personal"
},
- "check_in": {
- "tab_title": "Incheckning",
- "timer_status": "Timerstatus",
- "perform_check_in": "Checka in",
- "check_in_success": "Incheckning registrerad",
- "check_in_error": "Kunde inte registrera incheckning",
- "checked_in_by": "av {{name}}",
- "last_check_in": "Senaste incheckning",
- "elapsed": "Förfluten tid",
- "duration": "Varaktighet",
- "status_ok": "OK",
- "status_green": "OK",
- "status_warning": "Varning",
- "status_yellow": "Varning",
- "status_overdue": "Försenad",
- "status_red": "Försenad",
- "status_critical": "Kritisk",
- "history": "Incheckningshistorik",
- "no_timers": "Inga incheckningstimrar konfigurerade",
- "timers_disabled": "Incheckningstimrar är inaktiverade för detta ärende",
- "type_personnel": "Personal",
- "type_unit": "Enhet",
- "type_ic": "Insatsledare",
- "type_par": "PAR",
- "type_hazmat": "Farligt gods-exponering",
- "type_sector_rotation": "Sektorsrotation",
- "type_rehab": "Rehab",
- "add_note": "Lägg till anteckning (valfritt)",
- "confirm": "Bekräfta incheckning",
- "minutes_ago": "{{count}} min sedan",
- "select_target": "Välj enhet att checka in",
- "overdue_count": "{{count}} försenade",
- "warning_count": "{{count}} varning",
- "enable_timers": "Aktivera timrar",
- "disable_timers": "Inaktivera timrar",
- "summary": "{{overdue}} försenade, {{warning}} varning, {{ok}} ok",
- "par_title": "Personalkontroll (PAR)"
- },
"units": {
"search": "Sök enheter...",
"loading": "Läser in enheter...",
@@ -1163,6 +1370,63 @@
"no_destination": "Ingen",
"title": "Enheter"
},
+ "videoFeeds": {
+ "title": "Videoflöden",
+ "noFeeds": "Inga videoflöden för detta ärende",
+ "addFeed": "Lägg till videoflöde",
+ "editFeed": "Redigera videoflöde",
+ "deleteFeed": "Radera videoflöde",
+ "deleteConfirm": "Är du säker på att du vill ta bort detta videoflöde?",
+ "watch": "Titta",
+ "goLive": "Gå live",
+ "stopLive": "Stoppa live",
+ "flipCamera": "Vänd kamera",
+ "feedAdded": "Videoflöde tillagt",
+ "feedUpdated": "Videoflöde uppdaterat",
+ "feedDeleted": "Videoflöde borttaget",
+ "feedError": "Kunde inte ladda videoflöde",
+ "unsupportedFormat": "Detta strömformat stöds inte på mobil",
+ "copyUrl": "Kopiera URL",
+ "form": {
+ "name": "Flödesnamn",
+ "namePlaceholder": "t.ex. Släckbil 1 Drönare",
+ "url": "Ström-URL",
+ "urlPlaceholder": "t.ex. https://stream.example.com/live.m3u8",
+ "feedType": "Kameratyp",
+ "feedFormat": "Strömformat",
+ "description": "Beskrivning",
+ "descriptionPlaceholder": "Valfri beskrivning",
+ "status": "Status",
+ "sortOrder": "Sorteringsordning",
+ "cameraLocation": "Kameraplats",
+ "useCurrentLocation": "Använd nuvarande plats"
+ },
+ "type": {
+ "drone": "Drönare",
+ "fixedCamera": "Fast kamera",
+ "bodyCam": "Kroppskamera",
+ "trafficCam": "Trafikkamera",
+ "weatherCam": "Väderkamera",
+ "satelliteFeed": "Satellitflöde",
+ "webCam": "Webbkamera",
+ "other": "Övrigt"
+ },
+ "format": {
+ "rtsp": "RTSP",
+ "hls": "HLS",
+ "mjpeg": "MJPEG",
+ "youtubeLive": "YouTube Live",
+ "webrtc": "WebRTC",
+ "dash": "DASH",
+ "embed": "Inbäddad",
+ "other": "Övrigt"
+ },
+ "status": {
+ "active": "Aktiv",
+ "inactive": "Inaktiv",
+ "error": "Fel"
+ }
+ },
"weatherAlerts": {
"title": "Vädervarningar",
"activeAlerts": "Aktiva varningar",
@@ -1270,251 +1534,5 @@
},
"stats_label": "Vädervarningar"
},
- "videoFeeds": {
- "title": "Videoflöden",
- "noFeeds": "Inga videoflöden för detta ärende",
- "addFeed": "Lägg till videoflöde",
- "editFeed": "Redigera videoflöde",
- "deleteFeed": "Radera videoflöde",
- "deleteConfirm": "Är du säker på att du vill ta bort detta videoflöde?",
- "watch": "Titta",
- "goLive": "Gå live",
- "stopLive": "Stoppa live",
- "flipCamera": "Vänd kamera",
- "feedAdded": "Videoflöde tillagt",
- "feedUpdated": "Videoflöde uppdaterat",
- "feedDeleted": "Videoflöde borttaget",
- "feedError": "Kunde inte ladda videoflöde",
- "unsupportedFormat": "Detta strömformat stöds inte på mobil",
- "copyUrl": "Kopiera URL",
- "form": {
- "name": "Flödesnamn",
- "namePlaceholder": "t.ex. Släckbil 1 Drönare",
- "url": "Ström-URL",
- "urlPlaceholder": "t.ex. https://stream.example.com/live.m3u8",
- "feedType": "Kameratyp",
- "feedFormat": "Strömformat",
- "description": "Beskrivning",
- "descriptionPlaceholder": "Valfri beskrivning",
- "status": "Status",
- "sortOrder": "Sorteringsordning",
- "cameraLocation": "Kameraplats",
- "useCurrentLocation": "Använd nuvarande plats"
- },
- "type": {
- "drone": "Drönare",
- "fixedCamera": "Fast kamera",
- "bodyCam": "Kroppskamera",
- "trafficCam": "Trafikkamera",
- "weatherCam": "Väderkamera",
- "satelliteFeed": "Satellitflöde",
- "webCam": "Webbkamera",
- "other": "Övrigt"
- },
- "format": {
- "rtsp": "RTSP",
- "hls": "HLS",
- "mjpeg": "MJPEG",
- "youtubeLive": "YouTube Live",
- "webrtc": "WebRTC",
- "dash": "DASH",
- "embed": "Inbäddad",
- "other": "Övrigt"
- },
- "status": {
- "active": "Aktiv",
- "inactive": "Inaktiv",
- "error": "Fel"
- }
- },
- "welcome": "Välkommen till obytes app-webbplats",
- "incident_command": {
- "tab_title": "Ledning",
- "title": "Insatsledning",
- "open_full_board": "Öppna hela ledningstavlan",
- "no_command": "Ingen insatsledning upprättad",
- "no_command_description": "Upprätta en insatsledning för att samordna resurser, roller, mål och personalkontroll för det här larmet.",
- "establish": "Upprätta ledning",
- "establish_title": "Upprätta insatsledning",
- "establish_description": "Skapa valfritt ledningstavlan från en mall.",
- "establish_success": "Insatsledningen har upprättats",
- "establish_error": "Det gick inte att upprätta ledningen",
- "template": "Mall",
- "no_template": "Ingen mall (tom tavla)",
- "saved": "Sparat",
- "save_error": "Åtgärden misslyckades",
- "edit_action_plan": "Redigera insatsplan",
- "action_plan": "Insatsplan",
- "action_plan_placeholder": "Beskriv insatsplanen...",
- "save": "Spara",
- "no_action_plan": "Ingen insatsplan har angetts.",
- "add": "Lägg till",
- "add_objective": "Lägg till mål",
- "objective_name": "Mål",
- "objective_type": "Typ",
- "name_required": "Namn krävs",
- "add_lane": "Lägg till sektor",
- "lane_name": "Sektornamn",
- "lane_type": "Sektortyp",
- "assign_resource": "Tilldela resurs",
- "assign_resource_required": "Välj en sektor och en resurs",
- "lane": "Sektor",
- "select_lane": "Välj en sektor",
- "resource_type": "Resurstyp",
- "resource": "Resurs",
- "select_resource": "Välj en resurs",
- "unit": "Enhet",
- "personnel": "Personal",
- "assign": "Tilldela",
- "assign_role": "Tilldela roll",
- "assign_role_required": "Välj en person och en roll",
- "person": "Person",
- "select_person": "Välj en person",
- "role": "Roll",
- "select_role": "Välj en roll",
- "transfer_title": "Överför ledning",
- "transfer_notes": "Anteckningar",
- "transfer": "Överför",
- "transfer_command": "Överför",
- "transfer_success": "Ledningen har överförts",
- "close_command": "Avsluta ledning",
- "confirm_close": "Avsluta insatsledningen för det här larmet?",
- "status": "Status",
- "active": "Aktiv",
- "closed": "Avslutad",
- "commander": "Insatsledare",
- "established_on": "Upprättad",
- "edit": "Redigera",
- "roles": "Ledningsroller",
- "no_roles": "Inga roller har tilldelats.",
- "structure": "Ledningsstruktur",
- "no_lanes": "Inga sektorer har definierats.",
- "no_resources": "Inga resurser har tilldelats.",
- "release": "Frigör",
- "objectives": "Mål",
- "no_objectives": "Inga mål.",
- "complete": "Slutför",
- "completed": "Slutförd",
- "timers": "Tidtagare",
- "no_timers": "Inga tidtagare körs.",
- "acknowledge": "Bekräfta",
- "due": "Förfaller",
- "accountability": "Personalkontroll (PAR)",
- "run_par": "Genomför PAR",
- "green": "Grön",
- "warning": "Varning",
- "critical": "Kritisk",
- "no_accountability": "Ingen personal registrerad.",
- "timeline": "Ledningens tidslinje",
- "no_timeline": "Inga poster i tidslinjen.",
- "unassigned": "Ej tilldelad",
- "active_title": "Aktiva insatsledningar",
- "no_active": "Inga aktiva insatsledningar",
- "no_active_description": "Insatsledningar som upprättats för larm visas här.",
- "call": "Larm",
- "tactical_map": "Taktisk karta",
- "annotations": "Kartanteckningar",
- "no_annotations": "Inga anteckningar.",
- "open_tactical_map": "Öppna taktisk karta",
- "marker": "Markör",
- "add_marker": "Lägg till markör",
- "tap_to_place": "Tryck på kartan för att placera en markör",
- "marker_label": "Markörens etikett",
- "delete_annotation_confirm": "Ta bort den här anteckningen?",
- "voice_channels": "Röstkanaler",
- "no_channels": "Inga öppna kanaler.",
- "add_channel": "Lägg till kanal",
- "channel_name": "Kanalnamn",
- "close_all_channels": "Stäng alla kanaler",
- "join": "Anslut",
- "hold_to_talk": "Håll in för att tala",
- "talking": "Sänder...",
- "voice_joined": "Ansluten till röstkanalen",
- "voice_join_error": "Det gick inte att ansluta till röstkanalen",
- "move": "Flytta",
- "move_lane": "Flytta sektor",
- "parent_lane": "Överordnad sektor",
- "top_level": "Översta nivån",
- "move_resource": "Flytta resurs"
- },
- "chat": {
- "title": "Chatt",
- "assistant": "Assistent",
- "empty": "Inga konversationer ännu. Starta ett direktmeddelande eller skapa en grupp.",
- "section_assistant": "Assistent",
- "section_direct_messages": "Direktmeddelanden",
- "section_channels": "Kanaler",
- "section_incidents": "Händelser",
- "new_direct_message": "Nytt direktmeddelande",
- "new_group": "Ny grupp",
- "open_assistant": "Öppna assistent",
- "create_conversation_failed": "Det gick inte att starta konversationen",
- "group_name": "Gruppnamn",
- "search_people": "Sök personer",
- "no_people": "Inga personer hittades",
- "create_group_with": "Skapa grupp ({{count}})",
- "message_deleted": "Det här meddelandet har raderats",
- "urgent": "Brådskande",
- "urgent_will_send": "Det här meddelandet skickas som brådskande",
- "shared_location": "Delad plats",
- "thread_replies": "{{count}} svar",
- "edited": "(redigerad)",
- "failed_tap_retry": "Misslyckades – tryck för att försöka igen",
- "type_a_message": "Skriv ett meddelande",
- "emoji": "Emoji",
- "add_image": "Lägg till bild",
- "add_gif": "Lägg till GIF",
- "share_location": "Dela plats",
- "send": "Skicka",
- "someone": "Någon",
- "is_typing": "{{name}} skriver...",
- "are_typing": "{{count}} personer skriver...",
- "permission_photos_denied": "Åtkomst till fotobiblioteket nekades",
- "permission_location_denied": "Platsåtkomst nekades",
- "search_gifs": "Sök GIF-filer",
- "no_gifs": "Inga GIF-filer hittades",
- "flag_reason": "Varför rapporterar du detta?",
- "flag_inappropriate": "Olämpligt",
- "flag_harassment": "Trakasserier",
- "flag_spam": "Skräppost",
- "flag_sensitive": "Känslig information",
- "flag_policy": "Policyöverträdelse",
- "flag_other": "Annat",
- "reply_in_thread": "Svara i tråden",
- "copy": "Kopiera",
- "copied": "Kopierad",
- "copy_unavailable": "Kopiering är inte tillgänglig på den här enheten",
- "edit": "Redigera",
- "edit_message": "Redigera meddelande",
- "save": "Spara",
- "delete": "Radera",
- "pin": "Fäst",
- "unpin": "Ta bort fästning",
- "flag": "Rapportera",
- "moderator_delete": "Ta bort (moderator)",
- "moderator_removed": "Borttagen av moderator",
- "attachment_failed": "Uppladdning av bilaga misslyckades",
- "ack_required": "Bekräftelse krävs",
- "ack_pending_one": "Du har ett brådskande meddelande att bekräfta",
- "ack_pending_count": "Du har {{count}} brådskande meddelanden att bekräfta",
- "acknowledge": "Bekräfta",
- "thread": "Tråd",
- "original_message": "Ursprungligt meddelande",
- "reply_placeholder": "Svara...",
- "channel": "Kanal",
- "direct_message": "Direktmeddelande",
- "load_people_failed": "Det gick inte att läsa in personer",
- "reaction_failed": "Det gick inte att uppdatera reaktionen",
- "edit_failed": "Det gick inte att redigera meddelandet",
- "delete_failed": "Det gick inte att radera meddelandet",
- "pin_failed": "Det gick inte att uppdatera fästningen",
- "flag_failed": "Det gick inte att rapportera meddelandet"
- },
- "chatbot": {
- "title": "Assistent",
- "subtitle": "AI-hjälp för din avdelning",
- "new_session": "Ny session",
- "empty": "Fråga assistenten om vad som helst för att komma igång.",
- "ask_placeholder": "Fråga assistenten..."
- }
+ "welcome": "Välkommen till obytes app-webbplats"
}
diff --git a/src/translations/uk.json b/src/translations/uk.json
index 4e5a03bc..17fef411 100644
--- a/src/translations/uk.json
+++ b/src/translations/uk.json
@@ -371,6 +371,124 @@
"audio_name": "Аудіокліп"
}
},
+ "chat": {
+ "title": "Чат",
+ "assistant": "Асистент",
+ "empty": "Ще немає розмов. Розпочніть пряме повідомлення або створіть групу.",
+ "section_assistant": "Асистент",
+ "section_direct_messages": "Прямі повідомлення",
+ "section_channels": "Канали",
+ "section_incidents": "Інциденти",
+ "new_direct_message": "Нове пряме повідомлення",
+ "new_group": "Нова група",
+ "open_assistant": "Відкрити асистента",
+ "create_conversation_failed": "Не вдалося розпочати розмову",
+ "group_name": "Назва групи",
+ "search_people": "Пошук людей",
+ "no_people": "Людей не знайдено",
+ "create_group_with": "Створити групу ({{count}})",
+ "message_deleted": "Це повідомлення видалено",
+ "urgent": "Терміново",
+ "urgent_will_send": "Це повідомлення буде надіслано як термінове",
+ "shared_location": "Надіслане місцезнаходження",
+ "thread_replies": "{{count}} відповідей",
+ "edited": "(відредаговано)",
+ "failed_tap_retry": "Помилка – торкніться, щоб повторити",
+ "type_a_message": "Введіть повідомлення",
+ "emoji": "Емодзі",
+ "add_image": "Додати зображення",
+ "add_gif": "Додати GIF",
+ "share_location": "Поділитися місцезнаходженням",
+ "send": "Надіслати",
+ "someone": "Хтось",
+ "is_typing": "{{name}} пише...",
+ "are_typing": "{{count}} людей пишуть...",
+ "permission_photos_denied": "Доступ до фотогалереї відхилено",
+ "permission_location_denied": "Доступ до місцезнаходження відхилено",
+ "search_gifs": "Пошук GIF",
+ "no_gifs": "GIF не знайдено",
+ "flag_reason": "Чому ви це повідомляєте?",
+ "flag_inappropriate": "Неприйнятне",
+ "flag_harassment": "Переслідування",
+ "flag_spam": "Спам",
+ "flag_sensitive": "Конфіденційна інформація",
+ "flag_policy": "Порушення правил",
+ "flag_other": "Інше",
+ "reply_in_thread": "Відповісти в гілці",
+ "copy": "Копіювати",
+ "copied": "Скопійовано",
+ "copy_unavailable": "Копіювання недоступне на цьому пристрої",
+ "edit": "Редагувати",
+ "edit_message": "Редагувати повідомлення",
+ "save": "Зберегти",
+ "delete": "Видалити",
+ "pin": "Закріпити",
+ "unpin": "Відкріпити",
+ "flag": "Поскаржитися",
+ "moderator_delete": "Видалити (модератор)",
+ "moderator_removed": "Видалено модератором",
+ "attachment_failed": "Не вдалося завантажити вкладення",
+ "ack_required": "Потрібне підтвердження",
+ "ack_pending_one": "У вас є термінове повідомлення для підтвердження",
+ "ack_pending_count": "У вас {{count}} термінових повідомлень для підтвердження",
+ "acknowledge": "Підтвердити",
+ "thread": "Гілка",
+ "original_message": "Оригінальне повідомлення",
+ "reply_placeholder": "Відповісти...",
+ "channel": "Канал",
+ "direct_message": "Пряме повідомлення",
+ "load_people_failed": "Не вдалося завантажити людей",
+ "reaction_failed": "Не вдалося оновити реакцію",
+ "edit_failed": "Не вдалося відредагувати повідомлення",
+ "delete_failed": "Не вдалося видалити повідомлення",
+ "pin_failed": "Не вдалося оновити закріплення",
+ "flag_failed": "Не вдалося поскаржитися на повідомлення"
+ },
+ "chatbot": {
+ "title": "Асистент",
+ "subtitle": "ШІ-помічник для вашого підрозділу",
+ "new_session": "Нова сесія",
+ "empty": "Запитайте асистента про будь-що, щоб почати.",
+ "ask_placeholder": "Запитайте асистента..."
+ },
+ "check_in": {
+ "tab_title": "Перевірка",
+ "timer_status": "Статус таймера",
+ "perform_check_in": "Відмітитися",
+ "check_in_success": "Перевірку успішно зафіксовано",
+ "check_in_error": "Не вдалося зафіксувати перевірку",
+ "checked_in_by": "від {{name}}",
+ "last_check_in": "Остання перевірка",
+ "elapsed": "Минуло",
+ "duration": "Тривалість",
+ "status_ok": "OK",
+ "status_green": "OK",
+ "status_warning": "Попередження",
+ "status_yellow": "Попередження",
+ "status_overdue": "Прострочено",
+ "status_red": "Прострочено",
+ "status_critical": "Критично",
+ "history": "Історія перевірок",
+ "no_timers": "Таймери перевірки не налаштовано",
+ "timers_disabled": "Таймери перевірки вимкнено для цього виклику",
+ "type_personnel": "Персонал",
+ "type_unit": "Підрозділ",
+ "type_ic": "Керівник інциденту",
+ "type_par": "PAR",
+ "type_hazmat": "Хімічне ураження",
+ "type_sector_rotation": "Ротація секторів",
+ "type_rehab": "Реабілітація",
+ "add_note": "Додати нотатку (необов'язково)",
+ "confirm": "Підтвердити перевірку",
+ "minutes_ago": "{{count}} хв тому",
+ "select_target": "Виберіть об'єкт для перевірки",
+ "overdue_count": "{{count}} прострочено",
+ "warning_count": "{{count}} попередження",
+ "enable_timers": "Увімкнути таймери",
+ "disable_timers": "Вимкнути таймери",
+ "summary": "{{overdue}} прострочено, {{warning}} попередження, {{ok}} ок",
+ "par_title": "Облік персоналу (PAR)"
+ },
"common": {
"add": "Додати",
"back": "Назад",
@@ -502,178 +620,379 @@
"website": "Вебсайт",
"zip": "Поштовий індекс"
},
- "form": {
- "invalid_url": "Будь ласка, введіть дійсну URL-адресу, що починається з http:// або https://",
- "required": "Це поле є обов'язковим"
- },
- "livekit": {
- "audio_devices": "Аудіопристрої",
- "audio_settings": "Налаштування аудіо",
- "connected_to_room": "Підключено до каналу",
- "connecting": "Підключення...",
- "disconnect": "Відключити",
- "join": "Приєднатися",
- "microphone": "Мікрофон",
- "mute": "Вимкнути звук",
- "no_rooms_available": "Голосові канали недоступні",
- "speaker": "Динамік",
- "speaking": "Говорить",
- "title": "Голосові канали",
- "unmute": "Увімкнути звук"
- },
- "loading": {
- "loading": "Завантаження...",
- "loadingData": "Завантаження даних...",
- "pleaseWait": "Будь ласка, зачекайте",
- "processingRequest": "Обробка вашого запиту..."
- },
- "sso": {
- "authenticating": "Автентифікація...",
- "back_to_login": "Повернутися до входу",
- "back_to_lookup": "Змінити користувача",
- "continue_button": "Продовжити",
- "department_id_label": "ID підрозділу",
- "department_id_placeholder": "Введіть ID підрозділу",
- "error_generic": "Не вдалося увійти. Будь ласка, спробуйте ще раз.",
- "error_oidc_cancelled": "Вхід було скасовано.",
- "error_oidc_not_ready": "SSO-провайдер завантажується, будь ласка, зачекайте.",
- "error_sso_not_enabled": "Єдиний вхід не увімкнено для цього користувача.",
- "error_token_exchange": "Не вдалося завершити вхід. Будь ласка, спробуйте ще раз.",
- "error_user_not_found": "Користувача не знайдено. Будь ласка, перевірте та спробуйте ще раз.",
- "looking_up": "Пошук...",
- "optional": "необов'язково",
- "page_subtitle": "Введіть ваше ім'я користувача для пошуку параметрів входу вашої організації.",
- "page_title": "Єдиний вхід (SSO)",
- "provider_oidc": "OpenID Connect (OIDC)",
- "provider_saml": "SAML 2.0",
- "sign_in_button": "Увійти через SSO",
- "sign_in_title": "Вхід",
- "sso_button": "SSO Вхід"
- },
- "login": {
- "branding_subtitle": "Потужне диспетчерське програмне забезпечення для служб екстреної допомоги, пошуково-рятувальних служб та організацій громадської безпеки.",
- "branding_title": "Управління екстреним реагуванням",
- "errorModal": {
- "confirmButton": "OK",
- "message": "Будь ласка, перевірте ваше ім'я користувача та пароль і спробуйте ще раз.",
- "title": "Помилка входу"
- },
- "feature_dispatch_desc": "Миттєво диспетчеризуйте підрозділи та керуйте викликами з оновленнями в реальному часі на всіх пристроях.",
- "feature_dispatch_title": "Диспетчеризація в реальному часі",
- "feature_mapping_desc": "Відстежуйте підрозділи в реальному часі з детальними картами, маршрутизацією та керуванням місцезнаходженням.",
- "feature_mapping_title": "Розширена картографія",
- "feature_personnel_desc": "Керуйте вашою командою з рольовим доступом, відстеженням статусів та засобами зв'язку.",
- "feature_personnel_title": "Управління персоналом",
- "footer_text": "Створено з ❤️ на озері Тахо",
- "login": "Вхід",
- "login_button": "Увійти",
- "login_button_description": "Увійдіть до свого облікового запису для продовження",
- "login_button_error": "Помилка входу",
- "login_button_loading": "Вхід...",
- "login_button_success": "Успішний вхід",
- "no_account": "Немає облікового запису?",
- "page_subtitle": "Введіть ваші облікові дані для входу.",
- "page_title": "Resgrid Dispatch",
- "password": "Пароль",
- "password_incorrect": "Невірний пароль",
- "password_placeholder": "Введіть ваш пароль",
- "register": "Реєстрація",
- "title": "Вхід",
- "username": "Ім'я користувача",
- "username_placeholder": "Введіть ваше ім'я користувача",
- "welcome_title": "З поверненням"
- },
- "lockscreen": {
- "message": "Введіть ваш пароль для розблокування екрана",
- "not_you": "Це не ви? Повернутися до входу",
- "password": "Пароль",
- "password_placeholder": "Введіть ваш пароль",
- "title": "Екран блокування",
- "unlock_button": "Розблокувати",
- "unlock_failed": "Не вдалося розблокувати. Будь ласка, спробуйте ще раз.",
- "unlocking": "Розблокування...",
- "welcome_back": "З поверненням",
- "relogin_required": "Перевірка пароля недоступна для цього сеансу. Будь ласка, увійдіть знову."
- },
- "maintenance": {
- "downtime_message": "Ми працюємо над якнайшвидшим завершенням технічних робіт. Будь ласка, перевірте пізніше.",
- "downtime_title": "Який час простою?",
- "message": "Будь ласка, перевірте пізніше.",
- "support_message": "Якщо вам потрібна допомога, зв'яжіться з нами за адресою",
- "support_title": "Потрібна підтримка?",
- "title": "Сайт на технічному обслуговуванні",
- "why_down_message": "Ми проводимо планове технічне обслуговування для покращення вашого досвіду. Просимо вибачення за незручності.",
- "why_down_title": "Чому сайт недоступний?"
- },
- "menu": {
- "scheduled_calls": "Заплановані виклики",
- "pois": "POI",
- "calls": "Виклики",
- "calls_list": "Список викликів",
- "contacts": "Контакти",
- "home": "Головна",
- "map": "Карта",
- "menu": "Меню",
- "messages": "Повідомлення",
- "new_call": "Новий виклик",
- "personnel": "Персонал",
- "protocols": "Протоколи",
- "settings": "Налаштування",
+ "dispatch": {
+ "active_calls": "Активні виклики",
+ "pending_calls": "В очікуванні",
+ "scheduled_calls": "Заплановані",
+ "units_available": "Доступні",
+ "personnel_available": "Доступні",
+ "personnel_on_duty": "На чергуванні",
"units": "Підрозділи",
- "weatherAlerts": "Погодні попередження",
- "incident_command": "Командування інцидентом",
- "chat": "Чат",
- "assistant": "Асистент"
- },
- "pois": {
- "address": "Адреса",
- "all_types": "Усі типи",
- "destination": "Місце призначення",
- "details": "Відомості",
- "detail_not_found": "POI не знайдено",
- "detail_not_found_description": "Не вдалося завантажити запитаний POI.",
- "detail_title": "Відомості про POI",
- "empty": "POI не знайдено",
- "empty_description": "Для вашого відділу ще немає доступних об’єктів інтересу.",
- "empty_filtered": "Немає відповідних POI",
- "empty_filtered_description": "Очистьте пошук або виберіть інший тип POI.",
- "filter_by_type": "Фільтрувати за типом POI",
- "invalid_poi": "Недійсний POI",
- "invalid_poi_description": "Вибраний ідентифікатор POI недійсний.",
- "loading": "Завантаження POI...",
- "loading_detail": "Завантаження відомостей про POI...",
+ "personnel": "Персонал",
"map": "Карта",
- "no_location": "Місцеположення недоступне",
- "no_location_description": "Цей POI не має придатних координат.",
- "no_location_for_routing": "Немає даних про місцеположення для прокладання маршруту",
- "note": "Примітка",
- "route_error": "Не вдалося відкрити застосунок карт",
- "search": "Пошук POI...",
- "sort": "Сортувати",
- "sort_options": {
- "address-asc": "Адреса",
- "name-asc": "Назва (А–Я)",
- "name-desc": "Назва (Я–А)",
- "type-asc": "Тип"
+ "notes": "Нотатки",
+ "activity_log": "Журнал активності",
+ "communications": "Зв'язок",
+ "no_active_calls": "Немає активних викликів",
+ "no_units": "Підрозділи недоступні",
+ "no_personnel": "Персонал недоступний",
+ "no_notes": "Нотатки відсутні",
+ "no_activity": "Немає останньої активності",
+ "current_channel": "Поточний канал",
+ "audio_stream": "Аудіопотік",
+ "no_stream": "Немає активного потоку",
+ "ptt": "PTT",
+ "ptt_start": "PTT Старт",
+ "ptt_end": "PTT Кінець",
+ "transmitting_on": "Передача на {{channel}}",
+ "transmission_ended": "Передачу завершено",
+ "voice_disabled": "Голос вимкнено",
+ "disconnected": "Відключено",
+ "select_channel": "Вибрати канал",
+ "select_channel_description": "Виберіть голосовий канал для підключення",
+ "change_channel_warning": "Вибір нового каналу відключить від поточного",
+ "default_channel": "За замовчуванням",
+ "no_channels_available": "Голосові канали недоступні",
+ "system_update": "Системне оновлення",
+ "data_refreshed": "Дані оновлено з сервера",
+ "call_selected": "Виклик вибрано",
+ "unit_selected": "Підрозділ вибрано",
+ "unit_deselected": "Вибір підрозділу скасовано",
+ "personnel_selected": "Персонал вибрано",
+ "personnel_deselected": "Вибір персоналу скасовано",
+ "loading_map": "Завантаження карти...",
+ "map_not_available_web": "Карта недоступна на веб-платформі",
+ "filtering_by_call": "Фільтрація за викликом",
+ "clear_filter": "Очистити фільтр",
+ "call_filter_active": "Фільтр виклику активний",
+ "call_filter_cleared": "Фільтр виклику очищено",
+ "showing_all_data": "Показано всі дані",
+ "call_notes": "Нотатки виклику",
+ "no_call_notes": "Немає нотаток виклику",
+ "add_call_note_placeholder": "Додати нотатку...",
+ "note_added": "Нотатку додано",
+ "note_added_to_console": "Нову нотатку додано до консолі",
+ "add_note_title": "Додати нову нотатку",
+ "note_title_label": "Заголовок",
+ "note_title_placeholder": "Введіть заголовок нотатки...",
+ "note_category_label": "Категорія",
+ "note_category_placeholder": "Виберіть категорію",
+ "note_no_category": "Без категорії",
+ "note_body_label": "Зміст нотатки",
+ "note_body_placeholder": "Введіть зміст нотатки...",
+ "note_save_error": "Не вдалося зберегти нотатку: {{error}}",
+ "note_created": "Нотатку створено",
+ "units_on_call": "Підрозділи на виклику",
+ "no_units_on_call": "Немає підрозділів на виклику",
+ "personnel_on_call": "Персонал на виклику",
+ "no_personnel_on_call": "Немає персоналу на виклику",
+ "call_activity": "Активність виклику",
+ "no_call_activity": "Немає активності виклику",
+ "on_call": "На виклику",
+ "filtered": "Відфільтровано",
+ "active_filter": "Активний фільтр",
+ "unit_status_change": "Зміна статусу підрозділу",
+ "personnel_status_change": "Зміна статусу персоналу",
+ "view_call_details": "Переглянути деталі виклику",
+ "dispatched_resources": "Диспетчеризовано",
+ "unassigned": "Не призначено",
+ "available": "Доступний",
+ "unknown": "Невідомо",
+ "search_personnel_placeholder": "Пошук персоналу...",
+ "search_calls_placeholder": "Пошук викликів...",
+ "search_units_placeholder": "Пошук підрозділів...",
+ "search_notes_placeholder": "Пошук нотаток...",
+ "signalr_update": "Оновлення в реальному часі",
+ "signalr_connected": "Підключено",
+ "realtime_updates_active": "Оновлення в реальному часі тепер активні",
+ "personnel_status_updated": "Статус персоналу оновлено",
+ "personnel_staffing_updated": "Укомплектованість персоналу оновлено",
+ "unit_status_updated": "Статус підрозділу оновлено",
+ "calls_updated": "Виклики оновлено",
+ "call_added": "Новий виклик додано",
+ "call_closed": "Виклик закрито",
+ "check_ins": "Перевірки",
+ "no_check_ins": "Немає викликів з таймерами перевірки",
+ "radio_log": "Радіожурнал",
+ "radio": "Радіо",
+ "activity": "Активність",
+ "actions": "Дії",
+ "no_radio_activity": "Немає радіопередач",
+ "live": "НАЖИВО",
+ "currently_transmitting": "Зараз передає...",
+ "duration": "Тривалість",
+ "call_actions": "Дії з викликом",
+ "unit_actions": "Дії з підрозділом",
+ "personnel_actions": {
+ "title": "Дії з персоналом",
+ "status_tab": "Статус",
+ "staffing_tab": "Укомплектованість",
+ "select_status": "Вибрати статус",
+ "select_staffing": "Вибрати рівень укомплектованості",
+ "destination": "Призначення",
+ "no_destination": "Без призначення",
+ "note": "Нотатка",
+ "note_placeholder": "Додайте необов'язкову нотатку...",
+ "update_status": "Оновити статус",
+ "update_staffing": "Оновити укомплектованість",
+ "no_statuses_available": "Статуси недоступні",
+ "no_staffings_available": "Рівні укомплектованості недоступні"
},
- "title": "POI",
- "type": "Тип",
- "unknown_type": "Невідомий тип",
- "unnamed": "POI без назви"
+ "unit_actions_panel": {
+ "status": "Статус",
+ "select_status": "Вибрати статус",
+ "destination": "Призначення",
+ "no_destination": "Без призначення",
+ "note": "Нотатка",
+ "note_placeholder": "Додайте необов'язкову нотатку...",
+ "update_status": "Оновити статус",
+ "no_statuses_available": "Статуси недоступні",
+ "no_active_calls": "Немає активних викликів",
+ "no_stations_available": "Станції недоступні",
+ "no_destinations_available": "Призначення недоступні"
+ },
+ "call": "Виклик",
+ "station": "Станція",
+ "calls": "Виклики",
+ "stations": "Станції",
+ "no_stations_available": "Станції недоступні",
+ "new_call": "Новий виклик",
+ "view_details": "Деталі",
+ "add_note": "Додати нотатку",
+ "close_call": "Закрити",
+ "set_status": "Встановити статус",
+ "set_staffing": "Укомплектованість",
+ "dispatch": "Диспетчеризувати",
+ "select_items_for_actions": "Виберіть виклик, підрозділ або персонал для увімкнення контекстних дій",
+ "weather": {
+ "clear": "Ясно",
+ "mainly_clear": "Переважно ясно",
+ "partly_cloudy": "Мінлива хмарність",
+ "overcast": "Хмарно",
+ "fog": "Туман",
+ "drizzle": "Мряка",
+ "freezing_drizzle": "Крижана мряка",
+ "rain": "Дощ",
+ "freezing_rain": "Крижаний дощ",
+ "snow": "Сніг",
+ "rain_showers": "Зливи",
+ "snow_showers": "Снігопади",
+ "thunderstorm": "Гроза",
+ "thunderstorm_hail": "Гроза з градом",
+ "unknown": "Невідомо"
+ },
+ "available_only": "Лише доступні",
+ "single_list": "Єдиний список",
+ "resources": "Ресурси",
+ "search_resources_placeholder": "Пошук ресурсів...",
+ "no_resources": "Немає ресурсів"
},
- "scheduled_calls": {
- "title": "Заплановані виклики",
- "loading": "Завантаження запланованих викликів...",
- "no_scheduled_calls": "Немає запланованих викликів",
- "no_scheduled_calls_description": "Наразі немає запланованих викликів, що очікують.",
- "search": "Пошук запланованих викликів...",
- "scheduled_for": "Заплановано на",
- "table_number": "№ виклику",
- "table_name": "Назва",
- "table_type": "Тип",
- "table_priority": "Пріоритет",
- "table_address": "Адреса",
- "table_scheduled": "Заплановано на"
+ "form": {
+ "invalid_url": "Будь ласка, введіть дійсну URL-адресу, що починається з http:// або https://",
+ "required": "Це поле є обов'язковим"
+ },
+ "incident_command": {
+ "accountability": "Облік персоналу (PAR)",
+ "acknowledge": "Підтвердити",
+ "action_plan": "План дій",
+ "action_plan_placeholder": "Опишіть план дій під час інциденту...",
+ "active": "Активне",
+ "active_title": "Активні командування інцидентами",
+ "add": "Додати",
+ "add_channel": "Додати канал",
+ "add_lane": "Додати сектор",
+ "add_marker": "Додати маркер",
+ "add_objective": "Додати ціль",
+ "annotations": "Позначки на карті",
+ "assign": "Призначити",
+ "assign_resource": "Призначити ресурс",
+ "assign_resource_required": "Виберіть сектор і ресурс",
+ "assign_role": "Призначити роль",
+ "assign_role_required": "Виберіть особу та роль",
+ "call": "Виклик",
+ "channel_name": "Назва каналу",
+ "chat": "Chat",
+ "chat_frozen": "This incident is closed. Conversations are kept as a point-in-time record — no new messages.",
+ "close_all_channels": "Закрити всі канали",
+ "close_command": "Завершити командування",
+ "closed": "Завершене",
+ "command_channel": "Command chat",
+ "command_channel_hint": "Command staff and dispatch",
+ "command_channel_unavailable": "No command channel has been created for this incident yet.",
+ "commander": "Керівник",
+ "complete": "Завершити",
+ "completed": "Завершено",
+ "confirm_close": "Завершити командування інцидентом для цього виклику?",
+ "critical": "Критичний",
+ "delete_annotation_confirm": "Видалити цю позначку?",
+ "dispatch_channel": "Dispatch",
+ "dispatch_channel_hint": "The incident's line to the desk",
+ "dispatch_channel_unavailable": "No dispatch channel has been created for this incident yet.",
+ "dm_failed": "Couldn't open that conversation.",
+ "dm_unavailable": "That contact has no Resgrid account to message.",
+ "due": "Термін",
+ "edit": "Редагувати",
+ "edit_action_plan": "Редагувати план дій",
+ "establish": "Встановити командування",
+ "establish_description": "За бажанням створіть панель командування із шаблону.",
+ "establish_error": "Не вдалося встановити командування",
+ "establish_success": "Командування інцидентом встановлено",
+ "establish_title": "Встановити командування інцидентом",
+ "established_on": "Встановлено",
+ "green": "Зелений",
+ "hold_to_talk": "Утримуйте, щоб говорити",
+ "incident_channel": "Incident chat",
+ "incident_channel_hint": "Everyone working the incident",
+ "incident_channel_unavailable": "No incident channel has been created for this call yet.",
+ "join": "Приєднатися",
+ "lane": "Сектор",
+ "lane_name": "Назва сектора",
+ "lane_type": "Тип сектора",
+ "marker": "Маркер",
+ "marker_label": "Підпис маркера",
+ "move": "Перемістити",
+ "move_lane": "Перемістити сектор",
+ "move_resource": "Перемістити ресурс",
+ "name_required": "Ім’я є обов’язковим",
+ "no_accountability": "Персонал не відстежується.",
+ "no_action_plan": "План дій не задано.",
+ "no_active": "Немає активних командувань інцидентами",
+ "no_active_description": "Тут відображатимуться командування, встановлені для викликів.",
+ "no_annotations": "Позначок немає.",
+ "no_channels": "Немає відкритих каналів.",
+ "no_command": "Командування інцидентом не встановлено",
+ "no_command_description": "Встановіть командування інцидентом, щоб координувати ресурси, ролі, цілі та облік персоналу для цього виклику.",
+ "no_lanes": "Сектори не визначено.",
+ "no_objectives": "Цілей немає.",
+ "no_resources": "Ресурси не призначено.",
+ "no_roles": "Ролі не призначено.",
+ "no_template": "Без шаблону (порожня панель)",
+ "no_timeline": "Записів у хронології немає.",
+ "no_timers": "Немає активних таймерів.",
+ "not_authorized": "Command board unavailable",
+ "not_authorized_description": "Your department hasn't authorized you to work incident command. Ask an administrator for the Command App Login permission.",
+ "objective_name": "Ціль",
+ "objective_type": "Тип",
+ "objectives": "Цілі",
+ "open_chat": "Open",
+ "open_full_board": "Відкрити повну панель",
+ "open_tactical_map": "Відкрити тактичну карту",
+ "parent_lane": "Батьківський сектор",
+ "person": "Особа",
+ "personnel": "Персонал",
+ "release": "Звільнити",
+ "resource": "Ресурс",
+ "resource_type": "Тип ресурсу",
+ "role": "Роль",
+ "roles": "Ролі командування",
+ "run_par": "Провести PAR",
+ "save": "Зберегти",
+ "save_error": "Не вдалося виконати операцію",
+ "saved": "Збережено",
+ "select_lane": "Виберіть сектор",
+ "select_person": "Виберіть особу",
+ "select_resource": "Виберіть ресурс",
+ "select_role": "Виберіть роль",
+ "send_message": "Message",
+ "status": "Стан",
+ "structure": "Структура командування",
+ "tab_title": "Командування",
+ "tactical_map": "Тактична карта",
+ "talking": "Передавання...",
+ "tap_to_place": "Торкніться карти, щоб установити маркер",
+ "template": "Шаблон",
+ "timeline": "Хронологія командування",
+ "timers": "Таймери",
+ "title": "Командування інцидентом",
+ "top_level": "Верхній рівень",
+ "transfer": "Передати",
+ "transfer_command": "Передати",
+ "transfer_notes": "Примітки",
+ "transfer_success": "Командування передано",
+ "transfer_title": "Передати командування",
+ "unassigned": "Не призначено",
+ "unit": "Підрозділ",
+ "voice_channels": "Голосові канали",
+ "voice_join_error": "Не вдалося приєднатися до голосового каналу",
+ "voice_joined": "Приєднано до голосового каналу",
+ "warning": "Попередження"
+ },
+ "livekit": {
+ "audio_devices": "Аудіопристрої",
+ "audio_settings": "Налаштування аудіо",
+ "connected_to_room": "Підключено до каналу",
+ "connecting": "Підключення...",
+ "disconnect": "Відключити",
+ "join": "Приєднатися",
+ "microphone": "Мікрофон",
+ "mute": "Вимкнути звук",
+ "no_rooms_available": "Голосові канали недоступні",
+ "speaker": "Динамік",
+ "speaking": "Говорить",
+ "title": "Голосові канали",
+ "unmute": "Увімкнути звук"
+ },
+ "loading": {
+ "loading": "Завантаження...",
+ "loadingData": "Завантаження даних...",
+ "pleaseWait": "Будь ласка, зачекайте",
+ "processingRequest": "Обробка вашого запиту..."
+ },
+ "lockscreen": {
+ "message": "Введіть ваш пароль для розблокування екрана",
+ "not_you": "Це не ви? Повернутися до входу",
+ "password": "Пароль",
+ "password_placeholder": "Введіть ваш пароль",
+ "title": "Екран блокування",
+ "unlock_button": "Розблокувати",
+ "unlock_failed": "Не вдалося розблокувати. Будь ласка, спробуйте ще раз.",
+ "unlocking": "Розблокування...",
+ "welcome_back": "З поверненням",
+ "relogin_required": "Перевірка пароля недоступна для цього сеансу. Будь ласка, увійдіть знову."
+ },
+ "login": {
+ "branding_subtitle": "Потужне диспетчерське програмне забезпечення для служб екстреної допомоги, пошуково-рятувальних служб та організацій громадської безпеки.",
+ "branding_title": "Управління екстреним реагуванням",
+ "dispatch_not_authorized": "You are not authorized to use the Dispatch app. Contact your department administrator.",
+ "errorModal": {
+ "confirmButton": "OK",
+ "message": "Будь ласка, перевірте ваше ім'я користувача та пароль і спробуйте ще раз.",
+ "title": "Помилка входу"
+ },
+ "feature_dispatch_desc": "Миттєво диспетчеризуйте підрозділи та керуйте викликами з оновленнями в реальному часі на всіх пристроях.",
+ "feature_dispatch_title": "Диспетчеризація в реальному часі",
+ "feature_mapping_desc": "Відстежуйте підрозділи в реальному часі з детальними картами, маршрутизацією та керуванням місцезнаходженням.",
+ "feature_mapping_title": "Розширена картографія",
+ "feature_personnel_desc": "Керуйте вашою командою з рольовим доступом, відстеженням статусів та засобами зв'язку.",
+ "feature_personnel_title": "Управління персоналом",
+ "footer_text": "Створено з ❤️ на озері Тахо",
+ "login": "Вхід",
+ "login_button": "Увійти",
+ "login_button_description": "Увійдіть до свого облікового запису для продовження",
+ "login_button_error": "Помилка входу",
+ "login_button_loading": "Вхід...",
+ "login_button_success": "Успішний вхід",
+ "no_account": "Немає облікового запису?",
+ "page_subtitle": "Введіть ваші облікові дані для входу.",
+ "page_title": "Resgrid Dispatch",
+ "password": "Пароль",
+ "password_incorrect": "Невірний пароль",
+ "password_placeholder": "Введіть ваш пароль",
+ "register": "Реєстрація",
+ "title": "Вхід",
+ "username": "Ім'я користувача",
+ "username_placeholder": "Введіть ваше ім'я користувача",
+ "welcome_title": "З поверненням"
+ },
+ "maintenance": {
+ "downtime_message": "Ми працюємо над якнайшвидшим завершенням технічних робіт. Будь ласка, перевірте пізніше.",
+ "downtime_title": "Який час простою?",
+ "message": "Будь ласка, перевірте пізніше.",
+ "support_message": "Якщо вам потрібна допомога, зв'яжіться з нами за адресою",
+ "support_title": "Потрібна підтримка?",
+ "title": "Сайт на технічному обслуговуванні",
+ "why_down_message": "Ми проводимо планове технічне обслуговування для покращення вашого досвіду. Просимо вибачення за незручності.",
+ "why_down_title": "Чому сайт недоступний?"
},
"map": {
"view_poi_details": "Переглянути відомості про POI",
@@ -690,6 +1009,26 @@
"hide_all": "Сховати все",
"view_call_details": "Переглянути деталі виклику"
},
+ "menu": {
+ "scheduled_calls": "Заплановані виклики",
+ "pois": "POI",
+ "calls": "Виклики",
+ "calls_list": "Список викликів",
+ "contacts": "Контакти",
+ "home": "Головна",
+ "map": "Карта",
+ "menu": "Меню",
+ "messages": "Повідомлення",
+ "new_call": "Новий виклик",
+ "personnel": "Персонал",
+ "protocols": "Протоколи",
+ "settings": "Налаштування",
+ "units": "Підрозділи",
+ "weatherAlerts": "Погодні попередження",
+ "incident_command": "Командування інцидентом",
+ "chat": "Чат",
+ "assistant": "Асистент"
+ },
"notes": {
"actions": {
"add": "Додати нотатку",
@@ -709,6 +1048,23 @@
"search": "Пошук нотаток...",
"title": "Нотатки"
},
+ "onboarding": {
+ "screen1": {
+ "title": "Resgrid Dispatch",
+ "description": "Створюйте, диспетчеризуйте та керуйте екстреними викликами за допомогою потужного мобільного командного центру"
+ },
+ "screen2": {
+ "title": "Ситуаційна обізнаність у реальному часі",
+ "description": "Відстежуйте всі підрозділи, персонал та ресурси на інтерактивній карті з оновленнями статусу в реальному часі та AVL"
+ },
+ "screen3": {
+ "title": "Безперебійна координація",
+ "description": "Миттєво зв'язуйтеся з польовими підрозділами, оновлюйте статуси викликів та координуйте реагування з будь-якого місця"
+ },
+ "skip": "Пропустити",
+ "next": "Далі",
+ "getStarted": "Розпочати"
+ },
"personnel": {
"title": "Персонал",
"search": "Пошук персоналу...",
@@ -741,22 +1097,41 @@
"send_email": "Електронна пошта",
"custom_fields": "Додаткова інформація"
},
- "onboarding": {
- "screen1": {
- "title": "Resgrid Dispatch",
- "description": "Створюйте, диспетчеризуйте та керуйте екстреними викликами за допомогою потужного мобільного командного центру"
- },
- "screen2": {
- "title": "Ситуаційна обізнаність у реальному часі",
- "description": "Відстежуйте всі підрозділи, персонал та ресурси на інтерактивній карті з оновленнями статусу в реальному часі та AVL"
- },
- "screen3": {
- "title": "Безперебійна координація",
- "description": "Миттєво зв'язуйтеся з польовими підрозділами, оновлюйте статуси викликів та координуйте реагування з будь-якого місця"
+ "pois": {
+ "address": "Адреса",
+ "all_types": "Усі типи",
+ "destination": "Місце призначення",
+ "details": "Відомості",
+ "detail_not_found": "POI не знайдено",
+ "detail_not_found_description": "Не вдалося завантажити запитаний POI.",
+ "detail_title": "Відомості про POI",
+ "empty": "POI не знайдено",
+ "empty_description": "Для вашого відділу ще немає доступних об’єктів інтересу.",
+ "empty_filtered": "Немає відповідних POI",
+ "empty_filtered_description": "Очистьте пошук або виберіть інший тип POI.",
+ "filter_by_type": "Фільтрувати за типом POI",
+ "invalid_poi": "Недійсний POI",
+ "invalid_poi_description": "Вибраний ідентифікатор POI недійсний.",
+ "loading": "Завантаження POI...",
+ "loading_detail": "Завантаження відомостей про POI...",
+ "map": "Карта",
+ "no_location": "Місцеположення недоступне",
+ "no_location_description": "Цей POI не має придатних координат.",
+ "no_location_for_routing": "Немає даних про місцеположення для прокладання маршруту",
+ "note": "Примітка",
+ "route_error": "Не вдалося відкрити застосунок карт",
+ "search": "Пошук POI...",
+ "sort": "Сортувати",
+ "sort_options": {
+ "address-asc": "Адреса",
+ "name-asc": "Назва (А–Я)",
+ "name-desc": "Назва (Я–А)",
+ "type-asc": "Тип"
},
- "skip": "Пропустити",
- "next": "Далі",
- "getStarted": "Розпочати"
+ "title": "POI",
+ "type": "Тип",
+ "unknown_type": "Невідомий тип",
+ "unnamed": "POI без назви"
},
"protocols": {
"details": {
@@ -796,6 +1171,20 @@
"tap_to_manage": "Натисніть для керування ролями",
"unassigned": "Не призначено"
},
+ "scheduled_calls": {
+ "title": "Заплановані виклики",
+ "loading": "Завантаження запланованих викликів...",
+ "no_scheduled_calls": "Немає запланованих викликів",
+ "no_scheduled_calls_description": "Наразі немає запланованих викликів, що очікують.",
+ "search": "Пошук запланованих викликів...",
+ "scheduled_for": "Заплановано на",
+ "table_number": "№ виклику",
+ "table_name": "Назва",
+ "table_type": "Тип",
+ "table_priority": "Пріоритет",
+ "table_address": "Адреса",
+ "table_scheduled": "Заплановано на"
+ },
"settings": {
"about": "Про додаток",
"account": "Обліковий запис",
@@ -884,6 +1273,29 @@
"version": "Версія",
"website": "Вебсайт"
},
+ "sso": {
+ "authenticating": "Автентифікація...",
+ "back_to_login": "Повернутися до входу",
+ "back_to_lookup": "Змінити користувача",
+ "continue_button": "Продовжити",
+ "department_id_label": "ID підрозділу",
+ "department_id_placeholder": "Введіть ID підрозділу",
+ "error_generic": "Не вдалося увійти. Будь ласка, спробуйте ще раз.",
+ "error_oidc_cancelled": "Вхід було скасовано.",
+ "error_oidc_not_ready": "SSO-провайдер завантажується, будь ласка, зачекайте.",
+ "error_sso_not_enabled": "Єдиний вхід не увімкнено для цього користувача.",
+ "error_token_exchange": "Не вдалося завершити вхід. Будь ласка, спробуйте ще раз.",
+ "error_user_not_found": "Користувача не знайдено. Будь ласка, перевірте та спробуйте ще раз.",
+ "looking_up": "Пошук...",
+ "optional": "необов'язково",
+ "page_subtitle": "Введіть ваше ім'я користувача для пошуку параметрів входу вашої організації.",
+ "page_title": "Єдиний вхід (SSO)",
+ "provider_oidc": "OpenID Connect (OIDC)",
+ "provider_saml": "SAML 2.0",
+ "sign_in_button": "Увійти через SSO",
+ "sign_in_title": "Вхід",
+ "sso_button": "SSO Вхід"
+ },
"status": {
"all_destinations_enabled": "Може реагувати на виклики, станції або POI",
"calls_and_pois_destination_enabled": "Може реагувати на виклики або POI",
@@ -916,173 +1328,6 @@
"stations_tab": "Станції",
"status_saved_successfully": "Статус успішно збережено!"
},
- "dispatch": {
- "active_calls": "Активні виклики",
- "pending_calls": "В очікуванні",
- "scheduled_calls": "Заплановані",
- "units_available": "Доступні",
- "personnel_available": "Доступні",
- "personnel_on_duty": "На чергуванні",
- "units": "Підрозділи",
- "personnel": "Персонал",
- "map": "Карта",
- "notes": "Нотатки",
- "activity_log": "Журнал активності",
- "communications": "Зв'язок",
- "no_active_calls": "Немає активних викликів",
- "no_units": "Підрозділи недоступні",
- "no_personnel": "Персонал недоступний",
- "no_notes": "Нотатки відсутні",
- "no_activity": "Немає останньої активності",
- "current_channel": "Поточний канал",
- "audio_stream": "Аудіопотік",
- "no_stream": "Немає активного потоку",
- "ptt": "PTT",
- "ptt_start": "PTT Старт",
- "ptt_end": "PTT Кінець",
- "transmitting_on": "Передача на {{channel}}",
- "transmission_ended": "Передачу завершено",
- "voice_disabled": "Голос вимкнено",
- "disconnected": "Відключено",
- "select_channel": "Вибрати канал",
- "select_channel_description": "Виберіть голосовий канал для підключення",
- "change_channel_warning": "Вибір нового каналу відключить від поточного",
- "default_channel": "За замовчуванням",
- "no_channels_available": "Голосові канали недоступні",
- "system_update": "Системне оновлення",
- "data_refreshed": "Дані оновлено з сервера",
- "call_selected": "Виклик вибрано",
- "unit_selected": "Підрозділ вибрано",
- "unit_deselected": "Вибір підрозділу скасовано",
- "personnel_selected": "Персонал вибрано",
- "personnel_deselected": "Вибір персоналу скасовано",
- "loading_map": "Завантаження карти...",
- "map_not_available_web": "Карта недоступна на веб-платформі",
- "filtering_by_call": "Фільтрація за викликом",
- "clear_filter": "Очистити фільтр",
- "call_filter_active": "Фільтр виклику активний",
- "call_filter_cleared": "Фільтр виклику очищено",
- "showing_all_data": "Показано всі дані",
- "call_notes": "Нотатки виклику",
- "no_call_notes": "Немає нотаток виклику",
- "add_call_note_placeholder": "Додати нотатку...",
- "note_added": "Нотатку додано",
- "note_added_to_console": "Нову нотатку додано до консолі",
- "add_note_title": "Додати нову нотатку",
- "note_title_label": "Заголовок",
- "note_title_placeholder": "Введіть заголовок нотатки...",
- "note_category_label": "Категорія",
- "note_category_placeholder": "Виберіть категорію",
- "note_no_category": "Без категорії",
- "note_body_label": "Зміст нотатки",
- "note_body_placeholder": "Введіть зміст нотатки...",
- "note_save_error": "Не вдалося зберегти нотатку: {{error}}",
- "note_created": "Нотатку створено",
- "units_on_call": "Підрозділи на виклику",
- "no_units_on_call": "Немає підрозділів на виклику",
- "personnel_on_call": "Персонал на виклику",
- "no_personnel_on_call": "Немає персоналу на виклику",
- "call_activity": "Активність виклику",
- "no_call_activity": "Немає активності виклику",
- "on_call": "На виклику",
- "filtered": "Відфільтровано",
- "active_filter": "Активний фільтр",
- "unit_status_change": "Зміна статусу підрозділу",
- "personnel_status_change": "Зміна статусу персоналу",
- "view_call_details": "Переглянути деталі виклику",
- "dispatched_resources": "Диспетчеризовано",
- "unassigned": "Не призначено",
- "available": "Доступний",
- "unknown": "Невідомо",
- "search_personnel_placeholder": "Пошук персоналу...",
- "search_calls_placeholder": "Пошук викликів...",
- "search_units_placeholder": "Пошук підрозділів...",
- "search_notes_placeholder": "Пошук нотаток...",
- "signalr_update": "Оновлення в реальному часі",
- "signalr_connected": "Підключено",
- "realtime_updates_active": "Оновлення в реальному часі тепер активні",
- "personnel_status_updated": "Статус персоналу оновлено",
- "personnel_staffing_updated": "Укомплектованість персоналу оновлено",
- "unit_status_updated": "Статус підрозділу оновлено",
- "calls_updated": "Виклики оновлено",
- "call_added": "Новий виклик додано",
- "call_closed": "Виклик закрито",
- "check_ins": "Перевірки",
- "no_check_ins": "Немає викликів з таймерами перевірки",
- "radio_log": "Радіожурнал",
- "radio": "Радіо",
- "activity": "Активність",
- "actions": "Дії",
- "no_radio_activity": "Немає радіопередач",
- "live": "НАЖИВО",
- "currently_transmitting": "Зараз передає...",
- "duration": "Тривалість",
- "call_actions": "Дії з викликом",
- "unit_actions": "Дії з підрозділом",
- "personnel_actions": {
- "title": "Дії з персоналом",
- "status_tab": "Статус",
- "staffing_tab": "Укомплектованість",
- "select_status": "Вибрати статус",
- "select_staffing": "Вибрати рівень укомплектованості",
- "destination": "Призначення",
- "no_destination": "Без призначення",
- "note": "Нотатка",
- "note_placeholder": "Додайте необов'язкову нотатку...",
- "update_status": "Оновити статус",
- "update_staffing": "Оновити укомплектованість",
- "no_statuses_available": "Статуси недоступні",
- "no_staffings_available": "Рівні укомплектованості недоступні"
- },
- "unit_actions_panel": {
- "status": "Статус",
- "select_status": "Вибрати статус",
- "destination": "Призначення",
- "no_destination": "Без призначення",
- "note": "Нотатка",
- "note_placeholder": "Додайте необов'язкову нотатку...",
- "update_status": "Оновити статус",
- "no_statuses_available": "Статуси недоступні",
- "no_active_calls": "Немає активних викликів",
- "no_stations_available": "Станції недоступні",
- "no_destinations_available": "Призначення недоступні"
- },
- "call": "Виклик",
- "station": "Станція",
- "calls": "Виклики",
- "stations": "Станції",
- "no_stations_available": "Станції недоступні",
- "new_call": "Новий виклик",
- "view_details": "Деталі",
- "add_note": "Додати нотатку",
- "close_call": "Закрити",
- "set_status": "Встановити статус",
- "set_staffing": "Укомплектованість",
- "dispatch": "Диспетчеризувати",
- "select_items_for_actions": "Виберіть виклик, підрозділ або персонал для увімкнення контекстних дій",
- "weather": {
- "clear": "Ясно",
- "mainly_clear": "Переважно ясно",
- "partly_cloudy": "Мінлива хмарність",
- "overcast": "Хмарно",
- "fog": "Туман",
- "drizzle": "Мряка",
- "freezing_drizzle": "Крижана мряка",
- "rain": "Дощ",
- "freezing_rain": "Крижаний дощ",
- "snow": "Сніг",
- "rain_showers": "Зливи",
- "snow_showers": "Снігопади",
- "thunderstorm": "Гроза",
- "thunderstorm_hail": "Гроза з градом",
- "unknown": "Невідомо"
- },
- "available_only": "Лише доступні",
- "single_list": "Єдиний список",
- "resources": "Ресурси",
- "search_resources_placeholder": "Пошук ресурсів...",
- "no_resources": "Немає ресурсів"
- },
"tabs": {
"calls": "Виклики",
"calendar": "Календар",
@@ -1096,44 +1341,6 @@
"shifts": "Зміни",
"personnel": "Персонал"
},
- "check_in": {
- "tab_title": "Перевірка",
- "timer_status": "Статус таймера",
- "perform_check_in": "Відмітитися",
- "check_in_success": "Перевірку успішно зафіксовано",
- "check_in_error": "Не вдалося зафіксувати перевірку",
- "checked_in_by": "від {{name}}",
- "last_check_in": "Остання перевірка",
- "elapsed": "Минуло",
- "duration": "Тривалість",
- "status_ok": "OK",
- "status_green": "OK",
- "status_warning": "Попередження",
- "status_yellow": "Попередження",
- "status_overdue": "Прострочено",
- "status_red": "Прострочено",
- "status_critical": "Критично",
- "history": "Історія перевірок",
- "no_timers": "Таймери перевірки не налаштовано",
- "timers_disabled": "Таймери перевірки вимкнено для цього виклику",
- "type_personnel": "Персонал",
- "type_unit": "Підрозділ",
- "type_ic": "Керівник інциденту",
- "type_par": "PAR",
- "type_hazmat": "Хімічне ураження",
- "type_sector_rotation": "Ротація секторів",
- "type_rehab": "Реабілітація",
- "add_note": "Додати нотатку (необов'язково)",
- "confirm": "Підтвердити перевірку",
- "minutes_ago": "{{count}} хв тому",
- "select_target": "Виберіть об'єкт для перевірки",
- "overdue_count": "{{count}} прострочено",
- "warning_count": "{{count}} попередження",
- "enable_timers": "Увімкнути таймери",
- "disable_timers": "Вимкнути таймери",
- "summary": "{{overdue}} прострочено, {{warning}} попередження, {{ok}} ок",
- "par_title": "Облік персоналу (PAR)"
- },
"units": {
"search": "Пошук підрозділів...",
"loading": "Завантаження підрозділів...",
@@ -1163,6 +1370,63 @@
"no_destination": "Немає",
"title": "Підрозділи"
},
+ "videoFeeds": {
+ "title": "Відеопотоки",
+ "noFeeds": "Немає відеопотоків для цього виклику",
+ "addFeed": "Додати відеопотік",
+ "editFeed": "Редагувати відеопотік",
+ "deleteFeed": "Видалити відеопотік",
+ "deleteConfirm": "Ви впевнені, що хочете видалити цей відеопотік?",
+ "watch": "Дивитися",
+ "goLive": "Почати трансляцію",
+ "stopLive": "Зупинити трансляцію",
+ "flipCamera": "Перемкнути камеру",
+ "feedAdded": "Відеопотік додано",
+ "feedUpdated": "Відеопотік оновлено",
+ "feedDeleted": "Відеопотік видалено",
+ "feedError": "Не вдалося завантажити відеопотік",
+ "unsupportedFormat": "Цей формат потоку не підтримується на мобільному пристрої",
+ "copyUrl": "Копіювати URL",
+ "form": {
+ "name": "Назва потоку",
+ "namePlaceholder": "напр., Дрон Пожежна машина 1",
+ "url": "URL потоку",
+ "urlPlaceholder": "напр., https://stream.example.com/live.m3u8",
+ "feedType": "Тип камери",
+ "feedFormat": "Формат потоку",
+ "description": "Опис",
+ "descriptionPlaceholder": "Необов'язковий опис",
+ "status": "Статус",
+ "sortOrder": "Порядок сортування",
+ "cameraLocation": "Розташування камери",
+ "useCurrentLocation": "Використати поточне місцезнаходження"
+ },
+ "type": {
+ "drone": "Дрон",
+ "fixedCamera": "Стаціонарна камера",
+ "bodyCam": "Натільна камера",
+ "trafficCam": "Камера дорожнього руху",
+ "weatherCam": "Метеокамера",
+ "satelliteFeed": "Супутниковий потік",
+ "webCam": "Вебкамера",
+ "other": "Інше"
+ },
+ "format": {
+ "rtsp": "RTSP",
+ "hls": "HLS",
+ "mjpeg": "MJPEG",
+ "youtubeLive": "YouTube Live",
+ "webrtc": "WebRTC",
+ "dash": "DASH",
+ "embed": "Вбудований",
+ "other": "Інше"
+ },
+ "status": {
+ "active": "Активний",
+ "inactive": "Неактивний",
+ "error": "Помилка"
+ }
+ },
"weatherAlerts": {
"title": "Погодні попередження",
"activeAlerts": "Активні попередження",
@@ -1270,251 +1534,5 @@
},
"stats_label": "Погодні попередження"
},
- "videoFeeds": {
- "title": "Відеопотоки",
- "noFeeds": "Немає відеопотоків для цього виклику",
- "addFeed": "Додати відеопотік",
- "editFeed": "Редагувати відеопотік",
- "deleteFeed": "Видалити відеопотік",
- "deleteConfirm": "Ви впевнені, що хочете видалити цей відеопотік?",
- "watch": "Дивитися",
- "goLive": "Почати трансляцію",
- "stopLive": "Зупинити трансляцію",
- "flipCamera": "Перемкнути камеру",
- "feedAdded": "Відеопотік додано",
- "feedUpdated": "Відеопотік оновлено",
- "feedDeleted": "Відеопотік видалено",
- "feedError": "Не вдалося завантажити відеопотік",
- "unsupportedFormat": "Цей формат потоку не підтримується на мобільному пристрої",
- "copyUrl": "Копіювати URL",
- "form": {
- "name": "Назва потоку",
- "namePlaceholder": "напр., Дрон Пожежна машина 1",
- "url": "URL потоку",
- "urlPlaceholder": "напр., https://stream.example.com/live.m3u8",
- "feedType": "Тип камери",
- "feedFormat": "Формат потоку",
- "description": "Опис",
- "descriptionPlaceholder": "Необов'язковий опис",
- "status": "Статус",
- "sortOrder": "Порядок сортування",
- "cameraLocation": "Розташування камери",
- "useCurrentLocation": "Використати поточне місцезнаходження"
- },
- "type": {
- "drone": "Дрон",
- "fixedCamera": "Стаціонарна камера",
- "bodyCam": "Натільна камера",
- "trafficCam": "Камера дорожнього руху",
- "weatherCam": "Метеокамера",
- "satelliteFeed": "Супутниковий потік",
- "webCam": "Вебкамера",
- "other": "Інше"
- },
- "format": {
- "rtsp": "RTSP",
- "hls": "HLS",
- "mjpeg": "MJPEG",
- "youtubeLive": "YouTube Live",
- "webrtc": "WebRTC",
- "dash": "DASH",
- "embed": "Вбудований",
- "other": "Інше"
- },
- "status": {
- "active": "Активний",
- "inactive": "Неактивний",
- "error": "Помилка"
- }
- },
- "welcome": "Ласкаво просимо до додатку obytes",
- "incident_command": {
- "tab_title": "Командування",
- "title": "Командування інцидентом",
- "open_full_board": "Відкрити повну панель",
- "no_command": "Командування інцидентом не встановлено",
- "no_command_description": "Встановіть командування інцидентом, щоб координувати ресурси, ролі, цілі та облік персоналу для цього виклику.",
- "establish": "Встановити командування",
- "establish_title": "Встановити командування інцидентом",
- "establish_description": "За бажанням створіть панель командування із шаблону.",
- "establish_success": "Командування інцидентом встановлено",
- "establish_error": "Не вдалося встановити командування",
- "template": "Шаблон",
- "no_template": "Без шаблону (порожня панель)",
- "saved": "Збережено",
- "save_error": "Не вдалося виконати операцію",
- "edit_action_plan": "Редагувати план дій",
- "action_plan": "План дій",
- "action_plan_placeholder": "Опишіть план дій під час інциденту...",
- "save": "Зберегти",
- "no_action_plan": "План дій не задано.",
- "add": "Додати",
- "add_objective": "Додати ціль",
- "objective_name": "Ціль",
- "objective_type": "Тип",
- "name_required": "Ім’я є обов’язковим",
- "add_lane": "Додати сектор",
- "lane_name": "Назва сектора",
- "lane_type": "Тип сектора",
- "assign_resource": "Призначити ресурс",
- "assign_resource_required": "Виберіть сектор і ресурс",
- "lane": "Сектор",
- "select_lane": "Виберіть сектор",
- "resource_type": "Тип ресурсу",
- "resource": "Ресурс",
- "select_resource": "Виберіть ресурс",
- "unit": "Підрозділ",
- "personnel": "Персонал",
- "assign": "Призначити",
- "assign_role": "Призначити роль",
- "assign_role_required": "Виберіть особу та роль",
- "person": "Особа",
- "select_person": "Виберіть особу",
- "role": "Роль",
- "select_role": "Виберіть роль",
- "transfer_title": "Передати командування",
- "transfer_notes": "Примітки",
- "transfer": "Передати",
- "transfer_command": "Передати",
- "transfer_success": "Командування передано",
- "close_command": "Завершити командування",
- "confirm_close": "Завершити командування інцидентом для цього виклику?",
- "status": "Стан",
- "active": "Активне",
- "closed": "Завершене",
- "commander": "Керівник",
- "established_on": "Встановлено",
- "edit": "Редагувати",
- "roles": "Ролі командування",
- "no_roles": "Ролі не призначено.",
- "structure": "Структура командування",
- "no_lanes": "Сектори не визначено.",
- "no_resources": "Ресурси не призначено.",
- "release": "Звільнити",
- "objectives": "Цілі",
- "no_objectives": "Цілей немає.",
- "complete": "Завершити",
- "completed": "Завершено",
- "timers": "Таймери",
- "no_timers": "Немає активних таймерів.",
- "acknowledge": "Підтвердити",
- "due": "Термін",
- "accountability": "Облік персоналу (PAR)",
- "run_par": "Провести PAR",
- "green": "Зелений",
- "warning": "Попередження",
- "critical": "Критичний",
- "no_accountability": "Персонал не відстежується.",
- "timeline": "Хронологія командування",
- "no_timeline": "Записів у хронології немає.",
- "unassigned": "Не призначено",
- "active_title": "Активні командування інцидентами",
- "no_active": "Немає активних командувань інцидентами",
- "no_active_description": "Тут відображатимуться командування, встановлені для викликів.",
- "call": "Виклик",
- "tactical_map": "Тактична карта",
- "annotations": "Позначки на карті",
- "no_annotations": "Позначок немає.",
- "open_tactical_map": "Відкрити тактичну карту",
- "marker": "Маркер",
- "add_marker": "Додати маркер",
- "tap_to_place": "Торкніться карти, щоб установити маркер",
- "marker_label": "Підпис маркера",
- "delete_annotation_confirm": "Видалити цю позначку?",
- "voice_channels": "Голосові канали",
- "no_channels": "Немає відкритих каналів.",
- "add_channel": "Додати канал",
- "channel_name": "Назва каналу",
- "close_all_channels": "Закрити всі канали",
- "join": "Приєднатися",
- "hold_to_talk": "Утримуйте, щоб говорити",
- "talking": "Передавання...",
- "voice_joined": "Приєднано до голосового каналу",
- "voice_join_error": "Не вдалося приєднатися до голосового каналу",
- "move": "Перемістити",
- "move_lane": "Перемістити сектор",
- "parent_lane": "Батьківський сектор",
- "top_level": "Верхній рівень",
- "move_resource": "Перемістити ресурс"
- },
- "chat": {
- "title": "Чат",
- "assistant": "Асистент",
- "empty": "Ще немає розмов. Розпочніть пряме повідомлення або створіть групу.",
- "section_assistant": "Асистент",
- "section_direct_messages": "Прямі повідомлення",
- "section_channels": "Канали",
- "section_incidents": "Інциденти",
- "new_direct_message": "Нове пряме повідомлення",
- "new_group": "Нова група",
- "open_assistant": "Відкрити асистента",
- "create_conversation_failed": "Не вдалося розпочати розмову",
- "group_name": "Назва групи",
- "search_people": "Пошук людей",
- "no_people": "Людей не знайдено",
- "create_group_with": "Створити групу ({{count}})",
- "message_deleted": "Це повідомлення видалено",
- "urgent": "Терміново",
- "urgent_will_send": "Це повідомлення буде надіслано як термінове",
- "shared_location": "Надіслане місцезнаходження",
- "thread_replies": "{{count}} відповідей",
- "edited": "(відредаговано)",
- "failed_tap_retry": "Помилка – торкніться, щоб повторити",
- "type_a_message": "Введіть повідомлення",
- "emoji": "Емодзі",
- "add_image": "Додати зображення",
- "add_gif": "Додати GIF",
- "share_location": "Поділитися місцезнаходженням",
- "send": "Надіслати",
- "someone": "Хтось",
- "is_typing": "{{name}} пише...",
- "are_typing": "{{count}} людей пишуть...",
- "permission_photos_denied": "Доступ до фотогалереї відхилено",
- "permission_location_denied": "Доступ до місцезнаходження відхилено",
- "search_gifs": "Пошук GIF",
- "no_gifs": "GIF не знайдено",
- "flag_reason": "Чому ви це повідомляєте?",
- "flag_inappropriate": "Неприйнятне",
- "flag_harassment": "Переслідування",
- "flag_spam": "Спам",
- "flag_sensitive": "Конфіденційна інформація",
- "flag_policy": "Порушення правил",
- "flag_other": "Інше",
- "reply_in_thread": "Відповісти в гілці",
- "copy": "Копіювати",
- "copied": "Скопійовано",
- "copy_unavailable": "Копіювання недоступне на цьому пристрої",
- "edit": "Редагувати",
- "edit_message": "Редагувати повідомлення",
- "save": "Зберегти",
- "delete": "Видалити",
- "pin": "Закріпити",
- "unpin": "Відкріпити",
- "flag": "Поскаржитися",
- "moderator_delete": "Видалити (модератор)",
- "moderator_removed": "Видалено модератором",
- "attachment_failed": "Не вдалося завантажити вкладення",
- "ack_required": "Потрібне підтвердження",
- "ack_pending_one": "У вас є термінове повідомлення для підтвердження",
- "ack_pending_count": "У вас {{count}} термінових повідомлень для підтвердження",
- "acknowledge": "Підтвердити",
- "thread": "Гілка",
- "original_message": "Оригінальне повідомлення",
- "reply_placeholder": "Відповісти...",
- "channel": "Канал",
- "direct_message": "Пряме повідомлення",
- "load_people_failed": "Не вдалося завантажити людей",
- "reaction_failed": "Не вдалося оновити реакцію",
- "edit_failed": "Не вдалося відредагувати повідомлення",
- "delete_failed": "Не вдалося видалити повідомлення",
- "pin_failed": "Не вдалося оновити закріплення",
- "flag_failed": "Не вдалося поскаржитися на повідомлення"
- },
- "chatbot": {
- "title": "Асистент",
- "subtitle": "ШІ-помічник для вашого підрозділу",
- "new_session": "Нова сесія",
- "empty": "Запитайте асистента про будь-що, щоб почати.",
- "ask_placeholder": "Запитайте асистента..."
- }
+ "welcome": "Ласкаво просимо до додатку obytes"
}