Compare commits

..

9 Commits

Author SHA1 Message Date
lance chant
4813fdab81 Merge branch 'develop' into fix/network-location 2026-07-17 14:47:54 +02:00
Gauvain
6dd98ccae8 fix(home): black episode thumbnails in Next Up & Continue Watching (#1815) 2026-07-17 12:00:23 +02:00
kevbot
b4eb853048 fix(livetv): release live streams so the tuner doesn't wedge (#1799)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Gauvino <contact@uruk.dev>
2026-07-17 11:27:32 +02:00
lance chant
fd504cef14 Merge branch 'develop' into fix/network-location 2026-07-17 09:28:52 +02:00
github-actions[bot]
76d9b3177b feat: New Crowdin Translations (#1810)
Some checks failed
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (actions) (push) Has been cancelled
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (javascript-typescript) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Vulnerable Dependencies (push) Has been cancelled
🚦 Security & Quality Gate / 🚑 Expo Doctor Check (push) Has been cancelled
🏷️🔀Merge Conflict Labeler / 🏷️ Labeling Merge Conflicts (push) Has been cancelled
🌐 Translation Sync / sync-translations (push) Has been cancelled
🏗️ Build Apps / 🤖 Build Android APK (Phone) (push) Has been cancelled
🏗️ Build Apps / 🤖 Build Android APK (TV) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build iOS IPA (Phone) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build iOS IPA (Phone - Unsigned) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build tvOS IPA (push) Has been cancelled
🏗️ Build Apps / 🍎 Build tvOS IPA (Unsigned) (push) Has been cancelled
🔒 Lockfile Consistency Check / 🔍 Check bun.lock and package.json consistency (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (check) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (format) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (i18n:check) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (lint) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (typecheck) (push) Has been cancelled
🛡️ Trivy Security Scan / 🔎 Filesystem scan (push) Has been cancelled
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-07-15 20:47:34 +02:00
Gauvain
e0017b8092 Merge branch 'develop' into fix/network-location 2026-07-15 11:24:17 +02:00
lance chant
4c5a3dc658 Merge branch 'develop' into fix/network-location 2026-07-15 08:54:07 +02:00
Lance Chant
c4b6390a5e Addressing PR comments
Signed-off-by: Lance Chant <13349722+lancechant@users.noreply.github.com>
2026-07-15 08:29:18 +02:00
Lance Chant
64b9224b7a fix: network location settings
Fixed android location settings for wifi network connections as didn't
exist
Added some better wording to assist

Signed-off-by: Lance Chant <13349722+lancechant@users.noreply.github.com>
2026-07-14 14:43:12 +02:00
68 changed files with 1017 additions and 681 deletions

View File

@@ -324,44 +324,32 @@ export default function SettingsTV() {
// MPV alignment options // MPV alignment options
const alignXOptions: TVOptionItem<string>[] = useMemo( const alignXOptions: TVOptionItem<string>[] = useMemo(
() => [ () => [
{ label: "Left", value: "left", selected: currentAlignX === "left" },
{ {
label: t("home.settings.subtitles.align.left"), label: "Center",
value: "left",
selected: currentAlignX === "left",
},
{
label: t("home.settings.subtitles.align.center"),
value: "center", value: "center",
selected: currentAlignX === "center", selected: currentAlignX === "center",
}, },
{ { label: "Right", value: "right", selected: currentAlignX === "right" },
label: t("home.settings.subtitles.align.right"),
value: "right",
selected: currentAlignX === "right",
},
], ],
[currentAlignX, t], [currentAlignX],
); );
const alignYOptions: TVOptionItem<string>[] = useMemo( const alignYOptions: TVOptionItem<string>[] = useMemo(
() => [ () => [
{ label: "Top", value: "top", selected: currentAlignY === "top" },
{ {
label: t("home.settings.subtitles.align.top"), label: "Center",
value: "top",
selected: currentAlignY === "top",
},
{
label: t("home.settings.subtitles.align.center"),
value: "center", value: "center",
selected: currentAlignY === "center", selected: currentAlignY === "center",
}, },
{ {
label: t("home.settings.subtitles.align.bottom"), label: "Bottom",
value: "bottom", value: "bottom",
selected: currentAlignY === "bottom", selected: currentAlignY === "bottom",
}, },
], ],
[currentAlignY, t], [currentAlignY],
); );
// Cache mode options // Cache mode options
@@ -511,13 +499,13 @@ export default function SettingsTV() {
const alignXLabel = useMemo(() => { const alignXLabel = useMemo(() => {
const option = alignXOptions.find((o) => o.selected); const option = alignXOptions.find((o) => o.selected);
return option?.label || t("home.settings.subtitles.align.center"); return option?.label || "Center";
}, [alignXOptions, t]); }, [alignXOptions]);
const alignYLabel = useMemo(() => { const alignYLabel = useMemo(() => {
const option = alignYOptions.find((o) => o.selected); const option = alignYOptions.find((o) => o.selected);
return option?.label || t("home.settings.subtitles.align.bottom"); return option?.label || "Bottom";
}, [alignYOptions, t]); }, [alignYOptions]);
const typographyScaleLabel = useMemo(() => { const typographyScaleLabel = useMemo(() => {
const option = typographyScaleOptions.find((o) => o.selected); const option = typographyScaleOptions.find((o) => o.selected);
@@ -657,7 +645,7 @@ export default function SettingsTV() {
formatValue={(v) => `${v.toFixed(1)}x`} formatValue={(v) => `${v.toFixed(1)}x`}
/> />
<TVSettingsStepper <TVSettingsStepper
label={t("home.settings.subtitles.mpv_subtitle_margin_y")} label='Vertical Margin'
value={settings.mpvSubtitleMarginY ?? 0} value={settings.mpvSubtitleMarginY ?? 0}
onDecrease={() => { onDecrease={() => {
const newValue = Math.max( const newValue = Math.max(
@@ -675,11 +663,11 @@ export default function SettingsTV() {
}} }}
/> />
<TVSettingsOptionButton <TVSettingsOptionButton
label={t("home.settings.subtitles.mpv_subtitle_align_x")} label='Horizontal Alignment'
value={alignXLabel} value={alignXLabel}
onPress={() => onPress={() =>
showOptions({ showOptions({
title: t("home.settings.subtitles.mpv_subtitle_align_x"), title: "Horizontal Alignment",
options: alignXOptions, options: alignXOptions,
onSelect: (value) => onSelect: (value) =>
updateSettings({ updateSettings({
@@ -689,11 +677,11 @@ export default function SettingsTV() {
} }
/> />
<TVSettingsOptionButton <TVSettingsOptionButton
label={t("home.settings.subtitles.mpv_subtitle_align_y")} label='Vertical Alignment'
value={alignYLabel} value={alignYLabel}
onPress={() => onPress={() =>
showOptions({ showOptions({
title: t("home.settings.subtitles.mpv_subtitle_align_y"), title: "Vertical Alignment",
options: alignYOptions, options: alignYOptions,
onSelect: (value) => onSelect: (value) =>
updateSettings({ updateSettings({

View File

@@ -89,7 +89,7 @@ export default function ArtistsScreen() {
return ( return (
<View className='flex-1 justify-center items-center bg-black px-6'> <View className='flex-1 justify-center items-center bg-black px-6'>
<Text className='text-neutral-500 text-center'> <Text className='text-neutral-500 text-center'>
{t("music.missing_library_id")} Missing music library id.
</Text> </Text>
</View> </View>
); );

View File

@@ -122,7 +122,7 @@ export default function PlaylistsScreen() {
return ( return (
<View className='flex-1 justify-center items-center bg-black px-6'> <View className='flex-1 justify-center items-center bg-black px-6'>
<Text className='text-neutral-500 text-center'> <Text className='text-neutral-500 text-center'>
{t("music.missing_library_id")} Missing music library id.
</Text> </Text>
</View> </View>
); );

View File

@@ -226,7 +226,7 @@ export default function SuggestionsScreen() {
return ( return (
<View className='flex-1 justify-center items-center bg-black px-6'> <View className='flex-1 justify-center items-center bg-black px-6'>
<Text className='text-neutral-500 text-center'> <Text className='text-neutral-500 text-center'>
{t("music.missing_library_id")} Missing music library id.
</Text> </Text>
</View> </View>
); );

View File

@@ -14,7 +14,6 @@ import React, {
useRef, useRef,
useState, useState,
} from "react"; } from "react";
import { useTranslation } from "react-i18next";
import { import {
ActivityIndicator, ActivityIndicator,
Dimensions, Dimensions,
@@ -73,7 +72,6 @@ const ARTWORK_SIZE = SCREEN_WIDTH - 80;
type ViewMode = "player" | "queue"; type ViewMode = "player" | "queue";
export default function NowPlayingScreen() { export default function NowPlayingScreen() {
const { t } = useTranslation();
const [api] = useAtom(apiAtom); const [api] = useAtom(apiAtom);
const [user] = useAtom(userAtom); const [user] = useAtom(userAtom);
const router = useRouter(); const router = useRouter();
@@ -232,9 +230,7 @@ export default function NowPlayingScreen() {
paddingBottom: Platform.OS === "android" ? insets.bottom : 0, paddingBottom: Platform.OS === "android" ? insets.bottom : 0,
}} }}
> >
<Text className='text-neutral-500'> <Text className='text-neutral-500'>No track playing</Text>
{t("music.no_track_playing")}
</Text>
</View> </View>
</BottomSheetModalProvider> </BottomSheetModalProvider>
); );
@@ -271,7 +267,7 @@ export default function NowPlayingScreen() {
: "text-neutral-500" : "text-neutral-500"
} }
> >
{t("music.now_playing")} Now Playing
</Text> </Text>
</TouchableOpacity> </TouchableOpacity>
<TouchableOpacity <TouchableOpacity
@@ -722,7 +718,6 @@ const QueueView: React.FC<QueueViewProps> = ({
onRemoveFromQueue, onRemoveFromQueue,
onReorderQueue, onReorderQueue,
}) => { }) => {
const { t } = useTranslation();
const renderQueueItem = useCallback( const renderQueueItem = useCallback(
({ item, drag, isActive, getIndex }: RenderItemParams<BaseItemDto>) => { ({ item, drag, isActive, getIndex }: RenderItemParams<BaseItemDto>) => {
const index = getIndex() ?? 0; const index = getIndex() ?? 0;
@@ -836,15 +831,13 @@ const QueueView: React.FC<QueueViewProps> = ({
ListHeaderComponent={ ListHeaderComponent={
<View className='px-4 py-2'> <View className='px-4 py-2'>
<Text className='text-neutral-400 text-xs uppercase tracking-wider'> <Text className='text-neutral-400 text-xs uppercase tracking-wider'>
{history.length > 0 {history.length > 0 ? "Playing from queue" : "Up next"}
? t("music.playing_from_queue")
: t("music.up_next")}
</Text> </Text>
</View> </View>
} }
ListEmptyComponent={ ListEmptyComponent={
<View className='flex-1 items-center justify-center py-20'> <View className='flex-1 items-center justify-center py-20'>
<Text className='text-neutral-500'>{t("music.queue_empty")}</Text> <Text className='text-neutral-500'>Queue is empty</Text>
</View> </View>
} }
/> />

View File

@@ -7,6 +7,7 @@ import {
RepeatMode, RepeatMode,
} from "@jellyfin/sdk/lib/generated-client"; } from "@jellyfin/sdk/lib/generated-client";
import { import {
getMediaInfoApi,
getPlaystateApi, getPlaystateApi,
getUserLibraryApi, getUserLibraryApi,
} from "@jellyfin/sdk/lib/utils/api"; } from "@jellyfin/sdk/lib/utils/api";
@@ -310,6 +311,37 @@ export default function DirectPlayerPage() {
// Ref to store the stream fetch function for refreshing subtitle tracks // Ref to store the stream fetch function for refreshing subtitle tracks
const refetchStreamRef = useRef<(() => Promise<Stream | null>) | null>(null); const refetchStreamRef = useRef<(() => Promise<Stream | null>) | null>(null);
// Live TV opens a server-side live stream via autoOpenLiveStream. If it is
// never closed, Jellyfin's M3U tuner limit fills up and every channel then
// fails with "simultaneous stream limit has been reached". reportPlaybackStopped
// handles a clean stop, but a channel switch (the player re-fetches in place)
// or an unmount after an error bypass it, so track the open live stream and
// release it on those paths too.
const apiRef = useRef(api);
useEffect(() => {
apiRef.current = api;
}, [api]);
const releaseLiveStream = useCallback(
(liveStreamId: string | null) => {
if (!liveStreamId || !apiRef.current || offline) return;
// Best effort: a failed close must not break teardown, and the slot is
// also freed by the server's reap as a backstop.
getMediaInfoApi(apiRef.current)
.closeLiveStream({ liveStreamId })
.catch(() => {});
},
[offline],
);
// The effect cleanup releases the live stream both when it changes (channel
// switch, which re-runs the effect) and when the player unmounts, so no
// manual previous-id tracking is needed.
useEffect(() => {
const liveStreamId = stream?.mediaSource?.LiveStreamId ?? null;
return () => releaseLiveStream(liveStreamId);
}, [stream?.mediaSource?.LiveStreamId, releaseLiveStream]);
useEffect(() => { useEffect(() => {
const fetchStreamData = async (): Promise<Stream | null> => { const fetchStreamData = async (): Promise<Stream | null> => {
setStreamStatus({ isLoading: true, isError: false }); setStreamStatus({ isLoading: true, isError: false });
@@ -445,6 +477,12 @@ export default function DirectPlayerPage() {
MediaSourceId: mediaSourceId, MediaSourceId: mediaSourceId,
PositionTicks: currentTimeInTicks, PositionTicks: currentTimeInTicks,
PlaySessionId: stream.sessionId, PlaySessionId: stream.sessionId,
// Release the server-side live stream (and its tuner slot) on stop.
// Jellyfin only closes a live stream opened via autoOpenLiveStream when
// the stop report carries its LiveStreamId; without it the stream leaks
// and Live TV eventually fails for everyone with "M3U simultaneous
// stream limit has been reached". Undefined for non-live items (no-op).
LiveStreamId: stream.mediaSource?.LiveStreamId ?? undefined,
}, },
}); });
}, [api, item, mediaSourceId, stream, progress, offline]); }, [api, item, mediaSourceId, stream, progress, offline]);

View File

@@ -192,7 +192,6 @@ const SubtitleResultCard = React.forwardRef<
>(({ result, hasTVPreferredFocus, isDownloading, onPress }, ref) => { >(({ result, hasTVPreferredFocus, isDownloading, onPress }, ref) => {
const { focused, handleFocus, handleBlur, animatedStyle } = const { focused, handleFocus, handleBlur, animatedStyle } =
useTVFocusAnimation({ scaleAmount: 1.03 }); useTVFocusAnimation({ scaleAmount: 1.03 });
const { t } = useTranslation();
return ( return (
<Pressable <Pressable
@@ -329,7 +328,7 @@ const SubtitleResultCard = React.forwardRef<
]} ]}
> >
<Text style={[styles.flagText, { fontSize: scaleSize(10) }]}> <Text style={[styles.flagText, { fontSize: scaleSize(10) }]}>
{t("player.hash_match")} Hash Match
</Text> </Text>
</View> </View>
)} )}

View File

@@ -1,20 +1,17 @@
import { Link, Stack } from "expo-router"; import { Link, Stack } from "expo-router";
import { useTranslation } from "react-i18next";
import { StyleSheet } from "react-native"; import { StyleSheet } from "react-native";
import { ThemedText } from "@/components/ThemedText"; import { ThemedText } from "@/components/ThemedText";
import { ThemedView } from "@/components/ThemedView"; import { ThemedView } from "@/components/ThemedView";
export default function NotFoundScreen() { export default function NotFoundScreen() {
const { t } = useTranslation();
return ( return (
<> <>
<Stack.Screen options={{ title: t("home.oops") }} /> <Stack.Screen options={{ title: "Oops!" }} />
<ThemedView style={styles.container}> <ThemedView style={styles.container}>
<ThemedText type='title'>{t("not_found.title")}</ThemedText> <ThemedText type='title'>This screen doesn't exist.</ThemedText>
<Link href={"/home"} style={styles.link}> <Link href={"/home"} style={styles.link}>
<ThemedText type='link'>{t("not_found.go_home")}</ThemedText> <ThemedText type='link'>Go to home screen!</ThemedText>
</Link> </Link>
</ThemedView> </ThemedView>
</> </>

View File

@@ -27,6 +27,7 @@ import { NetworkStatusProvider } from "@/providers/NetworkStatusProvider";
import { PlaySettingsProvider } from "@/providers/PlaySettingsProvider"; import { PlaySettingsProvider } from "@/providers/PlaySettingsProvider";
import { ServerUrlProvider } from "@/providers/ServerUrlProvider"; import { ServerUrlProvider } from "@/providers/ServerUrlProvider";
import { WebSocketProvider } from "@/providers/WebSocketProvider"; import { WebSocketProvider } from "@/providers/WebSocketProvider";
import { WifiSsidProvider } from "@/providers/WifiSsidProvider";
import { useSettings } from "@/utils/atoms/settings"; import { useSettings } from "@/utils/atoms/settings";
import { import {
BACKGROUND_FETCH_TASK, BACKGROUND_FETCH_TASK,
@@ -425,6 +426,7 @@ function Layout() {
> >
<JellyfinProvider> <JellyfinProvider>
<InactivityProvider> <InactivityProvider>
<WifiSsidProvider>
<ServerUrlProvider> <ServerUrlProvider>
<NetworkStatusProvider> <NetworkStatusProvider>
<PlaySettingsProvider> <PlaySettingsProvider>
@@ -467,7 +469,8 @@ function Layout() {
options={{ options={{
headerShown: true, headerShown: true,
title: "", title: "",
headerTransparent: Platform.OS === "ios", headerTransparent:
Platform.OS === "ios",
}} }}
/> />
<Stack.Screen name='+not-found' /> <Stack.Screen name='+not-found' />
@@ -562,6 +565,7 @@ function Layout() {
</PlaySettingsProvider> </PlaySettingsProvider>
</NetworkStatusProvider> </NetworkStatusProvider>
</ServerUrlProvider> </ServerUrlProvider>
</WifiSsidProvider>
</InactivityProvider> </InactivityProvider>
</JellyfinProvider> </JellyfinProvider>
</PersistQueryClientProvider> </PersistQueryClientProvider>

View File

@@ -35,8 +35,10 @@ const ContinueWatchingPoster: React.FC<ContinueWatchingPosterProps> = ({
return `${api?.basePath}/Items/${item.Id}/Images/Primary?fillHeight=389&quality=80`; return `${api?.basePath}/Items/${item.Id}/Images/Primary?fillHeight=389&quality=80`;
} }
if (item.Type === "Episode") { if (item.Type === "Episode") {
if (item.ParentBackdropItemId && item.ParentThumbImageTag) { // Matched pair: the parent that owns the Thumb (ParentThumbItemId), not the
return `${api?.basePath}/Items/${item.ParentBackdropItemId}/Images/Thumb?fillHeight=389&quality=80&tag=${item.ParentThumbImageTag}`; // backdrop owner — otherwise the Thumb tag is requested on the wrong item → black.
if (item.ParentThumbItemId && item.ParentThumbImageTag) {
return `${api?.basePath}/Items/${item.ParentThumbItemId}/Images/Thumb?fillHeight=389&quality=80&tag=${item.ParentThumbImageTag}`;
} }
return `${api?.basePath}/Items/${item.Id}/Images/Primary?fillHeight=389&quality=80`; return `${api?.basePath}/Items/${item.Id}/Images/Primary?fillHeight=389&quality=80`;
@@ -61,7 +63,8 @@ const ContinueWatchingPoster: React.FC<ContinueWatchingPosterProps> = ({
} }
return `${api?.basePath}/Items/${item.Id}/Images/Primary?fillHeight=389&quality=80`; return `${api?.basePath}/Items/${item.Id}/Images/Primary?fillHeight=389&quality=80`;
}, [item]); // useEpisodePoster in deps so flipping the prop re-computes the URL live.
}, [api, item, useEpisodePoster]);
if (!url) if (!url)
return <View className='aspect-video border border-neutral-800 w-44' />; return <View className='aspect-video border border-neutral-800 w-44' />;

View File

@@ -1,7 +1,6 @@
import { Ionicons } from "@expo/vector-icons"; import { Ionicons } from "@expo/vector-icons";
import { BottomSheetScrollView } from "@gorhom/bottom-sheet"; import { BottomSheetScrollView } from "@gorhom/bottom-sheet";
import React, { useEffect } from "react"; import React, { useEffect } from "react";
import { useTranslation } from "react-i18next";
import { Platform, StyleSheet, TouchableOpacity, View } from "react-native"; import { Platform, StyleSheet, TouchableOpacity, View } from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context"; import { useSafeAreaInsets } from "react-native-safe-area-context";
import { Text } from "@/components/common/Text"; import { Text } from "@/components/common/Text";
@@ -210,7 +209,6 @@ const PlatformDropdownComponent = ({
expoUIConfig, expoUIConfig,
bottomSheetConfig, bottomSheetConfig,
}: PlatformDropdownProps) => { }: PlatformDropdownProps) => {
const { t } = useTranslation();
const { showModal, hideModal, isVisible } = useGlobalModal(); const { showModal, hideModal, isVisible } = useGlobalModal();
// Handle controlled open state for Android // Handle controlled open state for Android
@@ -382,7 +380,7 @@ const PlatformDropdownComponent = ({
return ( return (
<TouchableOpacity onPress={handlePress} activeOpacity={0.7}> <TouchableOpacity onPress={handlePress} activeOpacity={0.7}>
{trigger || <Text className='text-white'>{t("common.open_menu")}</Text>} {trigger || <Text className='text-white'>Open Menu</Text>}
</TouchableOpacity> </TouchableOpacity>
); );
}; };

View File

@@ -502,8 +502,8 @@ export const PlayButton: React.FC<Props> = ({
return ( return (
<TouchableOpacity <TouchableOpacity
disabled={!item} disabled={!item}
accessibilityLabel={t("accessibility.play_button")} accessibilityLabel='Play button'
accessibilityHint={t("accessibility.play_hint")} accessibilityHint='Tap to play the media'
onPress={onPress} onPress={onPress}
className={"relative flex-1"} className={"relative flex-1"}
> >

View File

@@ -2,7 +2,6 @@ import { Ionicons } from "@expo/vector-icons";
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client"; import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
import { useAtom } from "jotai"; import { useAtom } from "jotai";
import { useCallback, useEffect } from "react"; import { useCallback, useEffect } from "react";
import { useTranslation } from "react-i18next";
import { TouchableOpacity, View } from "react-native"; import { TouchableOpacity, View } from "react-native";
import Animated, { import Animated, {
Easing, Easing,
@@ -37,7 +36,6 @@ export const PlayButton: React.FC<Props> = ({
colors, colors,
...props ...props
}: Props) => { }: Props) => {
const { t } = useTranslation();
const [globalColorAtom] = useAtom(itemThemeColorAtom); const [globalColorAtom] = useAtom(itemThemeColorAtom);
// Use colors prop if provided, otherwise fallback to global atom // Use colors prop if provided, otherwise fallback to global atom
@@ -170,8 +168,8 @@ export const PlayButton: React.FC<Props> = ({
return ( return (
<TouchableOpacity <TouchableOpacity
accessibilityLabel={t("accessibility.play_button")} accessibilityLabel='Play button'
accessibilityHint={t("accessibility.play_hint")} accessibilityHint='Tap to play the media'
onPress={onPress} onPress={onPress}
className={"relative"} className={"relative"}
{...props} {...props}

View File

@@ -6,7 +6,6 @@ import {
import { getSessionApi } from "@jellyfin/sdk/lib/utils/api/session-api"; import { getSessionApi } from "@jellyfin/sdk/lib/utils/api/session-api";
import { useAtomValue } from "jotai"; import { useAtomValue } from "jotai";
import React, { useState } from "react"; import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { import {
FlatList, FlatList,
Modal, Modal,
@@ -32,7 +31,6 @@ export const PlayInRemoteSessionButton: React.FC<Props> = ({
const [modalVisible, setModalVisible] = useState(false); const [modalVisible, setModalVisible] = useState(false);
const api = useAtomValue(apiAtom); const api = useAtomValue(apiAtom);
const { sessions, isLoading } = useAllSessions({} as useSessionsProps); const { sessions, isLoading } = useAllSessions({} as useSessionsProps);
const { t } = useTranslation();
const handlePlayInSession = async (sessionId: string) => { const handlePlayInSession = async (sessionId: string) => {
if (!api || !item.Id) return; if (!api || !item.Id) return;
@@ -67,9 +65,7 @@ export const PlayInRemoteSessionButton: React.FC<Props> = ({
<View style={styles.centeredView}> <View style={styles.centeredView}>
<View style={styles.modalView}> <View style={styles.modalView}>
<View style={styles.modalHeader}> <View style={styles.modalHeader}>
<Text style={styles.modalTitle}> <Text style={styles.modalTitle}>Select Session</Text>
{t("home.sessions.select_session")}
</Text>
<TouchableOpacity onPress={() => setModalVisible(false)}> <TouchableOpacity onPress={() => setModalVisible(false)}>
<Ionicons name='close' size={24} color='white' /> <Ionicons name='close' size={24} color='white' />
</TouchableOpacity> </TouchableOpacity>
@@ -82,7 +78,7 @@ export const PlayInRemoteSessionButton: React.FC<Props> = ({
</View> </View>
) : !sessions || sessions.length === 0 ? ( ) : !sessions || sessions.length === 0 ? (
<Text style={styles.noSessionsText}> <Text style={styles.noSessionsText}>
{t("home.sessions.no_active_sessions")} No active sessions found
</Text> </Text>
) : ( ) : (
<FlatList <FlatList
@@ -102,7 +98,7 @@ export const PlayInRemoteSessionButton: React.FC<Props> = ({
</Text> </Text>
{session.NowPlayingItem && ( {session.NowPlayingItem && (
<Text style={styles.nowPlaying} numberOfLines={1}> <Text style={styles.nowPlaying} numberOfLines={1}>
{t("home.sessions.now_playing")}{" "} Now playing:{" "}
{session.NowPlayingItem.SeriesName {session.NowPlayingItem.SeriesName
? `${session.NowPlayingItem.SeriesName} :` ? `${session.NowPlayingItem.SeriesName} :`
: ""} : ""}

View File

@@ -1,7 +1,7 @@
import { Ionicons } from "@expo/vector-icons"; import { Ionicons } from "@expo/vector-icons";
import { Image } from "expo-image"; import { Image } from "expo-image";
import { t } from "i18next";
import { useMemo } from "react"; import { useMemo } from "react";
import { useTranslation } from "react-i18next";
import { import {
ActivityIndicator, ActivityIndicator,
TouchableOpacity, TouchableOpacity,
@@ -35,7 +35,6 @@ interface DownloadCardProps extends TouchableOpacityProps {
} }
export const DownloadCard = ({ process, ...props }: DownloadCardProps) => { export const DownloadCard = ({ process, ...props }: DownloadCardProps) => {
const { t } = useTranslation();
const { cancelDownload } = useDownload(); const { cancelDownload } = useDownload();
const router = useRouter(); const router = useRouter();
const queryClient = useNetworkAwareQueryClient(); const queryClient = useNetworkAwareQueryClient();
@@ -174,9 +173,7 @@ export const DownloadCard = ({ process, ...props }: DownloadCardProps) => {
{isTranscoding && ( {isTranscoding && (
<View className='bg-purple-600/20 px-2 py-0.5 rounded-md mt-1 self-start'> <View className='bg-purple-600/20 px-2 py-0.5 rounded-md mt-1 self-start'>
<Text className='text-xs text-purple-400'> <Text className='text-xs text-purple-400'>Transcoding</Text>
{t("home.downloads.transcoding")}
</Text>
</View> </View>
)} )}

View File

@@ -65,10 +65,11 @@ const HeroCard: React.FC<HeroCardProps> = React.memo(
const posterUrl = useMemo(() => { const posterUrl = useMemo(() => {
if (!api) return null; if (!api) return null;
// For episodes, always use series thumb // For episodes, always use series thumb.
// Matched pair: ParentThumbItemId owns the Thumb tag, not ParentBackdropItemId.
if (item.Type === "Episode") { if (item.Type === "Episode") {
if (item.ParentThumbImageTag) { if (item.ParentThumbItemId && item.ParentThumbImageTag) {
return `${api.basePath}/Items/${item.ParentBackdropItemId}/Images/Thumb?fillHeight=400&quality=80&tag=${item.ParentThumbImageTag}`; return `${api.basePath}/Items/${item.ParentThumbItemId}/Images/Thumb?fillHeight=400&quality=80&tag=${item.ParentThumbImageTag}`;
} }
if (item.SeriesId) { if (item.SeriesId) {
return `${api.basePath}/Items/${item.SeriesId}/Images/Thumb?fillHeight=400&quality=80`; return `${api.basePath}/Items/${item.SeriesId}/Images/Thumb?fillHeight=400&quality=80`;

View File

@@ -1,6 +1,5 @@
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client"; import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next";
import { Animated, Pressable, StyleSheet, View } from "react-native"; import { Animated, Pressable, StyleSheet, View } from "react-native";
import { Text } from "@/components/common/Text"; import { Text } from "@/components/common/Text";
import { useTVFocusAnimation } from "@/components/tv/hooks/useTVFocusAnimation"; import { useTVFocusAnimation } from "@/components/tv/hooks/useTVFocusAnimation";
@@ -23,7 +22,6 @@ export const TVGuideProgramCell: React.FC<TVGuideProgramCellProps> = ({
disabled = false, disabled = false,
refSetter, refSetter,
}) => { }) => {
const { t } = useTranslation();
const typography = useScaledTVTypography(); const typography = useScaledTVTypography();
const { focused, handleFocus, handleBlur } = useTVFocusAnimation({ const { focused, handleFocus, handleBlur } = useTVFocusAnimation({
scaleAmount: 1, scaleAmount: 1,
@@ -70,7 +68,7 @@ export const TVGuideProgramCell: React.FC<TVGuideProgramCellProps> = ({
<Text <Text
style={[styles.liveBadgeText, { fontSize: typography.callout }]} style={[styles.liveBadgeText, { fontSize: typography.callout }]}
> >
{t("player.live")} LIVE
</Text> </Text>
</View> </View>
)} )}

View File

@@ -235,7 +235,7 @@ export const TVLiveTVPage: React.FC = () => {
marginBottom: 24, marginBottom: 24,
}} }}
> >
{t("live_tv.title")} Live TV
</Text> </Text>
{/* Tab Bar */} {/* Tab Bar */}

View File

@@ -1,5 +1,4 @@
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next";
import { Animated, Pressable, View } from "react-native"; import { Animated, Pressable, View } from "react-native";
import { Text } from "@/components/common/Text"; import { Text } from "@/components/common/Text";
import { useTVFocusAnimation } from "@/components/tv/hooks/useTVFocusAnimation"; import { useTVFocusAnimation } from "@/components/tv/hooks/useTVFocusAnimation";
@@ -89,8 +88,6 @@ export const TVSearchTabBadges: React.FC<TVSearchTabBadgesProps> = ({
showDiscover, showDiscover,
disabled = false, disabled = false,
}) => { }) => {
const { t } = useTranslation();
if (!showDiscover) { if (!showDiscover) {
return null; return null;
} }
@@ -104,13 +101,13 @@ export const TVSearchTabBadges: React.FC<TVSearchTabBadgesProps> = ({
}} }}
> >
<TVSearchTabBadge <TVSearchTabBadge
label={t("search.library")} label='Library'
isSelected={searchType === "Library"} isSelected={searchType === "Library"}
onPress={() => setSearchType("Library")} onPress={() => setSearchType("Library")}
disabled={disabled} disabled={disabled}
/> />
<TVSearchTabBadge <TVSearchTabBadge
label={t("search.discover")} label='Discover'
isSelected={searchType === "Discover"} isSelected={searchType === "Discover"}
onPress={() => setSearchType("Discover")} onPress={() => setSearchType("Discover")}
disabled={disabled} disabled={disabled}

View File

@@ -5,6 +5,7 @@ import { useTranslation } from "react-i18next";
import { Switch, TouchableOpacity, View } from "react-native"; import { Switch, TouchableOpacity, View } from "react-native";
import { toast } from "sonner-native"; import { toast } from "sonner-native";
import { useWifiSSID } from "@/hooks/useWifiSSID"; import { useWifiSSID } from "@/hooks/useWifiSSID";
import { openLocationSettings } from "@/modules/wifi-ssid";
import { useServerUrl } from "@/providers/ServerUrlProvider"; import { useServerUrl } from "@/providers/ServerUrlProvider";
import { storage } from "@/utils/mmkv"; import { storage } from "@/utils/mmkv";
import { import {
@@ -26,16 +27,26 @@ const DEFAULT_CONFIG: LocalNetworkConfig = {
interface StatusDisplayProps { interface StatusDisplayProps {
currentSSID: string | null; currentSSID: string | null;
connectedToWifi: boolean;
isUsingLocalUrl: boolean; isUsingLocalUrl: boolean;
locationBlocked: boolean;
onOpenLocationSettings: () => void;
t: (key: string) => string; t: (key: string) => string;
} }
function StatusDisplay({ function StatusDisplay({
currentSSID, currentSSID,
connectedToWifi,
isUsingLocalUrl, isUsingLocalUrl,
locationBlocked,
onOpenLocationSettings,
t, t,
}: StatusDisplayProps): React.ReactElement { }: StatusDisplayProps): React.ReactElement {
const wifiStatus = currentSSID ?? t("home.settings.network.not_connected"); const wifiStatus = currentSSID
? currentSSID
: connectedToWifi
? t("home.settings.network.ssid_hidden")
: t("home.settings.network.not_connected");
const urlType = isUsingLocalUrl const urlType = isUsingLocalUrl
? t("home.settings.network.local") ? t("home.settings.network.local")
: t("home.settings.network.remote"); : t("home.settings.network.remote");
@@ -55,6 +66,23 @@ function StatusDisplay({
</Text> </Text>
<Text className={urlTypeColor}>{urlType}</Text> <Text className={urlTypeColor}>{urlType}</Text>
</View> </View>
{locationBlocked && (
<View className='mt-2 pt-2 border-t border-neutral-800'>
<Text className='text-xs text-amber-400'>
{t("home.settings.network.location_off_description")}
</Text>
<TouchableOpacity
onPress={onOpenLocationSettings}
className='mt-2 self-start'
hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
>
<Text className='text-xs text-blue-400 font-semibold'>
{t("home.settings.network.open_location_settings")}
</Text>
</TouchableOpacity>
</View>
)}
</View> </View>
); );
} }
@@ -62,7 +90,17 @@ function StatusDisplay({
export function LocalNetworkSettings(): React.ReactElement | null { export function LocalNetworkSettings(): React.ReactElement | null {
const { t } = useTranslation(); const { t } = useTranslation();
const { permissionStatus, requestPermission } = useWifiSSID(); const { permissionStatus, requestPermission } = useWifiSSID();
const { isUsingLocalUrl, currentSSID, refreshUrlState } = useServerUrl(); const { isUsingLocalUrl, currentSSID, connectedToWifi, refreshUrlState } =
useServerUrl();
// Connected to Wi-Fi and have permission, but the OS won't reveal the SSID —
// on Android this means device Location services are off.
const locationBlocked =
permissionStatus === "granted" && connectedToWifi && !currentSSID;
const handleOpenLocationSettings = useCallback(() => {
openLocationSettings();
}, []);
const remoteUrl = storage.getString("serverUrl"); const remoteUrl = storage.getString("serverUrl");
const [config, setConfig] = useState<LocalNetworkConfig>(DEFAULT_CONFIG); const [config, setConfig] = useState<LocalNetworkConfig>(DEFAULT_CONFIG);
@@ -195,6 +233,7 @@ export function LocalNetworkSettings(): React.ReactElement | null {
)} )}
</ListGroup> </ListGroup>
{!locationBlocked && (
<View className='py-2'> <View className='py-2'>
<Button <Button
onPress={handleAddCurrentNetwork} onPress={handleAddCurrentNetwork}
@@ -203,10 +242,14 @@ export function LocalNetworkSettings(): React.ReactElement | null {
{addNetworkButtonText} {addNetworkButtonText}
</Button> </Button>
</View> </View>
)}
<StatusDisplay <StatusDisplay
currentSSID={currentSSID} currentSSID={currentSSID}
connectedToWifi={connectedToWifi}
isUsingLocalUrl={isUsingLocalUrl} isUsingLocalUrl={isUsingLocalUrl}
locationBlocked={locationBlocked}
onOpenLocationSettings={handleOpenLocationSettings}
t={t} t={t}
/> />
</View> </View>

View File

@@ -1,6 +1,5 @@
import { Ionicons } from "@expo/vector-icons"; import { Ionicons } from "@expo/vector-icons";
import { useMemo } from "react"; import { useMemo } from "react";
import { useTranslation } from "react-i18next";
import { Platform, Switch, View, type ViewProps } from "react-native"; import { Platform, Switch, View, type ViewProps } from "react-native";
import { Stepper } from "@/components/inputs/Stepper"; import { Stepper } from "@/components/inputs/Stepper";
import { Text } from "../common/Text"; import { Text } from "../common/Text";
@@ -18,21 +17,20 @@ export const MpvSubtitleSettings: React.FC<Props> = ({ ...props }) => {
const isTv = Platform.isTV; const isTv = Platform.isTV;
const media = useMedia(); const media = useMedia();
const { settings, updateSettings } = media; const { settings, updateSettings } = media;
const { t } = useTranslation();
const alignXOptions: AlignX[] = ["left", "center", "right"]; const alignXOptions: AlignX[] = ["left", "center", "right"];
const alignYOptions: AlignY[] = ["top", "center", "bottom"]; const alignYOptions: AlignY[] = ["top", "center", "bottom"];
const alignXLabels: Record<AlignX, string> = { const alignXLabels: Record<AlignX, string> = {
left: t("home.settings.subtitles.align.left"), left: "Left",
center: t("home.settings.subtitles.align.center"), center: "Center",
right: t("home.settings.subtitles.align.right"), right: "Right",
}; };
const alignYLabels: Record<AlignY, string> = { const alignYLabels: Record<AlignY, string> = {
top: t("home.settings.subtitles.align.top"), top: "Top",
center: t("home.settings.subtitles.align.center"), center: "Center",
bottom: t("home.settings.subtitles.align.bottom"), bottom: "Bottom",
}; };
const alignXOptionGroups = useMemo(() => { const alignXOptionGroups = useMemo(() => {
@@ -62,18 +60,16 @@ export const MpvSubtitleSettings: React.FC<Props> = ({ ...props }) => {
return ( return (
<View {...props}> <View {...props}>
<ListGroup <ListGroup
title={t("home.settings.subtitles.mpv_settings_title")} title='MPV Subtitle Settings'
description={ description={
<Text className='text-[#8E8D91] text-xs'> <Text className='text-[#8E8D91] text-xs'>
{t("home.settings.subtitles.mpv_settings_description")} Advanced subtitle customization for MPV player
</Text> </Text>
} }
> >
{!isTv && ( {!isTv && (
<> <>
<ListItem <ListItem title='Vertical Margin'>
title={t("home.settings.subtitles.mpv_subtitle_margin_y")}
>
<Stepper <Stepper
value={settings.mpvSubtitleMarginY ?? 0} value={settings.mpvSubtitleMarginY ?? 0}
step={5} step={5}
@@ -85,7 +81,7 @@ export const MpvSubtitleSettings: React.FC<Props> = ({ ...props }) => {
/> />
</ListItem> </ListItem>
<ListItem title={t("home.settings.subtitles.mpv_subtitle_align_x")}> <ListItem title='Horizontal Alignment'>
<PlatformDropdown <PlatformDropdown
groups={alignXOptionGroups} groups={alignXOptionGroups}
trigger={ trigger={
@@ -100,11 +96,11 @@ export const MpvSubtitleSettings: React.FC<Props> = ({ ...props }) => {
/> />
</View> </View>
} }
title={t("home.settings.subtitles.mpv_subtitle_align_x")} title='Horizontal Alignment'
/> />
</ListItem> </ListItem>
<ListItem title={t("home.settings.subtitles.mpv_subtitle_align_y")}> <ListItem title='Vertical Alignment'>
<PlatformDropdown <PlatformDropdown
groups={alignYOptionGroups} groups={alignYOptionGroups}
trigger={ trigger={
@@ -119,13 +115,13 @@ export const MpvSubtitleSettings: React.FC<Props> = ({ ...props }) => {
/> />
</View> </View>
} }
title={t("home.settings.subtitles.mpv_subtitle_align_y")} title='Vertical Alignment'
/> />
</ListItem> </ListItem>
</> </>
)} )}
<ListItem title={t("home.settings.subtitles.opaque_background")}> <ListItem title='Opaque Background'>
<Switch <Switch
value={settings.mpvSubtitleBackgroundEnabled ?? false} value={settings.mpvSubtitleBackgroundEnabled ?? false}
onValueChange={(value) => onValueChange={(value) =>
@@ -135,7 +131,7 @@ export const MpvSubtitleSettings: React.FC<Props> = ({ ...props }) => {
</ListItem> </ListItem>
{settings.mpvSubtitleBackgroundEnabled && ( {settings.mpvSubtitleBackgroundEnabled && (
<ListItem title={t("home.settings.subtitles.background_opacity")}> <ListItem title='Background Opacity'>
<Stepper <Stepper
value={settings.mpvSubtitleBackgroundOpacity ?? 75} value={settings.mpvSubtitleBackgroundOpacity ?? 75}
step={5} step={5}

View File

@@ -139,9 +139,10 @@ export const TVPosterCard: React.FC<TVPosterCardProps> = ({
if (orientation === "horizontal") { if (orientation === "horizontal") {
// Episode: prefer series thumb image for consistent look (like hero section) // Episode: prefer series thumb image for consistent look (like hero section)
if (item.Type === "Episode") { if (item.Type === "Episode") {
// First try parent/series thumb (horizontal series artwork) // First try parent/series thumb (horizontal series artwork).
if (item.ParentBackdropItemId && item.ParentThumbImageTag) { // Matched pair: ParentThumbItemId owns the Thumb tag, not ParentBackdropItemId.
return `${api.basePath}/Items/${item.ParentBackdropItemId}/Images/Thumb?fillHeight=700&quality=80&tag=${item.ParentThumbImageTag}`; if (item.ParentThumbItemId && item.ParentThumbImageTag) {
return `${api.basePath}/Items/${item.ParentThumbItemId}/Images/Thumb?fillHeight=700&quality=80&tag=${item.ParentThumbImageTag}`;
} }
// Fall back to episode's own primary image // Fall back to episode's own primary image
if (item.ImageTags?.Primary) { if (item.ImageTags?.Primary) {

View File

@@ -1,6 +1,5 @@
import { Ionicons } from "@expo/vector-icons"; import { Ionicons } from "@expo/vector-icons";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next";
import { import {
ActivityIndicator, ActivityIndicator,
Animated, Animated,
@@ -29,7 +28,6 @@ export const TVSubtitleResultCard = React.forwardRef<
const styles = createStyles(typography); const styles = createStyles(typography);
const { focused, handleFocus, handleBlur, animatedStyle } = const { focused, handleFocus, handleBlur, animatedStyle } =
useTVFocusAnimation({ scaleAmount: 1.03 }); useTVFocusAnimation({ scaleAmount: 1.03 });
const { t } = useTranslation();
return ( return (
<Pressable <Pressable
@@ -154,7 +152,7 @@ export const TVSubtitleResultCard = React.forwardRef<
}, },
]} ]}
> >
<Text style={styles.flagText}>{t("player.hash_match")}</Text> <Text style={styles.flagText}>Hash Match</Text>
</View> </View>
)} )}
{result.hearingImpaired && ( {result.hearingImpaired && (

View File

@@ -183,7 +183,7 @@ export const BottomControls: FC<BottomControlsProps> = ({
<SkipButton <SkipButton
showButton={showSkipButton} showButton={showSkipButton}
onPress={skipIntro} onPress={skipIntro}
buttonText={t("player.skip_intro")} buttonText='Skip Intro'
/> />
{/* Smart Skip Credits behavior: {/* Smart Skip Credits behavior:
- Show "Skip Credits" if there's content after credits OR no next episode - Show "Skip Credits" if there's content after credits OR no next episode
@@ -193,7 +193,7 @@ export const BottomControls: FC<BottomControlsProps> = ({
showSkipCreditButton && (hasContentAfterCredits || !nextItem) showSkipCreditButton && (hasContentAfterCredits || !nextItem)
} }
onPress={skipCredit} onPress={skipCredit}
buttonText={t("player.skip_credits")} buttonText='Skip Credits'
/> />
{settings.autoPlayNextEpisode !== false && {settings.autoPlayNextEpisode !== false &&
(settings.maxAutoPlayEpisodeCount.value === -1 || (settings.maxAutoPlayEpisodeCount.value === -1 ||

View File

@@ -27,7 +27,7 @@ const ContinueWatchingOverlay: React.FC<ContinueWatchingOverlayProps> = ({
} }
> >
<Text className='text-2xl font-bold text-white py-4 '> <Text className='text-2xl font-bold text-white py-4 '>
{t("player.still_watching")} Are you still watching ?
</Text> </Text>
<Button <Button
onPress={() => { onPress={() => {

View File

@@ -4,7 +4,6 @@ import type {
MediaSourceInfo, MediaSourceInfo,
} from "@jellyfin/sdk/lib/generated-client"; } from "@jellyfin/sdk/lib/generated-client";
import { type FC, useCallback, useState } from "react"; import { type FC, useCallback, useState } from "react";
import { useTranslation } from "react-i18next";
import { Platform, TouchableOpacity, View } from "react-native"; import { Platform, TouchableOpacity, View } from "react-native";
import useRouter from "@/hooks/useAppRouter"; import useRouter from "@/hooks/useAppRouter";
import { useControlsSafeAreaInsets } from "@/hooks/useControlsSafeAreaInsets"; import { useControlsSafeAreaInsets } from "@/hooks/useControlsSafeAreaInsets";
@@ -58,7 +57,6 @@ export const HeaderControls: FC<HeaderControlsProps> = ({
showTechnicalInfo = false, showTechnicalInfo = false,
onToggleTechnicalInfo, onToggleTechnicalInfo,
}) => { }) => {
const { t } = useTranslation();
const router = useRouter(); const router = useRouter();
const insets = useControlsSafeAreaInsets(); const insets = useControlsSafeAreaInsets();
const lightHapticFeedback = useHaptic("light"); const lightHapticFeedback = useHaptic("light");
@@ -129,8 +127,8 @@ export const HeaderControls: FC<HeaderControlsProps> = ({
onPress={toggleOrientation} onPress={toggleOrientation}
disabled={isTogglingOrientation} disabled={isTogglingOrientation}
className='aspect-square flex flex-col rounded-xl items-center justify-center p-2' className='aspect-square flex flex-col rounded-xl items-center justify-center p-2'
accessibilityLabel={t("accessibility.toggle_orientation")} accessibilityLabel='Toggle screen orientation'
accessibilityHint={t("accessibility.toggle_orientation_hint")} accessibilityHint='Toggles the screen orientation between portrait and landscape'
> >
<MaterialIcons <MaterialIcons
name='screen-rotation' name='screen-rotation'

View File

@@ -7,7 +7,6 @@ import {
useMemo, useMemo,
useState, useState,
} from "react"; } from "react";
import { useTranslation } from "react-i18next";
import { Platform, StyleSheet, Text, View } from "react-native"; import { Platform, StyleSheet, Text, View } from "react-native";
import Animated, { import Animated, {
Easing, Easing,
@@ -185,7 +184,6 @@ export const TechnicalInfoOverlay: FC<TechnicalInfoOverlayProps> = memo(
currentAudioIndex, currentAudioIndex,
}) => { }) => {
const typography = useScaledTVTypography(); const typography = useScaledTVTypography();
const { t } = useTranslation();
const insets = useSafeAreaInsets(); const insets = useSafeAreaInsets();
const safeInsets = useControlsSafeAreaInsets(); const safeInsets = useControlsSafeAreaInsets();
const [info, setInfo] = useState<TechnicalInfo | null>(null); const [info, setInfo] = useState<TechnicalInfo | null>(null);
@@ -314,13 +312,13 @@ export const TechnicalInfoOverlay: FC<TechnicalInfoOverlayProps> = memo(
)} )}
{info?.videoCodec && ( {info?.videoCodec && (
<Text style={textStyle}> <Text style={textStyle}>
{t("player.technical_info.video")} {formatCodec(info.videoCodec)} Video: {formatCodec(info.videoCodec)}
{info.fps ? ` @ ${formatFps(info.fps)} fps` : ""} {info.fps ? ` @ ${formatFps(info.fps)} fps` : ""}
</Text> </Text>
)} )}
{info?.audioCodec && ( {info?.audioCodec && (
<Text style={textStyle}> <Text style={textStyle}>
{t("player.technical_info.audio")} {formatCodec(info.audioCodec)} Audio: {formatCodec(info.audioCodec)}
{streamInfo?.audioChannels {streamInfo?.audioChannels
? ` ${formatAudioChannels(streamInfo.audioChannels)}` ? ` ${formatAudioChannels(streamInfo.audioChannels)}`
: ""} : ""}
@@ -328,13 +326,12 @@ export const TechnicalInfoOverlay: FC<TechnicalInfoOverlayProps> = memo(
)} )}
{streamInfo?.subtitleCodec && ( {streamInfo?.subtitleCodec && (
<Text style={textStyle}> <Text style={textStyle}>
{t("player.technical_info.subtitle")}{" "} Subtitle: {formatCodec(streamInfo.subtitleCodec)}
{formatCodec(streamInfo.subtitleCodec)}
</Text> </Text>
)} )}
{(info?.videoBitrate || info?.audioBitrate) && ( {(info?.videoBitrate || info?.audioBitrate) && (
<Text style={textStyle}> <Text style={textStyle}>
{t("player.technical_info.bitrate")}{" "} Bitrate:{" "}
{info.videoBitrate {info.videoBitrate
? formatBitrate(info.videoBitrate) ? formatBitrate(info.videoBitrate)
: info.audioBitrate : info.audioBitrate
@@ -344,9 +341,7 @@ export const TechnicalInfoOverlay: FC<TechnicalInfoOverlayProps> = memo(
)} )}
{info?.cacheSeconds !== undefined && ( {info?.cacheSeconds !== undefined && (
<Text style={textStyle}> <Text style={textStyle}>
{t("player.technical_info.buffer_seconds", { Buffer: {info.cacheSeconds.toFixed(1)}s
seconds: info.cacheSeconds.toFixed(1),
})}
{info?.demuxerMaxBytes !== undefined {info?.demuxerMaxBytes !== undefined
? ` (cap ${info.demuxerMaxBytes}MB` + ? ` (cap ${info.demuxerMaxBytes}MB` +
`${info.demuxerMaxBackBytes !== undefined ? ` / ${info.demuxerMaxBackBytes}MB back` : ""}` + `${info.demuxerMaxBackBytes !== undefined ? ` / ${info.demuxerMaxBackBytes}MB back` : ""}` +
@@ -357,7 +352,7 @@ export const TechnicalInfoOverlay: FC<TechnicalInfoOverlayProps> = memo(
)} )}
{info?.voDriver && ( {info?.voDriver && (
<Text style={textStyle}> <Text style={textStyle}>
{t("player.technical_info.vo")} {info.voDriver} VO: {info.voDriver}
{info.hwdec ? ` / ${info.hwdec}` : ""} {info.hwdec ? ` / ${info.hwdec}` : ""}
</Text> </Text>
)} )}
@@ -369,14 +364,10 @@ export const TechnicalInfoOverlay: FC<TechnicalInfoOverlayProps> = memo(
)} )}
{info?.droppedFrames !== undefined && info.droppedFrames > 0 && ( {info?.droppedFrames !== undefined && info.droppedFrames > 0 && (
<Text style={[textStyle, styles.warningText]}> <Text style={[textStyle, styles.warningText]}>
{t("player.technical_info.dropped_frames", { Dropped: {info.droppedFrames} frames
count: info.droppedFrames,
})}
</Text> </Text>
)} )}
{!info && !playMethod && ( {!info && !playMethod && <Text style={textStyle}>Loading...</Text>}
<Text style={textStyle}>{t("player.technical_info.loading")}</Text>
)}
</View> </View>
</Animated.View> </Animated.View>
); );

View File

@@ -1,6 +1,5 @@
import { Ionicons } from "@expo/vector-icons"; import { Ionicons } from "@expo/vector-icons";
import React, { useMemo } from "react"; import React, { useMemo } from "react";
import { useTranslation } from "react-i18next";
import { Platform, View } from "react-native"; import { Platform, View } from "react-native";
import { import {
type OptionGroup, type OptionGroup,
@@ -55,7 +54,6 @@ export const AspectRatioSelector: React.FC<AspectRatioSelectorProps> = ({
onRatioChange, onRatioChange,
disabled = false, disabled = false,
}) => { }) => {
const { t } = useTranslation();
const lightHapticFeedback = useHaptic("light"); const lightHapticFeedback = useHaptic("light");
const handleRatioSelect = (ratio: AspectRatio) => { const handleRatioSelect = (ratio: AspectRatio) => {
@@ -68,10 +66,7 @@ export const AspectRatioSelector: React.FC<AspectRatioSelectorProps> = ({
{ {
options: ASPECT_RATIO_OPTIONS.map((option) => ({ options: ASPECT_RATIO_OPTIONS.map((option) => ({
type: "radio" as const, type: "radio" as const,
label: label: option.label,
option.id === "default"
? t("player.aspect_ratio_original")
: option.label,
value: option.id, value: option.id,
selected: option.id === currentRatio, selected: option.id === currentRatio,
onPress: () => handleRatioSelect(option.id), onPress: () => handleRatioSelect(option.id),
@@ -99,7 +94,7 @@ export const AspectRatioSelector: React.FC<AspectRatioSelectorProps> = ({
return ( return (
<PlatformDropdown <PlatformDropdown
title={t("player.aspect_ratio")} title='Aspect Ratio'
groups={optionGroups} groups={optionGroups}
trigger={trigger} trigger={trigger}
bottomSheetConfig={{ bottomSheetConfig={{

View File

@@ -1,7 +1,6 @@
import { Ionicons } from "@expo/vector-icons"; import { Ionicons } from "@expo/vector-icons";
import { useLocalSearchParams } from "expo-router"; import { useLocalSearchParams } from "expo-router";
import { useCallback, useMemo, useRef } from "react"; import { useCallback, useMemo, useRef } from "react";
import { useTranslation } from "react-i18next";
import { Platform, View } from "react-native"; import { Platform, View } from "react-native";
import { BITRATES } from "@/components/BitrateSelector"; import { BITRATES } from "@/components/BitrateSelector";
import { import {
@@ -48,7 +47,6 @@ const DropdownView = ({
const { settings, updateSettings } = useSettings(); const { settings, updateSettings } = useSettings();
const router = useRouter(); const router = useRouter();
const isOffline = useOfflineMode(); const isOffline = useOfflineMode();
const { t } = useTranslation();
const { subtitleIndex, audioIndex, bitrateValue, playbackPosition } = const { subtitleIndex, audioIndex, bitrateValue, playbackPosition } =
useLocalSearchParams<{ useLocalSearchParams<{
@@ -103,7 +101,7 @@ const DropdownView = ({
// Quality Section // Quality Section
if (!isOffline) { if (!isOffline) {
groups.push({ groups.push({
title: t("player.menu.quality"), title: "Quality",
options: options:
BITRATES?.map((bitrate) => ({ BITRATES?.map((bitrate) => ({
type: "radio" as const, type: "radio" as const,
@@ -118,7 +116,7 @@ const DropdownView = ({
// Subtitle Section // Subtitle Section
if (subtitleTracks && subtitleTracks.length > 0) { if (subtitleTracks && subtitleTracks.length > 0) {
groups.push({ groups.push({
title: t("player.menu.subtitles"), title: "Subtitles",
options: subtitleTracks.map((sub) => ({ options: subtitleTracks.map((sub) => ({
type: "radio" as const, type: "radio" as const,
label: sub.name, label: sub.name,
@@ -130,7 +128,7 @@ const DropdownView = ({
// Subtitle Scale Section // Subtitle Scale Section
groups.push({ groups.push({
title: t("player.menu.subtitle_scale"), title: "Subtitle Scale",
options: SUBTITLE_SCALE_PRESETS.map((preset) => ({ options: SUBTITLE_SCALE_PRESETS.map((preset) => ({
type: "radio" as const, type: "radio" as const,
label: preset.label, label: preset.label,
@@ -144,7 +142,7 @@ const DropdownView = ({
// Audio Section // Audio Section
if (audioTracks && audioTracks.length > 0) { if (audioTracks && audioTracks.length > 0) {
groups.push({ groups.push({
title: t("player.menu.audio"), title: "Audio",
options: audioTracks.map((track) => ({ options: audioTracks.map((track) => ({
type: "radio" as const, type: "radio" as const,
label: track.name, label: track.name,
@@ -158,7 +156,7 @@ const DropdownView = ({
// Speed Section // Speed Section
if (setPlaybackSpeed) { if (setPlaybackSpeed) {
groups.push({ groups.push({
title: t("player.menu.speed"), title: "Speed",
options: PLAYBACK_SPEEDS.map((speed) => ({ options: PLAYBACK_SPEEDS.map((speed) => ({
type: "radio" as const, type: "radio" as const,
label: speed.label, label: speed.label,
@@ -176,8 +174,8 @@ const DropdownView = ({
{ {
type: "action" as const, type: "action" as const,
label: showTechnicalInfo label: showTechnicalInfo
? t("player.menu.hide_technical_info") ? "Hide Technical Info"
: t("player.menu.show_technical_info"), : "Show Technical Info",
onPress: onToggleTechnicalInfo, onPress: onToggleTechnicalInfo,
}, },
], ],
@@ -187,7 +185,6 @@ const DropdownView = ({
return groups; return groups;
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [ }, [
t,
isOffline, isOffline,
bitrateValue, bitrateValue,
changeBitrate, changeBitrate,
@@ -220,7 +217,7 @@ const DropdownView = ({
return ( return (
<PlatformDropdown <PlatformDropdown
title={t("player.menu.playback_options")} title='Playback Options'
groups={optionGroups} groups={optionGroups}
trigger={trigger} trigger={trigger}
expoUIConfig={{}} expoUIConfig={{}}

View File

@@ -3,7 +3,6 @@ import { Alert } from "react-native";
import { type SharedValue, useSharedValue } from "react-native-reanimated"; import { type SharedValue, useSharedValue } from "react-native-reanimated";
import { useTVBackPress } from "@/hooks/useTVBackPress"; import { useTVBackPress } from "@/hooks/useTVBackPress";
import { useTVEventHandler } from "@/hooks/useTVEventHandler"; import { useTVEventHandler } from "@/hooks/useTVEventHandler";
import i18n from "@/i18n";
interface UseRemoteControlProps { interface UseRemoteControlProps {
showControls: boolean; showControls: boolean;
@@ -125,23 +124,17 @@ export function useRemoteControl({
// Controls are hidden, so confirm before leaving playback. // Controls are hidden, so confirm before leaving playback.
Alert.alert( Alert.alert(
i18n.t("player.stopPlayback"), "Stop Playback",
videoTitleRef.current videoTitleRef.current
? i18n.t("player.stopPlayingTitle", { ? `Stop playing "${videoTitleRef.current}"?`
title: videoTitleRef.current, : "Are you sure you want to stop playback?",
})
: i18n.t("player.stopPlayingConfirm"),
[ [
{ {
text: i18n.t("common.cancel"), text: "Cancel",
style: "cancel", style: "cancel",
onPress: () => onCancelExitRef.current?.(), onPress: () => onCancelExitRef.current?.(),
}, },
{ { text: "Stop", style: "destructive", onPress: onBackRef.current },
text: i18n.t("common.stop"),
style: "destructive",
onPress: onBackRef.current,
},
], ],
); );
return true; return true;

View File

@@ -143,7 +143,7 @@ export class JellyseerrApi {
if (inRange(status, 200, 299)) { if (inRange(status, 200, 299)) {
if (data.version < "2.0.0") { if (data.version < "2.0.0") {
const error = t( const error = t(
"jellyseerr.toasts.jellyseerr_does_not_meet_requirements", "jellyseerr.toasts.jellyseer_does_not_meet_requirements",
); );
toast.error(error); toast.error(error);
throw Error(error); throw Error(error);

View File

@@ -1,125 +1,5 @@
import { useCallback, useEffect, useState } from "react"; export {
import { Platform } from "react-native"; type PermissionStatus,
import { getSSID } from "@/modules/wifi-ssid"; type UseWifiSSIDReturn,
useWifiSsid as useWifiSSID,
export type PermissionStatus = } from "@/providers/WifiSsidProvider";
| "granted"
| "denied"
| "undetermined"
| "unavailable";
export interface UseWifiSSIDReturn {
ssid: string | null;
permissionStatus: PermissionStatus;
requestPermission: () => Promise<boolean>;
isLoading: boolean;
}
// WiFi SSID is not available on tvOS
if (Platform.isTV) {
// Export a stub hook for tvOS
module.exports = {
useWifiSSID: (): UseWifiSSIDReturn => ({
ssid: null,
permissionStatus: "unavailable" as PermissionStatus,
requestPermission: async () => false,
isLoading: false,
}),
};
}
// Only import Location on non-TV platforms
const Location = Platform.isTV ? null : require("expo-location");
function mapLocationStatus(status: number | undefined): PermissionStatus {
if (!Location) return "unavailable";
switch (status) {
case Location.PermissionStatus?.GRANTED:
return "granted";
case Location.PermissionStatus?.DENIED:
return "denied";
default:
return "undetermined";
}
}
export function useWifiSSID(): UseWifiSSIDReturn {
const [ssid, setSSID] = useState<string | null>(null);
const [permissionStatus, setPermissionStatus] = useState<PermissionStatus>(
Platform.isTV ? "unavailable" : "undetermined",
);
const [isLoading, setIsLoading] = useState(!Platform.isTV);
const fetchSSID = useCallback(async () => {
if (Platform.isTV) return;
const result = await getSSID();
setSSID(result);
}, []);
const requestPermission = useCallback(async (): Promise<boolean> => {
if (Platform.isTV || !Location) {
setPermissionStatus("unavailable");
return false;
}
try {
const { status } = await Location.requestForegroundPermissionsAsync();
const newStatus = mapLocationStatus(status);
setPermissionStatus(newStatus);
if (newStatus === "granted") {
await fetchSSID();
}
return newStatus === "granted";
} catch {
setPermissionStatus("unavailable");
return false;
}
}, [fetchSSID]);
useEffect(() => {
if (Platform.isTV || !Location) {
setIsLoading(false);
return;
}
async function initialize() {
setIsLoading(true);
try {
const { status } = await Location.getForegroundPermissionsAsync();
const mappedStatus = mapLocationStatus(status);
setPermissionStatus(mappedStatus);
if (mappedStatus === "granted") {
await fetchSSID();
}
} catch {
setPermissionStatus("unavailable");
}
setIsLoading(false);
}
initialize();
}, [fetchSSID]);
// Refresh SSID when permission status changes to granted
useEffect(() => {
if (Platform.isTV) return;
if (permissionStatus === "granted") {
fetchSSID();
// Also set up an interval to periodically check SSID
const interval = setInterval(fetchSSID, 10000); // Check every 10 seconds
return () => clearInterval(interval);
}
}, [permissionStatus, fetchSSID]);
return {
ssid,
permissionStatus,
requestPermission,
isLoading,
};
}

View File

@@ -0,0 +1,19 @@
apply plugin: 'expo-module-gradle-plugin'
group = 'expo.modules.wifissid'
version = '1.0.0'
expoModule {
canBePublished false
}
android {
namespace "expo.modules.wifissid"
defaultConfig {
versionCode 1
versionName "1.0.0"
}
}
dependencies {
}

View File

@@ -0,0 +1,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Reading the connected Wi-Fi SSID requires ACCESS_WIFI_STATE, and (since
Android 8) ACCESS_FINE_LOCATION granted at runtime. Location is requested
at runtime by the app via expo-location; the manifest declares the
Wi-Fi/network permissions this module needs directly. -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
</manifest>

View File

@@ -0,0 +1,121 @@
package expo.modules.wifissid
import android.Manifest
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.net.wifi.WifiInfo
import android.net.wifi.WifiManager
import android.os.Build
import android.provider.Settings
import android.util.Log
import expo.modules.kotlin.modules.Module
import expo.modules.kotlin.modules.ModuleDefinition
class WifiSsidModule : Module() {
companion object {
private const val TAG = "WifiSsid"
private const val UNKNOWN_SSID = "<unknown ssid>"
}
private val context
get() = requireNotNull(appContext.reactContext)
override fun definition() = ModuleDefinition {
Name("WifiSsid")
AsyncFunction("getSSID") {
val (ssid, connectedToWifi) = readWifi()
mapOf(
"ssid" to ssid,
"connectedToWifi" to connectedToWifi,
)
}
Function("getSSIDSync") {
val (ssid, connectedToWifi) = readWifi()
mapOf(
"ssid" to ssid,
"connectedToWifi" to connectedToWifi,
)
}
Function("openLocationSettings") {
openLocationSettings()
}
}
/**
* Returns the current Wi-Fi SSID (or null when it can't be read) and whether
* the device is connected to a Wi-Fi network. When the device is connected but
* the SSID is null, the OS is withholding the network name — on Android this
* typically means device Location services are off (the SSID is treated as
* location-sensitive).
*/
private fun readWifi(): Pair<String?, Boolean> {
val androidContext = context.applicationContext
var connectedToWifi = false
var ssid: String? = null
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
try {
val cm = androidContext.getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager
val caps = cm?.let { manager ->
manager.activeNetwork?.let { net -> manager.getNetworkCapabilities(net) }
}
connectedToWifi = caps?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) == true
if (hasFineLocationPermission()) {
ssid = (caps?.transportInfo as? WifiInfo)?.ssid?.sanitize()
}
} catch (e: Exception) {
Log.e(TAG, "Error reading Wi-Fi via ConnectivityManager", e)
}
} else {
// Pre-Q (API 2628): NetworkCapabilities/transportInfo aren't available, so
// detect a Wi-Fi connection via the legacy active-network API. Without this,
// connectedToWifi stays false on Android 8/9 even when connected, so the
// "connected but SSID hidden" state never surfaces there.
try {
val cm = androidContext.getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager
@Suppress("DEPRECATION")
connectedToWifi = cm?.activeNetworkInfo?.type == ConnectivityManager.TYPE_WIFI
} catch (e: Exception) {
Log.e(TAG, "Error reading Wi-Fi via ConnectivityManager (legacy)", e)
}
}
if (ssid == null && hasFineLocationPermission()) {
try {
val wm = androidContext.getSystemService(Context.WIFI_SERVICE) as? WifiManager
@Suppress("DEPRECATION")
ssid = wm?.connectionInfo?.ssid?.sanitize()
} catch (e: Exception) {
Log.e(TAG, "Error reading SSID via WifiManager", e)
}
}
return Pair(ssid, connectedToWifi)
}
private fun openLocationSettings() {
try {
val intent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
context.applicationContext.startActivity(intent)
} catch (e: Exception) {
Log.e(TAG, "Unable to open Location settings", e)
}
}
private fun hasFineLocationPermission(): Boolean =
context.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) ==
PackageManager.PERMISSION_GRANTED
private fun String.sanitize(): String? {
val cleaned = trim('"')
return if (cleaned.isBlank() || cleaned == UNKNOWN_SSID) null else cleaned
}
}

View File

@@ -1,8 +1,11 @@
{ {
"name": "wifi-ssid", "name": "wifi-ssid",
"version": "1.0.0", "version": "1.0.0",
"platforms": ["ios"], "platforms": ["ios", "android"],
"ios": { "ios": {
"modules": ["WifiSsidModule"] "modules": ["WifiSsidModule"]
},
"android": {
"modules": ["expo.modules.wifissid.WifiSsidModule"]
} }
} }

View File

@@ -1,44 +1,92 @@
import { Platform, requireNativeModule } from "expo-modules-core"; import { Platform, requireNativeModule } from "expo-modules-core";
import { Linking } from "react-native";
// Only load the native module on iOS export interface WifiSsidResult {
const WifiSsidModule = ssid: string | null;
Platform.OS === "ios" ? requireNativeModule("WifiSsid") : null; /**
* Whether the device is connected to a Wi-Fi network. When true but `ssid` is
* null, the OS is withholding the network name — on Android this happens when
* device Location services are off (the SSID is location-sensitive).
*/
connectedToWifi: boolean;
}
// Load the native module on iOS and Android (not TV/web). Wrapped so a
// missing/unlinked module degrades to null instead of crashing the bundle.
const WifiSsidModule = (() => {
if (Platform.OS !== "ios" && Platform.OS !== "android") return null;
try {
return requireNativeModule("WifiSsid");
} catch {
return null;
}
})();
/** /**
* Get the current WiFi SSID on iOS. * Get the current WiFi SSID and whether the device is on a Wi-Fi network.
* Returns null on Android or if not connected to WiFi.
* *
* Requires: * iOS requirements:
* - Location permission granted * - Location permission granted
* - com.apple.developer.networking.wifi-info entitlement * - com.apple.developer.networking.wifi-info entitlement
* - Access WiFi Information capability enabled in Apple Developer Portal *
* Android requirements:
* - ACCESS_FINE_LOCATION granted at runtime, and device Location services on
*/ */
export async function getSSID(): Promise<string | null> { export async function getSSID(): Promise<WifiSsidResult> {
if (!WifiSsidModule) { if (!WifiSsidModule) {
return null; return { ssid: null, connectedToWifi: false };
} }
try { try {
const ssid = await WifiSsidModule.getSSID(); return normalize(await WifiSsidModule.getSSID());
return ssid ?? null;
} catch (error) { } catch (error) {
console.error("[WifiSsid] Error getting SSID:", error); console.error("[WifiSsid] Error getting SSID:", error);
return null; return { ssid: null, connectedToWifi: false };
}
}
/** Synchronous version (uses the older CNCopyCurrentNetworkInfo API on iOS). */
export function getSSIDSync(): WifiSsidResult {
if (!WifiSsidModule) {
return { ssid: null, connectedToWifi: false };
}
try {
return normalize(WifiSsidModule.getSSIDSync());
} catch (error) {
console.error("[WifiSsid] Error getting SSID (sync):", error);
return { ssid: null, connectedToWifi: false };
} }
} }
/** /**
* Synchronous version - uses older CNCopyCurrentNetworkInfo API * Open the system Location settings so the user can enable Location services
* (required on Android for the OS to reveal the Wi-Fi SSID). Falls back to the
* app's settings page where there is no dedicated native action (e.g. iOS).
*/ */
export function getSSIDSync(): string | null { export function openLocationSettings(): void {
if (!WifiSsidModule) { try {
return null; if (WifiSsidModule?.openLocationSettings) {
WifiSsidModule.openLocationSettings();
return;
}
} catch (error) {
console.error("[WifiSsid] Error opening location settings:", error);
}
Linking.openSettings();
} }
try { // Both native modules return { ssid, connectedToWifi }. The string branch is a
return WifiSsidModule.getSSIDSync() ?? null; // defensive fallback in case a build returns the older plain-string shape.
} catch (error) { function normalize(result: unknown): WifiSsidResult {
console.error("[WifiSsid] Error getting SSID (sync):", error); if (typeof result === "string") {
return null; const ssid = result || null;
return { ssid, connectedToWifi: ssid !== null };
} }
if (result && typeof result === "object") {
const obj = result as { ssid?: string | null; connectedToWifi?: boolean };
return {
ssid: obj.ssid ?? null,
connectedToWifi: Boolean(obj.connectedToWifi),
};
}
return { ssid: null, connectedToWifi: false };
} }

View File

@@ -1,50 +1,113 @@
import ExpoModulesCore import ExpoModulesCore
#if !os(tvOS) #if !os(tvOS)
import Network
import NetworkExtension import NetworkExtension
import SystemConfiguration.CaptiveNetwork import SystemConfiguration.CaptiveNetwork
#endif #endif
import UIKit
public class WifiSsidModule: Module { public class WifiSsidModule: Module {
#if !os(tvOS)
// NWPathMonitor reports whether the active path uses Wi-Fi WITHOUT requiring
// the wifi-info entitlement, so we can tell "on Wi-Fi but can't read the SSID"
// apart from "not connected to Wi-Fi" even when fetchCurrent returns nil.
private let pathMonitor = NWPathMonitor()
private let monitorQueue = DispatchQueue(label: "expo.modules.wifissid.pathmonitor")
private let lock = NSLock()
private var cachedOnWifi = false
private var pathMonitorStarted = false
private var isOnWifi: Bool {
lock.lock()
defer { lock.unlock() }
return cachedOnWifi
}
private func startPathMonitorIfNeeded() {
lock.lock()
if pathMonitorStarted {
lock.unlock()
return
}
pathMonitorStarted = true
lock.unlock()
pathMonitor.pathUpdateHandler = { [weak self] path in
guard let self else { return }
let onWifi = path.status == .satisfied && path.usesInterfaceType(.wifi)
self.lock.lock()
self.cachedOnWifi = onWifi
self.lock.unlock()
}
pathMonitor.start(queue: monitorQueue)
}
deinit {
pathMonitor.cancel()
}
#endif
public func definition() -> ModuleDefinition { public func definition() -> ModuleDefinition {
Name("WifiSsid") Name("WifiSsid")
// Get current WiFi SSID using NEHotspotNetwork (iOS 14+) AsyncFunction("getSSID") { () -> [String: Any] in
// Not available on tvOS
AsyncFunction("getSSID") { () -> String? in
#if os(tvOS) #if os(tvOS)
return nil return ["ssid": NSNull(), "connectedToWifi": false]
#else #else
return await withCheckedContinuation { continuation in self.startPathMonitorIfNeeded()
NEHotspotNetwork.fetchCurrent { network in let ssid = await self.resolveSSID()
if let ssid = network?.ssid { var result: [String: Any] = ["connectedToWifi": self.isOnWifi]
print("[WifiSsid] Got SSID via NEHotspotNetwork: \(ssid)") if let ssid = ssid {
continuation.resume(returning: ssid) result["ssid"] = ssid
} else { } else {
// Fallback to CNCopyCurrentNetworkInfo for older iOS result["ssid"] = NSNull()
print("[WifiSsid] NEHotspotNetwork returned nil, trying CNCopyCurrentNetworkInfo")
let ssid = self.getSSIDViaCNCopy()
continuation.resume(returning: ssid)
}
}
} }
return result
#endif #endif
} }
// Synchronous version using only CNCopyCurrentNetworkInfo Function("getSSIDSync") { () -> [String: Any] in
// Not available on tvOS
Function("getSSIDSync") { () -> String? in
#if os(tvOS) #if os(tvOS)
return nil return ["ssid": NSNull(), "connectedToWifi": false]
#else #else
return self.getSSIDViaCNCopy() self.startPathMonitorIfNeeded()
let ssid = self.getSSIDViaCNCopy()
var result: [String: Any] = ["connectedToWifi": self.isOnWifi]
if let ssid = ssid {
result["ssid"] = ssid
} else {
result["ssid"] = NSNull()
}
return result
#endif
}
Function("openLocationSettings") {
#if !os(tvOS)
DispatchQueue.main.async {
if let url = URL(string: UIApplication.openSettingsURLString) {
UIApplication.shared.open(url)
}
}
#endif #endif
} }
} }
#if !os(tvOS) #if !os(tvOS)
private func resolveSSID() async -> String? {
await withCheckedContinuation { continuation in
NEHotspotNetwork.fetchCurrent { network in
if let ssid = network?.ssid {
continuation.resume(returning: ssid)
} else {
continuation.resume(returning: self.getSSIDViaCNCopy())
}
}
}
}
private func getSSIDViaCNCopy() -> String? { private func getSSIDViaCNCopy() -> String? {
guard let interfaces = CNCopySupportedInterfaces() as? [String] else { guard let interfaces = CNCopySupportedInterfaces() as? [String] else {
print("[WifiSsid] CNCopySupportedInterfaces returned nil")
return nil return nil
} }
@@ -54,12 +117,10 @@ public class WifiSsidModule: Module {
} }
if let ssid = networkInfo[kCNNetworkInfoKeySSID as String] as? String { if let ssid = networkInfo[kCNNetworkInfoKeySSID as String] as? String {
print("[WifiSsid] Got SSID via CNCopyCurrentNetworkInfo: \(ssid)")
return ssid return ssid
} }
} }
print("[WifiSsid] No SSID found via CNCopyCurrentNetworkInfo")
return nil return nil
} }
#endif #endif

View File

@@ -6,6 +6,7 @@ import {
useCallback, useCallback,
useContext, useContext,
useEffect, useEffect,
useMemo,
useRef, useRef,
useState, useState,
} from "react"; } from "react";
@@ -18,6 +19,7 @@ interface ServerUrlContextValue {
effectiveServerUrl: string | null; effectiveServerUrl: string | null;
isUsingLocalUrl: boolean; isUsingLocalUrl: boolean;
currentSSID: string | null; currentSSID: string | null;
connectedToWifi: boolean;
refreshUrlState: () => void; refreshUrlState: () => void;
} }
@@ -32,7 +34,7 @@ interface Props {
export function ServerUrlProvider({ children }: Props): React.ReactElement { export function ServerUrlProvider({ children }: Props): React.ReactElement {
const api = useAtomValue(apiAtom); const api = useAtomValue(apiAtom);
const { switchServerUrl } = useJellyfin(); const { switchServerUrl } = useJellyfin();
const { ssid, permissionStatus } = useWifiSSID(); const { ssid, connectedToWifi, permissionStatus } = useWifiSSID();
const [isUsingLocalUrl, setIsUsingLocalUrl] = useState(false); const [isUsingLocalUrl, setIsUsingLocalUrl] = useState(false);
const [effectiveServerUrl, setEffectiveServerUrl] = useState<string | null>( const [effectiveServerUrl, setEffectiveServerUrl] = useState<string | null>(
@@ -101,15 +103,25 @@ export function ServerUrlProvider({ children }: Props): React.ReactElement {
}; };
}, [ssid, permissionStatus, evaluateAndSwitchUrl]); }, [ssid, permissionStatus, evaluateAndSwitchUrl]);
return ( const value = useMemo(
<ServerUrlContext.Provider () => ({
value={{
effectiveServerUrl, effectiveServerUrl,
isUsingLocalUrl, isUsingLocalUrl,
currentSSID: ssid, currentSSID: ssid,
connectedToWifi,
refreshUrlState, refreshUrlState,
}} }),
> [
effectiveServerUrl,
isUsingLocalUrl,
ssid,
connectedToWifi,
refreshUrlState,
],
);
return (
<ServerUrlContext.Provider value={value}>
{children} {children}
</ServerUrlContext.Provider> </ServerUrlContext.Provider>
); );

View File

@@ -0,0 +1,157 @@
import type React from "react";
import {
createContext,
type ReactNode,
useCallback,
useContext,
useEffect,
useMemo,
useState,
} from "react";
import { Platform } from "react-native";
import { getSSID } from "@/modules/wifi-ssid";
export type PermissionStatus =
| "granted"
| "denied"
| "undetermined"
| "unavailable";
export interface UseWifiSSIDReturn {
ssid: string | null;
connectedToWifi: boolean;
permissionStatus: PermissionStatus;
requestPermission: () => Promise<boolean>;
isLoading: boolean;
}
const WifiSsidContext = createContext<UseWifiSSIDReturn | null>(null);
// WiFi SSID is not available on tvOS, and expo-location isn't needed there.
const Location = Platform.isTV ? null : require("expo-location");
const POLL_INTERVAL_MS = 10_000;
function mapLocationStatus(status: number | undefined): PermissionStatus {
if (!Location) return "unavailable";
switch (status) {
case Location.PermissionStatus?.GRANTED:
return "granted";
case Location.PermissionStatus?.DENIED:
return "denied";
default:
return "undetermined";
}
}
interface Props {
children: ReactNode;
}
/**
* Single source of truth for the current WiFi SSID and the location permission
* that gates it. State is shared via context so that granting permission in one
* place (e.g. the network settings screen) is reflected everywhere else — most
* importantly ServerUrlProvider, which switches between the local and remote
* server URLs based on the connected SSID.
*
* Previously each consumer created its own useWifiSSID instance with independent
* state, so the provider never learned that permission had been granted later
* in the session and reported "not connected to WiFi" forever.
*/
export function WifiSsidProvider({ children }: Props): React.ReactElement {
const [ssid, setSSID] = useState<string | null>(null);
const [connectedToWifi, setConnectedToWifi] = useState(false);
const [permissionStatus, setPermissionStatus] = useState<PermissionStatus>(
Platform.isTV ? "unavailable" : "undetermined",
);
const [isLoading, setIsLoading] = useState(!Platform.isTV);
const fetchSSID = useCallback(async () => {
if (Platform.isTV) return;
const result = await getSSID();
setSSID(result.ssid);
setConnectedToWifi(result.connectedToWifi);
}, []);
const requestPermission = useCallback(async (): Promise<boolean> => {
if (Platform.isTV || !Location) {
setPermissionStatus("unavailable");
return false;
}
try {
const { status } = await Location.requestForegroundPermissionsAsync();
const newStatus = mapLocationStatus(status);
setPermissionStatus(newStatus);
return newStatus === "granted";
} catch {
setPermissionStatus("unavailable");
return false;
}
}, []);
// Check the location permission once on mount. The poll effect below takes
// over fetching the SSID whenever permission becomes "granted".
useEffect(() => {
if (Platform.isTV || !Location) {
setIsLoading(false);
return;
}
let cancelled = false;
async function initialize() {
setIsLoading(true);
try {
const { status } = await Location.getForegroundPermissionsAsync();
if (cancelled) return;
setPermissionStatus(mapLocationStatus(status));
} catch {
if (!cancelled) setPermissionStatus("unavailable");
}
if (!cancelled) setIsLoading(false);
}
initialize();
return () => {
cancelled = true;
};
}, []);
// Fetch the SSID immediately, then poll, whenever permission is granted.
// Because permissionStatus is shared, this also covers the case where
// permission was granted later (from another consumer) than this provider's
// mount time — the original root cause of the "not connected to WiFi" bug.
useEffect(() => {
if (Platform.isTV || permissionStatus !== "granted") return;
fetchSSID();
const interval = setInterval(fetchSSID, POLL_INTERVAL_MS);
return () => clearInterval(interval);
}, [permissionStatus, fetchSSID]);
const value = useMemo(
() => ({
ssid,
connectedToWifi,
permissionStatus,
requestPermission,
isLoading,
}),
[ssid, connectedToWifi, permissionStatus, requestPermission, isLoading],
);
return (
<WifiSsidContext.Provider value={value}>
{children}
</WifiSsidContext.Provider>
);
}
export function useWifiSsid(): UseWifiSSIDReturn {
const context = useContext(WifiSsidContext);
if (!context) {
throw new Error("useWifiSsid must be used within WifiSsidProvider");
}
return context;
}

View File

@@ -16,14 +16,14 @@
"got_it": "حسنًا", "got_it": "حسنًا",
"connection_failed": "فشل الاتصال", "connection_failed": "فشل الاتصال",
"could_not_connect_to_server": "تعذر الاتصال بالخادم. يرجى التحقق من الرابط واتصال الشبكة.", "could_not_connect_to_server": "تعذر الاتصال بالخادم. يرجى التحقق من الرابط واتصال الشبكة.",
"an_unexpected_error_occured": "حدث خطأ غير متوقع", "an_unexpected_error_occurred": "حدث خطأ غير متوقع",
"change_server": "تغيير الخادم", "change_server": "تغيير الخادم",
"invalid_username_or_password": "اسم المستخدم أو كلمة المرور غير صالحة", "invalid_username_or_password": "اسم المستخدم أو كلمة المرور غير صالحة",
"user_does_not_have_permission_to_log_in": "ليس لدى المستخدم صَلاحِيَة تسجيل الدخول", "user_does_not_have_permission_to_log_in": "ليس لدى المستخدم صَلاحِيَة تسجيل الدخول",
"server_is_taking_too_long_to_respond_try_again_later": "يستغرق الخادم وقتًا طويلاً للرد، يرجى المحاولة مرة أخرى لاحقًا", "server_is_taking_too_long_to_respond_try_again_later": "يستغرق الخادم وقتًا طويلاً للرد، يرجى المحاولة مرة أخرى لاحقًا",
"server_received_too_many_requests_try_again_later": "تلقى الخادم عددًا كبيرًا جدًا من الطلبات، يرجى المحاولة مرة أخرى لاحقًا.", "server_received_too_many_requests_try_again_later": "تلقى الخادم عددًا كبيرًا جدًا من الطلبات، يرجى المحاولة مرة أخرى لاحقًا.",
"there_is_a_server_error": "هناك خطأ في الخادم", "there_is_a_server_error": "هناك خطأ في الخادم",
"an_unexpected_error_occured_did_you_enter_the_correct_url": "حدث خطأ غير متوقع. هل أدخلت رابط الخادم بشكل صحيح؟", "an_unexpected_error_occurred_did_you_enter_the_correct_url": "حدث خطأ غير متوقع. هل أدخلت رابط الخادم بشكل صحيح؟",
"too_old_server_text": "تم اكتشاف خادم Jellyfin غير مدعوم", "too_old_server_text": "تم اكتشاف خادم Jellyfin غير مدعوم",
"too_old_server_description": "يرجى تحديث Jellyfin إلى أحدث إصدار" "too_old_server_description": "يرجى تحديث Jellyfin إلى أحدث إصدار"
}, },
@@ -188,7 +188,7 @@
"authorize_button": "تفويض الدخول السريع", "authorize_button": "تفويض الدخول السريع",
"enter_the_quick_connect_code": "أدخل رمز الدخول السريع...", "enter_the_quick_connect_code": "أدخل رمز الدخول السريع...",
"success": "نجاح", "success": "نجاح",
"quick_connect_autorized": "تم تفويض الدخول السريع", "quick_connect_authorized": "تم تفويض الدخول السريع",
"error": "خطأ", "error": "خطأ",
"invalid_code": "رمز غير صالح", "invalid_code": "رمز غير صالح",
"authorize": "تفويض" "authorize": "تفويض"
@@ -298,7 +298,7 @@
"show_custom_menu_links": "إظهار روابط القائمة المخصصة", "show_custom_menu_links": "إظهار روابط القائمة المخصصة",
"show_large_home_carousel": "إظهار شريط العرض الكبير (تجريبي)", "show_large_home_carousel": "إظهار شريط العرض الكبير (تجريبي)",
"hide_libraries": "إخفاء المكتبات", "hide_libraries": "إخفاء المكتبات",
"select_liraries_you_want_to_hide": "اختر المكتبات التي تريد إخفاءها من تبويب المكتبة وأقسام الصفحة الرئيسية.", "select_libraries_you_want_to_hide": "اختر المكتبات التي تريد إخفاءها من تبويب المكتبة وأقسام الصفحة الرئيسية.",
"disable_haptic_feedback": "تعطيل ردود الفعل اللمسية", "disable_haptic_feedback": "تعطيل ردود الفعل اللمسية",
"default_quality": "الجودة الافتراضية", "default_quality": "الجودة الافتراضية",
"default_playback_speed": "سرعة التشغيل الافتراضية", "default_playback_speed": "سرعة التشغيل الافتراضية",
@@ -384,6 +384,8 @@
"device_usage": "الجهاز {{availableSpace}}%", "device_usage": "الجهاز {{availableSpace}}%",
"size_used": "تم استخدام {{used}} من {{total}}", "size_used": "تم استخدام {{used}} من {{total}}",
"delete_all_downloaded_files": "حذف جميع الملفات التي تم تنزيلها", "delete_all_downloaded_files": "حذف جميع الملفات التي تم تنزيلها",
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
"music_cache_title": "التخزين المؤقت للموسيقى", "music_cache_title": "التخزين المؤقت للموسيقى",
"music_cache_description": "تخزين الأغاني تلقائياً أثناء الاستماع لضمان تشغيل أكثر سلاسة ودعم الاستماع بدون اتصال", "music_cache_description": "تخزين الأغاني تلقائياً أثناء الاستماع لضمان تشغيل أكثر سلاسة ودعم الاستماع بدون اتصال",
"clear_music_cache": "مسح التخزين المؤقت للموسيقى", "clear_music_cache": "مسح التخزين المؤقت للموسيقى",
@@ -598,7 +600,7 @@
"mpv_player_title": "MPV Player", "mpv_player_title": "MPV 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": "حدث خطأ أثناء تشغيل الفيديو. تحقق من السجلات في الإعدادات.",
"client_error": "خطأ في المشغّل", "client_error": "خطأ في المشغّل",
"could_not_create_stream_for_chromecast": "تعذر إنشاء بث لـChromecast", "could_not_create_stream_for_chromecast": "تعذر إنشاء بث لـChromecast",
"message_from_server": "رسالة من الخادم: {{message}}", "message_from_server": "رسالة من الخادم: {{message}}",

View File

@@ -16,14 +16,14 @@
"got_it": "Entesos", "got_it": "Entesos",
"connection_failed": "Ha fallat la connexió", "connection_failed": "Ha fallat la connexió",
"could_not_connect_to_server": "No s'ha pogut connectar amb el servidor. Comproveu l'URL i la connexió de xarxa.", "could_not_connect_to_server": "No s'ha pogut connectar amb el servidor. Comproveu l'URL i la connexió de xarxa.",
"an_unexpected_error_occured": "S'ha produït un error inesperat", "an_unexpected_error_occurred": "S'ha produït un error inesperat",
"change_server": "Canvia el servidor", "change_server": "Canvia el servidor",
"invalid_username_or_password": "Nom d'usuari o contrasenya incorrectes", "invalid_username_or_password": "Nom d'usuari o contrasenya incorrectes",
"user_does_not_have_permission_to_log_in": "L'usuari no té permís per iniciar sessió", "user_does_not_have_permission_to_log_in": "L'usuari no té permís per iniciar sessió",
"server_is_taking_too_long_to_respond_try_again_later": "El servidor triga massa a respondre, torneu-ho a provar més tard", "server_is_taking_too_long_to_respond_try_again_later": "El servidor triga massa a respondre, torneu-ho a provar més tard",
"server_received_too_many_requests_try_again_later": "El servidor ha rebut massa sol·licituds, torneu-ho a provar més tard.", "server_received_too_many_requests_try_again_later": "El servidor ha rebut massa sol·licituds, torneu-ho a provar més tard.",
"there_is_a_server_error": "Error del servidor", "there_is_a_server_error": "Error del servidor",
"an_unexpected_error_occured_did_you_enter_the_correct_url": "S'ha produït un error inesperat. Heu introduït correctament l'URL del servidor?", "an_unexpected_error_occurred_did_you_enter_the_correct_url": "S'ha produït un error inesperat. Heu introduït correctament l'URL del servidor?",
"too_old_server_text": "Unsupported Jellyfin Server Discovered", "too_old_server_text": "Unsupported Jellyfin Server Discovered",
"too_old_server_description": "Please update Jellyfin to the latest version" "too_old_server_description": "Please update Jellyfin to the latest version"
}, },
@@ -188,7 +188,7 @@
"authorize_button": "Autoritza connexió ràpida", "authorize_button": "Autoritza connexió ràpida",
"enter_the_quick_connect_code": "Introdueix el codi de connexió ràpida...", "enter_the_quick_connect_code": "Introdueix el codi de connexió ràpida...",
"success": "Èxit", "success": "Èxit",
"quick_connect_autorized": "Connexió ràpida autoritzada", "quick_connect_authorized": "Connexió ràpida autoritzada",
"error": "Error", "error": "Error",
"invalid_code": "Codi invàlid", "invalid_code": "Codi invàlid",
"authorize": "Autoritza" "authorize": "Autoritza"
@@ -298,7 +298,7 @@
"show_custom_menu_links": "Mostrar enllaços del menú personalitzats", "show_custom_menu_links": "Mostrar enllaços del menú personalitzats",
"show_large_home_carousel": "Show Large Home Carousel (beta)", "show_large_home_carousel": "Show Large Home Carousel (beta)",
"hide_libraries": "Oculta biblioteques", "hide_libraries": "Oculta biblioteques",
"select_liraries_you_want_to_hide": "Seleccioneu les biblioteques que voleu ocultar de la pestanya Biblioteca i de les seccions de la pàgina d'inici.", "select_libraries_you_want_to_hide": "Seleccioneu les biblioteques que voleu ocultar de la pestanya Biblioteca i de les seccions de la pàgina d'inici.",
"disable_haptic_feedback": "Desactiva la resposta hàptica", "disable_haptic_feedback": "Desactiva la resposta hàptica",
"default_quality": "Qualitat per defecte", "default_quality": "Qualitat per defecte",
"default_playback_speed": "Default Playback Speed", "default_playback_speed": "Default Playback Speed",
@@ -384,6 +384,8 @@
"device_usage": "Dispositiu {{availableSpace}}%", "device_usage": "Dispositiu {{availableSpace}}%",
"size_used": "{{used}} de {{total}} utilitzat", "size_used": "{{used}} de {{total}} utilitzat",
"delete_all_downloaded_files": "Suprimeix tots els fitxers descarregats", "delete_all_downloaded_files": "Suprimeix tots els fitxers descarregats",
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
"music_cache_title": "Music Cache", "music_cache_title": "Music Cache",
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support", "music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
"clear_music_cache": "Clear Music Cache", "clear_music_cache": "Clear Music Cache",
@@ -598,7 +600,7 @@
"mpv_player_title": "MPV Player", "mpv_player_title": "MPV Player",
"error": "Error", "error": "Error",
"failed_to_get_stream_url": "No s'ha pogut obtenir l'URL del flux", "failed_to_get_stream_url": "No s'ha pogut obtenir l'URL del flux",
"an_error_occured_while_playing_the_video": "S'ha produït un error en reproduir el vídeo. Consulteu els registres a la configuració.", "an_error_occurred_while_playing_the_video": "S'ha produït un error en reproduir el vídeo. Consulteu els registres a la configuració.",
"client_error": "Error del client", "client_error": "Error del client",
"could_not_create_stream_for_chromecast": "No s'ha pogut crear un flux per a Chromecast", "could_not_create_stream_for_chromecast": "No s'ha pogut crear un flux per a Chromecast",
"message_from_server": "Missatge del servidor: {{message}}", "message_from_server": "Missatge del servidor: {{message}}",

View File

@@ -16,14 +16,14 @@
"got_it": "Rozumím", "got_it": "Rozumím",
"connection_failed": "Připojení se nezdařilo", "connection_failed": "Připojení se nezdařilo",
"could_not_connect_to_server": "Nelze se připojit k serveru. Zkontrolujte adresu URL a síťové připojení.", "could_not_connect_to_server": "Nelze se připojit k serveru. Zkontrolujte adresu URL a síťové připojení.",
"an_unexpected_error_occured": "Došlo k neočekávané chybě", "an_unexpected_error_occurred": "Došlo k neočekávané chybě",
"change_server": "Změnit server", "change_server": "Změnit server",
"invalid_username_or_password": "Neplatné uživatelské jméno nebo heslo", "invalid_username_or_password": "Neplatné uživatelské jméno nebo heslo",
"user_does_not_have_permission_to_log_in": "Uživatel nemá oprávnění k přihlášení", "user_does_not_have_permission_to_log_in": "Uživatel nemá oprávnění k přihlášení",
"server_is_taking_too_long_to_respond_try_again_later": "Reakce na server trvá příliš dlouho, zkuste to znovu později", "server_is_taking_too_long_to_respond_try_again_later": "Reakce na server trvá příliš dlouho, zkuste to znovu později",
"server_received_too_many_requests_try_again_later": "Server obdržel příliš mnoho požadavků, opakujte akci později.", "server_received_too_many_requests_try_again_later": "Server obdržel příliš mnoho požadavků, opakujte akci později.",
"there_is_a_server_error": "Došlo k chybě serveru", "there_is_a_server_error": "Došlo k chybě serveru",
"an_unexpected_error_occured_did_you_enter_the_correct_url": "Došlo k neočekávané chybě. Vložili jste adresu URL serveru?", "an_unexpected_error_occurred_did_you_enter_the_correct_url": "Došlo k neočekávané chybě. Vložili jste adresu URL serveru?",
"too_old_server_text": "Objeven nepodporovaný Jellyfin server", "too_old_server_text": "Objeven nepodporovaný Jellyfin server",
"too_old_server_description": "Prosím aktualizujte Jellyfin na nejnovější verzi" "too_old_server_description": "Prosím aktualizujte Jellyfin na nejnovější verzi"
}, },
@@ -188,7 +188,7 @@
"authorize_button": "Autorizovat rychlé připojení", "authorize_button": "Autorizovat rychlé připojení",
"enter_the_quick_connect_code": "Zadejte kód rychlého připojení...", "enter_the_quick_connect_code": "Zadejte kód rychlého připojení...",
"success": "Úspěšně", "success": "Úspěšně",
"quick_connect_autorized": "Oprávněné Rychlé připojení", "quick_connect_authorized": "Oprávněné Rychlé připojení",
"error": "Chyba", "error": "Chyba",
"invalid_code": "Neplatný kód", "invalid_code": "Neplatný kód",
"authorize": "Autorizovat" "authorize": "Autorizovat"
@@ -298,7 +298,7 @@
"show_custom_menu_links": "Zobrazit vlastní Menu odkazy", "show_custom_menu_links": "Zobrazit vlastní Menu odkazy",
"show_large_home_carousel": "Zobrazit velký přehled (beta)", "show_large_home_carousel": "Zobrazit velký přehled (beta)",
"hide_libraries": "Skrýt knihovny", "hide_libraries": "Skrýt knihovny",
"select_liraries_you_want_to_hide": "Vyberte knihovny, které chcete skrýt v záložce Knihovna a v sekcích domovské stránky.", "select_libraries_you_want_to_hide": "Vyberte knihovny, které chcete skrýt v záložce Knihovna a v sekcích domovské stránky.",
"disable_haptic_feedback": "Zakázat Haptickou zpětnou vazbu", "disable_haptic_feedback": "Zakázat Haptickou zpětnou vazbu",
"default_quality": "Výchozí kvalita", "default_quality": "Výchozí kvalita",
"default_playback_speed": "Default Playback Speed", "default_playback_speed": "Default Playback Speed",
@@ -384,6 +384,8 @@
"device_usage": "Zařízení {{availableSpace}}%", "device_usage": "Zařízení {{availableSpace}}%",
"size_used": "{{used}} z {{total}} využito", "size_used": "{{used}} z {{total}} využito",
"delete_all_downloaded_files": "Odstranit všechny stažené soubory", "delete_all_downloaded_files": "Odstranit všechny stažené soubory",
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
"music_cache_title": "Music Cache", "music_cache_title": "Music Cache",
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support", "music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
"clear_music_cache": "Clear Music Cache", "clear_music_cache": "Clear Music Cache",
@@ -598,7 +600,7 @@
"mpv_player_title": "MPV Player", "mpv_player_title": "MPV Player",
"error": "Chyba", "error": "Chyba",
"failed_to_get_stream_url": "Nepodařilo se získat URL streamu", "failed_to_get_stream_url": "Nepodařilo se získat URL streamu",
"an_error_occured_while_playing_the_video": "Při přehrávání videa došlo k chybě. Zkontrolujte logy v nastavení.", "an_error_occurred_while_playing_the_video": "Při přehrávání videa došlo k chybě. Zkontrolujte logy v nastavení.",
"client_error": "Chyba klienta", "client_error": "Chyba klienta",
"could_not_create_stream_for_chromecast": "Nelze vytvořit stream pro Chromecast", "could_not_create_stream_for_chromecast": "Nelze vytvořit stream pro Chromecast",
"message_from_server": "Zpráva od serveru: {{message}}", "message_from_server": "Zpráva od serveru: {{message}}",

View File

@@ -16,14 +16,14 @@
"got_it": "Forstået", "got_it": "Forstået",
"connection_failed": "Forbindelsen mislykkedes", "connection_failed": "Forbindelsen mislykkedes",
"could_not_connect_to_server": "Kunne ikke oprette forbindelse til serveren. Tjek venligst URL'en og din netværksforbindelse.", "could_not_connect_to_server": "Kunne ikke oprette forbindelse til serveren. Tjek venligst URL'en og din netværksforbindelse.",
"an_unexpected_error_occured": "Der opstod en uventet fejl", "an_unexpected_error_occurred": "Der opstod en uventet fejl",
"change_server": "Skift server", "change_server": "Skift server",
"invalid_username_or_password": "Ugyldigt brugernavn eller adgangskode", "invalid_username_or_password": "Ugyldigt brugernavn eller adgangskode",
"user_does_not_have_permission_to_log_in": "Brugeren har ikke tilladelse til at logge ind", "user_does_not_have_permission_to_log_in": "Brugeren har ikke tilladelse til at logge ind",
"server_is_taking_too_long_to_respond_try_again_later": "Serveren svarer for langsomt, prøv igen senere", "server_is_taking_too_long_to_respond_try_again_later": "Serveren svarer for langsomt, prøv igen senere",
"server_received_too_many_requests_try_again_later": "Serveren modtog for mange forespørgsler, prøv igen senere.", "server_received_too_many_requests_try_again_later": "Serveren modtog for mange forespørgsler, prøv igen senere.",
"there_is_a_server_error": "Der er en serverfejl", "there_is_a_server_error": "Der er en serverfejl",
"an_unexpected_error_occured_did_you_enter_the_correct_url": "Der opstod en uventet fejl. Har du indtastet serverens URL korrekt?", "an_unexpected_error_occurred_did_you_enter_the_correct_url": "Der opstod en uventet fejl. Har du indtastet serverens URL korrekt?",
"too_old_server_text": "Ikke Understøttet Jellyfin Server Opdaget", "too_old_server_text": "Ikke Understøttet Jellyfin Server Opdaget",
"too_old_server_description": "Opdater venligst Jellyfin til den seneste version" "too_old_server_description": "Opdater venligst Jellyfin til den seneste version"
}, },
@@ -188,7 +188,7 @@
"authorize_button": "Autoriser Hurtigforbindelse", "authorize_button": "Autoriser Hurtigforbindelse",
"enter_the_quick_connect_code": "Indtast koden til hurtigforbindelse...", "enter_the_quick_connect_code": "Indtast koden til hurtigforbindelse...",
"success": "Succes", "success": "Succes",
"quick_connect_autorized": "Hurtigforbindelse autoriseret", "quick_connect_authorized": "Hurtigforbindelse autoriseret",
"error": "Fejl", "error": "Fejl",
"invalid_code": "Ugyldig kode", "invalid_code": "Ugyldig kode",
"authorize": "Autoriser" "authorize": "Autoriser"
@@ -298,7 +298,7 @@
"show_custom_menu_links": "Vis tilpassede menulinks", "show_custom_menu_links": "Vis tilpassede menulinks",
"show_large_home_carousel": "Show Large Home Carousel (beta)", "show_large_home_carousel": "Show Large Home Carousel (beta)",
"hide_libraries": "Skjul biblioteker", "hide_libraries": "Skjul biblioteker",
"select_liraries_you_want_to_hide": "Vælg de biblioteker, du ønsker at skjule fra fanen Bibliotek og startside sektionerne.", "select_libraries_you_want_to_hide": "Vælg de biblioteker, du ønsker at skjule fra fanen Bibliotek og startside sektionerne.",
"disable_haptic_feedback": "Deaktiver haptisk feedback", "disable_haptic_feedback": "Deaktiver haptisk feedback",
"default_quality": "Standard kvalitet", "default_quality": "Standard kvalitet",
"default_playback_speed": "Default Playback Speed", "default_playback_speed": "Default Playback Speed",
@@ -384,6 +384,8 @@
"device_usage": "Enhedsforbrug: {{availableSpace}}%", "device_usage": "Enhedsforbrug: {{availableSpace}}%",
"size_used": "{{used}} af {{total}} brugt", "size_used": "{{used}} af {{total}} brugt",
"delete_all_downloaded_files": "Slet alle downloadede filer", "delete_all_downloaded_files": "Slet alle downloadede filer",
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
"music_cache_title": "Music Cache", "music_cache_title": "Music Cache",
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support", "music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
"clear_music_cache": "Clear Music Cache", "clear_music_cache": "Clear Music Cache",
@@ -598,7 +600,7 @@
"mpv_player_title": "MPV Player", "mpv_player_title": "MPV Player",
"error": "Fejl", "error": "Fejl",
"failed_to_get_stream_url": "Kunne ikke hente stream URL'en", "failed_to_get_stream_url": "Kunne ikke hente stream URL'en",
"an_error_occured_while_playing_the_video": "Der opstod en fejl under afspilning af videoen. Tjek logfilerne i indstillinger.", "an_error_occurred_while_playing_the_video": "Der opstod en fejl under afspilning af videoen. Tjek logfilerne i indstillinger.",
"client_error": "Klientfejl", "client_error": "Klientfejl",
"could_not_create_stream_for_chromecast": "Kunne ikke oprette en stream til Chromecast", "could_not_create_stream_for_chromecast": "Kunne ikke oprette en stream til Chromecast",
"message_from_server": "Besked fra server: {{message}}", "message_from_server": "Besked fra server: {{message}}",

View File

@@ -16,14 +16,14 @@
"got_it": "Verstanden", "got_it": "Verstanden",
"connection_failed": "Verbindung fehlgeschlagen", "connection_failed": "Verbindung fehlgeschlagen",
"could_not_connect_to_server": "Verbindung zum Server fehlgeschlagen. Bitte überprüf die URL und deine Netzwerkverbindung.", "could_not_connect_to_server": "Verbindung zum Server fehlgeschlagen. Bitte überprüf die URL und deine Netzwerkverbindung.",
"an_unexpected_error_occured": "Ein unerwarteter Fehler ist aufgetreten", "an_unexpected_error_occurred": "Ein unerwarteter Fehler ist aufgetreten",
"change_server": "Server wechseln", "change_server": "Server wechseln",
"invalid_username_or_password": "Ungültiger Benutzername oder Passwort", "invalid_username_or_password": "Ungültiger Benutzername oder Passwort",
"user_does_not_have_permission_to_log_in": "Benutzer hat keine Berechtigung, um sich anzumelden", "user_does_not_have_permission_to_log_in": "Benutzer hat keine Berechtigung, um sich anzumelden",
"server_is_taking_too_long_to_respond_try_again_later": "Der Server benötigt zu lange, um zu antworten. Bitte versuch es später erneut", "server_is_taking_too_long_to_respond_try_again_later": "Der Server benötigt zu lange, um zu antworten. Bitte versuch es später erneut",
"server_received_too_many_requests_try_again_later": "Der Server hat zu viele Anfragen erhalten. Bitte versuch es später erneut.", "server_received_too_many_requests_try_again_later": "Der Server hat zu viele Anfragen erhalten. Bitte versuch es später erneut.",
"there_is_a_server_error": "Es gibt einen Serverfehler", "there_is_a_server_error": "Es gibt einen Serverfehler",
"an_unexpected_error_occured_did_you_enter_the_correct_url": "Ein unerwarteter Fehler ist aufgetreten. Hast du die Server-URL korrekt eingegeben?", "an_unexpected_error_occurred_did_you_enter_the_correct_url": "Ein unerwarteter Fehler ist aufgetreten. Hast du die Server-URL korrekt eingegeben?",
"too_old_server_text": "Nicht unterstützter Jellyfin Server entdeckt", "too_old_server_text": "Nicht unterstützter Jellyfin Server entdeckt",
"too_old_server_description": "Bitte aktualisiere Jellyfin auf die neueste Version" "too_old_server_description": "Bitte aktualisiere Jellyfin auf die neueste Version"
}, },
@@ -188,7 +188,7 @@
"authorize_button": "Quick Connect autorisieren", "authorize_button": "Quick Connect autorisieren",
"enter_the_quick_connect_code": "Quick Connect-Code eingeben...", "enter_the_quick_connect_code": "Quick Connect-Code eingeben...",
"success": "Erfolgreich verbunden", "success": "Erfolgreich verbunden",
"quick_connect_autorized": "Quick Connect autorisiert", "quick_connect_authorized": "Quick Connect autorisiert",
"error": "Fehler", "error": "Fehler",
"invalid_code": "Ungültiger Code", "invalid_code": "Ungültiger Code",
"authorize": "Autorisieren" "authorize": "Autorisieren"
@@ -298,7 +298,7 @@
"show_custom_menu_links": "Benutzerdefinierte Menülinks anzeigen", "show_custom_menu_links": "Benutzerdefinierte Menülinks anzeigen",
"show_large_home_carousel": "Zeige große Startseiten-Übersicht (Beta)", "show_large_home_carousel": "Zeige große Startseiten-Übersicht (Beta)",
"hide_libraries": "Bibliotheken ausblenden", "hide_libraries": "Bibliotheken ausblenden",
"select_liraries_you_want_to_hide": "Bibliotheken auswählen die aus dem Bibliothekstab und der Startseite ausgeblendet werden sollen.", "select_libraries_you_want_to_hide": "Bibliotheken auswählen die aus dem Bibliothekstab und der Startseite ausgeblendet werden sollen.",
"disable_haptic_feedback": "Haptisches Feedback deaktivieren", "disable_haptic_feedback": "Haptisches Feedback deaktivieren",
"default_quality": "Standardqualität", "default_quality": "Standardqualität",
"default_playback_speed": "Standard-Wiedergabegeschwindigkeit", "default_playback_speed": "Standard-Wiedergabegeschwindigkeit",
@@ -384,6 +384,8 @@
"device_usage": "Gerät {{availableSpace}}%", "device_usage": "Gerät {{availableSpace}}%",
"size_used": "{{used}} von {{total}} genutzt", "size_used": "{{used}} von {{total}} genutzt",
"delete_all_downloaded_files": "Alle heruntergeladenen Dateien löschen", "delete_all_downloaded_files": "Alle heruntergeladenen Dateien löschen",
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
"music_cache_title": "Musik-Cache", "music_cache_title": "Musik-Cache",
"music_cache_description": "Beim Anhören Titel automatisch in den Cache laden um bessere Wiedergabe und Offline-Wiedergabe zu ermöglichen", "music_cache_description": "Beim Anhören Titel automatisch in den Cache laden um bessere Wiedergabe und Offline-Wiedergabe zu ermöglichen",
"clear_music_cache": "Musik-Cache leeren", "clear_music_cache": "Musik-Cache leeren",
@@ -598,7 +600,7 @@
"mpv_player_title": "MPV Player", "mpv_player_title": "MPV Player",
"error": "Fehler", "error": "Fehler",
"failed_to_get_stream_url": "Fehler beim Abrufen der Stream-URL", "failed_to_get_stream_url": "Fehler beim Abrufen der Stream-URL",
"an_error_occured_while_playing_the_video": "Ein Fehler ist beim Abspielen des Videos aufgetreten. Logs in den Einstellungen überprüfen.", "an_error_occurred_while_playing_the_video": "Ein Fehler ist beim Abspielen des Videos aufgetreten. Logs in den Einstellungen überprüfen.",
"client_error": "Client-Fehler", "client_error": "Client-Fehler",
"could_not_create_stream_for_chromecast": "Konnte keinen Stream für Chromecast erstellen", "could_not_create_stream_for_chromecast": "Konnte keinen Stream für Chromecast erstellen",
"message_from_server": "Nachricht vom Server: {{message}}", "message_from_server": "Nachricht vom Server: {{message}}",

View File

@@ -16,14 +16,14 @@
"got_it": "Το Κατάλαβα", "got_it": "Το Κατάλαβα",
"connection_failed": "Η Σύνδεση Απέτυχε", "connection_failed": "Η Σύνδεση Απέτυχε",
"could_not_connect_to_server": "Δεν ήταν δυνατή η σύνδεση με τον διακομιστή. Παρακαλώ ελέγξτε τη διεύθυνση URL και τη σύνδεση δικτύου σας.", "could_not_connect_to_server": "Δεν ήταν δυνατή η σύνδεση με τον διακομιστή. Παρακαλώ ελέγξτε τη διεύθυνση URL και τη σύνδεση δικτύου σας.",
"an_unexpected_error_occured": "Παρουσιάστηκε Απροσδόκητο Σφάλμα", "an_unexpected_error_occurred": "Παρουσιάστηκε Απροσδόκητο Σφάλμα",
"change_server": "Αλλαγή Διακομιστή", "change_server": "Αλλαγή Διακομιστή",
"invalid_username_or_password": "Μη έγκυρο όνομα χρήστη ή κωδικός πρόσβασης", "invalid_username_or_password": "Μη έγκυρο όνομα χρήστη ή κωδικός πρόσβασης",
"user_does_not_have_permission_to_log_in": "Ο χρήστης δεν έχει άδεια να συνδεθεί", "user_does_not_have_permission_to_log_in": "Ο χρήστης δεν έχει άδεια να συνδεθεί",
"server_is_taking_too_long_to_respond_try_again_later": "Ο διακομιστής καθυστερεί πάρα πολύ για να απαντήσει, δοκιμάστε ξανά αργότερα", "server_is_taking_too_long_to_respond_try_again_later": "Ο διακομιστής καθυστερεί πάρα πολύ για να απαντήσει, δοκιμάστε ξανά αργότερα",
"server_received_too_many_requests_try_again_later": "Ο διακομιστής έλαβε πάρα πολλά αιτήματα, δοκιμάστε ξανά αργότερα.", "server_received_too_many_requests_try_again_later": "Ο διακομιστής έλαβε πάρα πολλά αιτήματα, δοκιμάστε ξανά αργότερα.",
"there_is_a_server_error": "Υπάρχει ένα σφάλμα διακομιστή", "there_is_a_server_error": "Υπάρχει ένα σφάλμα διακομιστή",
"an_unexpected_error_occured_did_you_enter_the_correct_url": "Παρουσιάστηκε μη αναμενόμενο σφάλμα. Εισαγάγετε σωστά το URL του διακομιστή?", "an_unexpected_error_occurred_did_you_enter_the_correct_url": "Παρουσιάστηκε μη αναμενόμενο σφάλμα. Εισαγάγετε σωστά το URL του διακομιστή?",
"too_old_server_text": "Ανακαλύφθηκε Μη Υποστηριζόμενος Εξυπηρετητής Jellyfin", "too_old_server_text": "Ανακαλύφθηκε Μη Υποστηριζόμενος Εξυπηρετητής Jellyfin",
"too_old_server_description": "Παρακαλούμε ενημερώστε το Jellyfin στην τελευταία έκδοση" "too_old_server_description": "Παρακαλούμε ενημερώστε το Jellyfin στην τελευταία έκδοση"
}, },
@@ -188,7 +188,7 @@
"authorize_button": "Εξουσιοδότηση Γρήγορης Σύνδεσης", "authorize_button": "Εξουσιοδότηση Γρήγορης Σύνδεσης",
"enter_the_quick_connect_code": "Εισάγετε τον κωδικό γρήγορης σύνδεσης...", "enter_the_quick_connect_code": "Εισάγετε τον κωδικό γρήγορης σύνδεσης...",
"success": "Επιτυχία", "success": "Επιτυχία",
"quick_connect_autorized": "Γρήγορη Σύνδεση Εξουσιοδοτήθηκε", "quick_connect_authorized": "Γρήγορη Σύνδεση Εξουσιοδοτήθηκε",
"error": "Σφάλμα", "error": "Σφάλμα",
"invalid_code": "Μη Έγκυρος Κωδικός", "invalid_code": "Μη Έγκυρος Κωδικός",
"authorize": "Εξουσιοδότηση" "authorize": "Εξουσιοδότηση"
@@ -298,7 +298,7 @@
"show_custom_menu_links": "Εμφάνιση Προσαρμοσμένων Συνδέσμων Μενού", "show_custom_menu_links": "Εμφάνιση Προσαρμοσμένων Συνδέσμων Μενού",
"show_large_home_carousel": "Show Large Home Carousel (beta)", "show_large_home_carousel": "Show Large Home Carousel (beta)",
"hide_libraries": "Απόκρυψη Βιβλιοθηκών", "hide_libraries": "Απόκρυψη Βιβλιοθηκών",
"select_liraries_you_want_to_hide": "Επιλέξτε τις βιβλιοθήκες που θέλετε να αποκρύψετε από την καρτέλα της Βιβλιοθήκης και τις ενότητες της αρχικής σελίδας.", "select_libraries_you_want_to_hide": "Επιλέξτε τις βιβλιοθήκες που θέλετε να αποκρύψετε από την καρτέλα της Βιβλιοθήκης και τις ενότητες της αρχικής σελίδας.",
"disable_haptic_feedback": "Απενεργοποίηση Απτικής Ανατροφοδότησης", "disable_haptic_feedback": "Απενεργοποίηση Απτικής Ανατροφοδότησης",
"default_quality": "Προεπιλεγμένη Ποιότητα", "default_quality": "Προεπιλεγμένη Ποιότητα",
"default_playback_speed": "Default Playback Speed", "default_playback_speed": "Default Playback Speed",
@@ -384,6 +384,8 @@
"device_usage": "Device {{availableSpace}}%", "device_usage": "Device {{availableSpace}}%",
"size_used": "{{used}} {{total}} Χρησιμοποιείται", "size_used": "{{used}} {{total}} Χρησιμοποιείται",
"delete_all_downloaded_files": "Διαγραφή Όλων Των Ληφθέντων Αρχείων", "delete_all_downloaded_files": "Διαγραφή Όλων Των Ληφθέντων Αρχείων",
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
"music_cache_title": "Music Cache", "music_cache_title": "Music Cache",
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support", "music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
"clear_music_cache": "Clear Music Cache", "clear_music_cache": "Clear Music Cache",
@@ -598,7 +600,7 @@
"mpv_player_title": "MPV Player", "mpv_player_title": "MPV Player",
"error": "Σφάλμα", "error": "Σφάλμα",
"failed_to_get_stream_url": "Αποτυχία λήψης του URL ροής", "failed_to_get_stream_url": "Αποτυχία λήψης του URL ροής",
"an_error_occured_while_playing_the_video": "Παρουσιάστηκε σφάλμα κατά την αναπαραγωγή του βίντεο. Ελέγξτε τα αρχεία καταγραφής στις ρυθμίσεις.", "an_error_occurred_while_playing_the_video": "Παρουσιάστηκε σφάλμα κατά την αναπαραγωγή του βίντεο. Ελέγξτε τα αρχεία καταγραφής στις ρυθμίσεις.",
"client_error": "Σφάλμα Πελάτη", "client_error": "Σφάλμα Πελάτη",
"could_not_create_stream_for_chromecast": "Αδυναμία δημιουργίας ροής για το Chromecast", "could_not_create_stream_for_chromecast": "Αδυναμία δημιουργίας ροής για το Chromecast",
"message_from_server": "Μήνυμα από το διακομιστή: {{message}}", "message_from_server": "Μήνυμα από το διακομιστή: {{message}}",

View File

@@ -174,7 +174,10 @@
"network_already_added": "Network already added", "network_already_added": "Network already added",
"no_wifi_connected": "Not connected to Wi-Fi", "no_wifi_connected": "Not connected to Wi-Fi",
"permission_denied": "Location permission denied", "permission_denied": "Location permission denied",
"permission_denied_explanation": "Location permission is required to detect the Wi-Fi network for auto-switching. Please enable it in Settings." "permission_denied_explanation": "Location permission is required to detect the Wi-Fi network for auto-switching. Please enable it in Settings.",
"ssid_hidden": "SSID hidden",
"location_off_description": "Connected to Wi-Fi, but the network name is hidden. Enable device Location services and grant the app location permission to detect it.",
"open_location_settings": "Open Location settings"
}, },
"user_info": { "user_info": {
"user_info_title": "User info", "user_info_title": "User info",
@@ -270,10 +273,6 @@
"mpv_subtitle_margin_y": "Vertical margin", "mpv_subtitle_margin_y": "Vertical margin",
"mpv_subtitle_align_x": "Horizontal align", "mpv_subtitle_align_x": "Horizontal align",
"mpv_subtitle_align_y": "Vertical align", "mpv_subtitle_align_y": "Vertical align",
"mpv_settings_title": "MPV Subtitle Settings",
"mpv_settings_description": "Advanced subtitle customization for MPV player",
"opaque_background": "Opaque Background",
"background_opacity": "Background Opacity",
"align": { "align": {
"left": "Left", "left": "Left",
"center": "Center", "center": "Center",
@@ -441,13 +440,10 @@
}, },
"sessions": { "sessions": {
"title": "Sessions", "title": "Sessions",
"no_active_sessions": "No active sessions", "no_active_sessions": "No active sessions"
"select_session": "Select Session",
"now_playing": "Now playing:"
}, },
"downloads": { "downloads": {
"downloads_title": "Downloads", "downloads_title": "Downloads",
"transcoding": "Transcoding",
"series": "Series", "series": "Series",
"movies": "Movies", "movies": "Movies",
"other_media": "Other media", "other_media": "Other media",
@@ -503,8 +499,6 @@
"none": "None", "none": "None",
"track": "Track", "track": "Track",
"cancel": "Cancel", "cancel": "Cancel",
"stop": "Stop",
"open_menu": "Open Menu",
"delete": "Delete", "delete": "Delete",
"ok": "OK", "ok": "OK",
"remove": "Remove", "remove": "Remove",
@@ -606,31 +600,7 @@
}, },
"player": { "player": {
"live": "LIVE", "live": "LIVE",
"menu": {
"quality": "Quality",
"subtitles": "Subtitles",
"subtitle_scale": "Subtitle Scale",
"audio": "Audio",
"speed": "Speed",
"playback_options": "Playback Options",
"show_technical_info": "Show Technical Info",
"hide_technical_info": "Hide Technical Info"
},
"technical_info": {
"video": "Video:",
"audio": "Audio:",
"subtitle": "Subtitle:",
"bitrate": "Bitrate:",
"buffer_seconds": "Buffer: {{seconds}}s",
"vo": "VO:",
"dropped_frames": "Dropped: {{count}} frames",
"loading": "Loading..."
},
"mpv_player_title": "MPV player", "mpv_player_title": "MPV player",
"aspect_ratio": "Aspect Ratio",
"aspect_ratio_original": "Original",
"hash_match": "Hash Match",
"still_watching": "Are you still watching?",
"error": "Error", "error": "Error",
"failed_to_get_stream_url": "Failed to get the stream URL", "failed_to_get_stream_url": "Failed to get the stream URL",
"an_error_occurred_while_playing_the_video": "An error occurred while playing the video. Check logs in settings.", "an_error_occurred_while_playing_the_video": "An error occurred while playing the video. Check logs in settings.",
@@ -730,7 +700,6 @@
"no_data_available": "No data available" "no_data_available": "No data available"
}, },
"live_tv": { "live_tv": {
"title": "Live TV",
"next": "Next", "next": "Next",
"previous": "Previous", "previous": "Previous",
"coming_soon": "Coming soon", "coming_soon": "Coming soon",
@@ -802,7 +771,7 @@
"request_selected": "Request selected", "request_selected": "Request selected",
"n_selected": "{{count}} selected", "n_selected": "{{count}} selected",
"toasts": { "toasts": {
"jellyseerr_does_not_meet_requirements": "Seerr server does not meet minimum version requirements! Please update to at least 2.0.0", "jellyseer_does_not_meet_requirements": "Seerr server does not meet minimum version requirements! Please update to at least 2.0.0",
"jellyseerr_test_failed": "Seerr test failed. Please try again.", "jellyseerr_test_failed": "Seerr test failed. Please try again.",
"failed_to_test_jellyseerr_server_url": "Failed to test Seerr server url", "failed_to_test_jellyseerr_server_url": "Failed to test Seerr server url",
"issue_submitted": "Issue submitted!", "issue_submitted": "Issue submitted!",
@@ -815,16 +784,6 @@
"failed_to_decline_request": "Failed to decline request" "failed_to_decline_request": "Failed to decline request"
} }
}, },
"accessibility": {
"play_button": "Play button",
"play_hint": "Tap to play the media",
"toggle_orientation": "Toggle screen orientation",
"toggle_orientation_hint": "Toggles the screen orientation between portrait and landscape"
},
"not_found": {
"title": "This screen doesn't exist.",
"go_home": "Go to home screen!"
},
"tabs": { "tabs": {
"home": "Home", "home": "Home",
"search": "Search", "search": "Search",
@@ -835,12 +794,6 @@
}, },
"music": { "music": {
"title": "Music", "title": "Music",
"no_track_playing": "No track playing",
"queue_empty": "Queue is empty",
"playing_from_queue": "Playing from queue",
"up_next": "Up next",
"now_playing": "Now Playing",
"missing_library_id": "Missing music library id.",
"tabs": { "tabs": {
"suggestions": "Suggestions", "suggestions": "Suggestions",
"albums": "Albums", "albums": "Albums",

View File

@@ -16,14 +16,14 @@
"got_it": "Entendido", "got_it": "Entendido",
"connection_failed": "Conexión fallida", "connection_failed": "Conexión fallida",
"could_not_connect_to_server": "No se pudo conectar al servidor. Por favor comprueba la URL y tu conexión de red.", "could_not_connect_to_server": "No se pudo conectar al servidor. Por favor comprueba la URL y tu conexión de red.",
"an_unexpected_error_occured": "Ha ocurrido un error inesperado", "an_unexpected_error_occurred": "Ha ocurrido un error inesperado",
"change_server": "Cambiar servidor", "change_server": "Cambiar servidor",
"invalid_username_or_password": "Usuario o contraseña inválidos", "invalid_username_or_password": "Usuario o contraseña inválidos",
"user_does_not_have_permission_to_log_in": "El usuario no tiene permiso para iniciar sesión", "user_does_not_have_permission_to_log_in": "El usuario no tiene permiso para iniciar sesión",
"server_is_taking_too_long_to_respond_try_again_later": "El servidor está tardando mucho en responder, inténtalo de nuevo más tarde.", "server_is_taking_too_long_to_respond_try_again_later": "El servidor está tardando mucho en responder, inténtalo de nuevo más tarde.",
"server_received_too_many_requests_try_again_later": "El servidor está recibiendo muchas peticiones, inténtalo de nuevo más tarde.", "server_received_too_many_requests_try_again_later": "El servidor está recibiendo muchas peticiones, inténtalo de nuevo más tarde.",
"there_is_a_server_error": "Hay un error en el servidor", "there_is_a_server_error": "Hay un error en el servidor",
"an_unexpected_error_occured_did_you_enter_the_correct_url": "Ha ocurrido un error inesperado. ¿Has introducido la URL correcta?", "an_unexpected_error_occurred_did_you_enter_the_correct_url": "Ha ocurrido un error inesperado. ¿Has introducido la URL correcta?",
"too_old_server_text": "Servidor Jellyfin no soportado descubierto", "too_old_server_text": "Servidor Jellyfin no soportado descubierto",
"too_old_server_description": "Por favor, actualiza Jellyfin a la última versión" "too_old_server_description": "Por favor, actualiza Jellyfin a la última versión"
}, },
@@ -188,7 +188,7 @@
"authorize_button": "Autorizar conexión rápida", "authorize_button": "Autorizar conexión rápida",
"enter_the_quick_connect_code": "Introduce el código de conexión rápida...", "enter_the_quick_connect_code": "Introduce el código de conexión rápida...",
"success": "Hecho", "success": "Hecho",
"quick_connect_autorized": "Conexión rápida autorizada", "quick_connect_authorized": "Conexión rápida autorizada",
"error": "Error", "error": "Error",
"invalid_code": "Código inválido", "invalid_code": "Código inválido",
"authorize": "Autorizar" "authorize": "Autorizar"
@@ -298,7 +298,7 @@
"show_custom_menu_links": "Mostrar enlaces de menú personalizados", "show_custom_menu_links": "Mostrar enlaces de menú personalizados",
"show_large_home_carousel": "Mostrar carrusel del menú principal grande (beta)", "show_large_home_carousel": "Mostrar carrusel del menú principal grande (beta)",
"hide_libraries": "Ocultar bibliotecas", "hide_libraries": "Ocultar bibliotecas",
"select_liraries_you_want_to_hide": "Selecciona las bibliotecas que quieres ocultar de la pestaña Bibliotecas y de Inicio.", "select_libraries_you_want_to_hide": "Selecciona las bibliotecas que quieres ocultar de la pestaña Bibliotecas y de Inicio.",
"disable_haptic_feedback": "Desactivar feedback háptico", "disable_haptic_feedback": "Desactivar feedback háptico",
"default_quality": "Calidad por defecto", "default_quality": "Calidad por defecto",
"default_playback_speed": "Velocidad de reproducción predeterminada", "default_playback_speed": "Velocidad de reproducción predeterminada",
@@ -384,6 +384,8 @@
"device_usage": "Dispositivo {{availableSpace}}%", "device_usage": "Dispositivo {{availableSpace}}%",
"size_used": "{{used}} de {{total}} usado", "size_used": "{{used}} de {{total}} usado",
"delete_all_downloaded_files": "Eliminar todos los archivos descargados", "delete_all_downloaded_files": "Eliminar todos los archivos descargados",
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
"music_cache_title": "Caché de música", "music_cache_title": "Caché de música",
"music_cache_description": "Cachear automáticamente las canciones mientras escuchas una reproducción más suave y soporte sin conexión", "music_cache_description": "Cachear automáticamente las canciones mientras escuchas una reproducción más suave y soporte sin conexión",
"clear_music_cache": "Borrar Caché de Música", "clear_music_cache": "Borrar Caché de Música",
@@ -598,7 +600,7 @@
"mpv_player_title": "MPV Player", "mpv_player_title": "MPV Player",
"error": "Error", "error": "Error",
"failed_to_get_stream_url": "Error al obtener la URL del Steam", "failed_to_get_stream_url": "Error al obtener la URL del Steam",
"an_error_occured_while_playing_the_video": "Ha ocurrido un error al reproducir el vídeo. Comprueba los registros en la configuración.", "an_error_occurred_while_playing_the_video": "Ha ocurrido un error al reproducir el vídeo. Comprueba los registros en la configuración.",
"client_error": "Error del cliente", "client_error": "Error del cliente",
"could_not_create_stream_for_chromecast": "No se pudo crear el Steam para Chromecast", "could_not_create_stream_for_chromecast": "No se pudo crear el Steam para Chromecast",
"message_from_server": "Mensaje del servidor: {{message}}", "message_from_server": "Mensaje del servidor: {{message}}",

View File

@@ -16,14 +16,14 @@
"got_it": "Okei", "got_it": "Okei",
"connection_failed": "Yhteys epäonnistui", "connection_failed": "Yhteys epäonnistui",
"could_not_connect_to_server": "Yhteyttä palvelimeen ei voitu muodostaa. Tarkista URL-osoite ja verkkoyhteytesi.", "could_not_connect_to_server": "Yhteyttä palvelimeen ei voitu muodostaa. Tarkista URL-osoite ja verkkoyhteytesi.",
"an_unexpected_error_occured": "Odottamaton virhe tapahtui", "an_unexpected_error_occurred": "Odottamaton virhe tapahtui",
"change_server": "Vaihda palvelinta", "change_server": "Vaihda palvelinta",
"invalid_username_or_password": "Virheellinen käyttäjätunnus tai salasana", "invalid_username_or_password": "Virheellinen käyttäjätunnus tai salasana",
"user_does_not_have_permission_to_log_in": "Käyttäjällä ei ole oikeuksia kirjautua sisään", "user_does_not_have_permission_to_log_in": "Käyttäjällä ei ole oikeuksia kirjautua sisään",
"server_is_taking_too_long_to_respond_try_again_later": "Palvelin reagoi liian hitaasti, yritä myöhemmin uudelleen", "server_is_taking_too_long_to_respond_try_again_later": "Palvelin reagoi liian hitaasti, yritä myöhemmin uudelleen",
"server_received_too_many_requests_try_again_later": "Palvelin sai liian monta pyyntöä, yritä myöhemmin uudelleen.", "server_received_too_many_requests_try_again_later": "Palvelin sai liian monta pyyntöä, yritä myöhemmin uudelleen.",
"there_is_a_server_error": "Palvelimessa on virhe", "there_is_a_server_error": "Palvelimessa on virhe",
"an_unexpected_error_occured_did_you_enter_the_correct_url": "Odottamaton virhe tapahtui. Syötitkö palvelimen URL-osoitteen oikein?", "an_unexpected_error_occurred_did_you_enter_the_correct_url": "Odottamaton virhe tapahtui. Syötitkö palvelimen URL-osoitteen oikein?",
"too_old_server_text": "Ei-tuettu Jellyfin-palvelin löydetty", "too_old_server_text": "Ei-tuettu Jellyfin-palvelin löydetty",
"too_old_server_description": "Ole hyvä ja päivitä Jellyfin uusimpaan versioon" "too_old_server_description": "Ole hyvä ja päivitä Jellyfin uusimpaan versioon"
}, },
@@ -188,7 +188,7 @@
"authorize_button": "Valtuuta pikayhdistys", "authorize_button": "Valtuuta pikayhdistys",
"enter_the_quick_connect_code": "Syötä nopean yhteyden koodi...", "enter_the_quick_connect_code": "Syötä nopean yhteyden koodi...",
"success": "Onnistui", "success": "Onnistui",
"quick_connect_autorized": "Pikayhdistys valtuutettu", "quick_connect_authorized": "Pikayhdistys valtuutettu",
"error": "Virhe", "error": "Virhe",
"invalid_code": "Virheellinen koodi", "invalid_code": "Virheellinen koodi",
"authorize": "Valtuuta" "authorize": "Valtuuta"
@@ -298,7 +298,7 @@
"show_custom_menu_links": "Näytä mukautetut valikkolinkit", "show_custom_menu_links": "Näytä mukautetut valikkolinkit",
"show_large_home_carousel": "Näytä suuri kotikaruselli (beta)", "show_large_home_carousel": "Näytä suuri kotikaruselli (beta)",
"hide_libraries": "Piilota kirjastot", "hide_libraries": "Piilota kirjastot",
"select_liraries_you_want_to_hide": "Valitse kirjastot, jotka haluat piilottaa Kirjasto-välilehdeltä ja etusivun osioista.", "select_libraries_you_want_to_hide": "Valitse kirjastot, jotka haluat piilottaa Kirjasto-välilehdeltä ja etusivun osioista.",
"disable_haptic_feedback": "Poista haptinen palautteet käytöstä", "disable_haptic_feedback": "Poista haptinen palautteet käytöstä",
"default_quality": "Oletuslaatu", "default_quality": "Oletuslaatu",
"default_playback_speed": "Default Playback Speed", "default_playback_speed": "Default Playback Speed",
@@ -384,6 +384,8 @@
"device_usage": "Laitteen käyttö {{availableSpace}}%", "device_usage": "Laitteen käyttö {{availableSpace}}%",
"size_used": "{{used}} / {{total}} käytössä", "size_used": "{{used}} / {{total}} käytössä",
"delete_all_downloaded_files": "Poista kaikki ladatut tiedostot", "delete_all_downloaded_files": "Poista kaikki ladatut tiedostot",
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
"music_cache_title": "Music Cache", "music_cache_title": "Music Cache",
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support", "music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
"clear_music_cache": "Clear Music Cache", "clear_music_cache": "Clear Music Cache",
@@ -598,7 +600,7 @@
"mpv_player_title": "MPV Player", "mpv_player_title": "MPV Player",
"error": "Virhe", "error": "Virhe",
"failed_to_get_stream_url": "Lähetyksen URL-osoitteen haku epäonnistui", "failed_to_get_stream_url": "Lähetyksen URL-osoitteen haku epäonnistui",
"an_error_occured_while_playing_the_video": "Videon toiston yhteydessä tapahtui virhe. Tarkista lokit asetuksista.", "an_error_occurred_while_playing_the_video": "Videon toiston yhteydessä tapahtui virhe. Tarkista lokit asetuksista.",
"client_error": "Asiakkaan Virhe", "client_error": "Asiakkaan Virhe",
"could_not_create_stream_for_chromecast": "Suoratoistoa ei voitu luoda Chromecastia varten", "could_not_create_stream_for_chromecast": "Suoratoistoa ei voitu luoda Chromecastia varten",
"message_from_server": "Viesti palvelimelta: {{message}}", "message_from_server": "Viesti palvelimelta: {{message}}",

View File

@@ -16,14 +16,14 @@
"got_it": "D'accord", "got_it": "D'accord",
"connection_failed": "La connexion a échoué", "connection_failed": "La connexion a échoué",
"could_not_connect_to_server": "Impossible de se connecter au serveur. Veuillez vérifier lURL et votre connexion réseau.", "could_not_connect_to_server": "Impossible de se connecter au serveur. Veuillez vérifier lURL et votre connexion réseau.",
"an_unexpected_error_occured": "Une erreur inattendue s'est produite", "an_unexpected_error_occurred": "Une erreur inattendue s'est produite",
"change_server": "Changer de serveur", "change_server": "Changer de serveur",
"invalid_username_or_password": "Nom d'utilisateur ou mot de passe invalide", "invalid_username_or_password": "Nom d'utilisateur ou mot de passe invalide",
"user_does_not_have_permission_to_log_in": "L'utilisateur n'a pas la permission de se connecter", "user_does_not_have_permission_to_log_in": "L'utilisateur n'a pas la permission de se connecter",
"server_is_taking_too_long_to_respond_try_again_later": "Le serveur prend trop de temps à répondre, réessayez plus tard", "server_is_taking_too_long_to_respond_try_again_later": "Le serveur prend trop de temps à répondre, réessayez plus tard",
"server_received_too_many_requests_try_again_later": "Le serveur a reçu trop de demandes, réessayez plus tard.", "server_received_too_many_requests_try_again_later": "Le serveur a reçu trop de demandes, réessayez plus tard.",
"there_is_a_server_error": "Il y a une erreur de serveur", "there_is_a_server_error": "Il y a une erreur de serveur",
"an_unexpected_error_occured_did_you_enter_the_correct_url": "Une erreur inattendue sest produite. Avez-vous correctement saisi lURL du serveur ?", "an_unexpected_error_occurred_did_you_enter_the_correct_url": "Une erreur inattendue sest produite. Avez-vous correctement saisi lURL du serveur ?",
"too_old_server_text": "Serveur Jellyfin non pris en charge découvert", "too_old_server_text": "Serveur Jellyfin non pris en charge découvert",
"too_old_server_description": "Veuillez mettre à jour Jellyfin vers la dernière version" "too_old_server_description": "Veuillez mettre à jour Jellyfin vers la dernière version"
}, },
@@ -188,7 +188,7 @@
"authorize_button": "Autoriser une connexion rapide", "authorize_button": "Autoriser une connexion rapide",
"enter_the_quick_connect_code": "Entrez le code de connexion rapide...", "enter_the_quick_connect_code": "Entrez le code de connexion rapide...",
"success": "Succès", "success": "Succès",
"quick_connect_autorized": "Connexion rapide autorisée", "quick_connect_authorized": "Connexion rapide autorisée",
"error": "Erreur", "error": "Erreur",
"invalid_code": "Code invalide", "invalid_code": "Code invalide",
"authorize": "Autoriser" "authorize": "Autoriser"
@@ -298,7 +298,7 @@
"show_custom_menu_links": "Afficher les liens personnalisés", "show_custom_menu_links": "Afficher les liens personnalisés",
"show_large_home_carousel": "Afficher le grand carrousel daccueil (bêta)", "show_large_home_carousel": "Afficher le grand carrousel daccueil (bêta)",
"hide_libraries": "Masquer les bibliothèques", "hide_libraries": "Masquer les bibliothèques",
"select_liraries_you_want_to_hide": "Sélectionnez les bibliothèques que vous souhaitez masquer dans l'onglet Bibliothèque et les sections de la page d'accueil.", "select_libraries_you_want_to_hide": "Sélectionnez les bibliothèques que vous souhaitez masquer dans l'onglet Bibliothèque et les sections de la page d'accueil.",
"disable_haptic_feedback": "Désactiver le retour haptique", "disable_haptic_feedback": "Désactiver le retour haptique",
"default_quality": "Qualité par défaut", "default_quality": "Qualité par défaut",
"default_playback_speed": "Vitesse de lecture par défaut", "default_playback_speed": "Vitesse de lecture par défaut",
@@ -384,6 +384,8 @@
"device_usage": "Appareil {{availableSpace}}%", "device_usage": "Appareil {{availableSpace}}%",
"size_used": "{{used}} de {{total}} utilisés", "size_used": "{{used}} de {{total}} utilisés",
"delete_all_downloaded_files": "Supprimer tous les fichiers téléchargés", "delete_all_downloaded_files": "Supprimer tous les fichiers téléchargés",
"delete_all_downloaded_files_confirm": "Supprimer tous les fichiers téléchargés ?",
"delete_all_downloaded_files_confirm_desc": "Êtes-vous sûr de vouloir supprimer tous les fichiers téléchargés ? Cette action est irréversible.",
"music_cache_title": "Mise en cache de la musique", "music_cache_title": "Mise en cache de la musique",
"music_cache_description": "Mettez automatiquement en cache les chansons au fur et à mesure que vous écoutez pour une lecture plus fluide et une prise en charge hors ligne", "music_cache_description": "Mettez automatiquement en cache les chansons au fur et à mesure que vous écoutez pour une lecture plus fluide et une prise en charge hors ligne",
"clear_music_cache": "Vider le cache de la musique", "clear_music_cache": "Vider le cache de la musique",
@@ -598,7 +600,7 @@
"mpv_player_title": "Lecteur MPV", "mpv_player_title": "Lecteur MPV",
"error": "Erreur", "error": "Erreur",
"failed_to_get_stream_url": "Échec de l'obtention de l'URL du flux", "failed_to_get_stream_url": "Échec de l'obtention de l'URL du flux",
"an_error_occured_while_playing_the_video": "Une erreur sest produite lors de la lecture de la vidéo. Vérifiez les journaux dans les paramètres.", "an_error_occurred_while_playing_the_video": "Une erreur sest produite lors de la lecture de la vidéo. Vérifiez les journaux dans les paramètres.",
"client_error": "Erreur du client", "client_error": "Erreur du client",
"could_not_create_stream_for_chromecast": "Impossible de créer un flux sur la Chromecast", "could_not_create_stream_for_chromecast": "Impossible de créer un flux sur la Chromecast",
"message_from_server": "Message du serveur : {{message}}", "message_from_server": "Message du serveur : {{message}}",

View File

@@ -16,14 +16,14 @@
"got_it": "קיבלתי", "got_it": "קיבלתי",
"connection_failed": "ההתחברות נכשלה", "connection_failed": "ההתחברות נכשלה",
"could_not_connect_to_server": "לא היה ניתן להתחבר לשרת. אנא בדוק את הקישור ואת חיבור הרשת שלך.", "could_not_connect_to_server": "לא היה ניתן להתחבר לשרת. אנא בדוק את הקישור ואת חיבור הרשת שלך.",
"an_unexpected_error_occured": "קרתה שגיאה לא צפויה", "an_unexpected_error_occurred": "קרתה שגיאה לא צפויה",
"change_server": "החלף שרת", "change_server": "החלף שרת",
"invalid_username_or_password": "שם משתמש או סיסמה שגויים", "invalid_username_or_password": "שם משתמש או סיסמה שגויים",
"user_does_not_have_permission_to_log_in": "לחשבון זה אין גישה להתחבר", "user_does_not_have_permission_to_log_in": "לחשבון זה אין גישה להתחבר",
"server_is_taking_too_long_to_respond_try_again_later": "השרת לוקח יותר מדי זמן כדי להגיב, נסה שוב מאוחר יותר", "server_is_taking_too_long_to_respond_try_again_later": "השרת לוקח יותר מדי זמן כדי להגיב, נסה שוב מאוחר יותר",
"server_received_too_many_requests_try_again_later": "השרת קיבל יותר מדי קריאות, נסה שוב מאוחר יותר", "server_received_too_many_requests_try_again_later": "השרת קיבל יותר מדי קריאות, נסה שוב מאוחר יותר",
"there_is_a_server_error": "קרתה שגיאה בצד השרת", "there_is_a_server_error": "קרתה שגיאה בצד השרת",
"an_unexpected_error_occured_did_you_enter_the_correct_url": "קרתה שגיאה לא צפויה. האם כתובת השרת שהוקלדה נכונה?", "an_unexpected_error_occurred_did_you_enter_the_correct_url": "קרתה שגיאה לא צפויה. האם כתובת השרת שהוקלדה נכונה?",
"too_old_server_text": "נמצא שרת Jellyfin שלא נתמך", "too_old_server_text": "נמצא שרת Jellyfin שלא נתמך",
"too_old_server_description": "אנא עדכן את גרסת ה-Jellyfin למעודכנת ביותר" "too_old_server_description": "אנא עדכן את גרסת ה-Jellyfin למעודכנת ביותר"
}, },
@@ -188,7 +188,7 @@
"authorize_button": "אשר התחברות מהירה", "authorize_button": "אשר התחברות מהירה",
"enter_the_quick_connect_code": "הקלד את קוד ההתחברות המהירה...", "enter_the_quick_connect_code": "הקלד את קוד ההתחברות המהירה...",
"success": "הצלחה", "success": "הצלחה",
"quick_connect_autorized": "חיבור מהיר אושר", "quick_connect_authorized": "חיבור מהיר אושר",
"error": "שגיאה", "error": "שגיאה",
"invalid_code": "קוד שגוי", "invalid_code": "קוד שגוי",
"authorize": "אשר" "authorize": "אשר"
@@ -298,7 +298,7 @@
"show_custom_menu_links": "הצג קישורים לתפריטים מותאמים אישית", "show_custom_menu_links": "הצג קישורים לתפריטים מותאמים אישית",
"show_large_home_carousel": "הצג קרוסלה גדולה במסך הבית (בטא)", "show_large_home_carousel": "הצג קרוסלה גדולה במסך הבית (בטא)",
"hide_libraries": "הסתר ספריות", "hide_libraries": "הסתר ספריות",
"select_liraries_you_want_to_hide": "בחר את הספריות שתרצה להסתיר ממסך הספריות וגם ממסך הבית.", "select_libraries_you_want_to_hide": "בחר את הספריות שתרצה להסתיר ממסך הספריות וגם ממסך הבית.",
"disable_haptic_feedback": "בטל משוב רטט", "disable_haptic_feedback": "בטל משוב רטט",
"default_quality": "איכות ברירת מחדל", "default_quality": "איכות ברירת מחדל",
"default_playback_speed": "Default Playback Speed", "default_playback_speed": "Default Playback Speed",
@@ -384,6 +384,8 @@
"device_usage": "מכשיר {{availableSpace}}%", "device_usage": "מכשיר {{availableSpace}}%",
"size_used": "השתמשת ב-{{used}} מתוך {{total}}", "size_used": "השתמשת ב-{{used}} מתוך {{total}}",
"delete_all_downloaded_files": "מחק את כל הקבצים שהורדו", "delete_all_downloaded_files": "מחק את כל הקבצים שהורדו",
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
"music_cache_title": "Music Cache", "music_cache_title": "Music Cache",
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support", "music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
"clear_music_cache": "Clear Music Cache", "clear_music_cache": "Clear Music Cache",
@@ -598,7 +600,7 @@
"mpv_player_title": "MPV Player", "mpv_player_title": "MPV 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": "קרתה תקלה במהלך הניגון של הקובץ. בדוק את הלוגים בהגדרות.",
"client_error": "שגיאת לקוח", "client_error": "שגיאת לקוח",
"could_not_create_stream_for_chromecast": "נכשל ביצירת זרם עבור Chromecast", "could_not_create_stream_for_chromecast": "נכשל ביצירת זרם עבור Chromecast",
"message_from_server": "הודעה מהשרת: {{message}}", "message_from_server": "הודעה מהשרת: {{message}}",

View File

@@ -16,14 +16,14 @@
"got_it": "Értettem", "got_it": "Értettem",
"connection_failed": "Kapcsolódás Sikertelen", "connection_failed": "Kapcsolódás Sikertelen",
"could_not_connect_to_server": "Nem sikerült csatlakozni a szerverhez. Kérjük, ellenőrizd az URL-t és a hálózati kapcsolatot.", "could_not_connect_to_server": "Nem sikerült csatlakozni a szerverhez. Kérjük, ellenőrizd az URL-t és a hálózati kapcsolatot.",
"an_unexpected_error_occured": "Váratlan Hiba Történt", "an_unexpected_error_occurred": "Váratlan Hiba Történt",
"change_server": "Szerverváltás", "change_server": "Szerverváltás",
"invalid_username_or_password": "Érvénytelen Felhasználónév vagy Jelszó", "invalid_username_or_password": "Érvénytelen Felhasználónév vagy Jelszó",
"user_does_not_have_permission_to_log_in": "A felhasználónak nincs jogosultsága a bejelentkezéshez", "user_does_not_have_permission_to_log_in": "A felhasználónak nincs jogosultsága a bejelentkezéshez",
"server_is_taking_too_long_to_respond_try_again_later": "A szerver túl sokáig válaszol, próbáld újra később", "server_is_taking_too_long_to_respond_try_again_later": "A szerver túl sokáig válaszol, próbáld újra később",
"server_received_too_many_requests_try_again_later": "A szerver túl sok kérést kapott, próbáld újra később.", "server_received_too_many_requests_try_again_later": "A szerver túl sok kérést kapott, próbáld újra később.",
"there_is_a_server_error": "Szerverhiba történt", "there_is_a_server_error": "Szerverhiba történt",
"an_unexpected_error_occured_did_you_enter_the_correct_url": "Váratlan hiba történt. Helyesen adtad meg a szerver URL-jét?", "an_unexpected_error_occurred_did_you_enter_the_correct_url": "Váratlan hiba történt. Helyesen adtad meg a szerver URL-jét?",
"too_old_server_text": "Nem Támogatott Jellyfin-szerver", "too_old_server_text": "Nem Támogatott Jellyfin-szerver",
"too_old_server_description": "Frissítsd a Jellyfint a legújabb verzióra" "too_old_server_description": "Frissítsd a Jellyfint a legújabb verzióra"
}, },
@@ -188,7 +188,7 @@
"authorize_button": "Gyorscsatlakozás Engedélyezése", "authorize_button": "Gyorscsatlakozás Engedélyezése",
"enter_the_quick_connect_code": "Add meg a gyors csatlakozási kódot...", "enter_the_quick_connect_code": "Add meg a gyors csatlakozási kódot...",
"success": "Siker", "success": "Siker",
"quick_connect_autorized": "Gyorscsatlakozás Engedélyezve", "quick_connect_authorized": "Gyorscsatlakozás Engedélyezve",
"error": "Hiba", "error": "Hiba",
"invalid_code": "Érvénytelen Kód", "invalid_code": "Érvénytelen Kód",
"authorize": "Engedélyezés" "authorize": "Engedélyezés"
@@ -298,7 +298,7 @@
"show_custom_menu_links": "Egyéni Menülinkek Megjelenítése", "show_custom_menu_links": "Egyéni Menülinkek Megjelenítése",
"show_large_home_carousel": "Show Large Home Carousel (beta)", "show_large_home_carousel": "Show Large Home Carousel (beta)",
"hide_libraries": "Könyvtárak Elrejtése", "hide_libraries": "Könyvtárak Elrejtése",
"select_liraries_you_want_to_hide": "Válaszd ki azokat a könyvtárakat, amelyeket el szeretnél rejteni a Könyvtár fülön és a kezdőlapon.", "select_libraries_you_want_to_hide": "Válaszd ki azokat a könyvtárakat, amelyeket el szeretnél rejteni a Könyvtár fülön és a kezdőlapon.",
"disable_haptic_feedback": "Haptikus Visszajelzés Letiltása", "disable_haptic_feedback": "Haptikus Visszajelzés Letiltása",
"default_quality": "Alapértelmezett Minőség", "default_quality": "Alapértelmezett Minőség",
"default_playback_speed": "Default Playback Speed", "default_playback_speed": "Default Playback Speed",
@@ -384,6 +384,8 @@
"device_usage": "Eszköz {{availableSpace}}%", "device_usage": "Eszköz {{availableSpace}}%",
"size_used": "{{used}} / {{total}} Használatban", "size_used": "{{used}} / {{total}} Használatban",
"delete_all_downloaded_files": "Minden Letöltött Fájl Törlése", "delete_all_downloaded_files": "Minden Letöltött Fájl Törlése",
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
"music_cache_title": "Music Cache", "music_cache_title": "Music Cache",
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support", "music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
"clear_music_cache": "Clear Music Cache", "clear_music_cache": "Clear Music Cache",
@@ -598,7 +600,7 @@
"mpv_player_title": "MPV Player", "mpv_player_title": "MPV Player",
"error": "Hiba", "error": "Hiba",
"failed_to_get_stream_url": "Nem sikerült lekérni a stream URL-t", "failed_to_get_stream_url": "Nem sikerült lekérni a stream URL-t",
"an_error_occured_while_playing_the_video": "Hiba történt a videó lejátszása közben. Ellenőrizd a naplókat a beállításokban.", "an_error_occurred_while_playing_the_video": "Hiba történt a videó lejátszása közben. Ellenőrizd a naplókat a beállításokban.",
"client_error": "Kliens Hiba", "client_error": "Kliens Hiba",
"could_not_create_stream_for_chromecast": "A Chromecast stream létrehozása sikertelen volt", "could_not_create_stream_for_chromecast": "A Chromecast stream létrehozása sikertelen volt",
"message_from_server": "Üzenet a szervertől: {{message}}", "message_from_server": "Üzenet a szervertől: {{message}}",

View File

@@ -16,14 +16,14 @@
"got_it": "Capito", "got_it": "Capito",
"connection_failed": "Connessione fallita", "connection_failed": "Connessione fallita",
"could_not_connect_to_server": "Impossibile connettersi al server. Controllare l'URL e la connessione di rete.", "could_not_connect_to_server": "Impossibile connettersi al server. Controllare l'URL e la connessione di rete.",
"an_unexpected_error_occured": "Si è verificato un errore inaspettato", "an_unexpected_error_occurred": "Si è verificato un errore inaspettato",
"change_server": "Cambiare il server", "change_server": "Cambiare il server",
"invalid_username_or_password": "Nome utente o password non validi", "invalid_username_or_password": "Nome utente o password non validi",
"user_does_not_have_permission_to_log_in": "L'utente non ha il permesso di accedere", "user_does_not_have_permission_to_log_in": "L'utente non ha il permesso di accedere",
"server_is_taking_too_long_to_respond_try_again_later": "Il server sta impiegando troppo tempo per rispondere, riprovare più tardi", "server_is_taking_too_long_to_respond_try_again_later": "Il server sta impiegando troppo tempo per rispondere, riprovare più tardi",
"server_received_too_many_requests_try_again_later": "Il server ha ricevuto troppe richieste, riprovare più tardi.", "server_received_too_many_requests_try_again_later": "Il server ha ricevuto troppe richieste, riprovare più tardi.",
"there_is_a_server_error": "Si è verificato un errore del server", "there_is_a_server_error": "Si è verificato un errore del server",
"an_unexpected_error_occured_did_you_enter_the_correct_url": "Si è verificato un errore imprevisto. L'URL del server è stato inserito correttamente?", "an_unexpected_error_occurred_did_you_enter_the_correct_url": "Si è verificato un errore imprevisto. L'URL del server è stato inserito correttamente?",
"too_old_server_text": "Scoperto Server Jellyfin non supportato", "too_old_server_text": "Scoperto Server Jellyfin non supportato",
"too_old_server_description": "Aggiorna Jellyfin all'ultima versione" "too_old_server_description": "Aggiorna Jellyfin all'ultima versione"
}, },
@@ -188,7 +188,7 @@
"authorize_button": "Autorizza Connessione Rapida", "authorize_button": "Autorizza Connessione Rapida",
"enter_the_quick_connect_code": "Inserisci il codice per la Connessione Rapida...", "enter_the_quick_connect_code": "Inserisci il codice per la Connessione Rapida...",
"success": "Successo", "success": "Successo",
"quick_connect_autorized": "Connessione Rapida autorizzata", "quick_connect_authorized": "Connessione Rapida autorizzata",
"error": "Errore", "error": "Errore",
"invalid_code": "Codice invalido", "invalid_code": "Codice invalido",
"authorize": "Autorizza" "authorize": "Autorizza"
@@ -298,7 +298,7 @@
"show_custom_menu_links": "Mostra i link del menu personalizzato", "show_custom_menu_links": "Mostra i link del menu personalizzato",
"show_large_home_carousel": "Mostra Carosello Grande nella Home (beta)", "show_large_home_carousel": "Mostra Carosello Grande nella Home (beta)",
"hide_libraries": "Nascondi Librerie", "hide_libraries": "Nascondi Librerie",
"select_liraries_you_want_to_hide": "Selezionate le librerie che volete nascondere dalla scheda Libreria e dalle sezioni della pagina iniziale.", "select_libraries_you_want_to_hide": "Selezionate le librerie che volete nascondere dalla scheda Libreria e dalle sezioni della pagina iniziale.",
"disable_haptic_feedback": "Disabilita il feedback aptico", "disable_haptic_feedback": "Disabilita il feedback aptico",
"default_quality": "Qualità predefinita", "default_quality": "Qualità predefinita",
"default_playback_speed": "Default Playback Speed", "default_playback_speed": "Default Playback Speed",
@@ -384,6 +384,8 @@
"device_usage": "Dispositivo {{availableSpace}}%", "device_usage": "Dispositivo {{availableSpace}}%",
"size_used": "{{used}} di {{total}} usato", "size_used": "{{used}} di {{total}} usato",
"delete_all_downloaded_files": "Cancella Tutti i File Scaricati", "delete_all_downloaded_files": "Cancella Tutti i File Scaricati",
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
"music_cache_title": "Music Cache", "music_cache_title": "Music Cache",
"music_cache_description": "Precarica automaticamente i brani mentre ascolti per una riproduzione più fluida e il supporto offline", "music_cache_description": "Precarica automaticamente i brani mentre ascolti per una riproduzione più fluida e il supporto offline",
"clear_music_cache": "Clear Music Cache", "clear_music_cache": "Clear Music Cache",
@@ -598,7 +600,7 @@
"mpv_player_title": "MPV Player", "mpv_player_title": "MPV Player",
"error": "Errore", "error": "Errore",
"failed_to_get_stream_url": "Impossibile ottenere l'URL dello stream", "failed_to_get_stream_url": "Impossibile ottenere l'URL dello stream",
"an_error_occured_while_playing_the_video": "Si è verificato un errore durante la riproduzione del video. Controllare i log nelle impostazioni.", "an_error_occurred_while_playing_the_video": "Si è verificato un errore durante la riproduzione del video. Controllare i log nelle impostazioni.",
"client_error": "Errore del client", "client_error": "Errore del client",
"could_not_create_stream_for_chromecast": "Impossibile creare uno stream per Chromecast", "could_not_create_stream_for_chromecast": "Impossibile creare uno stream per Chromecast",
"message_from_server": "Messaggio dal server", "message_from_server": "Messaggio dal server",

View File

@@ -16,14 +16,14 @@
"got_it": "了解", "got_it": "了解",
"connection_failed": "接続に失敗しました", "connection_failed": "接続に失敗しました",
"could_not_connect_to_server": "サーバーに接続できませんでした。URLとネットワーク接続を確認してください。", "could_not_connect_to_server": "サーバーに接続できませんでした。URLとネットワーク接続を確認してください。",
"an_unexpected_error_occured": "予期しないエラーが発生しました", "an_unexpected_error_occurred": "予期しないエラーが発生しました",
"change_server": "サーバーの変更", "change_server": "サーバーの変更",
"invalid_username_or_password": "ユーザー名またはパスワードが無効です", "invalid_username_or_password": "ユーザー名またはパスワードが無効です",
"user_does_not_have_permission_to_log_in": "ユーザーにログイン権限がありません", "user_does_not_have_permission_to_log_in": "ユーザーにログイン権限がありません",
"server_is_taking_too_long_to_respond_try_again_later": "サーバーの応答に時間がかかりすぎています。しばらくしてからもう一度お試しください。", "server_is_taking_too_long_to_respond_try_again_later": "サーバーの応答に時間がかかりすぎています。しばらくしてからもう一度お試しください。",
"server_received_too_many_requests_try_again_later": "サーバーにリクエストが多すぎます。後でもう一度お試しください。", "server_received_too_many_requests_try_again_later": "サーバーにリクエストが多すぎます。後でもう一度お試しください。",
"there_is_a_server_error": "サーバーエラーが発生しました", "there_is_a_server_error": "サーバーエラーが発生しました",
"an_unexpected_error_occured_did_you_enter_the_correct_url": "予期しないエラーが発生しました。サーバーのURLを正しく入力しましたか", "an_unexpected_error_occurred_did_you_enter_the_correct_url": "予期しないエラーが発生しました。サーバーのURLを正しく入力しましたか",
"too_old_server_text": "サポートされていないJellyfinサーバー発見", "too_old_server_text": "サポートされていないJellyfinサーバー発見",
"too_old_server_description": "Jellyfinを最新バージョンにアップデートしてください" "too_old_server_description": "Jellyfinを最新バージョンにアップデートしてください"
}, },
@@ -188,7 +188,7 @@
"authorize_button": "クイックコネクトを承認する", "authorize_button": "クイックコネクトを承認する",
"enter_the_quick_connect_code": "クイックコネクトコードを入力...", "enter_the_quick_connect_code": "クイックコネクトコードを入力...",
"success": "成功しました", "success": "成功しました",
"quick_connect_autorized": "クイックコネクトが承認されました", "quick_connect_authorized": "クイックコネクトが承認されました",
"error": "エラー", "error": "エラー",
"invalid_code": "無効なコードです", "invalid_code": "無効なコードです",
"authorize": "承認" "authorize": "承認"
@@ -298,7 +298,7 @@
"show_custom_menu_links": "カスタムメニューのリンクを表示", "show_custom_menu_links": "カスタムメニューのリンクを表示",
"show_large_home_carousel": "大きなヒーローBeta", "show_large_home_carousel": "大きなヒーローBeta",
"hide_libraries": "ライブラリを非表示", "hide_libraries": "ライブラリを非表示",
"select_liraries_you_want_to_hide": "ライブラリタブとホームページセクションから非表示にするライブラリを選択します。", "select_libraries_you_want_to_hide": "ライブラリタブとホームページセクションから非表示にするライブラリを選択します。",
"disable_haptic_feedback": "触覚フィードバックを無効にする", "disable_haptic_feedback": "触覚フィードバックを無効にする",
"default_quality": "デフォルトの品質", "default_quality": "デフォルトの品質",
"default_playback_speed": "Default Playback Speed", "default_playback_speed": "Default Playback Speed",
@@ -384,6 +384,8 @@
"device_usage": "デバイス {{availableSpace}}%", "device_usage": "デバイス {{availableSpace}}%",
"size_used": "{{used}} / {{total}} 使用済み", "size_used": "{{used}} / {{total}} 使用済み",
"delete_all_downloaded_files": "すべてのダウンロードファイルを削除", "delete_all_downloaded_files": "すべてのダウンロードファイルを削除",
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
"music_cache_title": "Music Cache", "music_cache_title": "Music Cache",
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support", "music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
"clear_music_cache": "Clear Music Cache", "clear_music_cache": "Clear Music Cache",
@@ -598,7 +600,7 @@
"mpv_player_title": "MPV Player", "mpv_player_title": "MPV Player",
"error": "エラー", "error": "エラー",
"failed_to_get_stream_url": "ストリームURLを取得できませんでした", "failed_to_get_stream_url": "ストリームURLを取得できませんでした",
"an_error_occured_while_playing_the_video": "動画の再生中にエラーが発生しました。設定でログを確認してください。", "an_error_occurred_while_playing_the_video": "動画の再生中にエラーが発生しました。設定でログを確認してください。",
"client_error": "クライアントエラー", "client_error": "クライアントエラー",
"could_not_create_stream_for_chromecast": "Chromecastのストリームを作成できませんでした", "could_not_create_stream_for_chromecast": "Chromecastのストリームを作成できませんでした",
"message_from_server": "サーバーからのメッセージ", "message_from_server": "サーバーからのメッセージ",

View File

@@ -16,14 +16,14 @@
"got_it": "성공", "got_it": "성공",
"connection_failed": "연결 실패", "connection_failed": "연결 실패",
"could_not_connect_to_server": "서버에 연결되지 않았습니다. URL과 네트워크 상태를 확인하세요.", "could_not_connect_to_server": "서버에 연결되지 않았습니다. URL과 네트워크 상태를 확인하세요.",
"an_unexpected_error_occured": "예기치 않은 오류가 발생했습니다", "an_unexpected_error_occurred": "예기치 않은 오류가 발생했습니다",
"change_server": "서버 변경", "change_server": "서버 변경",
"invalid_username_or_password": "잘못된 아이디 혹은 비밀번호입니다", "invalid_username_or_password": "잘못된 아이디 혹은 비밀번호입니다",
"user_does_not_have_permission_to_log_in": "로그인 하기 위한 권한이 없습니다", "user_does_not_have_permission_to_log_in": "로그인 하기 위한 권한이 없습니다",
"server_is_taking_too_long_to_respond_try_again_later": "서버 응답이 너무 느립니다. 나중에 다시 시도하세요", "server_is_taking_too_long_to_respond_try_again_later": "서버 응답이 너무 느립니다. 나중에 다시 시도하세요",
"server_received_too_many_requests_try_again_later": "서버가 너무 많은 요청을 받았습니다. 나중에 다시 시도하세요.", "server_received_too_many_requests_try_again_later": "서버가 너무 많은 요청을 받았습니다. 나중에 다시 시도하세요.",
"there_is_a_server_error": "서버 에러", "there_is_a_server_error": "서버 에러",
"an_unexpected_error_occured_did_you_enter_the_correct_url": "예기치 않은 오류가 발생했습니다. 서버 URL을 올바르게 입력하셨습니까?", "an_unexpected_error_occurred_did_you_enter_the_correct_url": "예기치 않은 오류가 발생했습니다. 서버 URL을 올바르게 입력하셨습니까?",
"too_old_server_text": "Unsupported Jellyfin Server Discovered", "too_old_server_text": "Unsupported Jellyfin Server Discovered",
"too_old_server_description": "Please update Jellyfin to the latest version" "too_old_server_description": "Please update Jellyfin to the latest version"
}, },
@@ -188,7 +188,7 @@
"authorize_button": "퀵 커넥트 승인", "authorize_button": "퀵 커넥트 승인",
"enter_the_quick_connect_code": "퀵 커넥트 코드 입력...", "enter_the_quick_connect_code": "퀵 커넥트 코드 입력...",
"success": "성공", "success": "성공",
"quick_connect_autorized": "퀵 커넥트 승인됨", "quick_connect_authorized": "퀵 커넥트 승인됨",
"error": "오류", "error": "오류",
"invalid_code": "유효하지 않은 코드", "invalid_code": "유효하지 않은 코드",
"authorize": "승인" "authorize": "승인"
@@ -298,7 +298,7 @@
"show_custom_menu_links": "사용자 지정 메뉴 링크 표시", "show_custom_menu_links": "사용자 지정 메뉴 링크 표시",
"show_large_home_carousel": "대형 홈 슬라이드 배너 표시 (베타)", "show_large_home_carousel": "대형 홈 슬라이드 배너 표시 (베타)",
"hide_libraries": "라이브러리 숨기기", "hide_libraries": "라이브러리 숨기기",
"select_liraries_you_want_to_hide": "Select the libraries you want to hide from the Library tab and home page sections.", "select_libraries_you_want_to_hide": "Select the libraries you want to hide from the Library tab and home page sections.",
"disable_haptic_feedback": "Disable Haptic Feedback", "disable_haptic_feedback": "Disable Haptic Feedback",
"default_quality": "Default Quality", "default_quality": "Default Quality",
"default_playback_speed": "Default Playback Speed", "default_playback_speed": "Default Playback Speed",
@@ -384,6 +384,8 @@
"device_usage": "디바이스 {{availableSpace}}%", "device_usage": "디바이스 {{availableSpace}}%",
"size_used": "{{used}} of {{total}} Used", "size_used": "{{used}} of {{total}} Used",
"delete_all_downloaded_files": "Delete All Downloaded Files", "delete_all_downloaded_files": "Delete All Downloaded Files",
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
"music_cache_title": "Music Cache", "music_cache_title": "Music Cache",
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support", "music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
"clear_music_cache": "Clear Music Cache", "clear_music_cache": "Clear Music Cache",
@@ -598,7 +600,7 @@
"mpv_player_title": "MPV Player", "mpv_player_title": "MPV Player",
"error": "Error", "error": "Error",
"failed_to_get_stream_url": "Failed to get the stream URL", "failed_to_get_stream_url": "Failed to get the stream URL",
"an_error_occured_while_playing_the_video": "An error occurred while playing the video. Check logs in settings.", "an_error_occurred_while_playing_the_video": "An error occurred while playing the video. Check logs in settings.",
"client_error": "Client Error", "client_error": "Client Error",
"could_not_create_stream_for_chromecast": "Could not create a stream for Chromecast", "could_not_create_stream_for_chromecast": "Could not create a stream for Chromecast",
"message_from_server": "Message from Server: {{message}}", "message_from_server": "Message from Server: {{message}}",

View File

@@ -16,14 +16,14 @@
"got_it": "Begrepen", "got_it": "Begrepen",
"connection_failed": "Verbinding mislukt", "connection_failed": "Verbinding mislukt",
"could_not_connect_to_server": "Kon niet verbinden met de server. Controleer de URL en je netwerkverbinding.", "could_not_connect_to_server": "Kon niet verbinden met de server. Controleer de URL en je netwerkverbinding.",
"an_unexpected_error_occured": "Er is een onverwachte fout opgetreden", "an_unexpected_error_occurred": "Er is een onverwachte fout opgetreden",
"change_server": "Server wijzigen", "change_server": "Server wijzigen",
"invalid_username_or_password": "Onjuiste gebruikersnaam of wachtwoord", "invalid_username_or_password": "Onjuiste gebruikersnaam of wachtwoord",
"user_does_not_have_permission_to_log_in": "Gebruiker heeft geen rechten om aan te melden", "user_does_not_have_permission_to_log_in": "Gebruiker heeft geen rechten om aan te melden",
"server_is_taking_too_long_to_respond_try_again_later": "De server doet er te lang over om te antwoorden, probeer later opnieuw", "server_is_taking_too_long_to_respond_try_again_later": "De server doet er te lang over om te antwoorden, probeer later opnieuw",
"server_received_too_many_requests_try_again_later": "De server heeft te veel aanvragen ontvangen, probeer later opnieuw", "server_received_too_many_requests_try_again_later": "De server heeft te veel aanvragen ontvangen, probeer later opnieuw",
"there_is_a_server_error": "Er is een serverfout", "there_is_a_server_error": "Er is een serverfout",
"an_unexpected_error_occured_did_you_enter_the_correct_url": "Er is een onverwachte fout opgetreden. Heb je de server URL correct ingegeven?", "an_unexpected_error_occurred_did_you_enter_the_correct_url": "Er is een onverwachte fout opgetreden. Heb je de server URL correct ingegeven?",
"too_old_server_text": "Niet-ondersteunde Jellyfin Server Ontdekt", "too_old_server_text": "Niet-ondersteunde Jellyfin Server Ontdekt",
"too_old_server_description": "Werk Jellyfin bij naar de laatste versie" "too_old_server_description": "Werk Jellyfin bij naar de laatste versie"
}, },
@@ -188,7 +188,7 @@
"authorize_button": "Snel Verbinden toestaan", "authorize_button": "Snel Verbinden toestaan",
"enter_the_quick_connect_code": "Vul de Snel Verbinden code in...", "enter_the_quick_connect_code": "Vul de Snel Verbinden code in...",
"success": "Succes", "success": "Succes",
"quick_connect_autorized": "Snel Verbinden toegestaan", "quick_connect_authorized": "Snel Verbinden toegestaan",
"error": "Fout", "error": "Fout",
"invalid_code": "Ongeldige code", "invalid_code": "Ongeldige code",
"authorize": "Toestaan" "authorize": "Toestaan"
@@ -298,7 +298,7 @@
"show_custom_menu_links": "Aangepaste menulinks tonen", "show_custom_menu_links": "Aangepaste menulinks tonen",
"show_large_home_carousel": "Toon grote carrousel op startpagina (bèta)", "show_large_home_carousel": "Toon grote carrousel op startpagina (bèta)",
"hide_libraries": "Verberg Bibliotheken", "hide_libraries": "Verberg Bibliotheken",
"select_liraries_you_want_to_hide": "Selecteer de bibliotheken die je wil verbergen van de Bibliotheektab en hoofdpagina onderdelen.", "select_libraries_you_want_to_hide": "Selecteer de bibliotheken die je wil verbergen van de Bibliotheektab en hoofdpagina onderdelen.",
"disable_haptic_feedback": "Haptische feedback uitschakelen", "disable_haptic_feedback": "Haptische feedback uitschakelen",
"default_quality": "Standaard kwaliteit", "default_quality": "Standaard kwaliteit",
"default_playback_speed": "Standaard Afspeelsnelheid", "default_playback_speed": "Standaard Afspeelsnelheid",
@@ -384,6 +384,8 @@
"device_usage": "Toestel {{availableSpace}}%", "device_usage": "Toestel {{availableSpace}}%",
"size_used": "{{used}} van {{total}} gebruikt", "size_used": "{{used}} van {{total}} gebruikt",
"delete_all_downloaded_files": "Verwijder alle gedownloade bestanden", "delete_all_downloaded_files": "Verwijder alle gedownloade bestanden",
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
"music_cache_title": "Music Cache", "music_cache_title": "Music Cache",
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support", "music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
"clear_music_cache": "Clear Music Cache", "clear_music_cache": "Clear Music Cache",
@@ -598,7 +600,7 @@
"mpv_player_title": "MPV Player", "mpv_player_title": "MPV Player",
"error": "Fout", "error": "Fout",
"failed_to_get_stream_url": "De stream-URL kon niet worden verkregen", "failed_to_get_stream_url": "De stream-URL kon niet worden verkregen",
"an_error_occured_while_playing_the_video": "Er is een fout opgetreden tijdens het afspelen van de video. Controleer de logs in de instellingen.", "an_error_occurred_while_playing_the_video": "Er is een fout opgetreden tijdens het afspelen van de video. Controleer de logs in de instellingen.",
"client_error": "Fout van de client", "client_error": "Fout van de client",
"could_not_create_stream_for_chromecast": "Kon geen stream maken voor Chromecast", "could_not_create_stream_for_chromecast": "Kon geen stream maken voor Chromecast",
"message_from_server": "Bericht van de server", "message_from_server": "Bericht van de server",

View File

@@ -16,14 +16,14 @@
"got_it": "Har det", "got_it": "Har det",
"connection_failed": "Tilkobling mislyktes", "connection_failed": "Tilkobling mislyktes",
"could_not_connect_to_server": "Kunne ikke koble til serveren. Kontroller nettadressen og nettverkstilkoblingen.", "could_not_connect_to_server": "Kunne ikke koble til serveren. Kontroller nettadressen og nettverkstilkoblingen.",
"an_unexpected_error_occured": "En uventet feil oppstod", "an_unexpected_error_occurred": "En uventet feil oppstod",
"change_server": "Endre server", "change_server": "Endre server",
"invalid_username_or_password": "Ugyldig brukernavn eller passord", "invalid_username_or_password": "Ugyldig brukernavn eller passord",
"user_does_not_have_permission_to_log_in": "Brukeren har ikke tillatelse til å logge inn", "user_does_not_have_permission_to_log_in": "Brukeren har ikke tillatelse til å logge inn",
"server_is_taking_too_long_to_respond_try_again_later": "Serveren tar for lang tid å svare, prøv igjen senere", "server_is_taking_too_long_to_respond_try_again_later": "Serveren tar for lang tid å svare, prøv igjen senere",
"server_received_too_many_requests_try_again_later": "Serveren mottok for mange forespørsler, prøv igjen senere.", "server_received_too_many_requests_try_again_later": "Serveren mottok for mange forespørsler, prøv igjen senere.",
"there_is_a_server_error": "Det er en serverfeil", "there_is_a_server_error": "Det er en serverfeil",
"an_unexpected_error_occured_did_you_enter_the_correct_url": "Det oppstod en uventet feil. Sendte du inn URLen til serveren riktig?", "an_unexpected_error_occurred_did_you_enter_the_correct_url": "Det oppstod en uventet feil. Sendte du inn URLen til serveren riktig?",
"too_old_server_text": "Ustøttet Jellyfin Server oppdaget", "too_old_server_text": "Ustøttet Jellyfin Server oppdaget",
"too_old_server_description": "Vennligst oppdater Jellyfin til nyeste versjon" "too_old_server_description": "Vennligst oppdater Jellyfin til nyeste versjon"
}, },
@@ -188,7 +188,7 @@
"authorize_button": "Autoriser rask tilkobling", "authorize_button": "Autoriser rask tilkobling",
"enter_the_quick_connect_code": "Skriv inn hurtig-tilkobling kode...", "enter_the_quick_connect_code": "Skriv inn hurtig-tilkobling kode...",
"success": "Vellykket", "success": "Vellykket",
"quick_connect_autorized": "Hurtig tilkobling autorisert", "quick_connect_authorized": "Hurtig tilkobling autorisert",
"error": "Feil", "error": "Feil",
"invalid_code": "Ugyldig kode", "invalid_code": "Ugyldig kode",
"authorize": "Autoriser" "authorize": "Autoriser"
@@ -298,7 +298,7 @@
"show_custom_menu_links": "Vis tilpassede menylenker", "show_custom_menu_links": "Vis tilpassede menylenker",
"show_large_home_carousel": "Show Large Home Carousel (beta)", "show_large_home_carousel": "Show Large Home Carousel (beta)",
"hide_libraries": "Skjul biblioteker", "hide_libraries": "Skjul biblioteker",
"select_liraries_you_want_to_hide": "Velg bibliotekene du vil skjule deg for Biblioteket og avsnittene for hjemmesider.", "select_libraries_you_want_to_hide": "Velg bibliotekene du vil skjule deg for Biblioteket og avsnittene for hjemmesider.",
"disable_haptic_feedback": "Deaktiver Haptisk tilbakemelding", "disable_haptic_feedback": "Deaktiver Haptisk tilbakemelding",
"default_quality": "Standard kvalitet", "default_quality": "Standard kvalitet",
"default_playback_speed": "Default Playback Speed", "default_playback_speed": "Default Playback Speed",
@@ -384,6 +384,8 @@
"device_usage": "Enhet {{availableSpace}}%", "device_usage": "Enhet {{availableSpace}}%",
"size_used": "{{used}} av {{total}} er i bruk", "size_used": "{{used}} av {{total}} er i bruk",
"delete_all_downloaded_files": "Slett alle nedlastede filer", "delete_all_downloaded_files": "Slett alle nedlastede filer",
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
"music_cache_title": "Music Cache", "music_cache_title": "Music Cache",
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support", "music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
"clear_music_cache": "Clear Music Cache", "clear_music_cache": "Clear Music Cache",
@@ -598,7 +600,7 @@
"mpv_player_title": "MPV Player", "mpv_player_title": "MPV Player",
"error": "Feil", "error": "Feil",
"failed_to_get_stream_url": "Kan ikke hente nettadressen for stream", "failed_to_get_stream_url": "Kan ikke hente nettadressen for stream",
"an_error_occured_while_playing_the_video": "En feil oppstod under video. Sjekk loggene i innstillingene.", "an_error_occurred_while_playing_the_video": "En feil oppstod under video. Sjekk loggene i innstillingene.",
"client_error": "Feil med annonsør", "client_error": "Feil med annonsør",
"could_not_create_stream_for_chromecast": "Kan ikke opprette en strøm for Chromecast", "could_not_create_stream_for_chromecast": "Kan ikke opprette en strøm for Chromecast",
"message_from_server": "Melding fra tjener: {{message}}", "message_from_server": "Melding fra tjener: {{message}}",

View File

@@ -16,14 +16,14 @@
"got_it": "Rozumiem", "got_it": "Rozumiem",
"connection_failed": "Połączenie nieudane", "connection_failed": "Połączenie nieudane",
"could_not_connect_to_server": "Nie można połączyć się z serwerem. Sprawdź adres URL oraz połączenie sieciowe.", "could_not_connect_to_server": "Nie można połączyć się z serwerem. Sprawdź adres URL oraz połączenie sieciowe.",
"an_unexpected_error_occured": "Wystąpił nieoczekiwany błąd", "an_unexpected_error_occurred": "Wystąpił nieoczekiwany błąd",
"change_server": "Zmień serwer", "change_server": "Zmień serwer",
"invalid_username_or_password": "Nieprawidłowa nazwa użytkownika lub hasło", "invalid_username_or_password": "Nieprawidłowa nazwa użytkownika lub hasło",
"user_does_not_have_permission_to_log_in": "Użytkownik nie ma uprawnień do logowania", "user_does_not_have_permission_to_log_in": "Użytkownik nie ma uprawnień do logowania",
"server_is_taking_too_long_to_respond_try_again_later": "Serwer zbyt długo nie odpowiada spróbuj ponownie później", "server_is_taking_too_long_to_respond_try_again_later": "Serwer zbyt długo nie odpowiada spróbuj ponownie później",
"server_received_too_many_requests_try_again_later": "Serwer otrzymał zbyt wiele żądań spróbuj ponownie później.", "server_received_too_many_requests_try_again_later": "Serwer otrzymał zbyt wiele żądań spróbuj ponownie później.",
"there_is_a_server_error": "Wystąpił błąd serwera", "there_is_a_server_error": "Wystąpił błąd serwera",
"an_unexpected_error_occured_did_you_enter_the_correct_url": "Wystąpił nieoczekiwany błąd. Czy wpisałeś poprawny adres URL?", "an_unexpected_error_occurred_did_you_enter_the_correct_url": "Wystąpił nieoczekiwany błąd. Czy wpisałeś poprawny adres URL?",
"too_old_server_text": "Wykryto nieobsługiwany serwer Jellyfin", "too_old_server_text": "Wykryto nieobsługiwany serwer Jellyfin",
"too_old_server_description": "Proszę zaktualizować Jellyfin do najnowszej wersji" "too_old_server_description": "Proszę zaktualizować Jellyfin do najnowszej wersji"
}, },
@@ -188,7 +188,7 @@
"authorize_button": "Autoryzuj szybkie połączenie", "authorize_button": "Autoryzuj szybkie połączenie",
"enter_the_quick_connect_code": "Wpisz kod szybkiego połączenia...", "enter_the_quick_connect_code": "Wpisz kod szybkiego połączenia...",
"success": "Sukces", "success": "Sukces",
"quick_connect_autorized": "Szybkie połączenie autoryzowane", "quick_connect_authorized": "Szybkie połączenie autoryzowane",
"error": "Błąd", "error": "Błąd",
"invalid_code": "Nieprawidłowy kod", "invalid_code": "Nieprawidłowy kod",
"authorize": "Autoryzuj" "authorize": "Autoryzuj"
@@ -298,7 +298,7 @@
"show_custom_menu_links": "Pokaż niestandardowe odnośniki w menu", "show_custom_menu_links": "Pokaż niestandardowe odnośniki w menu",
"show_large_home_carousel": "Wyświetl Dużą Karuzelę na ekranie głównym (beta)", "show_large_home_carousel": "Wyświetl Dużą Karuzelę na ekranie głównym (beta)",
"hide_libraries": "Ukryj biblioteki", "hide_libraries": "Ukryj biblioteki",
"select_liraries_you_want_to_hide": "Wybierz biblioteki, które chcesz ukryć na karcie Biblioteka i w sekcjach strony głównej.", "select_libraries_you_want_to_hide": "Wybierz biblioteki, które chcesz ukryć na karcie Biblioteka i w sekcjach strony głównej.",
"disable_haptic_feedback": "Wyłącz wibracje", "disable_haptic_feedback": "Wyłącz wibracje",
"default_quality": "Domyślna jakość", "default_quality": "Domyślna jakość",
"default_playback_speed": "Domyślna prędkość odtwarzania", "default_playback_speed": "Domyślna prędkość odtwarzania",
@@ -384,6 +384,8 @@
"device_usage": "Urządzenie {{availableSpace}}%", "device_usage": "Urządzenie {{availableSpace}}%",
"size_used": "{{used}} z {{total}} wykorzystane", "size_used": "{{used}} z {{total}} wykorzystane",
"delete_all_downloaded_files": "Usuń wszystkie pobrane pliki", "delete_all_downloaded_files": "Usuń wszystkie pobrane pliki",
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
"music_cache_title": "Bufor muzyki", "music_cache_title": "Bufor muzyki",
"music_cache_description": "Automatycznie buforuj piosenki w trakcie słuchania dla płynniejszego odtwarzania i wsparcia offline", "music_cache_description": "Automatycznie buforuj piosenki w trakcie słuchania dla płynniejszego odtwarzania i wsparcia offline",
"clear_music_cache": "Wyczyść bufor muzyki", "clear_music_cache": "Wyczyść bufor muzyki",
@@ -598,7 +600,7 @@
"mpv_player_title": "MPV Player", "mpv_player_title": "MPV Player",
"error": "Błąd", "error": "Błąd",
"failed_to_get_stream_url": "Nie udało się pobrać adresu strumienia", "failed_to_get_stream_url": "Nie udało się pobrać adresu strumienia",
"an_error_occured_while_playing_the_video": "Wystąpił błąd podczas odtwarzania wideo. Sprawdź logi w ustawieniach.", "an_error_occurred_while_playing_the_video": "Wystąpił błąd podczas odtwarzania wideo. Sprawdź logi w ustawieniach.",
"client_error": "Błąd klienta", "client_error": "Błąd klienta",
"could_not_create_stream_for_chromecast": "Nie udało się utworzyć strumienia dla Chromecasta", "could_not_create_stream_for_chromecast": "Nie udało się utworzyć strumienia dla Chromecasta",
"message_from_server": "Wiadomość z serwera: {{message}}", "message_from_server": "Wiadomość z serwera: {{message}}",

View File

@@ -16,14 +16,14 @@
"got_it": "Tenho isso", "got_it": "Tenho isso",
"connection_failed": "Falha na conexão", "connection_failed": "Falha na conexão",
"could_not_connect_to_server": "Não foi possível conectar ao servidor. Verifique a URL e sua conexão de rede.", "could_not_connect_to_server": "Não foi possível conectar ao servidor. Verifique a URL e sua conexão de rede.",
"an_unexpected_error_occured": "Ocorreu um erro inesperado", "an_unexpected_error_occurred": "An unexpected error occurred",
"change_server": "Alterar Servidor", "change_server": "Alterar Servidor",
"invalid_username_or_password": "Usuário ou senha inválidos", "invalid_username_or_password": "Usuário ou senha inválidos",
"user_does_not_have_permission_to_log_in": "Usuário não tem permissão para fazer login", "user_does_not_have_permission_to_log_in": "Usuário não tem permissão para fazer login",
"server_is_taking_too_long_to_respond_try_again_later": "O servidor está demorando muito para responder, tente novamente mais tarde", "server_is_taking_too_long_to_respond_try_again_later": "O servidor está demorando muito para responder, tente novamente mais tarde",
"server_received_too_many_requests_try_again_later": "O servidor recebeu muitos pedidos, tente novamente mais tarde.", "server_received_too_many_requests_try_again_later": "O servidor recebeu muitos pedidos, tente novamente mais tarde.",
"there_is_a_server_error": "Existe um erro no servidor", "there_is_a_server_error": "Existe um erro no servidor",
"an_unexpected_error_occured_did_you_enter_the_correct_url": "Ocorreu um erro inesperado. Você inseriu a URL do servidor corretamente?", "an_unexpected_error_occurred_did_you_enter_the_correct_url": "An unexpected error occurred. Did you enter the server URL correctly?",
"too_old_server_text": "Servidor Jellyfin Descoberto Não Suportado", "too_old_server_text": "Servidor Jellyfin Descoberto Não Suportado",
"too_old_server_description": "Por favor, atualize o Jellyfin para a versão mais recente" "too_old_server_description": "Por favor, atualize o Jellyfin para a versão mais recente"
}, },
@@ -188,7 +188,7 @@
"authorize_button": "Autorizar conexão rápida", "authorize_button": "Autorizar conexão rápida",
"enter_the_quick_connect_code": "Insira o código de conexão rápida...", "enter_the_quick_connect_code": "Insira o código de conexão rápida...",
"success": "Sucesso", "success": "Sucesso",
"quick_connect_autorized": "Acesso Rápido Autorizado", "quick_connect_authorized": "Quick Connect authorized",
"error": "Erro", "error": "Erro",
"invalid_code": "Código inválido", "invalid_code": "Código inválido",
"authorize": "Autorizar" "authorize": "Autorizar"
@@ -298,7 +298,7 @@
"show_custom_menu_links": "Mostrar Links de Menu Personalizado", "show_custom_menu_links": "Mostrar Links de Menu Personalizado",
"show_large_home_carousel": "Mostrar Carrossel Grande (beta)", "show_large_home_carousel": "Mostrar Carrossel Grande (beta)",
"hide_libraries": "Ocultar bibliotecas", "hide_libraries": "Ocultar bibliotecas",
"select_liraries_you_want_to_hide": "Selecione as bibliotecas que você deseja ocultar da aba Biblioteca e seções da página inicial.", "select_libraries_you_want_to_hide": "Select the libraries you want to hide from the Library tab and home page sections.",
"disable_haptic_feedback": "Desativar o retorno tátil", "disable_haptic_feedback": "Desativar o retorno tátil",
"default_quality": "Qualidade Padrão", "default_quality": "Qualidade Padrão",
"default_playback_speed": "Velocidade padrão de reprodução", "default_playback_speed": "Velocidade padrão de reprodução",
@@ -384,6 +384,8 @@
"device_usage": "Dispositivo {{availableSpace}}%", "device_usage": "Dispositivo {{availableSpace}}%",
"size_used": "{{used}} de {{total}} Utilizados", "size_used": "{{used}} de {{total}} Utilizados",
"delete_all_downloaded_files": "Excluir todos os arquivos baixados", "delete_all_downloaded_files": "Excluir todos os arquivos baixados",
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
"music_cache_title": "Cache de Música", "music_cache_title": "Cache de Música",
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support", "music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
"clear_music_cache": "Limpar Cache de Música", "clear_music_cache": "Limpar Cache de Música",
@@ -598,7 +600,7 @@
"mpv_player_title": "MPV Player", "mpv_player_title": "MPV Player",
"error": "ERRO", "error": "ERRO",
"failed_to_get_stream_url": "Falha ao obter a URL de transmissão", "failed_to_get_stream_url": "Falha ao obter a URL de transmissão",
"an_error_occured_while_playing_the_video": "Ocorreu um erro ao reproduzir o vídeo. Verifique os logs nas configurações.", "an_error_occurred_while_playing_the_video": "An error occurred while playing the video. Check logs in settings.",
"client_error": "Erro de Cliente", "client_error": "Erro de Cliente",
"could_not_create_stream_for_chromecast": "Não foi possível criar um fluxo para o Chromecast", "could_not_create_stream_for_chromecast": "Não foi possível criar um fluxo para o Chromecast",
"message_from_server": "Mensagem do Servidor: {{message}}", "message_from_server": "Mensagem do Servidor: {{message}}",

View File

@@ -16,14 +16,14 @@
"got_it": "Am înţeles", "got_it": "Am înţeles",
"connection_failed": "Conectare eșuată", "connection_failed": "Conectare eșuată",
"could_not_connect_to_server": "Nu s-a putut conecta la server. Verificați adresa URL și conexiunea la internet.", "could_not_connect_to_server": "Nu s-a putut conecta la server. Verificați adresa URL și conexiunea la internet.",
"an_unexpected_error_occured": "A apărut o eroare neașteptată", "an_unexpected_error_occurred": "A apărut o eroare neașteptată",
"change_server": "Schimba", "change_server": "Schimba",
"invalid_username_or_password": "Nume de utilizator sau parolă greșită", "invalid_username_or_password": "Nume de utilizator sau parolă greșită",
"user_does_not_have_permission_to_log_in": "Utilizatorul nu are permisiunea de a se conecta", "user_does_not_have_permission_to_log_in": "Utilizatorul nu are permisiunea de a se conecta",
"server_is_taking_too_long_to_respond_try_again_later": "Serverul răspunde prea greu, încearcă din nou mai târziu.", "server_is_taking_too_long_to_respond_try_again_later": "Serverul răspunde prea greu, încearcă din nou mai târziu.",
"server_received_too_many_requests_try_again_later": "Serverul a primit prea multe solicitări, încercați din nou mai târziu.", "server_received_too_many_requests_try_again_later": "Serverul a primit prea multe solicitări, încercați din nou mai târziu.",
"there_is_a_server_error": "Eroare de server", "there_is_a_server_error": "Eroare de server",
"an_unexpected_error_occured_did_you_enter_the_correct_url": "A apărut o eroare neașteptată. Ați introdus corect adresa URL a serverului?", "an_unexpected_error_occurred_did_you_enter_the_correct_url": "A apărut o eroare neașteptată. Ați introdus corect adresa URL a serverului?",
"too_old_server_text": "Serverul Jellyfin Neacceptat Descoperit", "too_old_server_text": "Serverul Jellyfin Neacceptat Descoperit",
"too_old_server_description": "Vă rugăm să actualizați Jellyfin la cea mai recentă versiune" "too_old_server_description": "Vă rugăm să actualizați Jellyfin la cea mai recentă versiune"
}, },
@@ -188,7 +188,7 @@
"authorize_button": "Autorizare conectare rapidă", "authorize_button": "Autorizare conectare rapidă",
"enter_the_quick_connect_code": "Introduceți codul pt. conectare rapidă...", "enter_the_quick_connect_code": "Introduceți codul pt. conectare rapidă...",
"success": "Succes", "success": "Succes",
"quick_connect_autorized": "Conectare rapidă autorizată", "quick_connect_authorized": "Conectare rapidă autorizată",
"error": "Eroare", "error": "Eroare",
"invalid_code": "Cod invalid.", "invalid_code": "Cod invalid.",
"authorize": "Autorizează" "authorize": "Autorizează"
@@ -298,7 +298,7 @@
"show_custom_menu_links": "Afișează link-uri personalizate în meniu", "show_custom_menu_links": "Afișează link-uri personalizate în meniu",
"show_large_home_carousel": "Arată Caruselul Media Mare (beta)", "show_large_home_carousel": "Arată Caruselul Media Mare (beta)",
"hide_libraries": "Ascunde bibliotecile", "hide_libraries": "Ascunde bibliotecile",
"select_liraries_you_want_to_hide": "Selectează bibliotecile pe care dorești să le ascunzi din fila Bibliotecă și din secțiunile paginii principale.", "select_libraries_you_want_to_hide": "Selectează bibliotecile pe care dorești să le ascunzi din fila Bibliotecă și din secțiunile paginii principale.",
"disable_haptic_feedback": "Dezactivează vibrațiile tactile", "disable_haptic_feedback": "Dezactivează vibrațiile tactile",
"default_quality": "Calitate implicită", "default_quality": "Calitate implicită",
"default_playback_speed": "Default Playback Speed", "default_playback_speed": "Default Playback Speed",
@@ -384,6 +384,8 @@
"device_usage": "Dispozitiv {{availableSpace}}%", "device_usage": "Dispozitiv {{availableSpace}}%",
"size_used": "{{used}} din {{total}} folosit", "size_used": "{{used}} din {{total}} folosit",
"delete_all_downloaded_files": "Ștergeți toate fișierele descărcate", "delete_all_downloaded_files": "Ștergeți toate fișierele descărcate",
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
"music_cache_title": "Music Cache", "music_cache_title": "Music Cache",
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support", "music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
"clear_music_cache": "Clear Music Cache", "clear_music_cache": "Clear Music Cache",
@@ -598,7 +600,7 @@
"mpv_player_title": "MPV Player", "mpv_player_title": "MPV Player",
"error": "Eroare", "error": "Eroare",
"failed_to_get_stream_url": "Nu s-a putut obține adresa URL a fluxului", "failed_to_get_stream_url": "Nu s-a putut obține adresa URL a fluxului",
"an_error_occured_while_playing_the_video": "A apărut o eroare la redarea videoclipului. Verificați jurnalele în setări.", "an_error_occurred_while_playing_the_video": "A apărut o eroare la redarea videoclipului. Verificați jurnalele în setări.",
"client_error": "Eroare client", "client_error": "Eroare client",
"could_not_create_stream_for_chromecast": "Nu s-a putut crea un flux pentru Chromecast", "could_not_create_stream_for_chromecast": "Nu s-a putut crea un flux pentru Chromecast",
"message_from_server": "Mesaj de la server: {{message}}", "message_from_server": "Mesaj de la server: {{message}}",

View File

@@ -16,14 +16,14 @@
"got_it": "Принято", "got_it": "Принято",
"connection_failed": "Соединение не удалось", "connection_failed": "Соединение не удалось",
"could_not_connect_to_server": "Не удалось подключиться к серверу. Пожалуйста, проверьте URL и ваше интернет-соединение.", "could_not_connect_to_server": "Не удалось подключиться к серверу. Пожалуйста, проверьте URL и ваше интернет-соединение.",
"an_unexpected_error_occured": "Возникла непредвиденная ошибка", "an_unexpected_error_occurred": "Возникла непредвиденная ошибка",
"change_server": "Поменять сервер", "change_server": "Поменять сервер",
"invalid_username_or_password": "Неправильное имя пользователя или пароль", "invalid_username_or_password": "Неправильное имя пользователя или пароль",
"user_does_not_have_permission_to_log_in": "Пользователь не имеет прав на вход", "user_does_not_have_permission_to_log_in": "Пользователь не имеет прав на вход",
"server_is_taking_too_long_to_respond_try_again_later": "Сервер долго не отвечает, попробуйте позже", "server_is_taking_too_long_to_respond_try_again_later": "Сервер долго не отвечает, попробуйте позже",
"server_received_too_many_requests_try_again_later": "Сервер получил слишком много запросов, попробуйте позже.", "server_received_too_many_requests_try_again_later": "Сервер получил слишком много запросов, попробуйте позже.",
"there_is_a_server_error": "Возникла ошибка сервера", "there_is_a_server_error": "Возникла ошибка сервера",
"an_unexpected_error_occured_did_you_enter_the_correct_url": "Возникла непредвиденная ошибка. Вы правильно ввели URL?", "an_unexpected_error_occurred_did_you_enter_the_correct_url": "Возникла непредвиденная ошибка. Вы правильно ввели URL?",
"too_old_server_text": "Обнаружен неподдерживаемый сервер Jellyfin", "too_old_server_text": "Обнаружен неподдерживаемый сервер Jellyfin",
"too_old_server_description": "Пожалуйста, обновите Jellyfin до последней версии" "too_old_server_description": "Пожалуйста, обновите Jellyfin до последней версии"
}, },
@@ -188,7 +188,7 @@
"authorize_button": "Авторизовать через быстрое подключение", "authorize_button": "Авторизовать через быстрое подключение",
"enter_the_quick_connect_code": "Введите код для быстрого подключения...", "enter_the_quick_connect_code": "Введите код для быстрого подключения...",
"success": "Успех", "success": "Успех",
"quick_connect_autorized": "Быстрое подключение авторизовано", "quick_connect_authorized": "Быстрое подключение авторизовано",
"error": "Ошибка", "error": "Ошибка",
"invalid_code": "Неверный код", "invalid_code": "Неверный код",
"authorize": "Авторизовать" "authorize": "Авторизовать"
@@ -298,7 +298,7 @@
"show_custom_menu_links": "Показать ссылки пользовательского меню", "show_custom_menu_links": "Показать ссылки пользовательского меню",
"show_large_home_carousel": "Показывать большую карусель (beta)", "show_large_home_carousel": "Показывать большую карусель (beta)",
"hide_libraries": "Скрыть библиотеки", "hide_libraries": "Скрыть библиотеки",
"select_liraries_you_want_to_hide": "Выберите Библиотеки, которое хотите спрятать из вкладки Библиотеки и домашней страницы.", "select_libraries_you_want_to_hide": "Выберите Библиотеки, которое хотите спрятать из вкладки Библиотеки и домашней страницы.",
"disable_haptic_feedback": "Отключить тактильную обратную связь", "disable_haptic_feedback": "Отключить тактильную обратную связь",
"default_quality": "Качество по умолчанию", "default_quality": "Качество по умолчанию",
"default_playback_speed": "Скорость воспроизведения по умолчанию", "default_playback_speed": "Скорость воспроизведения по умолчанию",
@@ -384,6 +384,8 @@
"device_usage": "Устройство {{availableSpace}}%", "device_usage": "Устройство {{availableSpace}}%",
"size_used": "{{used}} из {{total}} использовано", "size_used": "{{used}} из {{total}} использовано",
"delete_all_downloaded_files": "Удалить все загруженные файлы", "delete_all_downloaded_files": "Удалить все загруженные файлы",
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
"music_cache_title": "Кеш музыки", "music_cache_title": "Кеш музыки",
"music_cache_description": "Автоматически кешировать песни по мере прослушивания для плавного воспроизведения и поддержки отсутствия интернета", "music_cache_description": "Автоматически кешировать песни по мере прослушивания для плавного воспроизведения и поддержки отсутствия интернета",
"clear_music_cache": "Очистить кеш музыки", "clear_music_cache": "Очистить кеш музыки",
@@ -598,7 +600,7 @@
"mpv_player_title": "MPV Player", "mpv_player_title": "MPV Player",
"error": "Ошибка", "error": "Ошибка",
"failed_to_get_stream_url": "Не удалось получить URL потока", "failed_to_get_stream_url": "Не удалось получить URL потока",
"an_error_occured_while_playing_the_video": "Возникла Неожиданная ошибка во время воспроизведения. Проверьте логи в настройках.", "an_error_occurred_while_playing_the_video": "Возникла Неожиданная ошибка во время воспроизведения. Проверьте логи в настройках.",
"client_error": "Ошибка клиента", "client_error": "Ошибка клиента",
"could_not_create_stream_for_chromecast": "Не удалось создать поток для Chromecast", "could_not_create_stream_for_chromecast": "Не удалось создать поток для Chromecast",
"message_from_server": "Сообщение от сервера: {{message}}", "message_from_server": "Сообщение от сервера: {{message}}",

View File

@@ -16,14 +16,14 @@
"got_it": "Koden har mottagits", "got_it": "Koden har mottagits",
"connection_failed": "Anslutningen Misslyckades", "connection_failed": "Anslutningen Misslyckades",
"could_not_connect_to_server": "Det gick inte att ansluta till servern. Kontrollera webbadressen och din nätverksanslutning.", "could_not_connect_to_server": "Det gick inte att ansluta till servern. Kontrollera webbadressen och din nätverksanslutning.",
"an_unexpected_error_occured": "Ett Oväntat Fel Uppstod", "an_unexpected_error_occurred": "Ett Oväntat Fel Uppstod",
"change_server": "Byt Server", "change_server": "Byt Server",
"invalid_username_or_password": "Ogiltigt användarnamn eller lösenord", "invalid_username_or_password": "Ogiltigt användarnamn eller lösenord",
"user_does_not_have_permission_to_log_in": "Användaren har inte behörighet att logga in", "user_does_not_have_permission_to_log_in": "Användaren har inte behörighet att logga in",
"server_is_taking_too_long_to_respond_try_again_later": "Servern svarar för långsamt, försök igen senare", "server_is_taking_too_long_to_respond_try_again_later": "Servern svarar för långsamt, försök igen senare",
"server_received_too_many_requests_try_again_later": "Servern har fått för många förfrågningar, försök igen senare.", "server_received_too_many_requests_try_again_later": "Servern har fått för många förfrågningar, försök igen senare.",
"there_is_a_server_error": "Ett serverfel uppstod", "there_is_a_server_error": "Ett serverfel uppstod",
"an_unexpected_error_occured_did_you_enter_the_correct_url": "Ett oväntat fel uppstod. Har du angett serverns URL korrekt?", "an_unexpected_error_occurred_did_you_enter_the_correct_url": "Ett oväntat fel uppstod. Har du angett serverns URL korrekt?",
"too_old_server_text": "Jellyfin Servern Stöds Inte", "too_old_server_text": "Jellyfin Servern Stöds Inte",
"too_old_server_description": "Var God Uppdatera Jellyfin Till Den Senaste Versionen" "too_old_server_description": "Var God Uppdatera Jellyfin Till Den Senaste Versionen"
}, },
@@ -188,7 +188,7 @@
"authorize_button": "Godkänn snabbanslutning", "authorize_button": "Godkänn snabbanslutning",
"enter_the_quick_connect_code": "Ange snabbanslutningskod...", "enter_the_quick_connect_code": "Ange snabbanslutningskod...",
"success": "Snabbanslutning lyckades", "success": "Snabbanslutning lyckades",
"quick_connect_autorized": "Snabbanslutning Godkänd", "quick_connect_authorized": "Snabbanslutning Godkänd",
"error": "Fel", "error": "Fel",
"invalid_code": "Ogiltig kod", "invalid_code": "Ogiltig kod",
"authorize": "Autentisera" "authorize": "Autentisera"
@@ -298,7 +298,7 @@
"show_custom_menu_links": "Visa anpassade menylänkar", "show_custom_menu_links": "Visa anpassade menylänkar",
"show_large_home_carousel": "Visa toppbanner (beta)", "show_large_home_carousel": "Visa toppbanner (beta)",
"hide_libraries": "Dölj bibliotek", "hide_libraries": "Dölj bibliotek",
"select_liraries_you_want_to_hide": "Välj de bibliotek du vill dölja på fliken Bibliotek och på startsidan.", "select_libraries_you_want_to_hide": "Välj de bibliotek du vill dölja på fliken Bibliotek och på startsidan.",
"disable_haptic_feedback": "Stäng av vibrationer", "disable_haptic_feedback": "Stäng av vibrationer",
"default_quality": "Förvald Kvalitet", "default_quality": "Förvald Kvalitet",
"default_playback_speed": "Default Playback Speed", "default_playback_speed": "Default Playback Speed",
@@ -384,6 +384,8 @@
"device_usage": "Telefon {{availableSpace}}%", "device_usage": "Telefon {{availableSpace}}%",
"size_used": "{{used}} av {{total}} används", "size_used": "{{used}} av {{total}} används",
"delete_all_downloaded_files": "Ta bort alla nerladdade filer", "delete_all_downloaded_files": "Ta bort alla nerladdade filer",
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
"music_cache_title": "Musikcache", "music_cache_title": "Musikcache",
"music_cache_description": "Cacha automatiskt låtar när du lyssnar för smidigare uppspelning och offline-stöd", "music_cache_description": "Cacha automatiskt låtar när du lyssnar för smidigare uppspelning och offline-stöd",
"clear_music_cache": "Rensa musikcache", "clear_music_cache": "Rensa musikcache",
@@ -598,7 +600,7 @@
"mpv_player_title": "MPV Player", "mpv_player_title": "MPV Player",
"error": "Fel", "error": "Fel",
"failed_to_get_stream_url": "Kunde inte hämta stream-URL", "failed_to_get_stream_url": "Kunde inte hämta stream-URL",
"an_error_occured_while_playing_the_video": "Ett fel uppstod vid uppspelning av videon. Kontrollera loggarna i inställningarna.", "an_error_occurred_while_playing_the_video": "Ett fel uppstod vid uppspelning av videon. Kontrollera loggarna i inställningarna.",
"client_error": "Klientfel", "client_error": "Klientfel",
"could_not_create_stream_for_chromecast": "Kunde inte skapa stream för Chromecast", "could_not_create_stream_for_chromecast": "Kunde inte skapa stream för Chromecast",
"message_from_server": "Meddelande från servern: {{message}}", "message_from_server": "Meddelande från servern: {{message}}",

View File

@@ -16,14 +16,14 @@
"got_it": "Got It", "got_it": "Got It",
"connection_failed": "Connection Failed", "connection_failed": "Connection Failed",
"could_not_connect_to_server": "Could not connect to the server. Please check the URL and your network connection.", "could_not_connect_to_server": "Could not connect to the server. Please check the URL and your network connection.",
"an_unexpected_error_occured": "An Unexpected Error Occurred", "an_unexpected_error_occurred": "An unexpected error occurred",
"change_server": "Change Server", "change_server": "Change Server",
"invalid_username_or_password": "Invalid Username or Password", "invalid_username_or_password": "Invalid Username or Password",
"user_does_not_have_permission_to_log_in": "ผู้ใช้ไม่มีสิทธิ์ในการเข้าสู่ระบบ", "user_does_not_have_permission_to_log_in": "ผู้ใช้ไม่มีสิทธิ์ในการเข้าสู่ระบบ",
"server_is_taking_too_long_to_respond_try_again_later": "Server is taking too long to respond, try again later", "server_is_taking_too_long_to_respond_try_again_later": "Server is taking too long to respond, try again later",
"server_received_too_many_requests_try_again_later": "Server received too many requests, try again later.", "server_received_too_many_requests_try_again_later": "Server received too many requests, try again later.",
"there_is_a_server_error": "There is a server error", "there_is_a_server_error": "There is a server error",
"an_unexpected_error_occured_did_you_enter_the_correct_url": "", "an_unexpected_error_occurred_did_you_enter_the_correct_url": "An unexpected error occurred. Did you enter the server URL correctly?",
"too_old_server_text": "Unsupported Jellyfin Server Discovered", "too_old_server_text": "Unsupported Jellyfin Server Discovered",
"too_old_server_description": "Please update Jellyfin to the latest version" "too_old_server_description": "Please update Jellyfin to the latest version"
}, },
@@ -188,7 +188,7 @@
"authorize_button": "Authorize Quick Connect", "authorize_button": "Authorize Quick Connect",
"enter_the_quick_connect_code": "Enter the quick connect code...", "enter_the_quick_connect_code": "Enter the quick connect code...",
"success": "Success", "success": "Success",
"quick_connect_autorized": "Quick Connect Authorized", "quick_connect_authorized": "Quick Connect authorized",
"error": "Error", "error": "Error",
"invalid_code": "Invalid Code", "invalid_code": "Invalid Code",
"authorize": "Authorize" "authorize": "Authorize"
@@ -298,7 +298,7 @@
"show_custom_menu_links": "Show Custom Menu Links", "show_custom_menu_links": "Show Custom Menu Links",
"show_large_home_carousel": "Show Large Home Carousel (beta)", "show_large_home_carousel": "Show Large Home Carousel (beta)",
"hide_libraries": "Hide Libraries", "hide_libraries": "Hide Libraries",
"select_liraries_you_want_to_hide": "Select the libraries you want to hide from the Library tab and home page sections.", "select_libraries_you_want_to_hide": "Select the libraries you want to hide from the Library tab and home page sections.",
"disable_haptic_feedback": "Disable Haptic Feedback", "disable_haptic_feedback": "Disable Haptic Feedback",
"default_quality": "Default Quality", "default_quality": "Default Quality",
"default_playback_speed": "Default Playback Speed", "default_playback_speed": "Default Playback Speed",
@@ -384,6 +384,8 @@
"device_usage": "Device {{availableSpace}}%", "device_usage": "Device {{availableSpace}}%",
"size_used": "{{used}} of {{total}} Used", "size_used": "{{used}} of {{total}} Used",
"delete_all_downloaded_files": "Delete All Downloaded Files", "delete_all_downloaded_files": "Delete All Downloaded Files",
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
"music_cache_title": "Music Cache", "music_cache_title": "Music Cache",
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support", "music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
"clear_music_cache": "Clear Music Cache", "clear_music_cache": "Clear Music Cache",
@@ -598,7 +600,7 @@
"mpv_player_title": "MPV Player", "mpv_player_title": "MPV Player",
"error": "Error", "error": "Error",
"failed_to_get_stream_url": "Failed to get the stream URL", "failed_to_get_stream_url": "Failed to get the stream URL",
"an_error_occured_while_playing_the_video": "An error occurred while playing the video. Check logs in settings.", "an_error_occurred_while_playing_the_video": "An error occurred while playing the video. Check logs in settings.",
"client_error": "Client Error", "client_error": "Client Error",
"could_not_create_stream_for_chromecast": "Could not create a stream for Chromecast", "could_not_create_stream_for_chromecast": "Could not create a stream for Chromecast",
"message_from_server": "Message from Server: {{message}}", "message_from_server": "Message from Server: {{message}}",

View File

@@ -16,14 +16,14 @@
"got_it": "jIyaj", "got_it": "jIyaj",
"connection_failed": "ngoQlaHbe'", "connection_failed": "ngoQlaHbe'",
"could_not_connect_to_server": "SeHlaw veS Ho'Do'laHbe'. URL 'ej ret ghun mej.", "could_not_connect_to_server": "SeHlaw veS Ho'Do'laHbe'. URL 'ej ret ghun mej.",
"an_unexpected_error_occured": "num ghIq Doch", "an_unexpected_error_occurred": "num ghIq Doch",
"change_server": "Ho'Do' veS yIghoS", "change_server": "Ho'Do' veS yIghoS",
"invalid_username_or_password": "tlhIngan pagh ngoq De' law'be'", "invalid_username_or_password": "tlhIngan pagh ngoq De' law'be'",
"user_does_not_have_permission_to_log_in": "tlhIngan lut 'el je'laHbe'", "user_does_not_have_permission_to_log_in": "tlhIngan lut 'el je'laHbe'",
"server_is_taking_too_long_to_respond_try_again_later": "Ho'Do' veS jachrup. pItlh yIHaD", "server_is_taking_too_long_to_respond_try_again_later": "Ho'Do' veS jachrup. pItlh yIHaD",
"server_received_too_many_requests_try_again_later": "Ho'Do' veS lutlh ngeb petlh law'. pItlh yIHaD.", "server_received_too_many_requests_try_again_later": "Ho'Do' veS lutlh ngeb petlh law'. pItlh yIHaD.",
"there_is_a_server_error": "Ho'Do' veS ghIq maS", "there_is_a_server_error": "Ho'Do' veS ghIq maS",
"an_unexpected_error_occured_did_you_enter_the_correct_url": "num ghIq Doch. URL mej Danej'a'?", "an_unexpected_error_occurred_did_you_enter_the_correct_url": "num ghIq Doch. URL mej Danej'a'?",
"too_old_server_text": "Unsupported Jellyfin Server Discovered", "too_old_server_text": "Unsupported Jellyfin Server Discovered",
"too_old_server_description": "**Jellyfin chu'qu' Dotlh yIchoHmoH**" "too_old_server_description": "**Jellyfin chu'qu' Dotlh yIchoHmoH**"
}, },
@@ -188,7 +188,7 @@
"authorize_button": "parmaq ngoQ yIje'", "authorize_button": "parmaq ngoQ yIje'",
"enter_the_quick_connect_code": "parmaq ngoQ De' yI'el...", "enter_the_quick_connect_code": "parmaq ngoQ De' yI'el...",
"success": "Qapla'", "success": "Qapla'",
"quick_connect_autorized": "parmaq ngoQ je'laH", "quick_connect_authorized": "parmaq ngoQ je'laH",
"error": "ghIq", "error": "ghIq",
"invalid_code": "De' law'be'", "invalid_code": "De' law'be'",
"authorize": "yIje'" "authorize": "yIje'"
@@ -298,7 +298,7 @@
"show_custom_menu_links": "menuDaq ret teqlu' yInej", "show_custom_menu_links": "menuDaq ret teqlu' yInej",
"show_large_home_carousel": "Show Large Home Carousel (beta)", "show_large_home_carousel": "Show Large Home Carousel (beta)",
"hide_libraries": "De'wI' bom yIQIj", "hide_libraries": "De'wI' bom yIQIj",
"select_liraries_you_want_to_hide": "De'wI' bom Danej QIj yIwIv.", "select_libraries_you_want_to_hide": "De'wI' bom Danej QIj yIwIv.",
"disable_haptic_feedback": "Qub quvHa' yIQIj", "disable_haptic_feedback": "Qub quvHa' yIQIj",
"default_quality": "wa' luj", "default_quality": "wa' luj",
"default_playback_speed": "Default Playback Speed", "default_playback_speed": "Default Playback Speed",
@@ -384,6 +384,8 @@
"device_usage": "naDev {{availableSpace}}%", "device_usage": "naDev {{availableSpace}}%",
"size_used": "{{used}} / {{total}} ram", "size_used": "{{used}} / {{total}} ram",
"delete_all_downloaded_files": "Hoch Qaw' Doch yIQaw'", "delete_all_downloaded_files": "Hoch Qaw' Doch yIQaw'",
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
"music_cache_title": "Music Cache", "music_cache_title": "Music Cache",
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support", "music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
"clear_music_cache": "Clear Music Cache", "clear_music_cache": "Clear Music Cache",
@@ -598,7 +600,7 @@
"mpv_player_title": "MPV Player", "mpv_player_title": "MPV Player",
"error": "ghIq", "error": "ghIq",
"failed_to_get_stream_url": "tlhol ret URL tu'laHbe'", "failed_to_get_stream_url": "tlhol ret URL tu'laHbe'",
"an_error_occured_while_playing_the_video": "mu'tlhegh tlholDI' ghIq. menDaq De' qon mej.", "an_error_occurred_while_playing_the_video": "mu'tlhegh tlholDI' ghIq. menDaq De' qon mej.",
"client_error": "lut 'el ghIq", "client_error": "lut 'el ghIq",
"could_not_create_stream_for_chromecast": "Chromecast tlhol ret qonlaHbe'", "could_not_create_stream_for_chromecast": "Chromecast tlhol ret qonlaHbe'",
"message_from_server": "Ho'Do' veS jach: {{message}}", "message_from_server": "Ho'Do' veS jach: {{message}}",

View File

@@ -16,14 +16,14 @@
"got_it": "Anlaşıldı", "got_it": "Anlaşıldı",
"connection_failed": "Bağlantı başarısız", "connection_failed": "Bağlantı başarısız",
"could_not_connect_to_server": "Sunucuya bağlanılamadı. Lütfen URL'yi ve ağ bağlantınızı kontrol edin.", "could_not_connect_to_server": "Sunucuya bağlanılamadı. Lütfen URL'yi ve ağ bağlantınızı kontrol edin.",
"an_unexpected_error_occured": "Beklenmedik bir hata oluştu", "an_unexpected_error_occurred": "Beklenmedik bir hata oluştu",
"change_server": "Sunucu değiştir", "change_server": "Sunucu değiştir",
"invalid_username_or_password": "Geçersiz kullanıcı adı veya şifre", "invalid_username_or_password": "Geçersiz kullanıcı adı veya şifre",
"user_does_not_have_permission_to_log_in": "Kullanıcının giriş yapma izni yok", "user_does_not_have_permission_to_log_in": "Kullanıcının giriş yapma izni yok",
"server_is_taking_too_long_to_respond_try_again_later": "Sunucunun yanıt vermesi çok uzun sürüyor, lütfen daha sonra tekrar deneyin", "server_is_taking_too_long_to_respond_try_again_later": "Sunucunun yanıt vermesi çok uzun sürüyor, lütfen daha sonra tekrar deneyin",
"server_received_too_many_requests_try_again_later": "Sunucu çok fazla istek aldı, lütfen daha sonra tekrar deneyin.", "server_received_too_many_requests_try_again_later": "Sunucu çok fazla istek aldı, lütfen daha sonra tekrar deneyin.",
"there_is_a_server_error": "Sunucu hatası var", "there_is_a_server_error": "Sunucu hatası var",
"an_unexpected_error_occured_did_you_enter_the_correct_url": "Beklenmedik bir hata oluştu. Sunucu URL'sini doğru girdiğinizden emin misiniz?", "an_unexpected_error_occurred_did_you_enter_the_correct_url": "Beklenmedik bir hata oluştu. Sunucu URL'sini doğru girdiğinizden emin misiniz?",
"too_old_server_text": "Desteklenmeyen Jellyfin Sunucu sürümü bulundu.", "too_old_server_text": "Desteklenmeyen Jellyfin Sunucu sürümü bulundu.",
"too_old_server_description": "Lütfen Jellyfin'i en son sürüme güncelleyin." "too_old_server_description": "Lütfen Jellyfin'i en son sürüme güncelleyin."
}, },
@@ -188,7 +188,7 @@
"authorize_button": "Hızlı Bağlantıyı Yetkilendir", "authorize_button": "Hızlı Bağlantıyı Yetkilendir",
"enter_the_quick_connect_code": "Hızlı bağlantı kodunu girin...", "enter_the_quick_connect_code": "Hızlı bağlantı kodunu girin...",
"success": "Başarılı", "success": "Başarılı",
"quick_connect_autorized": "Hızlı Bağlantı Yetkilendirildi", "quick_connect_authorized": "Hızlı Bağlantı Yetkilendirildi",
"error": "Hata", "error": "Hata",
"invalid_code": "Geçersiz kod", "invalid_code": "Geçersiz kod",
"authorize": "Yetkilendir" "authorize": "Yetkilendir"
@@ -298,7 +298,7 @@
"show_custom_menu_links": "Özel Menü Bağlantılarını Göster", "show_custom_menu_links": "Özel Menü Bağlantılarını Göster",
"show_large_home_carousel": "Show Large Home Carousel (beta)", "show_large_home_carousel": "Show Large Home Carousel (beta)",
"hide_libraries": "Kütüphaneleri Gizle", "hide_libraries": "Kütüphaneleri Gizle",
"select_liraries_you_want_to_hide": "Kütüphane sekmesinden ve ana sayfa bölümlerinden gizlemek istediğiniz kütüphaneleri seçin.", "select_libraries_you_want_to_hide": "Kütüphane sekmesinden ve ana sayfa bölümlerinden gizlemek istediğiniz kütüphaneleri seçin.",
"disable_haptic_feedback": "Dokunsal Geri Bildirimi Devre Dışı Bırak", "disable_haptic_feedback": "Dokunsal Geri Bildirimi Devre Dışı Bırak",
"default_quality": "Varsayılan kalite", "default_quality": "Varsayılan kalite",
"default_playback_speed": "Varsayılan Oynatma Hızı", "default_playback_speed": "Varsayılan Oynatma Hızı",
@@ -384,6 +384,8 @@
"device_usage": "Cihaz {{availableSpace}}%", "device_usage": "Cihaz {{availableSpace}}%",
"size_used": "{{used}} / {{total}} kullanıldı", "size_used": "{{used}} / {{total}} kullanıldı",
"delete_all_downloaded_files": "Tüm indirilen dosyaları sil", "delete_all_downloaded_files": "Tüm indirilen dosyaları sil",
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
"music_cache_title": "Müzik Ön Belleği", "music_cache_title": "Müzik Ön Belleği",
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support", "music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
"clear_music_cache": "Müzik Ön Belleğini Temizle", "clear_music_cache": "Müzik Ön Belleğini Temizle",
@@ -598,7 +600,7 @@
"mpv_player_title": "MPV Player", "mpv_player_title": "MPV Player",
"error": "Hata", "error": "Hata",
"failed_to_get_stream_url": "Yayın URL'si alınamadı", "failed_to_get_stream_url": "Yayın URL'si alınamadı",
"an_error_occured_while_playing_the_video": "Video oynatılırken bir hata oluştu. Ayarlardaki günlüklere bakın.", "an_error_occurred_while_playing_the_video": "Video oynatılırken bir hata oluştu. Ayarlardaki günlüklere bakın.",
"client_error": "İstemci hatası", "client_error": "İstemci hatası",
"could_not_create_stream_for_chromecast": "Chromecast için yayın oluşturulamadı", "could_not_create_stream_for_chromecast": "Chromecast için yayın oluşturulamadı",
"message_from_server": "Sunucudan mesaj: {{message}}", "message_from_server": "Sunucudan mesaj: {{message}}",

View File

@@ -16,14 +16,14 @@
"got_it": "Готово", "got_it": "Готово",
"connection_failed": "Помилка зʼєднання", "connection_failed": "Помилка зʼєднання",
"could_not_connect_to_server": "Неможливо підʼєднатися до серверу. Будь ласка перевірте URL і ваше зʼєднання з мережею", "could_not_connect_to_server": "Неможливо підʼєднатися до серверу. Будь ласка перевірте URL і ваше зʼєднання з мережею",
"an_unexpected_error_occured": "Сталася несподівана помилка", "an_unexpected_error_occurred": "Сталася несподівана помилка",
"change_server": "Змінити сервер", "change_server": "Змінити сервер",
"invalid_username_or_password": "Неправильні імʼя користувача або пароль", "invalid_username_or_password": "Неправильні імʼя користувача або пароль",
"user_does_not_have_permission_to_log_in": "Користувач не маю дозволу на вхід", "user_does_not_have_permission_to_log_in": "Користувач не маю дозволу на вхід",
"server_is_taking_too_long_to_respond_try_again_later": "Сервер відповідає занадто довго, будь-ласка спробуйте пізніше", "server_is_taking_too_long_to_respond_try_again_later": "Сервер відповідає занадто довго, будь-ласка спробуйте пізніше",
"server_received_too_many_requests_try_again_later": "Сервер отримав забагато запитів, будь ласка спробуйте пізніше.", "server_received_too_many_requests_try_again_later": "Сервер отримав забагато запитів, будь ласка спробуйте пізніше.",
"there_is_a_server_error": "Відбулася помилка на стороні сервера", "there_is_a_server_error": "Відбулася помилка на стороні сервера",
"an_unexpected_error_occured_did_you_enter_the_correct_url": "Відбулася несподівана помилка. Чи введений URL сервера правильний?", "an_unexpected_error_occurred_did_you_enter_the_correct_url": "Відбулася несподівана помилка. Чи введений URL сервера правильний?",
"too_old_server_text": "Unsupported Jellyfin Server Discovered", "too_old_server_text": "Unsupported Jellyfin Server Discovered",
"too_old_server_description": "Please update Jellyfin to the latest version" "too_old_server_description": "Please update Jellyfin to the latest version"
}, },
@@ -188,7 +188,7 @@
"authorize_button": "Авторизуйте Швидке Зʼєднання", "authorize_button": "Авторизуйте Швидке Зʼєднання",
"enter_the_quick_connect_code": "Введіть код для швидкого зʼєднання...", "enter_the_quick_connect_code": "Введіть код для швидкого зʼєднання...",
"success": "Успіх", "success": "Успіх",
"quick_connect_autorized": "Швидке Зʼєднання авторизовано", "quick_connect_authorized": "Швидке Зʼєднання авторизовано",
"error": "Помилка", "error": "Помилка",
"invalid_code": "Не правильний код", "invalid_code": "Не правильний код",
"authorize": "Авторизувати" "authorize": "Авторизувати"
@@ -298,7 +298,7 @@
"show_custom_menu_links": "Показати користувацькі посилання меню", "show_custom_menu_links": "Показати користувацькі посилання меню",
"show_large_home_carousel": "Show Large Home Carousel (beta)", "show_large_home_carousel": "Show Large Home Carousel (beta)",
"hide_libraries": "Сховати медіатеки", "hide_libraries": "Сховати медіатеки",
"select_liraries_you_want_to_hide": "Виберіть медіатеки, що бажаєте приховати з вкладки Медіатека і з секції на головній сторінці.", "select_libraries_you_want_to_hide": "Виберіть медіатеки, що бажаєте приховати з вкладки Медіатека і з секції на головній сторінці.",
"disable_haptic_feedback": "Вимкнути тактильний зворотний зв'язок", "disable_haptic_feedback": "Вимкнути тактильний зворотний зв'язок",
"default_quality": "Якість за замовченням", "default_quality": "Якість за замовченням",
"default_playback_speed": "Default Playback Speed", "default_playback_speed": "Default Playback Speed",
@@ -384,6 +384,8 @@
"device_usage": "Гаджет {{availableSpace}}%", "device_usage": "Гаджет {{availableSpace}}%",
"size_used": "{{used}} з {{total}} використано", "size_used": "{{used}} з {{total}} використано",
"delete_all_downloaded_files": "Видалити усі завантаженні файли", "delete_all_downloaded_files": "Видалити усі завантаженні файли",
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
"music_cache_title": "Music Cache", "music_cache_title": "Music Cache",
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support", "music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
"clear_music_cache": "Clear Music Cache", "clear_music_cache": "Clear Music Cache",
@@ -598,7 +600,7 @@
"mpv_player_title": "MPV Player", "mpv_player_title": "MPV Player",
"error": "Помилка", "error": "Помилка",
"failed_to_get_stream_url": "Не вдалося отримати URL-адресу потоку", "failed_to_get_stream_url": "Не вдалося отримати URL-адресу потоку",
"an_error_occured_while_playing_the_video": "Під час відтворення відео сталася помилка. Перевірте журнал в налаштуваннях.", "an_error_occurred_while_playing_the_video": "Під час відтворення відео сталася помилка. Перевірте журнал в налаштуваннях.",
"client_error": "Помилка клієнту", "client_error": "Помилка клієнту",
"could_not_create_stream_for_chromecast": "Не вдалося створити потік для Chromecast", "could_not_create_stream_for_chromecast": "Не вдалося створити потік для Chromecast",
"message_from_server": "Повідомлення від серверу: {{message}}", "message_from_server": "Повідомлення від серверу: {{message}}",

View File

@@ -16,14 +16,14 @@
"got_it": "Đã hiểu", "got_it": "Đã hiểu",
"connection_failed": "Kết nối thất bại", "connection_failed": "Kết nối thất bại",
"could_not_connect_to_server": "Không thể kết nối tới máy chủ. Vui lòng kiểm tra URL và kết nối mạng.", "could_not_connect_to_server": "Không thể kết nối tới máy chủ. Vui lòng kiểm tra URL và kết nối mạng.",
"an_unexpected_error_occured": "Đã xảy ra lỗi không mong muốn", "an_unexpected_error_occurred": "Đã xảy ra lỗi không mong muốn",
"change_server": "Đổi máy chủ", "change_server": "Đổi máy chủ",
"invalid_username_or_password": "Tên đăng nhập hoặc mật khẩu không đúng", "invalid_username_or_password": "Tên đăng nhập hoặc mật khẩu không đúng",
"user_does_not_have_permission_to_log_in": "Người dùng không có quyền đăng nhập", "user_does_not_have_permission_to_log_in": "Người dùng không có quyền đăng nhập",
"server_is_taking_too_long_to_respond_try_again_later": "Máy chủ phản hồi quá lâu, vui lòng thử lại sau", "server_is_taking_too_long_to_respond_try_again_later": "Máy chủ phản hồi quá lâu, vui lòng thử lại sau",
"server_received_too_many_requests_try_again_later": "Máy chủ nhận quá nhiều yêu cầu, vui lòng thử lại sau.", "server_received_too_many_requests_try_again_later": "Máy chủ nhận quá nhiều yêu cầu, vui lòng thử lại sau.",
"there_is_a_server_error": "Đã xảy ra lỗi ở máy chủ", "there_is_a_server_error": "Đã xảy ra lỗi ở máy chủ",
"an_unexpected_error_occured_did_you_enter_the_correct_url": "Đã xảy ra lỗi không mong muốn. Bạn có chắc đã nhập đúng URL máy chủ?", "an_unexpected_error_occurred_did_you_enter_the_correct_url": "Đã xảy ra lỗi không mong muốn. Bạn có chắc đã nhập đúng URL máy chủ?",
"too_old_server_text": "Unsupported Jellyfin Server Discovered", "too_old_server_text": "Unsupported Jellyfin Server Discovered",
"too_old_server_description": "Please update Jellyfin to the latest version" "too_old_server_description": "Please update Jellyfin to the latest version"
}, },
@@ -188,7 +188,7 @@
"authorize_button": "Cho phép Kết nối nhanh", "authorize_button": "Cho phép Kết nối nhanh",
"enter_the_quick_connect_code": "Nhập mã kết nối nhanh...", "enter_the_quick_connect_code": "Nhập mã kết nối nhanh...",
"success": "Thành công", "success": "Thành công",
"quick_connect_autorized": "Kết nối nhanh đã được cho phép", "quick_connect_authorized": "Kết nối nhanh đã được cho phép",
"error": "Lỗi", "error": "Lỗi",
"invalid_code": "Mã không hợp lệ", "invalid_code": "Mã không hợp lệ",
"authorize": "Cho phép" "authorize": "Cho phép"
@@ -298,7 +298,7 @@
"show_custom_menu_links": "Hiện liên kết tùy chỉnh", "show_custom_menu_links": "Hiện liên kết tùy chỉnh",
"show_large_home_carousel": "Show Large Home Carousel (beta)", "show_large_home_carousel": "Show Large Home Carousel (beta)",
"hide_libraries": "Ẩn thư viện", "hide_libraries": "Ẩn thư viện",
"select_liraries_you_want_to_hide": "Chọn các thư viện muốn ẩn khỏi mục Thư viện và Trang chủ.", "select_libraries_you_want_to_hide": "Chọn các thư viện muốn ẩn khỏi mục Thư viện và Trang chủ.",
"disable_haptic_feedback": "Tắt phản hồi rung", "disable_haptic_feedback": "Tắt phản hồi rung",
"default_quality": "Chất lượng mặc định", "default_quality": "Chất lượng mặc định",
"default_playback_speed": "Default Playback Speed", "default_playback_speed": "Default Playback Speed",
@@ -384,6 +384,8 @@
"device_usage": "Thiết bị sử dụng {{availableSpace}}%", "device_usage": "Thiết bị sử dụng {{availableSpace}}%",
"size_used": "{{used}} / {{total}} đã dùng", "size_used": "{{used}} / {{total}} đã dùng",
"delete_all_downloaded_files": "Xóa toàn bộ tập tin đã tải", "delete_all_downloaded_files": "Xóa toàn bộ tập tin đã tải",
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
"music_cache_title": "Music Cache", "music_cache_title": "Music Cache",
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support", "music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
"clear_music_cache": "Clear Music Cache", "clear_music_cache": "Clear Music Cache",
@@ -598,7 +600,7 @@
"mpv_player_title": "MPV Player", "mpv_player_title": "MPV Player",
"error": "Lỗi", "error": "Lỗi",
"failed_to_get_stream_url": "Không thể lấy URL phát trực tiếp", "failed_to_get_stream_url": "Không thể lấy URL phát trực tiếp",
"an_error_occured_while_playing_the_video": "Có lỗi khi phát video. Xem nhật ký trong cài đặt.", "an_error_occurred_while_playing_the_video": "Có lỗi khi phát video. Xem nhật ký trong cài đặt.",
"client_error": "Lỗi phía máy khách", "client_error": "Lỗi phía máy khách",
"could_not_create_stream_for_chromecast": "Không thể tạo luồng cho Chromecast", "could_not_create_stream_for_chromecast": "Không thể tạo luồng cho Chromecast",
"message_from_server": "Thông báo từ máy chủ: {{message}}", "message_from_server": "Thông báo từ máy chủ: {{message}}",

View File

@@ -16,14 +16,14 @@
"got_it": "Got It", "got_it": "Got It",
"connection_failed": "Connection Failed", "connection_failed": "Connection Failed",
"could_not_connect_to_server": "Could not connect to the server. Please check the URL and your network connection.", "could_not_connect_to_server": "Could not connect to the server. Please check the URL and your network connection.",
"an_unexpected_error_occured": "An Unexpected Error Occurred", "an_unexpected_error_occurred": "An unexpected error occurred",
"change_server": "Change Server", "change_server": "Change Server",
"invalid_username_or_password": "Invalid Username or Password", "invalid_username_or_password": "Invalid Username or Password",
"user_does_not_have_permission_to_log_in": "User does not have permission to log in", "user_does_not_have_permission_to_log_in": "User does not have permission to log in",
"server_is_taking_too_long_to_respond_try_again_later": "Server is taking too long to respond, try again later", "server_is_taking_too_long_to_respond_try_again_later": "Server is taking too long to respond, try again later",
"server_received_too_many_requests_try_again_later": "Server received too many requests, try again later.", "server_received_too_many_requests_try_again_later": "Server received too many requests, try again later.",
"there_is_a_server_error": "There is a server error", "there_is_a_server_error": "There is a server error",
"an_unexpected_error_occured_did_you_enter_the_correct_url": "An unexpected error occurred. Did you enter the server URL correctly?", "an_unexpected_error_occurred_did_you_enter_the_correct_url": "An unexpected error occurred. Did you enter the server URL correctly?",
"too_old_server_text": "Unsupported Jellyfin Server Discovered", "too_old_server_text": "Unsupported Jellyfin Server Discovered",
"too_old_server_description": "Please update Jellyfin to the latest version" "too_old_server_description": "Please update Jellyfin to the latest version"
}, },
@@ -188,7 +188,7 @@
"authorize_button": "Authorize Quick Connect", "authorize_button": "Authorize Quick Connect",
"enter_the_quick_connect_code": "Enter the quick connect code...", "enter_the_quick_connect_code": "Enter the quick connect code...",
"success": "Success", "success": "Success",
"quick_connect_autorized": "Quick Connect Authorized", "quick_connect_authorized": "Quick Connect authorized",
"error": "Error", "error": "Error",
"invalid_code": "Invalid Code", "invalid_code": "Invalid Code",
"authorize": "Authorize" "authorize": "Authorize"
@@ -298,7 +298,7 @@
"show_custom_menu_links": "Show Custom Menu Links", "show_custom_menu_links": "Show Custom Menu Links",
"show_large_home_carousel": "Show Large Home Carousel (beta)", "show_large_home_carousel": "Show Large Home Carousel (beta)",
"hide_libraries": "Hide Libraries", "hide_libraries": "Hide Libraries",
"select_liraries_you_want_to_hide": "Select the libraries you want to hide from the Library tab and home page sections.", "select_libraries_you_want_to_hide": "Select the libraries you want to hide from the Library tab and home page sections.",
"disable_haptic_feedback": "Disable Haptic Feedback", "disable_haptic_feedback": "Disable Haptic Feedback",
"default_quality": "Default Quality", "default_quality": "Default Quality",
"default_playback_speed": "Default Playback Speed", "default_playback_speed": "Default Playback Speed",
@@ -384,6 +384,8 @@
"device_usage": "Device {{availableSpace}}%", "device_usage": "Device {{availableSpace}}%",
"size_used": "{{used}} of {{total}} Used", "size_used": "{{used}} of {{total}} Used",
"delete_all_downloaded_files": "Delete All Downloaded Files", "delete_all_downloaded_files": "Delete All Downloaded Files",
"delete_all_downloaded_files_confirm": "Delete All Downloaded Files?",
"delete_all_downloaded_files_confirm_desc": "Are you sure you want to delete all downloaded files? This action cannot be undone.",
"music_cache_title": "Music Cache", "music_cache_title": "Music Cache",
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support", "music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
"clear_music_cache": "Clear Music Cache", "clear_music_cache": "Clear Music Cache",
@@ -598,7 +600,7 @@
"mpv_player_title": "MPV Player", "mpv_player_title": "MPV Player",
"error": "Error", "error": "Error",
"failed_to_get_stream_url": "Failed to get the stream URL", "failed_to_get_stream_url": "Failed to get the stream URL",
"an_error_occured_while_playing_the_video": "An error occurred while playing the video. Check logs in settings.", "an_error_occurred_while_playing_the_video": "An error occurred while playing the video. Check logs in settings.",
"client_error": "Client Error", "client_error": "Client Error",
"could_not_create_stream_for_chromecast": "Could not create a stream for Chromecast", "could_not_create_stream_for_chromecast": "Could not create a stream for Chromecast",
"message_from_server": "Message from Server: {{message}}", "message_from_server": "Message from Server: {{message}}",