From 53ea1cc899f7d0b9e65c0fc8fb1cfd642fb9e7ee Mon Sep 17 00:00:00 2001 From: Simon Caron <8635747+simoncaron@users.noreply.github.com> Date: Sat, 4 Jan 2025 16:41:54 -0500 Subject: [PATCH] More Translations --- app/(auth)/(tabs)/(custom-links)/index.tsx | 4 +- .../(home)/settings/jellyseerr/page.tsx | 11 +-- .../(home)/settings/optimized-server/page.tsx | 15 +++-- .../actors/[actorId].tsx | 4 +- .../albums/[albumId].tsx | 3 +- .../artists/[artistId].tsx | 3 +- .../artists/index.tsx | 3 +- .../collections/[collectionId].tsx | 2 +- .../items/page.tsx | 4 +- .../jellyseerr/page.tsx | 18 ++--- app/(auth)/(tabs)/(search)/index.tsx | 6 +- app/(auth)/player/direct-player.tsx | 2 +- app/(auth)/player/music-player.tsx | 6 +- app/(auth)/player/transcoding-player.tsx | 6 +- components/MediaSourceSelector.tsx | 5 +- .../common/InfiniteHorrizontalScroll.tsx | 3 +- components/downloads/ActiveDownloads.tsx | 2 +- components/filters/FilterSheet.tsx | 2 +- components/home/ScrollingCollectionList.tsx | 5 +- components/series/SeasonPicker.tsx | 2 +- components/settings/DownloadSettings.tsx | 4 +- components/settings/OptimizedServerForm.tsx | 12 ++-- components/settings/QuickConnect.tsx | 2 +- .../controls/NextEpisodeCountDownButton.tsx | 5 +- components/vlc/VideoDebugInfo.tsx | 15 +++-- translations/en.json | 51 ++++++++++++-- translations/fr.json | 67 +++++++++++++++---- 27 files changed, 189 insertions(+), 73 deletions(-) diff --git a/app/(auth)/(tabs)/(custom-links)/index.tsx b/app/(auth)/(tabs)/(custom-links)/index.tsx index bf6dc46b..b7607569 100644 --- a/app/(auth)/(tabs)/(custom-links)/index.tsx +++ b/app/(auth)/(tabs)/(custom-links)/index.tsx @@ -7,6 +7,7 @@ import { ListItem } from "@/components/list/ListItem"; import * as WebBrowser from "expo-web-browser"; import Ionicons from "@expo/vector-icons/Ionicons"; import { Text } from "@/components/common/Text"; +import { useTranslation } from "react-i18next"; export interface MenuLink { name: string; @@ -18,6 +19,7 @@ export default function menuLinks() { const [api] = useAtom(apiAtom); const insets = useSafeAreaInsets(); const [menuLinks, setMenuLinks] = useState([]); + const { t } = useTranslation(); const getMenuLinks = useCallback(async () => { try { @@ -67,7 +69,7 @@ export default function menuLinks() { )} ListEmptyComponent={ - No links + {t("custom_links.no_links")} } /> diff --git a/app/(auth)/(tabs)/(home)/settings/jellyseerr/page.tsx b/app/(auth)/(tabs)/(home)/settings/jellyseerr/page.tsx index af4247d5..eaa5e8ae 100644 --- a/app/(auth)/(tabs)/(home)/settings/jellyseerr/page.tsx +++ b/app/(auth)/(tabs)/(home)/settings/jellyseerr/page.tsx @@ -11,10 +11,13 @@ import { useAtom } from "jotai"; import { useEffect, useState } from "react"; import { ActivityIndicator, TouchableOpacity, View } from "react-native"; import { toast } from "sonner-native"; +import { useTranslation } from "react-i18next"; export default function page() { const navigation = useNavigation(); + const { t } = useTranslation(); + const [api] = useAtom(apiAtom); const [settings, updateSettings] = useSettings(); @@ -24,7 +27,7 @@ export default function page() { const saveMutation = useMutation({ mutationFn: async (newVal: string) => { if (newVal.length === 0 || !newVal.startsWith("http")) { - toast.error("Invalid URL"); + toast.error(t("home.settings.toasts.invalid_url")); return; } @@ -42,13 +45,13 @@ export default function page() { }, onSuccess: (data) => { if (data) { - toast.success("Connected"); + toast.success(t("home.settings.toasts.connected")); } else { - toast.error("Could not connect"); + toast.error(t("home.settings.toasts.could_not_connect")); } }, onError: () => { - toast.error("Could not connect"); + toast.error(t("home.settings.toasts.could_not_connect")); }, }); diff --git a/app/(auth)/(tabs)/(home)/settings/optimized-server/page.tsx b/app/(auth)/(tabs)/(home)/settings/optimized-server/page.tsx index b47d565f..425efc66 100644 --- a/app/(auth)/(tabs)/(home)/settings/optimized-server/page.tsx +++ b/app/(auth)/(tabs)/(home)/settings/optimized-server/page.tsx @@ -10,10 +10,13 @@ import { useAtom } from "jotai"; import { useEffect, useState } from "react"; import { ActivityIndicator, TouchableOpacity, View } from "react-native"; import { toast } from "sonner-native"; +import { useTranslation } from "react-i18next"; export default function page() { const navigation = useNavigation(); + const { t } = useTranslation(); + const [api] = useAtom(apiAtom); const [settings, updateSettings] = useSettings(); @@ -23,7 +26,7 @@ export default function page() { const saveMutation = useMutation({ mutationFn: async (newVal: string) => { if (newVal.length === 0 || !newVal.startsWith("http")) { - toast.error("Invalid URL"); + toast.error(t("home.settings.toasts.invalid_url")); return; } @@ -41,13 +44,13 @@ export default function page() { }, onSuccess: (data) => { if (data) { - toast.success("Connected"); + toast.success(t("home.settings.toasts.connected")); } else { - toast.error("Could not connect"); + toast.error(t("home.settings.toasts.could_not_connect")); } }, onError: () => { - toast.error("Could not connect"); + toast.error(t("home.settings.toasts.could_not_connect")); }, }); @@ -57,13 +60,13 @@ export default function page() { useEffect(() => { navigation.setOptions({ - title: "Optimized Server", + title: t("home.settings.downloads.optimized_server"), headerRight: () => saveMutation.isPending ? ( ) : ( onSave(optimizedVersionsServerUrl)}> - Save + {t("home.settings.downloads.save_button")} ), }); diff --git a/app/(auth)/(tabs)/(home,libraries,search,favorites)/actors/[actorId].tsx b/app/(auth)/(tabs)/(home,libraries,search,favorites)/actors/[actorId].tsx index 45dc8a4d..d2c15c3d 100644 --- a/app/(auth)/(tabs)/(home,libraries,search,favorites)/actors/[actorId].tsx +++ b/app/(auth)/(tabs)/(home,libraries,search,favorites)/actors/[actorId].tsx @@ -18,10 +18,12 @@ import { useLocalSearchParams } from "expo-router"; import { useAtom } from "jotai"; import { useCallback, useMemo } from "react"; import { View } from "react-native"; +import { useTranslation } from "react-i18next"; const page: React.FC = () => { const local = useLocalSearchParams(); const { actorId } = local as { actorId: string }; + const { t } = useTranslation(); const [api] = useAtom(apiAtom); const [user] = useAtom(userAtom); @@ -110,7 +112,7 @@ const page: React.FC = () => { - Appeared In + {t("item_card.appeared_in")} {album?.Name} - {songs?.TotalRecordCount} songs + {t("item_card.x_songs", { count: songs?.TotalRecordCount })} diff --git a/app/(auth)/(tabs)/(home,libraries,search,favorites)/artists/[artistId].tsx b/app/(auth)/(tabs)/(home,libraries,search,favorites)/artists/[artistId].tsx index 8d82d205..aea5af16 100644 --- a/app/(auth)/(tabs)/(home,libraries,search,favorites)/artists/[artistId].tsx +++ b/app/(auth)/(tabs)/(home,libraries,search,favorites)/artists/[artistId].tsx @@ -5,6 +5,7 @@ import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models"; import { getItemsApi } from "@jellyfin/sdk/lib/utils/api"; import { useQuery } from "@tanstack/react-query"; import { router, useLocalSearchParams, useNavigation } from "expo-router"; +import { t } from "i18next"; import { useAtom } from "jotai"; import { useEffect, useState } from "react"; import { FlatList, ScrollView, TouchableOpacity, View } from "react-native"; @@ -107,7 +108,7 @@ export default function page() { {artist?.Name} - {albums.TotalRecordCount} albums + {t("item_card.x_albums", { count: albums.TotalRecordCount })} diff --git a/app/(auth)/(tabs)/(home,libraries,search,favorites)/artists/index.tsx b/app/(auth)/(tabs)/(home,libraries,search,favorites)/artists/index.tsx index 4827287e..55471ed5 100644 --- a/app/(auth)/(tabs)/(home,libraries,search,favorites)/artists/index.tsx +++ b/app/(auth)/(tabs)/(home,libraries,search,favorites)/artists/index.tsx @@ -7,6 +7,7 @@ import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models"; import { getArtistsApi, getItemsApi } from "@jellyfin/sdk/lib/utils/api"; import { useQuery } from "@tanstack/react-query"; import { router, useLocalSearchParams } from "expo-router"; +import { t } from "i18next"; import { useAtom } from "jotai"; import { useMemo, useState } from "react"; import { FlatList, TouchableOpacity, View } from "react-native"; @@ -81,7 +82,7 @@ export default function page() { }} ListHeaderComponent={ - Artists + {t("item_card.artists")} } nestedScrollEnabled diff --git a/app/(auth)/(tabs)/(home,libraries,search,favorites)/collections/[collectionId].tsx b/app/(auth)/(tabs)/(home,libraries,search,favorites)/collections/[collectionId].tsx index cee529bc..c751c1a1 100644 --- a/app/(auth)/(tabs)/(home,libraries,search,favorites)/collections/[collectionId].tsx +++ b/app/(auth)/(tabs)/(home,libraries,search,favorites)/collections/[collectionId].tsx @@ -377,7 +377,7 @@ const page: React.FC = () => { - No results + {t("search.no_results")} } extraData={[ diff --git a/app/(auth)/(tabs)/(home,libraries,search,favorites)/items/page.tsx b/app/(auth)/(tabs)/(home,libraries,search,favorites)/items/page.tsx index 38b0115d..a61114bd 100644 --- a/app/(auth)/(tabs)/(home,libraries,search,favorites)/items/page.tsx +++ b/app/(auth)/(tabs)/(home,libraries,search,favorites)/items/page.tsx @@ -13,11 +13,13 @@ import Animated, { useSharedValue, withTiming, } from "react-native-reanimated"; +import { useTranslation } from "react-i18next"; const Page: React.FC = () => { const [api] = useAtom(apiAtom); const [user] = useAtom(userAtom); const { id } = useLocalSearchParams() as { id: string }; + const { t } = useTranslation(); const { data: item, isError } = useQuery({ queryKey: ["item", id], @@ -74,7 +76,7 @@ const Page: React.FC = () => { if (isError) return ( - Could not load item + {t("item_card.could_not_load_item")} ); diff --git a/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/page.tsx b/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/page.tsx index 42edcb59..92a48c9c 100644 --- a/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/page.tsx +++ b/app/(auth)/(tabs)/(home,libraries,search,favorites)/jellyseerr/page.tsx @@ -27,10 +27,12 @@ import * as DropdownMenu from "zeego/dropdown-menu"; import { TvDetails } from "@/utils/jellyseerr/server/models/Tv"; import JellyseerrSeasons from "@/components/series/JellyseerrSeasons"; import { JellyserrRatings } from "@/components/Ratings"; +import { useTranslation } from "react-i18next"; const Page: React.FC = () => { const insets = useSafeAreaInsets(); const params = useLocalSearchParams(); + const { t } = useTranslation(); const { mediaTitle, releaseYear, @@ -184,7 +186,7 @@ const Page: React.FC = () => { {canRequest ? ( ) : ( )} @@ -231,7 +233,7 @@ const Page: React.FC = () => { - Whats wrong? + {t("jellyseerr.whats_wrong")} @@ -240,13 +242,13 @@ const Page: React.FC = () => { - Issue Type + {t("jellyseerr.issue_type")} {issueType ? IssueTypeName[issueType] - : "Select an issue"} + : t("jellyseerr.select_an_issue")} @@ -260,7 +262,7 @@ const Page: React.FC = () => { collisionPadding={0} sideOffset={0} > - Types + {t("jellyseerr.types")} {Object.entries(IssueTypeName) .reverse() .map(([key, value], idx) => ( @@ -287,7 +289,7 @@ const Page: React.FC = () => { maxLength={254} style={{color: "white"}} clearButtonMode="always" - placeholder="(optional) Describe the issue..." + placeholder={t("jellyseerr.describe_the_issue")} placeholderTextColor="#9CA3AF" // Issue with multiline + Textinput inside a portal // https://github.com/callstack/react-native-paper/issues/1668 @@ -297,7 +299,7 @@ const Page: React.FC = () => { diff --git a/app/(auth)/(tabs)/(search)/index.tsx b/app/(auth)/(tabs)/(search)/index.tsx index 2d7afff8..614f9fa4 100644 --- a/app/(auth)/(tabs)/(search)/index.tsx +++ b/app/(auth)/(tabs)/(search)/index.tsx @@ -320,7 +320,7 @@ export default function search() { setSearchType("Library")}> setSearchType("Discover")}> - Results for {q} + {t("search.results_for_x")} {q} )} diff --git a/app/(auth)/player/direct-player.tsx b/app/(auth)/player/direct-player.tsx index a689ccb6..606c2d1e 100644 --- a/app/(auth)/player/direct-player.tsx +++ b/app/(auth)/player/direct-player.tsx @@ -423,7 +423,7 @@ export default function page() { if (isErrorItem || isErrorStreamUrl) return ( - Error + {t("player.error")} ); diff --git a/app/(auth)/player/music-player.tsx b/app/(auth)/player/music-player.tsx index eca16b4c..d48b0840 100644 --- a/app/(auth)/player/music-player.tsx +++ b/app/(auth)/player/music-player.tsx @@ -25,6 +25,7 @@ import React, { useCallback, useMemo, useRef, useState } from "react"; import { Pressable, useWindowDimensions, View } from "react-native"; import { useSharedValue } from "react-native-reanimated"; import Video, { OnProgressData, VideoRef } from "react-native-video"; +import { useTranslation } from "react-i18next"; export default function page() { const api = useAtomValue(apiAtom); @@ -32,6 +33,7 @@ export default function page() { const [settings] = useSettings(); const videoRef = useRef(null); const windowDimensions = useWindowDimensions(); + const { t } = useTranslation(); const firstTime = useRef(true); @@ -278,14 +280,14 @@ export default function page() { if (isErrorItem || isErrorStreamUrl) return ( - Error + {t("player.error")} ); if (!item || !stream) return ( - Error + {t("player.error")} ); diff --git a/app/(auth)/player/transcoding-player.tsx b/app/(auth)/player/transcoding-player.tsx index bcb9a6e4..b174cc41 100644 --- a/app/(auth)/player/transcoding-player.tsx +++ b/app/(auth)/player/transcoding-player.tsx @@ -39,12 +39,14 @@ import Video, { VideoRef, } from "react-native-video"; import { SubtitleHelper } from "@/utils/SubtitleHelper"; +import { useTranslation } from "react-i18next"; const Player = () => { const api = useAtomValue(apiAtom); const user = useAtomValue(userAtom); const [settings] = useSettings(); const videoRef = useRef(null); + const { t } = useTranslation(); const firstTime = useRef(true); const revalidateProgressCache = useInvalidatePlaybackProgressCache(); @@ -373,7 +375,7 @@ const Player = () => { if (isErrorItem || isErrorStreamUrl) return ( - Error + {t("player.error")} ); @@ -440,7 +442,7 @@ const Player = () => { /> ) : ( - No video source... + {t("player.no_video_source")} )} diff --git a/components/MediaSourceSelector.tsx b/components/MediaSourceSelector.tsx index 34f02fd9..ec757f23 100644 --- a/components/MediaSourceSelector.tsx +++ b/components/MediaSourceSelector.tsx @@ -8,6 +8,7 @@ import { TouchableOpacity, View } from "react-native"; import * as DropdownMenu from "zeego/dropdown-menu"; import { Text } from "./common/Text"; import { convertBitsToMegabitsOrGigabits } from "@/utils/bToMb"; +import { useTranslation } from "react-i18next"; interface Props extends React.ComponentProps { item: BaseItemDto; @@ -29,6 +30,8 @@ export const MediaSourceSelector: React.FC = ({ [item, selected] ); + const { t } = useTranslation(); + return ( = ({ - Video + {t("item_card.video")} {selectedName} diff --git a/components/common/InfiniteHorrizontalScroll.tsx b/components/common/InfiniteHorrizontalScroll.tsx index e8281d0e..f3c504f1 100644 --- a/components/common/InfiniteHorrizontalScroll.tsx +++ b/components/common/InfiniteHorrizontalScroll.tsx @@ -15,6 +15,7 @@ import Animated, { } from "react-native-reanimated"; import { Loader } from "../Loader"; import { Text } from "./Text"; +import { t } from "i18next"; interface HorizontalScrollProps extends Omit, "renderItem" | "data" | "style"> { @@ -136,7 +137,7 @@ export function InfiniteHorizontalScroll({ showsHorizontalScrollIndicator={false} ListEmptyComponent={ - No data available + {t("item_card.no_data_available")} } {...props} diff --git a/components/downloads/ActiveDownloads.tsx b/components/downloads/ActiveDownloads.tsx index 54e8ef59..3008c5ff 100644 --- a/components/downloads/ActiveDownloads.tsx +++ b/components/downloads/ActiveDownloads.tsx @@ -154,7 +154,7 @@ const DownloadCard = ({ process, ...props }: DownloadCardProps) => { {process.speed?.toFixed(2)}x )} {eta(process) && ( - ETA {eta(process)} + {t("home.downloads.eta", {eta: eta(process)})} )} diff --git a/components/filters/FilterSheet.tsx b/components/filters/FilterSheet.tsx index 25486ce9..cc5d4300 100644 --- a/components/filters/FilterSheet.tsx +++ b/components/filters/FilterSheet.tsx @@ -155,7 +155,7 @@ export const FilterSheet = ({ > {title} - {_data?.length} items + {t("search.items", {count: _data?.length})} {showSearch && ( = ({ if (hideIfEmpty === true && data?.length === 0) return null; + const { t } = useTranslation(); + return ( @@ -50,7 +53,7 @@ export const ScrollingCollectionList: React.FC = ({ {isLoading === false && data?.length === 0 && ( - No items + {t("home.no_items")} )} {isLoading ? ( diff --git a/components/series/SeasonPicker.tsx b/components/series/SeasonPicker.tsx index 927b741a..be625432 100644 --- a/components/series/SeasonPicker.tsx +++ b/components/series/SeasonPicker.tsx @@ -211,7 +211,7 @@ export const SeasonPicker: React.FC = ({ item, initialSeasonIndex }) => { {(episodes?.length || 0) === 0 ? ( - {t("item_card.no_episodes_for_this_seasonz")} + {t("item_card.no_episodes_for_this_season")} ) : null} diff --git a/components/settings/DownloadSettings.tsx b/components/settings/DownloadSettings.tsx index 98c01199..131ec9a6 100644 --- a/components/settings/DownloadSettings.tsx +++ b/components/settings/DownloadSettings.tsx @@ -30,8 +30,8 @@ export const DownloadSettings: React.FC = ({ ...props }) => { {settings.downloadMethod === "remux" - ? "Default" - : "Optimized"} + ? t("home.settings.downloads.default") + : t("home.settings.downloads.optimized")} = ({ Linking.openURL("https://github.com/streamyfin/optimized-versions-server"); }; + const { t } = useTranslation(); + return ( - URL + {t("home.settings.downloads.url")} = ({ - Enter the URL for the optimize server. The URL should include http or - https and optionally the port.{" "} + {t("home.settings.downloads.optimized_version_hint")}{" "} - Read more about the optimize server. + {t("home.settings.downloads.read_more_about_optimized_server")} diff --git a/components/settings/QuickConnect.tsx b/components/settings/QuickConnect.tsx index 5cab8b15..04e0ee5d 100644 --- a/components/settings/QuickConnect.tsx +++ b/components/settings/QuickConnect.tsx @@ -86,7 +86,7 @@ export const QuickConnect: React.FC = ({ ...props }) => { - Quick Connect + {t("home.settings.quick_connect.quick_connect_title")} diff --git a/components/video-player/controls/NextEpisodeCountDownButton.tsx b/components/video-player/controls/NextEpisodeCountDownButton.tsx index 6f5239e6..e77c6198 100644 --- a/components/video-player/controls/NextEpisodeCountDownButton.tsx +++ b/components/video-player/controls/NextEpisodeCountDownButton.tsx @@ -9,6 +9,7 @@ import Animated, { runOnJS, } from "react-native-reanimated"; import { Colors } from "@/constants/Colors"; +import { useTranslation } from "react-i18next"; interface NextEpisodeCountDownButtonProps extends TouchableOpacityProps { onFinish?: () => void; @@ -63,6 +64,8 @@ const NextEpisodeCountDownButton: React.FC = ({ return null; } + const { t } = useTranslation(); + return ( = ({ > - Next Episode + {t("player.next_episode")} ); diff --git a/components/vlc/VideoDebugInfo.tsx b/components/vlc/VideoDebugInfo.tsx index 5ae04517..852dcc2f 100644 --- a/components/vlc/VideoDebugInfo.tsx +++ b/components/vlc/VideoDebugInfo.tsx @@ -6,6 +6,7 @@ import React, { useEffect, useState } from "react"; import { TouchableOpacity, View, ViewProps } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { Text } from "../common/Text"; +import { useTranslation } from "react-i18next"; interface Props extends ViewProps { playerRef: React.RefObject; @@ -32,6 +33,8 @@ export const VideoDebugInfo: React.FC = ({ playerRef, ...props }) => { const insets = useSafeAreaInsets(); + const { t } = useTranslation(); + return ( = ({ playerRef, ...props }) => { }} {...props} > - Playback State: - Audio Tracks: + {t("item_card.playback_state")} + {t("item_card.audio_tracks")} {audioTracks && audioTracks.map((track, index) => ( - {track.name} (Index: {track.index}) + {track.name} ({t("item_card.index")} {track.index}) ))} - Subtitle Tracks: + {t("item_card.subtitles_tracks")} {subtitleTracks && subtitleTracks.map((track, index) => ( - {track.name} (Index: {track.index}) + {track.name} ({t("item_card.index")} {track.index}) ))} = ({ playerRef, ...props }) => { } }} > - Refresh Tracks + {t("item_card.refresh_tracks")} ); diff --git a/translations/en.json b/translations/en.json index f10f05ec..83f4716b 100644 --- a/translations/en.json +++ b/translations/en.json @@ -26,6 +26,7 @@ "home": { "home": "Home", "no_internet": "No Internet", + "no_items": "No items", "no_internet_message": "No worries, you can still watch\ndownloaded content.", "go_to_downloads": "Go to downloads", "oops": "Oops!", @@ -46,7 +47,7 @@ "app_version": "App Version" }, "quick_connect": { - "quick_connect_title": "Quick connect", + "quick_connect_title": "Quick Connect", "authorize_button": "Authorize Quick Connect", "enter_the_quick_connect_code": "Enter the Quick Connect code", "success": "Success", @@ -87,7 +88,15 @@ "download_method": "Download method", "remux_max_download": "Remux max download", "auto_download": "Auto download", - "optimized_versions_server": "Optimized versions server" + "optimized_versions_server": "Optimized versions server", + "save_button": "Save", + "optimized_server": "Optimized Server", + "optimized": "Optimized", + "default": "Default", + "optimized_version_hint": "Enter the URL for the optimize server. The URL should include http or https and optionally the port.", + "read_more_about_optimized_server": "Read more about the optimize server.", + "url":"URL", + "server_url_placeholder": "http(s)://domain.org:port" }, "plugins": { "plugins_title": "Plugins", @@ -149,7 +158,8 @@ "background_downloads_enabled": "Background downloads enabled", "background_downloads_disabled": "Background downloads disabled", "connected": "Connected", - "could_not_connect": "Could not connect" + "could_not_connect": "Could not connect", + "invalid_url": "Invalid URL" } }, "downloads": { @@ -172,6 +182,7 @@ "delete": "Delete", "something_went_wrong": "Something went wrong", "could_not_get_stream_url_from_jellyfin": "Could not get stream URL from Jellyfin", + "eta": "ETA {{eta}}", "toasts": { "you_are_not_allowed_to_download_files": "You are not allowed to download files.", "deleted_all_movies_successfully": "Deleted all movies successfully!", @@ -199,9 +210,14 @@ } }, "search": { + "results_for_x": "Results for ", "search_title": "Search", "search_here": "Search here...", "search": "Search...", + "x_items": "{{count}} items", + "library": "Library", + "discover": "Discover", + "no_results": "No results", "no_results_found_for": "No results found for", "movies": "Movies", "series": "Series", @@ -248,6 +264,9 @@ "music_albums": "Music Albums", "audio": "Audio" }, + "custom_links": { + "no_links": "No links" + }, "player": { "error": "Error", "failed_to_get_stream_url": "Failed to get stream URL", @@ -255,7 +274,9 @@ "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!" + "video_has_finished_playing": "Video has finished playing!", + "no_video_source": "No video source...", + "next_episode": "Next Episode" }, "item_card": { "next_up": "Next up", @@ -264,7 +285,6 @@ "series": "Series", "seasons": "Seasons", "season": "Season", - "episodes": "Episodes", "no_episodes_for_this_season": "No episodes for this season", "overview": "Overview", "more_with": "More with {{name}}", @@ -277,18 +297,37 @@ "subtitles": "Subtitle", "show_more": "Show more", "show_less": "Show less", + "appeared_in": "Appeared in", + "x_songs": "{{count}} songs", + "x_albums": "{{count}} albums", + "artists": "Artists", + "could_not_load_item": "Could not load item", + "refresh_tracks": "Refresh Tracks", + "subtitle_tracks": "Subtitle Tracks:", + "audio_tracks": "Audio Tracks:", + "playback_state": "Playback State:", + "no_data_available": "No data available", + "index": "Index:", "download": { "download_season": "Download Season", "download_x_item": "Download {{item_count}} items", "download_button": "Download", "using_optimized_server": "Using optimized server", - "using_default_method": "Using default method", + "using_default_method": "Using default method" } }, "jellyseerr":{ "confirm": "Confirm", "cancel": "Cancel", "yes": "Yes", + "whats_wrong": "What's wrong?", + "issue_type": "Issue type", + "select_an_issue": "Select an issue", + "types": "Types", + "describe_the_issue": "(optional) Describe the issue...", + "submit_button": "Submit", + "report_issue_button": "Report issue", + "request_button": "Request", "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": { diff --git a/translations/fr.json b/translations/fr.json index e2c32397..755d4efa 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -6,11 +6,11 @@ "login_to_title": "Se connecter à", "username_placeholder": "Nom d'utilisateur", "password_placeholder": "Mot de passe", - "use_quick_connect": "Utiliser Quick Connect", + "use_quick_connect": "Utiliser Connexion Rapide", "login_button": "Se connecter", - "quick_connect": "Quick Connect", + "quick_connect": "Connexion Rapide", "enter_code_to_login": "Entrez le code {{code}} pour vous connecter", - "failed_to_initiate_quick_connect": "Échec de l'initialisation de Quick Connect", + "failed_to_initiate_quick_connect": "Échec de l'initialisation de Connexion Rapide", "got_it": "D'accord", "connection_failed": "La connection a échouée", "could_not_connect_to_server": "Impossible de se connecter au serveur. Veuillez vérifier l'URL et votre connection réseau." @@ -26,6 +26,7 @@ "home": { "home": "Accueil", "no_internet": "Pas d'Internet", + "no_items": "Aucun item", "no_internet_message": "Aucun problème, vous pouvez toujours regarder\nle contenu téléchargé.", "go_to_downloads": "Aller aux téléchargements", "oops": "Oups!", @@ -46,11 +47,11 @@ "app_version": "Version de l'application" }, "quick_connect": { - "quick_connect_title": "Quick connect", - "authorize_button": "Autoriser Quick Connect", - "enter_the_quick_connect_code": "Entrez le code Quick Connect", + "quick_connect_title": "Connexion Rapide", + "authorize_button": "Autoriser Connexion Rapide", + "enter_the_quick_connect_code": "Entrez le code Connexion Rapide", "success": "Succès", - "quick_connect_autorized": "Quick Connect autorisé", + "quick_connect_autorized": "Connexion Rapide autorisé", "error": "Errur", "invalid_code": "Code invalide" }, @@ -63,7 +64,8 @@ "audio_title": "Audio", "set_audio_track": "Configurer la piste audio à partir de l'élément précédent", "audio_language": "Langue audio", - "audio_hint": "Chosissez une langue audio par défaut." + "audio_hint": "Chosissez une langue audio par défaut.", + "none": "Aucun" }, "subtitles": { "subtitle_title": "Sous-titres", @@ -71,7 +73,8 @@ "subtitle_mode": "Mode des sous-titres", "set_subtitle_track": "Configurer la piste de sous-titres à partir de l'élément précédent", "subtitle_size": "Taille des sous-titres", - "subtitle_hint": "Configurez les préférences des sous-titres." + "subtitle_hint": "Configurez les préférences des sous-titres.", + "none": "Aucun" }, "other": { "other_title": "Autres", @@ -85,7 +88,15 @@ "download_method": "Méthode de téléchargement", "remux_max_download": "Téléchargement max remux", "auto_download": "Téléchargement automatique", - "optimized_versions_server": "Serveur de versions optimisées" + "optimized_versions_server": "Serveur de versions optimisées", + "save_button": "Enregistrer", + "optimized_server": "Serveur optimisé", + "optimized": "Optimisé", + "default": "Défaut", + "optimized_version_hint": "Entrez l'URL du serveur de versions optimisées. L'URL devrait inclure http ou https et optionnellement le port.", + "read_more_about_optimized_server": "Lisez-en plus sur le serveur de versions optimisées.", + "url": "URL", + "server_url_placeholder": "http(s)://domaine.org:port" }, "plugins": { "plugins_title": "Plugiciels", @@ -147,7 +158,8 @@ "background_downloads_enabled": "Téléchargements en arrière-plan activés", "background_downloads_disabled": "Téléchargements en arrière-plan désactivés", "connected": "Connecté", - "could_not_connect": "Impossible de se connecter" + "could_not_connect": "Impossible de se connecter", + "invalid_url": "URL invalide" } }, "downloads": { @@ -170,6 +182,7 @@ "delete": "Supprimer", "something_went_wrong": "Quelque chose s'est mal passé", "could_not_get_stream_url_from_jellyfin": "Impossible d'obtenir l'URL du flux depuis Jellyfin", + "eta": "ETA {{eta}}", "toasts": { "you_are_not_allowed_to_download_files": "Vous n'êtes pas autorisé à télécharger des fichiers", "deleted_all_movies_successfully": "Tous les films ont été supprimés avec succès!", @@ -197,9 +210,14 @@ } }, "search": { + "results_for_x": "Résultats pour ", "search_title": "Recherche", "search_here": "Rechercher ici...", "search": "Rechercher...", + "x_items": "{{count}} items", + "library": "Bibliothèque", + "discover": "Découvrir", + "no_results": "Aucun résultat", "no_results_found_for": "Aucun résultat trouvé pour", "movies": "Films", "series": "Séries", @@ -245,6 +263,9 @@ "playlists": "Listes de lecture", "music_albums": "Albums de musique", "audio": "Audio" + }, + "custom_links": { + "no_links": "Aucun lien" }, "player": { "error": "Erreur", @@ -253,7 +274,9 @@ "client_error": "Erreur client", "could_not_create_stream_for_chromecast": "Impossible de créer un flux pour Chromecast", "message_from_server": "Message du serveur: {{message}}", - "video_has_finished_playing": "La vidéo a fini de jouer!" + "video_has_finished_playing": "La vidéo a fini de jouer!", + "no_video_source": "Aucune source vidéo...", + "next_episode": "Épisode suivant" }, "item_card": { "next_up": "À suivre", @@ -262,7 +285,6 @@ "series": "Séries", "seasons": "Saisons", "season": "Saison", - "episodes": "Épisodes", "no_episodes_for_this_season": "Aucun épisode pour cette saison", "overview": "Aperçu", "more_with": "Plus avec {{name}}", @@ -275,6 +297,17 @@ "subtitles": "Sous-titres", "show_more": "Afficher plus", "show_less": "Afficher moins", + "appeared_in": "Apparu dans", + "x_songs": "{{count}} chansons", + "x_albums": "{{count}} albums", + "artists": "Artistes", + "could_not_load_item": "Impossible de charger l'item", + "refresh_tracks": "Rafraîchir les pistes", + "subtitle_tracks": "Pistes de sous-titres:", + "audio_tracks": "Pistes audio:", + "playback_state": "État de lecture:", + "no_data_available": "Aucune donnée disponible", + "index": "Index:", "download": { "download_season": "Télécharger la saison", "download_x_item": "Télécharger {{item_count}} items", @@ -287,6 +320,14 @@ "confirm": "Confirmer", "cancel": "Annuler", "yes": "Oui", + "whats_wrong": "Qu'est-ce qui ne va pas?", + "issue_type": "Type de problème", + "select_an_issue": "Sélectionnez un problème", + "types": "Types", + "describe_the_issue": "(optionnel) Décrivez le problème...", + "submit_button": "Soumettre", + "report_issue_button": "Signaler un problème", + "request_button": "Demander", "are_you_sure_you_want_to_request_all_seasons": "Êtes-vous sûr de vouloir demander toutes les saisons?", "failed_to_login": "Échec de la connexion", "toasts": {