From e833b4bc68bed332570f92fd009151c62c9405be Mon Sep 17 00:00:00 2001 From: Simon Caron <8635747+simoncaron@users.noreply.github.com> Date: Wed, 1 Jan 2025 21:31:04 -0500 Subject: [PATCH] Alert and Toasts --- app/(auth)/(tabs)/(home)/downloads/index.tsx | 17 ++--- app/(auth)/(tabs)/(home)/settings.tsx | 2 +- app/(auth)/player/direct-player.tsx | 8 ++- app/(auth)/trailer/page.tsx | 4 +- components/DownloadItem.tsx | 8 ++- components/PlayButton.tsx | 6 +- components/downloads/ActiveDownloads.tsx | 7 +- components/series/JellyseerrSeasons.tsx | 7 +- components/settings/Jellyseerr.tsx | 18 +++--- components/settings/SettingToggles.tsx | 8 +-- hooks/useJellyseerr.ts | 15 +++-- hooks/useRemuxHlsToMp4.ts | 6 +- hooks/useWebsockets.ts | 4 +- providers/DownloadProvider.tsx | 32 +++++----- translations/en.json | 67 +++++++++++++++++++- 15 files changed, 143 insertions(+), 66 deletions(-) diff --git a/app/(auth)/(tabs)/(home)/downloads/index.tsx b/app/(auth)/(tabs)/(home)/downloads/index.tsx index fd197600..b89e7cf6 100644 --- a/app/(auth)/(tabs)/(home)/downloads/index.tsx +++ b/app/(auth)/(tabs)/(home)/downloads/index.tsx @@ -17,6 +17,7 @@ import {BottomSheetBackdrop, BottomSheetBackdropProps, BottomSheetModal, BottomS import {toast} from "sonner-native"; import {writeToLog} from "@/utils/log"; import { useTranslation } from "react-i18next"; +import { t } from 'i18next'; export default function page() { const navigation = useNavigation(); @@ -68,16 +69,16 @@ export default function page() { }, [downloadedFiles]); const deleteMovies = () => deleteFileByType("Movie") - .then(() => toast.success("Deleted all movies successfully!")) + .then(() => toast.success(t("home.downloads.toasts.deleted_all_movies_successfully"))) .catch((reason) => { writeToLog("ERROR", reason); - toast.error("Failed to delete all movies"); + toast.error(t("home.downloads.toasts.failed_to_delete_all_movies")); }); const deleteShows = () => deleteFileByType("Episode") - .then(() => toast.success("Deleted all TV-Series successfully!")) + .then(() => toast.success(t("home.downloads.toasts.deleted_all_tvseries_successfully"))) .catch((reason) => { writeToLog("ERROR", reason); - toast.error("Failed to delete all TV-Series"); + toast.error(t("home.downloads.toasts.failed_to_delete_all_tvseries")); }); const deleteAllMedia = async () => await Promise.all([deleteMovies(), deleteShows()]) @@ -216,15 +217,15 @@ function migration_20241124() { const router = useRouter(); const { deleteAllFiles } = useDownload(); Alert.alert( - "New app version requires re-download", - "The new update reqires content to be downloaded again. Please remove all downloaded content and try again.", + t("home.downloads.new_app_version_requires_re_download"), + t("home.downloads.new_app_version_requires_re_download_description"), [ { - text: "Back", + text: t("home.downloads.back"), onPress: () => router.back(), }, { - text: "Delete", + text: t("home.downloads.delete"), style: "destructive", onPress: async () => await deleteAllFiles(), }, diff --git a/app/(auth)/(tabs)/(home)/settings.tsx b/app/(auth)/(tabs)/(home)/settings.tsx index cbe9871d..6c936c3e 100644 --- a/app/(auth)/(tabs)/(home)/settings.tsx +++ b/app/(auth)/(tabs)/(home)/settings.tsx @@ -75,7 +75,7 @@ export default function settings() { Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); } catch (e) { Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error); - toast.error("Error deleting files"); + toast.error(t("home.settings.toasts.error_deleting_files")); } }; diff --git a/app/(auth)/player/direct-player.tsx b/app/(auth)/player/direct-player.tsx index e4b49320..a20f3d61 100644 --- a/app/(auth)/player/direct-player.tsx +++ b/app/(auth)/player/direct-player.tsx @@ -48,11 +48,13 @@ import { import { useSharedValue } from "react-native-reanimated"; import settings from "../(tabs)/(home)/settings"; import { useSettings } from "@/utils/atoms/settings"; +import { useTranslation } from "react-i18next"; export default function page() { const videoRef = useRef(null); const user = useAtomValue(userAtom); const api = useAtomValue(apiAtom); + const { t } = useTranslation(); const [isPlaybackStopped, setIsPlaybackStopped] = useState(false); const [showControls, _setShowControls] = useState(true); @@ -160,7 +162,7 @@ export default function page() { const { mediaSource, sessionId, url } = res; if (!sessionId || !mediaSource || !url) { - Alert.alert("Error", "Failed to get stream url"); + Alert.alert(t("player.error"), t("player.failed_to_get_stream_url")); return null; } @@ -466,8 +468,8 @@ export default function page() { onVideoError={(e) => { console.error("Video Error:", e.nativeEvent); Alert.alert( - "Error", - "An error occurred while playing the video. Check logs in settings." + t("player.error"), + t("player.an_error_occured_while_playing_the_video") ); writeToLog("ERROR", "Video Error", e.nativeEvent); }} diff --git a/app/(auth)/trailer/page.tsx b/app/(auth)/trailer/page.tsx index c2a84809..c172c8d0 100644 --- a/app/(auth)/trailer/page.tsx +++ b/app/(auth)/trailer/page.tsx @@ -3,10 +3,12 @@ import { useState, useCallback, useEffect, useMemo } from "react"; import { Button, Dimensions } from "react-native"; import { Alert, View } from "react-native"; import YoutubePlayer, { PLAYER_STATES } from "react-native-youtube-iframe"; +import { useTranslation } from "react-i18next"; export default function page() { const searchParams = useGlobalSearchParams(); const navigation = useNavigation(); + const { t } = useTranslation(); console.log(searchParams); const { url } = searchParams as { url: string }; @@ -20,7 +22,7 @@ export default function page() { const onStateChange = useCallback((state: PLAYER_STATES) => { if (state === "ended") { setPlaying(false); - Alert.alert("video has finished playing!"); + Alert.alert(t("player.video_has_finished_playing")); } }, []); diff --git a/components/DownloadItem.tsx b/components/DownloadItem.tsx index 4618bb4f..737ff6cc 100644 --- a/components/DownloadItem.tsx +++ b/components/DownloadItem.tsx @@ -32,6 +32,7 @@ import { MediaSourceSelector } from "./MediaSourceSelector"; import ProgressCircle from "./ProgressCircle"; import { RoundButton } from "./RoundButton"; import { SubtitleTrackSelector } from "./SubtitleTrackSelector"; +import { useTranslation } from "react-i18next"; interface DownloadProps extends ViewProps { items: BaseItemDto[]; @@ -55,6 +56,7 @@ export const DownloadItems: React.FC = ({ const [user] = useAtom(userAtom); const [queue, setQueue] = useAtom(queueAtom); const [settings] = useSettings(); + const { t } = useTranslation(); const { processes, startBackgroundDownload, downloadedFiles } = useDownload(); const { startRemuxing } = useRemuxHlsToMp4(); @@ -160,7 +162,7 @@ export const DownloadItems: React.FC = ({ ); } } else { - toast.error("You are not allowed to download files."); + toast.error(t("home.downloads.toasts.you_are_not_allowed_to_download_files")); } }, [ queue, @@ -212,8 +214,8 @@ export const DownloadItems: React.FC = ({ if (!res) { Alert.alert( - "Something went wrong", - "Could not get stream url from Jellyfin" + t("home.downloads.something_went_wrong"), + t("home.downloads.could_not_get_stream_url_from_jellyfin") ); continue; } diff --git a/components/PlayButton.tsx b/components/PlayButton.tsx index e5c5dd87..5a6b6dbf 100644 --- a/components/PlayButton.tsx +++ b/components/PlayButton.tsx @@ -33,6 +33,7 @@ import { Button } from "./Button"; import { SelectedOptions } from "./ItemContent"; import { chromecastProfile } from "@/utils/profiles/chromecast"; import * as Haptics from "expo-haptics"; +import { useTranslation } from "react-i18next"; interface Props extends React.ComponentProps { item: BaseItemDto; @@ -50,6 +51,7 @@ export const PlayButton: React.FC = ({ const { showActionSheetWithOptions } = useActionSheet(); const client = useRemoteMediaClient(); const mediaStatus = useMediaStatus(); + const { t } = useTranslation(); const [colorAtom] = useAtom(itemThemeColorAtom); const api = useAtomValue(apiAtom); @@ -131,8 +133,8 @@ export const PlayButton: React.FC = ({ if (!data?.url) { console.warn("No URL returned from getStreamUrl", data); Alert.alert( - "Client error", - "Could not create stream for Chromecast" + t("player.client_error"), + t("player.could_not_create_stream_for_chromecast") ); return; } diff --git a/components/downloads/ActiveDownloads.tsx b/components/downloads/ActiveDownloads.tsx index dc397a10..54e8ef59 100644 --- a/components/downloads/ActiveDownloads.tsx +++ b/components/downloads/ActiveDownloads.tsx @@ -22,13 +22,12 @@ import { Button } from "../Button"; import { Image } from "expo-image"; import { useMemo } from "react"; import { storage } from "@/utils/mmkv"; -import { useTranslation } from "react-i18next"; +import { t } from "i18next"; interface Props extends ViewProps {} export const ActiveDownloads: React.FC = ({ ...props }) => { const { processes } = useDownload(); - const { t } = useTranslation(); if (processes?.length === 0) return ( @@ -84,11 +83,11 @@ const DownloadCard = ({ process, ...props }: DownloadCardProps) => { } }, onSuccess: () => { - toast.success("Download canceled"); + toast.success(t("home.downloads.toasts.download_cancelled")); }, onError: (e) => { console.error(e); - toast.error("Could not cancel download"); + toast.error(t("home.downloads.toasts.could_not_cancel_download")); }, }); diff --git a/components/series/JellyseerrSeasons.tsx b/components/series/JellyseerrSeasons.tsx index f0efa418..52e80ccf 100644 --- a/components/series/JellyseerrSeasons.tsx +++ b/components/series/JellyseerrSeasons.tsx @@ -20,6 +20,7 @@ import { HorizontalScroll } from "@/components/common/HorrizontalScroll"; import { Image } from "expo-image"; import MediaRequest from "@/utils/jellyseerr/server/entity/MediaRequest"; import { Loader } from "../Loader"; +import { t } from "i18next"; const JellyseerrSeasonEpisodes: React.FC<{ details: TvDetails; @@ -155,13 +156,13 @@ const JellyseerrSeasons: React.FC<{ const promptRequestAll = useCallback( () => - Alert.alert("Confirm", "Are you sure you want to request all seasons?", [ + Alert.alert(t("jellyseerr.confirm"), t("jellyseerr.are_you_sure_you_want_to_request_all_seasons"), [ { - text: "Cancel", + text: t("jellyseerr.cancel"), style: "cancel", }, { - text: "Yes", + text: t("jellyseerr.yes"), onPress: requestAll, }, ]), diff --git a/components/settings/Jellyseerr.tsx b/components/settings/Jellyseerr.tsx index f55dcb71..9543a664 100644 --- a/components/settings/Jellyseerr.tsx +++ b/components/settings/Jellyseerr.tsx @@ -50,7 +50,7 @@ export const JellyseerrSettings = () => { updateSettings({ jellyseerrServerUrl }); }, onError: () => { - toast.error("Failed to login"); + toast.error(t("jellyseerr.failed_to_login")); }, onSettled: () => { setJellyseerrPassword(undefined); @@ -124,16 +124,16 @@ export const JellyseerrSettings = () => { ) : ( - {t("home.settings.jellyseer.jellyseer_warning")} + {t("home.settings.jellyseerr.jellyseerr_warning")} - {t("home.settings.jellyseer.server_url")} + {t("home.settings.jellyseerr.server_url")} - {t("home.settings.jellyseer.server_url_hint")} + {t("home.settings.jellyseerr.server_url_hint")} { marginBottom: 8, }} > - {promptForJellyseerrPass ? t("home.settings.jellyseer.clear_button") : t("home.settings.jellyseer.save_button")} + {promptForJellyseerrPass ? t("home.settings.jellyseerr.clear_button") : t("home.settings.jellyseerr.save_button")} { opacity: promptForJellyseerrPass ? 1 : 0.5, }} > - {t("home.settings.jellyseer.password")} + {t("home.settings.jellyseerr.password")} { className="h-12 mt-2" onPress={() => loginToJellyseerrMutation.mutate()} > - {t("home.settings.jellyseer.login_button")} + {t("home.settings.jellyseerr.login_button")} diff --git a/components/settings/SettingToggles.tsx b/components/settings/SettingToggles.tsx index d32b3b24..02e2e799 100644 --- a/components/settings/SettingToggles.tsx +++ b/components/settings/SettingToggles.tsx @@ -77,10 +77,10 @@ export const SettingToggles: React.FC = ({ ...props }) => { if (settings?.autoDownload === true && !registered) { registerBackgroundFetchAsync(); - toast.success("Background downloads enabled"); + toast.success(t("home.settings.toasts.background_downloads_enabled")); } else if (settings?.autoDownload === false && registered) { unregisterBackgroundFetchAsync(); - toast.info("Background downloads disabled"); + toast.info(t("home.settings.toasts.background_downloads_disabled")); } else if (settings?.autoDownload === true && registered) { // Don't to anything } else if (settings?.autoDownload === false && !registered) { @@ -654,8 +654,8 @@ export const SettingToggles: React.FC = ({ ...props }) => { deviceId: await getOrSetDeviceId(), }); if (res) { - toast.success("Connected"); - } else toast.error("Could not connect"); + toast.success(t("home.settings.toasts.connected")); + } else toast.error(t("home.settings.toasts.could_not_connect")); }} > {t("home.settings.downloads.save_button")} diff --git a/hooks/useJellyseerr.ts b/hooks/useJellyseerr.ts index c0a8af22..95b00b12 100644 --- a/hooks/useJellyseerr.ts +++ b/hooks/useJellyseerr.ts @@ -28,6 +28,7 @@ import Issue from "@/utils/jellyseerr/server/entity/Issue"; import { RTRating } from "@/utils/jellyseerr/server/api/rating/rottentomatoes"; import { writeErrorLog } from "@/utils/log"; import DiscoverSlider from "@/utils/jellyseerr/server/entity/DiscoverSlider"; +import { t } from "i18next"; interface SearchParams { query: string; @@ -113,7 +114,7 @@ export class JellyseerrApi { if (inRange(status, 200, 299)) { if (data.version < "2.0.0") { const error = - "Jellyseerr server does not meet minimum version requirements! Please update to at least 2.0.0"; + t("jellyseerr.toasts.jellyseer_does_not_meet_requirements"); toast.error(error); throw Error(error); } @@ -127,7 +128,7 @@ export class JellyseerrApi { requiresPass: true, }; } - toast.error(`Jellyseerr test failed. Please try again.`); + toast.error(t("jellyseerr.toasts.jellyseerr_test_failed")); writeErrorLog( `Jellyseerr returned a ${status} for url:\n` + response.config.url + @@ -140,7 +141,7 @@ export class JellyseerrApi { }; }) .catch((e) => { - const msg = "Failed to test jellyseerr server url"; + const msg = t("jellyseerr.toasts.failed_to_test_jellyseerr_server_url"); toast.error(msg); console.error(msg, e); return { @@ -259,7 +260,7 @@ export class JellyseerrApi { const issue = response.data; if (issue.status === IssueStatus.OPEN) { - toast.success("Issue submitted!"); + toast.success(t("jellyseerr.toasts.issue_submitted")); } return issue; }); @@ -342,13 +343,13 @@ export const useJellyseerr = () => { switch (mediaRequest.status) { case MediaRequestStatus.PENDING: case MediaRequestStatus.APPROVED: - toast.success(`Requested ${title}!`); + toast.success(t("jellyseerr.toasts.requested_item", {item: title})); break; case MediaRequestStatus.DECLINED: - toast.error(`You don't have permission to request!`); + toast.error(t("jellyseerr.toasts.you_dont_have_permission_to_request")); break; case MediaRequestStatus.FAILED: - toast.error(`Something went wrong requesting media!`); + toast.error(t("jellyseerr.toasts.something_went_wrong_requesting_media")); break; } }); diff --git a/hooks/useRemuxHlsToMp4.ts b/hooks/useRemuxHlsToMp4.ts index 25492e33..e3990965 100644 --- a/hooks/useRemuxHlsToMp4.ts +++ b/hooks/useRemuxHlsToMp4.ts @@ -18,6 +18,7 @@ import useDownloadHelper from "@/utils/download"; import { Api } from "@jellyfin/sdk"; import { useSettings } from "@/utils/atoms/settings"; import { JobStatus } from "@/utils/optimize-server"; +import { useTranslation } from "react-i18next"; const createFFmpegCommand = (url: string, output: string) => [ "-y", // overwrite output files without asking @@ -49,6 +50,7 @@ export const useRemuxHlsToMp4 = () => { const api = useAtomValue(apiAtom); const router = useRouter(); const queryClient = useQueryClient(); + const { t } = useTranslation(); const [settings] = useSettings(); const { saveImage } = useImageStorage(); @@ -84,7 +86,7 @@ export const useRemuxHlsToMp4 = () => { queryKey: ["downloadedItems"], }); saveDownloadedItemInfo(item, stat.getSize()); - toast.success("Download completed"); + toast.success(t("home.downloads.toasts.download_completed")); } setProcesses((prev) => { @@ -144,7 +146,7 @@ export const useRemuxHlsToMp4 = () => { // First lets save any important assets we want to present to the user offline await onSaveAssets(api, item); - toast.success(`Download started for ${item.Name}`, { + toast.success(t("home.downloads.toasts.download_started_for", {item: item.Name}), { action: { label: "Go to download", onClick: () => { diff --git a/hooks/useWebsockets.ts b/hooks/useWebsockets.ts index 75199b31..d9e6096a 100644 --- a/hooks/useWebsockets.ts +++ b/hooks/useWebsockets.ts @@ -2,6 +2,7 @@ import { useEffect } from "react"; import { Alert } from "react-native"; import { useRouter } from "expo-router"; import { useWebSocketContext } from "@/providers/WebSocketProvider"; +import { useTranslation } from "react-i18next"; interface UseWebSocketProps { isPlaying: boolean; @@ -18,6 +19,7 @@ export const useWebSocket = ({ }: UseWebSocketProps) => { const router = useRouter(); const { ws } = useWebSocketContext(); + const { t } = useTranslation(); useEffect(() => { if (!ws) return; @@ -40,7 +42,7 @@ export const useWebSocket = ({ console.log("Command ~ DisplayMessage"); const title = json?.Data?.Arguments?.Header; const body = json?.Data?.Arguments?.Text; - Alert.alert("Message from server: " + title, body); + Alert.alert(t("player.message_from_server", {message: title}), body); } }; diff --git a/providers/DownloadProvider.tsx b/providers/DownloadProvider.tsx index 706f96f5..eca1c1fb 100644 --- a/providers/DownloadProvider.tsx +++ b/providers/DownloadProvider.tsx @@ -50,6 +50,7 @@ import useDownloadHelper from "@/utils/download"; import { FileInfo } from "expo-file-system"; import * as Haptics from "expo-haptics"; import * as Application from "expo-application"; +import { useTranslation } from "react-i18next"; export type DownloadedItem = { item: Partial; @@ -68,6 +69,7 @@ const DownloadContext = createContext { router.push("/downloads"); toast.dismiss(); @@ -222,9 +224,9 @@ function useDownloadProvider() { }, }); - toast.info(`Download started for ${process.item.Name}`, { + toast.info(t("home.downloads.toasts.download_stated_for_item", {item: process.item.Name}), { action: { - label: "Go to downloads", + label: t("home.downloads.toasts.go_to_downloads"), onClick: () => { router.push("/downloads"); toast.dismiss(); @@ -273,10 +275,10 @@ function useDownloadProvider() { process.item, doneHandler.bytesDownloaded ); - toast.success(`Download completed for ${process.item.Name}`, { + toast.success(t("home.downloads.toasts.download_completed_for_item", {item: process.item.Name}), { duration: 3000, action: { - label: "Go to downloads", + label: t("home.downloads.toasts.go_to_downloads"), onClick: () => { router.push("/downloads"); toast.dismiss(); @@ -298,7 +300,7 @@ function useDownloadProvider() { if (error.errorCode === 404) { errorMsg = "File not found on server"; } - toast.error(`Download failed for ${process.item.Name} - ${errorMsg}`); + toast.error(t("home.downloads.toasts.download_failed_for_item", {item: process.item.Name, error: errorMsg})); writeToLog("ERROR", `Download failed for ${process.item.Name}`, { error, processDetails: { @@ -355,9 +357,9 @@ function useDownloadProvider() { throw new Error("Failed to start optimization job"); } - toast.success(`Queued ${item.Name} for optimization`, { + toast.success(t("home.downloads.toasts.queued_item_for_optimization", {item: item.Name}), { action: { - label: "Go to download", + label: t("home.downloads.toasts.go_to_downloads"), onClick: () => { router.push("/downloads"); toast.dismiss(); @@ -375,21 +377,21 @@ function useDownloadProvider() { headers: error.response?.headers, }); toast.error( - `Failed to start download for ${item.Name}: ${error.message}` + t("home.downloads.toasts.failed_to_start_download_for_item", {item: item.Name, message: error.message}) ); if (error.response) { toast.error( - `Server responded with status ${error.response.status}` + t("home.downloads.toasts.server_responded_with_status", {statusCode: error.response.status}) ); } else if (error.request) { - toast.error("No response received from server"); + t("home.downloads.toasts.no_response_received_from_server"); } else { toast.error("Error setting up the request"); } } else { console.error("Non-Axios error:", error); toast.error( - `Failed to start download for ${item.Name}: Unexpected error` + t("home.downloads.toasts.failed_to_start_download_for_item_unexpected_error", {item: item.Name}) ); } } @@ -405,11 +407,11 @@ function useDownloadProvider() { queryClient.invalidateQueries({ queryKey: ["downloadedItems"] }), ]) .then(() => - toast.success("All files, folders, and jobs deleted successfully") + toast.success(t("home.downloads.toasts.all_files_folders_jobs_deleted")) ) .catch((reason) => { console.error("Failed to delete all files, folders, and jobs:", reason); - toast.error("An error occurred while deleting files and jobs"); + toast.error(t("home.downloads.toasts.an_error_occured_while_deleting_files_and_jobs")); }); }; diff --git a/translations/en.json b/translations/en.json index 4aac97be..17090a71 100644 --- a/translations/en.json +++ b/translations/en.json @@ -107,8 +107,8 @@ "optimized_versions_server_hint": "Set the URL for the optimized versions server for downloads.", "save_button": "Save" }, - "jellyseer": { - "jellyseer_warning": "This integration is in its early stages. Expect things to change.", + "jellyseerr": { + "jellyseerr_warning": "This integration is in its early stages. Expect things to change.", "server_url": "Server URL", "server_url_hint": "Example: http(s)://your-host.url\n(add port if required)", "server_url_placeholder": "Jellyseerr URL...", @@ -134,6 +134,13 @@ "app_language": "App language", "app_language_description": "Select the language for the app.", "system": "System" + }, + "toasts":{ + "error_deleting_files": "Error deleting files", + "background_downloads_enabled": "Background downloads enabled", + "background_downloads_disabled": "Background downloads disabled", + "connected": "Connected", + "could_not_connect": "Could not connect" } }, "downloads": { @@ -150,7 +157,36 @@ "active_download": "Active download", "no_active_downloads": "No active downloads", "active_downloads": "Active downloads", - "toasts": {} + "new_app_version_requires_re_download": "New app version requires re-download", + "new_app_version_requires_re_download_description": "The new update reqires content to be downloaded again. Please remove all downloaded content and try again.", + "back": "Back", + "delete": "Delete", + "something_went_wrong": "Something went wrong", + "could_not_get_stream_url_from_jellyfin": "Could not get stream URL from Jellyfin", + "toasts": { + "you_are_not_allowed_to_download_files": "You are not allowed to download files.", + "deleted_all_movies_successfully": "Deleted all movies successfully!", + "failed_to_delete_all_movies": "Failed to delete all movies", + "deleted_all_tvseries_successfully": "Deleted all TV-Series successfully!", + "failed_to_delete_all_tvseries": "Failed to delete all TV-Series", + "download_cancelled": "Download cancelled", + "could_not_cancel_download": "Could not cancel download", + "download_completed": "Download completed", + "download_started_for": "Download started for {{item}}", + "item_is_ready_to_be_downloaded": "{{item}} is ready to be downloaded", + "download_stated_for_item": "Download started for {{item}}", + "download_failed_for_item": "Download failed for {{item}} - {{error}}", + "download_completed_for_item": "Download completed for {{item}}", + "queued_item_for_optimization": "Queued {{item}} for optimization", + "failed_to_start_download_for_item": "Failed to start download for {{item}}: {{message}}", + "server_responded_with_status_code": "Server responded with status {{statusCode}}", + "no_response_received_from_server": "No response received from server", + "error_setting_up_the_request": "Error setting up the request", + "failed_to_start_download_for_item_unexpected_error": "Failed to start download for {{item}}: Unexpected error", + "all_files_folders_and_jobs_deleted_successfully": "All files, folders and jobs deleted successfully", + "an_error_occured_while_deleting_files_and_jobs": "An error occurred while deleting files and jobs", + "go_to_downloads": "Go to downloads" + } } }, "search": { @@ -164,6 +200,31 @@ "no_results": "No results", "no_libraries_found": "No libraries found" }, + "player": { + "error": "Error", + "failed_to_get_stream_url": "Failed to get stream URL", + "an_error_occured_while_playing_the_video": "An error occurred while playing the video. Check logs in settings.", + "client_error": "Client error", + "could_not_create_stream_for_chromecast": "Could not create stream for Chromecast", + "message_from_server": "Message from server: {{message}}", + "video_has_finished_playing": "Video has finished playing!" + }, + "jellyseerr":{ + "confirm": "Confirm", + "cancel": "Cancel", + "yes": "Yes", + "are_you_sure_you_want_to_request_all_seasons": "Are you sure you want to request all seasons?", + "failed_to_login": "Failed to login", + "toasts": { + "jellyseer_does_not_meet_requirements": "Jellyseerr server does not meet minimum version requirements! Please update to at least 2.0.0", + "jellyseerr_test_failed": "Jellyseerr test failed. Please try again.", + "failed_to_test_jellyseerr_server_url": "Failed to test jellyseerr server url", + "issue_submitted": "Issue submitted!", + "requested_item": "Requested {{item}}!", + "you_dont_have_permission_to_request": "You don't have permission to request!", + "something_went_wrong_requesting_media": "Something went wrong requesting media!" + } + }, "tabs": { "home": "Home", "search": "Search",