From e3f95f75e539b19029a64bfcbea8eb0b81cffc90 Mon Sep 17 00:00:00 2001 From: cyberalby2 Date: Fri, 6 Mar 2026 19:36:49 +0100 Subject: [PATCH] TMDB Settings Localization Patch --- android/app/src/main/AndroidManifest.xml | 6 +- android/sentry.properties | 4 +- .../exoplayer/ReactExoplayerView.java | 43 +- src/i18n/locales/en.json | 2986 ++++++------ src/i18n/locales/it.json | 2984 ++++++------ src/screens/SettingsScreen.tsx | 2336 +++++----- src/screens/TMDBSettingsScreen.tsx | 4089 ++++++++++------- src/screens/TraktSettingsScreen.tsx | 2 +- 8 files changed, 6583 insertions(+), 5867 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index cde7c0b0..aab98261 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -2,12 +2,12 @@ + - @@ -21,7 +21,7 @@ - + @@ -37,4 +37,4 @@ - + \ No newline at end of file diff --git a/android/sentry.properties b/android/sentry.properties index ae003a4b..74f8fa34 100644 --- a/android/sentry.properties +++ b/android/sentry.properties @@ -1,4 +1,4 @@ defaults.url=https://sentry.io/ -defaults.org=tapframe +defaults.org=fatticazzituoi defaults.project=react-native -auth.token=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 +# Using SENTRY_AUTH_TOKEN environment variable \ No newline at end of file diff --git a/node_modules/react-native-video/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/node_modules/react-native-video/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 7db96bd3..72007063 100644 --- a/node_modules/react-native-video/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/node_modules/react-native-video/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -1599,29 +1599,6 @@ public class ReactExoplayerView extends FrameLayout implements Track audioTrack = exoplayerTrackToGenericTrack(format, groupIndex, selection, group); audioTrack.setBitrate(format.bitrate == Format.NO_VALUE ? 0 : format.bitrate); audioTrack.setSelected(isSelected); - // Encode channel count, bitrate and mimeType into title so JS can read them reliably - // e.g. "English|ch:6|br:640000|mt:audio/ac3" - String existing = audioTrack.getTitle() != null ? audioTrack.getTitle() : ""; - if (format.channelCount != Format.NO_VALUE && format.channelCount > 0) { - existing = existing + "|ch:" + format.channelCount; - } - // Use bitrate, fall back to averageBitrate then peakBitrate - int effectiveBitrate = format.bitrate; - if (effectiveBitrate == Format.NO_VALUE || effectiveBitrate <= 0) { - effectiveBitrate = format.averageBitrate; - } - if (effectiveBitrate == Format.NO_VALUE || effectiveBitrate <= 0) { - effectiveBitrate = format.peakBitrate; - } - if (effectiveBitrate != Format.NO_VALUE && effectiveBitrate > 0) { - existing = existing + "|br:" + effectiveBitrate; - } - if (format.sampleMimeType != null && !format.sampleMimeType.isEmpty()) { - existing = existing + "|mt:" + format.sampleMimeType; - } - if (!existing.isEmpty()) { - audioTrack.setTitle(existing); - } audioTracks.add(audioTrack); } @@ -1817,25 +1794,7 @@ public class ReactExoplayerView extends FrameLayout implements Track track = new Track(); track.setIndex(groupIndex); track.setLanguage(format.language != null ? format.language : "unknown"); - String baseTitle = format.label != null ? format.label : ""; - if (format.channelCount != Format.NO_VALUE && format.channelCount > 0) { - baseTitle = baseTitle + "|ch:" + format.channelCount; - } - // Use bitrate, fall back to averageBitrate then peakBitrate - int effectiveBitrate = format.bitrate; - if (effectiveBitrate == Format.NO_VALUE || effectiveBitrate <= 0) { - effectiveBitrate = format.averageBitrate; - } - if (effectiveBitrate == Format.NO_VALUE || effectiveBitrate <= 0) { - effectiveBitrate = format.peakBitrate; - } - if (effectiveBitrate != Format.NO_VALUE && effectiveBitrate > 0) { - baseTitle = baseTitle + "|br:" + effectiveBitrate; - } - if (format.sampleMimeType != null && !format.sampleMimeType.isEmpty()) { - baseTitle = baseTitle + "|mt:" + format.sampleMimeType; - } - track.setTitle(baseTitle); + track.setTitle(format.label != null ? format.label : "Track " + (groupIndex + 1)); track.setSelected(false); // Don't report selection status - let PlayerView handle it if (format.sampleMimeType != null) track.setMimeType(format.sampleMimeType); diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index c696c261..6379b240 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -1,1492 +1,1498 @@ { - "common": { - "loading": "Loading...", - "cancel": "Cancel", - "save": "Save", - "delete": "Delete", - "edit": "Edit", - "search": "Search", - "error": "Error", - "success": "Success", - "ok": "OK", - "unknown": "Unknown", - "retry": "Retry", - "try_again": "Try Again", - "go_back": "Go Back", - "settings": "Settings", - "close": "Close", - "enable": "Enable", - "disable": "Disable", - "show_more": "Show More", - "show_less": "Show Less", - "load_more": "Load More", - "unknown_date": "Unknown date", - "anonymous_user": "Anonymous User", - "time": { - "now": "Just now", - "minutes_ago": "{{count}}m ago", - "hours_ago": "{{count}}h ago", - "days_ago": "{{count}}d ago" - }, - "days_short": { - "sun": "Sun", - "mon": "Mon", - "tue": "Tue", - "wed": "Wed", - "thu": "Thu", - "fri": "Fri", - "sat": "Sat" - }, - "email": "Email", - "status": "Status" - }, - "home": { - "categories": { - "movies": "Movies", - "series": "Series", - "channels": "Channels" - }, - "movies": "Movies", - "tv_shows": "TV Shows", - "load_more_catalogs": "Load More Catalogs", - "no_content": "No content available", - "add_catalogs": "Add Catalogs", - "sign_in_available": "Sign In Available", - "sign_in_desc": "You can sign in anytime from Settings → Account", - "view_all": "View All", - "this_week": "This Week", - "upcoming": "Upcoming", - "recently_released": "Recently Released", - "no_scheduled_episodes": "Series with No Scheduled Episodes", - "check_back_later": "Check back later", - "continue_watching": "Continue Watching", - "up_next": "Up Next", - "up_next_caps": "UP NEXT", - "released": "Released", - "new": "New", - "tba": "TBA", - "new_episodes": "{{count}} New Episodes", - "season_short": "S{{season}}", - "episode_short": "E{{episode}}", - "season": "Season {{season}}", - "episode": "Episode {{episode}}", - "movie": "Movie", - "series": "Series", - "tv_show": "TV Show", - "percent_watched": "{{percent}}% watched", - "view_details": "View Details", - "remove": "Remove", - "play": "Play", - "play_now": "Play Now", - "resume": "Resume", - "info": "Info", - "more_info": "More Info", - "my_list": "My List", - "save": "Save", - "saved": "Saved", - "retry": "Retry", - "install_addons": "Install Addons", - "settings": "Settings", - "no_featured_content": "No Featured Content", - "couldnt_load_featured": "Couldn't load featured content", - "no_featured_desc": "Install addons with catalogs or change the content source in your settings.", - "load_error_desc": "There was a problem fetching featured content. Please check your connection and try again.", - "no_featured_available": "No featured content available", - "no_description": "No description available" - }, - "navigation": { - "home": "Home", - "library": "Library", - "search": "Search", - "downloads": "Downloads", - "settings": "Settings" - }, - "search": { - "title": "Search", - "recent_searches": "Recent Searches", - "discover": "Discover", - "movies": "Movies", - "tv_shows": "TV Shows", - "select_catalog": "Select Catalog", - "all_genres": "All Genres", - "discovering": "Discovering content...", - "show_more": "Show More ({{count}})", - "no_content_found": "No content found", - "try_different": "Try a different genre or catalog", - "select_catalog_desc": "Select a catalog to discover", - "tap_catalog_desc": "Tap the catalog chip above to get started", - "placeholder": "Search movies, shows...", - "keep_typing": "Keep typing...", - "type_characters": "Type at least 2 characters to search", - "no_results": "No results found", - "try_keywords": "Try different keywords or check your spelling", - "select_type": "Select Type", - "browse_movies": "Browse movie catalogs", - "browse_tv": "Browse TV series catalogs", - "select_genre": "Select Genre", - "show_all_content": "Show all content", - "genres_count": "{{count}} genres" - }, - "library": { - "title": "Library", - "watched": "Watched", - "continue": "Continue", - "watchlist": "Watchlist", - "collection": "Collection", - "rated": "Rated", - "items": "items", - "trakt_collections": "Trakt collections", - "trakt_collection": "Trakt Collection", - "no_trakt": "No Trakt collections", - "no_trakt_desc": "Your Trakt collections will appear here once you start using Trakt", - "load_collections": "Load Collections", - "empty_folder": "No content in {{folder}}", - "empty_folder_desc": "This collection is empty", - "refresh": "Refresh", - "no_movies": "No movies yet", - "no_series": "No TV shows yet", - "no_content": "No content yet", - "add_content_desc": "Add some content to your library to see it here", - "find_something": "Find something to watch", - "removed_from_library": "Removed from Library", - "item_removed": "Item removed from your library", - "failed_update_library": "Failed to update Library", - "unable_remove": "Unable to remove item from library", - "marked_watched": "Marked as Watched", - "marked_unwatched": "Marked as Unwatched", - "item_marked_watched": "Item marked as watched", - "item_marked_unwatched": "Item marked as unwatched", - "failed_update_watched": "Failed to update watched status", - "unable_update_watched": "Unable to update watched status", - "added_to_library": "Added to Library", - "item_added": "Added to your local library", - "add_to_library": "Add to Library", - "remove_from_library": "Remove from Library", - "mark_watched": "Mark as Watched", - "mark_unwatched": "Mark as Unwatched", - "share": "Share", - "add_to_watchlist": "Add to Trakt Watchlist", - "remove_from_watchlist": "Remove from Trakt Watchlist", - "added_to_watchlist": "Added to Watchlist", - "added_to_watchlist_desc": "Added to your Trakt watchlist", - "removed_from_watchlist": "Removed from Watchlist", - "removed_from_watchlist_desc": "Removed from your Trakt watchlist", - "add_to_collection": "Add to Trakt Collection", - "remove_from_collection": "Remove from Trakt Collection", - "added_to_collection": "Added to Collection", - "added_to_collection_desc": "Added to your Trakt collection", - "removed_from_collection": "Removed from Collection", - "removed_from_collection_desc": "Removed from your Trakt collection" - }, - "metadata": { - "unable_to_load": "Unable to Load Content", - "error_code": "Error Code: {{code}}", - "content_not_found": "Content not found", - "content_not_found_desc": "This content doesn't exist or may have been removed.", - "server_error": "Server error", - "server_error_desc": "The server is temporarily unavailable. Please try again later.", - "bad_gateway": "Bad gateway", - "bad_gateway_desc": "The server is experiencing issues. Please try again later.", - "service_unavailable": "Service unavailable", - "service_unavailable_desc": "The service is currently down for maintenance. Please try again later.", - "too_many_requests": "Too many requests", - "too_many_requests_desc": "You're making too many requests. Please wait a moment and try again.", - "request_timeout": "Request timeout", - "request_timeout_desc": "The request took too long. Please try again.", - "network_error": "Network error", - "network_error_desc": "Please check your internet connection and try again.", - "auth_error": "Authentication error", - "auth_error_desc": "Please check your account settings and try again.", - "access_denied": "Access denied", - "access_denied_desc": "You don't have permission to access this content.", - "connection_error": "Connection error", - "streams_unavailable": "Streams unavailable", - "streams_unavailable_desc": "Streaming sources are currently unavailable. Please try again later.", - "unknown_error": "Unknown error", - "something_went_wrong": "Something went wrong. Please try again.", - "cast": "Cast", - "more_like_this": "More Like This", - "collection": "Collection", - "episodes": "Episodes", - "seasons": "Seasons", - "posters": "Posters", - "banners": "Banners", - "specials": "Specials", - "season_number": "Season {{number}}", - "episode_count": "{{count}} Episode", - "episode_count_plural": "{{count}} Episodes", - "no_episodes": "No episodes available", - "no_episodes_for_season": "No episodes available for Season {{season}}", - "episodes_not_released": "Episodes may not be released yet", - "no_description": "No description available", - "episode_label": "EPISODE {{number}}", - "watch_again": "Watch Again", - "completed": "Completed", - "play_episode": "Play S{{season}}E{{episode}}", - "play": "Play", - "watched": "Watched", - "watched_on_trakt": "Watched on Trakt", - "synced_with_trakt": "Synced with Trakt", - "saved": "Saved", - "director": "Director", - "directors": "Directors", - "creator": "Creator", - "creators": "Creators", - "production": "Production", - "network": "Network", - "mark_watched": "Mark as Watched", - "mark_unwatched": "Mark as Unwatched", - "marking": "Marking...", - "removing": "Removing...", - "unmark_season": "Unmark Season {{season}}", - "mark_season": "Mark Season {{season}}", - "resume": "Resume", - "spoiler_warning": "Spoiler Warning", - "spoiler_warning_desc": "This comment contains spoilers. Are you sure you want to reveal it?", - "cancel": "Cancel", - "reveal_spoilers": "Reveal Spoilers", - "movie_details": "Movie Details", - "show_details": "Show Details", - "tagline": "Tagline", - "status": "Status", - "release_date": "Release Date", - "runtime": "Runtime", - "budget": "Budget", - "revenue": "Revenue", - "origin_country": "Origin Country", - "original_language": "Original Language", - "first_air_date": "First Air Date", - "last_air_date": "Last Air Date", - "total_episodes": "Total Episodes", - "episode_runtime": "Episode Runtime", - "created_by": "Created By", - "backdrop_gallery": "Backdrop Gallery", - "loading_episodes": "Loading episodes...", - "no_episodes_available": "No episodes available", - "play_next": "Play S{{season}}E{{episode}}", - "play_next_episode": "Play Next Episode", - "save": "Save", - "percent_watched": "{{percent}}% watched", - "percent_watched_trakt": "{{percent}}% watched ({{traktPercent}}% on Trakt)", - "synced_with_trakt_progress": "Synced with Trakt", - "using_trakt_progress": "Using Trakt progress", - "added_to_collection_hero": "Added to Collection", - "added_to_collection_desc_hero": "Added to your Trakt collection", - "removed_from_collection_hero": "Removed from Collection", - "removed_from_collection_desc_hero": "Removed from your Trakt collection", - "mark_as_watched": "Mark as Watched", - "mark_as_unwatched": "Mark as Unwatched" - }, - "cast": { - "biography": "Biography", - "known_for": "Known For", - "personal_info": "Personal Info", - "born_in": "Born in {{place}}", - "filmography": "Filmography", - "also_known_as": "Also Known As", - "no_info_available": "No additional information available", - "as_character": "as {{character}}", - "loading_details": "Loading details...", - "years_old": "{{age}} years old", - "view_filmography": "View Filmography", - "filter": "Filter", - "sort_by": "Sort By", - "sort_popular": "Popular", - "sort_latest": "Latest", - "sort_upcoming": "Upcoming", - "upcoming_badge": "UPCOMING", - "coming_soon": "Coming Soon", - "filmography_count": "Filmography • {{count}} titles", - "loading_filmography": "Loading filmography...", - "load_more_remaining": "Load More ({{count}} remaining)", - "alert_error_title": "Error", - "alert_error_message": "Unable to load \"{{title}}\". Please try again later.", - "alert_ok": "OK", - "no_upcoming": "No upcoming releases available for this actor", - "no_content": "No content available for this actor", - "no_movies": "No movies available for this actor", - "no_tv": "No TV shows available for this actor" - }, - "comments": { - "title": "Trakt Comments", - "spoiler_warning": "⚠️ This comment contains spoilers. Tap to reveal.", - "spoiler": "Spoiler", - "contains_spoilers": "Contains spoilers", - "reveal": "Reveal", - "vip": "VIP", - "unavailable": "Comments unavailable", - "no_comments": "No comments on Trakt yet", - "not_in_database": "This content may not be in Trakt's database yet", - "check_trakt": "Check Trakt" - }, - "trailers": { - "title": "Trailers", - "official_trailers": "Official Trailers", - "official_trailer": "Official Trailer", - "teasers": "Teasers", - "teaser": "Teaser", - "clips_scenes": "Clips & Scenes", - "clip": "Clip", - "featurettes": "Featurettes", - "featurette": "Featurette", - "behind_the_scenes": "Behind the Scenes", - "no_trailers": "No trailers available", - "unavailable": "Trailer Unavailable", - "unavailable_desc": "This trailer could not be loaded at this time. Please try again later.", - "unable_to_play": "Unable to play trailer. Please try again.", - "watch_on_youtube": "Watch on YouTube" - }, - "catalog": { - "no_content_found": "No content found", - "no_content_filters": "No content found for the selected filters", - "loading_content": "Loading content...", - "back": "Back", - "in_theaters": "In Theaters", - "all": "All", - "failed_tmdb": "Failed to load content from TMDB", - "movies": "Movies", - "tv_shows": "TV Shows", - "channels": "Channels" - }, - "streams": { - "back_to_episodes": "Back to Episodes", - "back_to_info": "Back to Info", - "fetching_from": "Fetching from:", - "no_sources_available": "No streaming sources available", - "add_sources_desc": "Please add streaming sources in settings", - "add_sources": "Add Sources", - "finding_streams": "Finding available streams...", - "finding_best_stream": "Finding best stream for autoplay...", - "still_fetching": "Still fetching streams…", - "no_streams_available": "No streams available", - "starting_best_stream": "Starting best stream...", - "loading_more_sources": "Loading more sources..." - }, - "player_ui": { - "via": "via {{name}}", - "audio_tracks": "Audio Tracks", - "no_audio_tracks": "No audio tracks available", - "playback_speed": "Playback Speed", - "on_hold": "On Hold", - "playback_error": "Playback Error", - "unknown_error": "An unknown error occurred during playback.", - "copy_error": "Copy error details", - "copied_to_clipboard": "Copied to clipboard", - "dismiss": "Dismiss", - "continue_watching": "Continue Watching", - "start_over": "Start Over", - "resume": "Resume", - "change_source": "Change Source", - "switching_source": "Switching source...", - "no_sources_found": "No sources found", - "sources": "Sources", - "finding_sources": "Finding sources...", - "unknown_source": "Unknown Source", - "sources_limited": "Sources might be limited due to provider errors.", - "episodes": "Episodes", - "specials": "Specials", - "season": "Season {{season}}", - "stream": "Stream {{number}}", - "subtitles": "Subtitles", - "built_in": "Built-in", - "addons": "Addons", - "style": "Style", - "none": "None", - "search_online_subtitles": "Search Online Subtitles", - "preview": "Preview", - "quick_presets": "Quick Presets", - "default": "Default", - "yellow": "Yellow", - "high_contrast": "High Contrast", - "large": "Large", - "core": "Core", - "font_size": "Font Size", - "show_background": "Show Background", - "advanced": "Advanced", - "position": "Position", - "text_color": "Text Color", - "align": "Align", - "bottom_offset": "Bottom Offset", - "background_opacity": "Background Opacity", - "text_shadow": "Text Shadow", - "on": "On", - "off": "Off", - "outline_color": "Outline Color", - "outline": "Outline", - "outline_width": "Outline Width", - "letter_spacing": "Letter Spacing", - "line_height": "Line Height", - "timing_offset": "Timing Offset (s)", - "visual_sync": "Visual Sync", - "timing_hint": "Nudge subtitles earlier (-) or later (+) to sync if needed.", - "reset_defaults": "Reset to defaults", - "mark_intro_start": "Mark Intro Start", - "mark_intro_end": "Mark Intro End", - "intro_start_marked": "Intro start marked", - "intro_submitted": "Intro submitted successfully", - "intro_submit_failed": "Failed to submit intro" - }, - "downloads": { - "title": "Downloads", - "no_downloads": "No Downloads Yet", - "no_downloads_desc": "Downloaded content will appear here for offline viewing", - "explore": "Explore Content", - "path_copied": "Path Copied", - "path_copied_desc": "Local file path copied to clipboard", - "copied": "Copied", - "incomplete": "Download Incomplete", - "incomplete_desc": "Download is not complete yet", - "not_available": "Not Available", - "not_available_desc": "The local file path is available only after the download is complete.", - "status_downloading": "Downloading", - "status_completed": "Completed", - "status_paused": "Paused", - "status_error": "Error", - "status_queued": "Queued", - "status_unknown": "Unknown", - "provider": "Provider", - "streaming_playlist_warning": "May not play - streaming playlist", - "remaining": "remaining", - "not_ready": "Download not ready", - "not_ready_desc": "Please wait until the download completes.", - "filter_all": "All", - "filter_active": "Active", - "filter_done": "Done", - "filter_paused": "Paused", - "no_filter_results": "No {{filter}} downloads", - "try_different_filter": "Try selecting a different filter", - "limitations_title": "Download Limitations", - "limitations_msg": "• Files smaller than 1MB are typically M3U8 streaming playlists and cannot be downloaded for offline viewing. These only work with online streaming and contain links to video segments, not the actual video content.", - "remove_title": "Remove Download", - "remove_confirm": "Remove \"{{title}}\"{{season_episode}}?", - "cancel": "Cancel", - "remove": "Remove" - }, - "addons": { - "title": "Addons", - "reorder_mode": "Reorder Mode", - "reorder_info": "Addons at the top have higher priority when loading content", - "add_addon_placeholder": "Addon URL", - "add_button": "Add Addon", - "my_addons": "My Addons", - "community_addons": "Community Addons", - "no_addons": "No addons installed", - "uninstall_title": "Uninstall Addon", - "uninstall_message": "Are you sure you want to uninstall {{name}}?", - "uninstall_button": "Uninstall", - "install_success": "Addon installed successfully", - "install_error": "Failed to install addon", - "load_error": "Failed to load addons", - "fetch_error": "Failed to fetch addon details", - "invalid_url": "Please enter an addon URL", - "configure": "Configure", - "version": "Version: {{version}}", - "installed_addons": "INSTALLED ADDONS", - "reorder_drag_title": "DRAG ADDONS TO REORDER", - "install": "Install", - "config_unavailable_title": "Configuration Unavailable", - "config_unavailable_msg": "Could not determine configuration URL for this addon.", - "cannot_open_config_title": "Cannot Open Configuration", - "cannot_open_config_msg": "The configuration URL ({{url}}) cannot be opened. The addon may not have a configuration page.", - "description": "Description", - "supported_types": "Supported Types", - "catalogs": "Catalogs", - "no_description": "No description available", - "overview": "OVERVIEW", - "no_categories": "No categories", - "pre_installed": "PRE-INSTALLED" - }, - "trakt": { - "title": "Trakt Settings", - "settings_title": "Trakt Settings", - "connect_title": "Connect with Trakt", - "connect_desc": "Sync your watch history, watchlist, and collection with Trakt.tv", - "sign_in": "Sign In with Trakt", - "sign_out": "Sign Out", - "sign_out_confirm": "Are you sure you want to sign out of your Trakt account?", - "joined": "Joined {{date}}", - "sync_settings_title": "Sync Settings", - "sync_info": "When connected to Trakt, full history is synced directly from the API and is not written to local storage. Your Continue Watching list reflects your global Trakt progress.", - "auto_sync_label": "Auto-sync playback progress", - "auto_sync_desc": "Automatically sync watch progress to Trakt", - "import_history_label": "Import watched history", - "import_history_desc": "Use \"Sync Now\" to import your watch history and progress from Trakt", - "sync_now_button": "Sync Now", - "display_settings_title": "Display Settings", - "show_comments_label": "Show Trakt Comments", - "show_comments_desc": "Display Trakt comments in metadata screens when available", - "maintenance_title": "Under Maintenance", - "maintenance_unavailable": "Trakt Unavailable", - "maintenance_desc": "The Trakt integration is temporarily paused for maintenance. All syncing and authentication is disabled until maintenance is complete.", - "maintenance_button": "Service Under Maintenance", - "auth_success_title": "Successfully Connected", - "auth_success_msg": "Your Trakt account has been connected successfully.", - "auth_error_title": "Authentication Error", - "auth_error_msg": "Failed to complete authentication with Trakt.", - "auth_error_generic": "An error occurred during authentication.", - "sign_out_error": "Failed to sign out of Trakt.", - "sync_complete_title": "Sync Complete", - "sync_success_msg": "Successfully synced your watch progress with Trakt.", - "sync_error_msg": "Sync failed. Please try again." - }, - "simkl": { - "title": "Simkl Settings", - "settings_title": "Simkl Settings", - "connect_title": "Connect with Simkl", - "connect_desc": "Sync your watch history and track what you're watching", - "sign_in": "Sign In with Simkl", - "sign_out": "Disconnect", - "sign_out_confirm": "Are you sure you want to disconnect from Simkl?", - "syncing_desc": "Your watched items are syncing with Simkl.", - "auth_success_title": "Successfully Connected", - "auth_success_msg": "Your Simkl account has been connected successfully.", - "auth_error_title": "Authentication Error", - "auth_error_msg": "Failed to complete authentication with Simkl.", - "auth_error_generic": "An error occurred during authentication.", - "sign_out_error": "Failed to disconnect from Simkl.", - "config_error_title": "Configuration Error", - "config_error_msg": "Simkl Client ID is missing in environment variables.", - "conflict_title": "Conflict", - "conflict_msg": "You cannot connect to Simkl while Trakt is connected. Please disconnect Trakt first.", - "disclaimer": "Nuvio is not affiliated with Simkl." - }, - "tmdb_settings": { - "title": "TMDb Settings", - "metadata_enrichment": "Metadata Enrichment", - "metadata_enrichment_desc": "Enhance your content metadata with TMDb data for better details and information.", - "enable_enrichment": "Enable Enrichment", - "enable_enrichment_desc": "Augments addon metadata with TMDb for cast, certification, logos/posters, and production info.", - "localized_text": "Localized Text", - "localized_text_desc": "Fetch titles and descriptions in your preferred language from TMDb.", - "language": "Language", - "change": "Change", - "logo_preview": "Logo Preview", - "logo_preview_desc": "Preview shows how localized logos will appear in the selected language.", - "example": "Example:", - "no_logo": "No logo available", - "enrichment_options": "Enrichment Options", - "enrichment_options_desc": "Control which data is fetched from TMDb. Disabled options will use addon data if available.", - "cast_crew": "Cast & Crew", - "cast_crew_desc": "Actors, directors, writers with profile photos", - "title_description": "Title & Description", - "title_description_desc": "Use TMDb localized title and overview text", - "title_logos": "Title Logos", - "title_logos_desc": "High-quality title treatment images", - "banners_backdrops": "Banners & Backdrops", - "banners_backdrops_desc": "High-resolution backdrop images", - "certification": "Content Certification", - "certification_desc": "Age ratings (PG-13, R, TV-MA, etc.)", - "recommendations": "Recommendations", - "recommendations_desc": "Similar content suggestions", - "episode_data": "Episode Data", - "episode_data_desc": "Episode thumbnails, info & fallbacks for TV shows", - "season_posters": "Season Posters", - "season_posters_desc": "Season-specific poster images", - "production_info": "Production Info", - "production_info_desc": "Networks & production companies with logos", - "movie_details": "Movie Details", - "movie_details_desc": "Budget, revenue, runtime, tagline", - "tv_details": "TV Show Details", - "tv_details_desc": "Status, seasons count, networks, creators", - "movie_collections": "Movie Collections", - "movie_collections_desc": "Franchise movies (Marvel, Star Wars, etc.)", - "api_configuration": "API Configuration", - "api_configuration_desc": "Configure your TMDb API access for enhanced functionality.", - "custom_api_key": "Custom API Key", - "custom_api_key_desc": "Use your own TMDb API key for better performance and dedicated rate limits.", - "custom_key_active": "Custom API key active", - "api_key_required": "API key required", - "api_key_placeholder": "Paste your TMDb API key (v3)", - "how_to_get_key": "How to get a TMDb API key?", - "built_in_key_msg": "Currently using built-in API key. Consider using your own key for better performance.", - "cache_size": "Cache Size", - "clear_cache": "Clear Cache", - "cache_days": "TMDB responses are cached for 7 days to improve performance", - "choose_language": "Choose Language", - "choose_language_desc": "Select your preferred language for TMDb content", - "popular": "Popular", - "all_languages": "All Languages", - "search_results": "Search Results", - "no_languages_found": "No languages found for \"{{query}}\"", - "clear_search": "Clear Search", - "clear_cache_title": "Clear TMDB Cache", - "clear_cache_msg": "This will clear all cached TMDB data ({{size}}). This may temporarily slow down loading until cache rebuilds.", - "clear_cache_success": "TMDB cache cleared successfully.", - "clear_cache_error": "Failed to clear cache.", - "clear_api_key_title": "Clear API Key", - "clear_api_key_msg": "Are you sure you want to remove your custom API key and revert to the default?", - "clear_api_key_success": "API key cleared successfully", - "clear_api_key_error": "Failed to clear API key", - "empty_api_key": "API Key cannot be empty.", - "invalid_api_key": "Invalid API key. Please check and try again.", - "save_error": "An error occurred while saving. Please try again.", - "using_builtin_key": "Now using the built-in TMDb API key.", - "using_custom_key": "Now using your custom TMDb API key.", - "enter_custom_key": "Please enter and save your custom TMDb API key.", - "key_verified": "API key verified and saved successfully." - }, - "settings": { - "language": "Language", - "select_language": "Select Language", - "english": "English", - "portuguese": "Portuguese", - "portuguese_br": "Portuguese (Brazil)", - "portuguese_pt": "Portuguese (Portugal)", - "german": "German", - "arabic": "Arabic", - "spanish": "Spanish", - "french": "French", - "italian": "Italian", - "croatian": "Croatian", - "chinese": "Chinese (Simplified)", - "hindi": "Hindi", - "serbian": "Serbian", - "hebrew": "Hebrew", - "bulgarian": "Bulgarian", - "polish": "Polish", - "czech": "Czech", - "turkish": "Turkish", - "slovenian": "Slovenian", - "macedonian": "Macedonian", - "russian": "Russian", - "filipino": "Filipino", - "dutch_nl": "Dutch (Netherlands)", - "romanian": "Romanian", - "albanian": "Albanian", - "catalan": "Catalan", - "account": "Account", - "content_discovery": "Content & Discovery", - "appearance": "Appearance", - "integrations": "Integrations", - "playback": "Playback", - "backup_restore": "Backup & Restore", - "backup_restore_desc": "Create and restore app backups", - "updates": "Updates", - "about": "About", - "developer": "Developer", - "cache": "Cache", - "title": "Settings", - "settings_title": "Settings", - "sign_in_sync": "Sign in to sync", - "add_catalogs_sources": "Addons, catalogs, and sources", - "player_trailers_downloads": "Player, trailers, downloads", - "mdblist_tmdb_ai": "MDBList, TMDB, AI", - "check_updates": "Check for updates", - "clear_mdblist_cache": "Clear MDBList Cache", - "cache_management": "CACHE MANAGEMENT", - "downloads_counter": "downloads and counting", - "made_with_love": "Made with ❤️ by Tapframe and friends", - "sections": { - "information": "INFORMATION", - "account": "ACCOUNT", - "theme": "THEME", - "layout": "LAYOUT", - "sources": "SOURCES", - "catalogs": "CATALOGS", - "discovery": "DISCOVERY", - "metadata": "METADATA", - "ai_assistant": "AI ASSISTANT", - "video_player": "VIDEO PLAYER", - "audio_subtitles": "AUDIO & SUBTITLES", - "media": "MEDIA", - "notifications": "NOTIFICATIONS", - "testing": "TESTING", - "danger_zone": "DANGER ZONE" - }, - "items": { - "legal": "Legal & Disclaimer", - "privacy_policy": "Privacy Policy", - "report_issue": "Report Issue", - "version": "Version", - "contributors": "Contributors", - "view_contributors": "View all contributors", - "theme": "Theme", - "episode_layout": "Episode Layout", - "streams_backdrop": "Streams Backdrop", - "streams_backdrop_desc": "Show blurred backdrop on mobile streams", - "addons": "Addons", - "installed": "installed", - "debrid_integration": "Debrid Integration", - "debrid_desc": "Connect Torbox", - "plugins": "Plugins", - "plugins_desc": "Manage plugins and repositories", - "catalogs": "Catalogs", - "active": "active", - "home_screen": "Home Screen", - "home_screen_desc": "Layout and content", - "continue_watching": "Continue Watching", - "continue_watching_desc": "Cache and playback behavior", - "show_discover": "Show Discover Section", - "show_discover_desc": "Display discover content in Search", - "mdblist": "MDBList", - "mdblist_connected": "Connected", - "mdblist_desc": "Enable to add ratings & reviews", - "simkl": "Simkl", - "simkl_connected": "Connected", - "simkl_desc": "Track what you watch", - "tmdb": "TMDB", - "tmdb_desc": "Metadata & logo source provider", - "openrouter": "OpenRouter API", - "openrouter_connected": "Connected", - "openrouter_desc": "Add your API key to enable AI chat", - "video_player": "Video Player", - "built_in": "Built-in", - "external": "External", - "preferred_audio": "Preferred Audio Language", - "preferred_subtitle": "Preferred Subtitle Language", - "subtitle_source": "Subtitle Source Priority", - "auto_select_subs": "Auto-Select Subtitles", - "auto_select_subs_desc": "Automatically select subtitles matching your preferences", - "show_trailers": "Show Trailers", - "show_trailers_desc": "Display trailers in hero section", - "enable_downloads": "Enable Downloads", - "enable_downloads_desc": "Show Downloads tab and enable saving streams", - "notifications": "Notifications", - "notifications_desc": "Episode reminders", - "developer_tools": "Developer Tools", - "developer_tools_desc": "Testing and debug options", - "test_onboarding": "Test Onboarding", - "reset_onboarding": "Reset Onboarding", - "test_announcement": "Test Announcement", - "test_announcement_desc": "Show what's new overlay", - "reset_campaigns": "Reset Campaigns", - "reset_campaigns_desc": "Clear campaign impressions", - "clear_all_data": "Clear All Data", - "clear_all_data_desc": "Reset all settings and cached data" - }, - "options": { - "horizontal": "Horizontal", - "vertical": "Vertical", - "internal_first": "Internal First", - "internal_first_desc": "Prefer embedded subtitles, then external", - "external_first": "External First", - "external_first_desc": "Prefer addon subtitles, then embedded", - "any_available": "Any Available", - "any_available_desc": "Use first available subtitle track" - }, - "clear_data_desc": "This will reset all settings and clear all cached data. Are you sure?", - "app_updates": "App Updates", - "about_nuvio": "About Nuvio", - "cloud_sync": { - "title": "Nuvio Sync", - "description": "Sync data across your Nuvio devices", - "hero_title": "Cloud Sync", - "hero_subtitle": "Keep your addons, progress, and library aligned across all devices.", - "auth": { - "account": "Account", - "not_configured": "Supabase not configured", - "not_authenticated": "Not authenticated", - "email_session": "Email session", - "signed_in_as": "Signed in as {{email}}", - "not_signed_in": "Not signed in", - "effective_owner": "Effective owner: {{id}}" - }, - "stats": { - "title": "Database Statistics", - "plugins": "Plugins", - "addons": "Addons", - "watch_progress": "Watch Progress", - "library_items": "Library Items", - "watched_items": "Watched Items", - "signin_required": "Sign in to load remote data counts." - }, - "actions": { - "title": "Actions", - "description": "Pull to update this device from the cloud, or push from this device as the latest source.", - "pull_btn": "Pull from Cloud", - "push_btn": "Push from Device", - "manage_account": "Manage Account", - "sign_out": "Sign Out", - "sign_in_up": "Sign In / Up" - }, - "alerts": { - "pull_success_title": "Cloud Data Pulled", - "pull_success_msg": "The latest cloud data has been downloaded to this device.", - "pull_failed_title": "Pull Failed", - "pull_failed_msg": "Failed to download data from the cloud", - "push_success_title": "Push Completed", - "push_success_msg": "Device data has been uploaded to the cloud.", - "push_failed_title": "Push Failed", - "push_failed_msg": "Failed to upload local data", - "sign_out_failed": "Sign Out Failed", - "sign_out_failed_title": "Logout Error" - }, - "external_sync": { - "title": "External Sync Priority", - "active_msg": "{{services}} is active. Watch progress and library updates are managed by these services instead of Nuvio cloud database.", - "inactive_msg": "If Trakt or Simkl sync is enabled, watch progress and library updates will use those services instead of Nuvio cloud database." - }, - "pre_auth": { - "title": "Before Syncing", - "description": "Sign in to start cloud sync and keep your data consistent across devices.", - "point_1": "• Addons and plugins settings", - "point_2": "• Watch progress and library", - "env_warning": "Set EXPO_PUBLIC_SUPABASE_URL and EXPO_PUBLIC_SUPABASE_ANON_KEY to enable sync." - }, - "connection": "Connection" - } - }, - "privacy": { - "title": "Privacy & Data", - "settings_desc": "Control telemetry and data collection", - "info_title": "Your Privacy Matters", - "info_description": "Control what data is collected and shared. Analytics are off by default and crash reports are anonymous by default.", - "analytics_enabled_title": "Analytics Enabled", - "analytics_enabled_message": "Usage data will be collected to help improve the app. You can disable this at any time.", - "disable_error_reporting_title": "Disable Error Reporting?", - "disable_error_reporting_message": "Disabling error reporting means we won’t be notified of crashes or issues you experience. This may affect our ability to fix bugs.", - "enable_session_replay_title": "Enable Session Replay?", - "enable_session_replay_message": "Session replay records your screen when errors occur to help us understand what happened. This may capture visible content on your screen.", - "enable_pii_title": "Enable PII Collection?", - "enable_pii_message": "This allows collection of personally identifiable information like IP address and device details. This data helps diagnose issues but increases privacy exposure.", - "disable_all_title": "Disable All Telemetry?", - "disable_all_message": "This will disable all analytics, error reporting, and session replay. We won’t receive any data about app usage or crashes.", - "disable_all_button": "Disable All", - "all_disabled_title": "All Telemetry Disabled", - "all_disabled_message": "All data collection has been disabled. Changes take effect on next app restart.", - "reset_title": "Reset to Recommended", - "reset_message": "Privacy settings have been reset to recommended defaults (error reporting enabled, analytics disabled).", - "section_analytics": "ANALYTICS", - "analytics_title": "Usage Analytics", - "analytics_description": "Collect anonymous usage patterns and screen views", - "section_error_reporting": "ERROR REPORTING", - "error_reporting_title": "Crash Reports", - "error_reporting_description": "Send anonymous crash reports to improve stability", - "session_replay_title": "Session Replay", - "session_replay_description": "Record screen when errors occur", - "pii_title": "Include Device Info", - "pii_description": "Send IP address and device details with reports", - "section_quick_actions": "QUICK ACTIONS", - "disable_all": "Disable All Telemetry", - "disable_all_desc": "Turn off all data collection", - "reset_recommended": "Reset to Recommended", - "reset_recommended_desc": "Privacy-first defaults with error reporting", - "section_learn_more": "LEARN MORE", - "privacy_policy": "Privacy Policy", - "current_settings": "Current Settings Summary", - "summary_analytics": "Analytics", - "summary_errors": "Error Reports", - "summary_replay": "Session Replay", - "summary_pii": "Device Info", - "restart_note_detailed": "* Analytics and error reporting changes take effect immediately. Session replay and PII settings require app restart." - }, - "ai_settings": { - "title": "AI Assistant", - "info_title": "AI-Powered Chat", - "info_desc": "Ask questions about any movie or TV show episode using advanced AI. Get insights about plot, characters, themes, trivia, and more - all powered by comprehensive TMDB data.", - "feature_1": "Episode-specific context and analysis", - "feature_2": "Plot explanations and character insights", - "feature_3": "Behind-the-scenes trivia and facts", - "feature_4": "Your own free OpenRouter API key", - "api_key_section": "OPENROUTER API KEY", - "api_key_label": "API Key", - "api_key_desc": "Enter your OpenRouter API key to enable AI chat features", - "save_api_key": "Save API Key", - "saving": "Saving...", - "update": "Update", - "remove": "Remove", - "get_free_key": "Get Free API Key from OpenRouter", - "enable_chat": "Enable AI Chat", - "enable_chat_desc": "When enabled, the Ask AI button will appear on content pages.", - "chat_enabled": "AI Chat Enabled", - "chat_enabled_desc": "You can now ask questions about movies and TV shows. Look for the \"Ask AI\" button on content pages!", - "how_it_works": "How it works", - "how_it_works_desc": "• OpenRouter provides access to multiple AI models\n• Your API key stays private and secure\n• Free tier includes generous usage limits\n• Chat with context about specific episodes/movies\n• Get detailed analysis and explanations", - "error_invalid_key": "Please enter a valid API key", - "error_key_format": "OpenRouter API keys should start with \"sk-or-\"", - "success_saved": "OpenRouter API key saved successfully!", - "error_save": "Failed to save API key", - "confirm_remove_title": "Remove API Key", - "confirm_remove_msg": "Are you sure you want to remove your OpenRouter API key? This will disable AI chat features.", - "success_removed": "API key removed successfully", - "error_remove": "Failed to remove API key" - }, - "catalog_settings": { - "title": "Catalogs", - "layout_phone": "LAYOUT CATALOGSCREEN (PHONE)", - "posters_per_row": "Posters per row", - "auto": "Auto", - "show_titles": "Show Poster Titles", - "show_titles_desc": "Display title text below each poster", - "phone_only_hint": "Applies to phones only. Tablets keep adaptive layout.", - "catalogs_group": "Catalogs", - "enabled_count": "{{enabled}} of {{total}} enabled", - "rename_hint": "Long-press a catalog to rename", - "rename_modal_title": "Rename Catalog", - "rename_placeholder": "Enter new catalog name", - "error_save_name": "Could not save the custom name." - }, - "continue_watching_settings": { - "title": "Continue Watching", - "playback_behavior": "PLAYBACK BEHAVIOR", - "use_cached": "Use Cached Streams", - "use_cached_desc": "When enabled, clicking Continue Watching items will open the player directly using previously played streams. When disabled, opens a content screen instead.", - "open_metadata": "Open Metadata Screen", - "open_metadata_desc": "When cached streams are disabled, open the Metadata screen instead of the Streams screen. This shows content details and allows manual stream selection.", - "card_appearance": "CARD APPEARANCE", - "card_style": "Card Style", - "card_style_desc": "Choose how Continue Watching items appear on the home screen", - "wide": "Wide", - "poster": "Poster", - "cache_settings": "CACHE SETTINGS", - "cache_duration": "Stream Cache Duration", - "cache_duration_desc": "How long to keep cached stream links before they expire", - "important_note": "Important Note", - "important_note_text": "Not all stream links may remain active for the full cache duration. Longer cache times may result in expired links. If a cached link fails, the app will fall back to fetching fresh streams.", - "how_it_works": "How it works", - "how_it_works_cached": "• Streams are cached for your selected duration after playing\n• Cached streams are validated before use\n• If cache is invalid or expired, falls back to content screen\n• \"Use Cached Streams\" controls direct player vs screen navigation\n• \"Open Metadata Screen\" appears only when cached streams are disabled", - "how_it_works_uncached": "• When cached streams are disabled, clicking Continue Watching items opens content screens\n• \"Open Metadata Screen\" option controls which screen to open\n• Metadata screen shows content details and allows manual stream selection\n• Streams screen shows available streams for immediate playback", - "changes_saved": "Changes saved", - "min": "min", - "hour": "hour", - "hours": "hours" - }, - "contributors": { - "title": "Contributors", - "special_mentions": "Special Mentions", - "tab_contributors": "Contributors", - "tab_special": "Special Mentions", - "tab_donors": "Donors", - "manager_role": "Community Manager", - "manager_desc": "Manages the Discord & Reddit communities for Nuvio", - "sponsor_role": "Server Sponsor", - "sponsor_desc": "Sponsored the server infrastructure for Nuvio", - "mod_role": "Discord Mod", - "mod_desc": "Helps moderate the Nuvio Discord community", - "loading": "Loading...", - "discord_user": "Discord User", - "contributions": "contributions", - "gratitude_title": "We're grateful for every contribution", - "gratitude_desc": "Each line of code, bug report, and suggestion helps make Nuvio better for everyone", - "special_thanks_title": "Special Thanks", - "special_thanks_desc": "These amazing people help keep the Nuvio community running and the servers online", - "donors_desc": "Thank you for believing in what we're building. Your support keeps Nuvio free and constantly improving.", - "latest_donations": "Latest", - "leaderboard": "Leaderboard", - "loading_donors": "Loading donors…", - "no_donors": "No donors yet", - "error_rate_limit": "GitHub API rate limit exceeded. Please try again later or pull to refresh.", - "error_failed": "Failed to load contributors. Please check your internet connection.", - "retry": "Try Again", - "no_contributors": "No contributors found", - "loading_contributors": "Loading contributors..." - }, - "debrid": { - "title": "Debrid Integration", - "description_torbox": "Connect Torbox to use your account-based source preferences. Enter your API key below to configure the integration.", - "description_torrentio": "Configure Torrentio as an external source integration. A compatible debrid account may be required depending on your setup.", - "tab_torbox": "TorBox", - "tab_torrentio": "Torrentio", - "status_connected": "Connected", - "status_disconnected": "Disconnected", - "enable_addon": "Enable Addon", - "disconnect_button": "Disconnect & Remove", - "disconnect_loading": "Disconnecting...", - "account_info": "Account Information", - "plan": "Plan", - "plan_free": "Free", - "plan_essential": "Essential ($3/mo)", - "plan_pro": "Pro ($10/mo)", - "plan_standard": "Standard ($5/mo)", - "plan_unknown": "Unknown", - "expires": "Expires", - "downloaded": "Downloaded", - "status_active": "Active", - "connected_title": "✓ Connected to TorBox", - "connected_desc": "Your TorBox addon is active and providing premium streams.", - "configure_title": "Configure Addon", - "configure_desc": "Customize your streaming experience. Sort by quality, filter file sizes, and manage other integration settings.", - "open_settings": "Open Settings", - "what_is_debrid": "What is a Debrid Service?", - "enter_api_key": "Enter your API Key", - "connect_button": "Connect & Install", - "connecting": "Connecting...", - "unlock_speeds_title": "Optional Torbox Subscription", - "unlock_speeds_desc": "Torbox offers account tiers with enhanced performance and availability features.", - "get_subscription": "Get Subscription", - "powered_by": "Powered by", - "disclaimer_torbox": "Nuvio is not affiliated with Torbox in any way.", - "disclaimer_torrentio": "Nuvio is not affiliated with Torrentio in any way.", - "installed_badge": "✓ INSTALLED", - "promo_title": "⚡ Need a Debrid Service?", - "promo_desc": "Use TorBox if you want account-managed performance features for supported integrations.", - "promo_button": "Get TorBox Subscription", - "service_label": "Debrid Service *", - "api_key_label": "API Key *", - "sorting_label": "Sorting", - "exclude_qualities": "Exclude Qualities", - "priority_languages": "Priority Languages", - "max_results": "Max Results", - "additional_options": "Additional Options", - "no_download_links": "Don't show download links", - "no_debrid_catalog": "Don't show debrid catalog", - "install_button": "Install Torrentio", - "installing": "Installing...", - "update_button": "Update Configuration", - "updating": "Updating...", - "remove_button": "Remove Torrentio", - "error_api_required": "API Key Required", - "error_api_required_desc": "Please enter your debrid service API key to install Torrentio.", - "success_installed": "Torrentio addon installed successfully!", - "success_removed": "Torrentio addon removed successfully", - "alert_disconnect_title": "Disconnect Torbox", - "alert_disconnect_msg": "Are you sure you want to disconnect Torbox? This will remove the addon and clear your saved API key." - }, - "home_screen": { - "title": "Home Screen Settings", - "changes_applied": "Changes Applied", - "display_options": "DISPLAY OPTIONS", - "show_hero": "Show Hero Section", - "show_hero_desc": "Featured content at the top", - "show_this_week": "Show This Week Section", - "show_this_week_desc": "New episodes from current week", - "select_catalogs": "Select Catalogs", - "all_catalogs": "All catalogs", - "selected": "selected", - "prefer_external_meta": "Prefer External Meta Addon", - "prefer_external_meta_desc": "Use external metadata on detail page", - "hero_layout": "Hero Layout", - "layout_legacy": "Legacy", - "layout_carousel": "Carousel", - "layout_appletv": "Apple TV", - "layout_desc": "Full-width banner, swipeable cards, or Apple TV style", - "featured_source": "Featured Source", - "using_catalogs": "Using Catalogs", - "manage_selected_catalogs": "Manage selected catalogs", - "dynamic_bg": "Dynamic Hero Background", - "dynamic_bg_desc": "Blurred banner behind carousel", - "performance_note": "May impact performance on low-end devices.", - "posters": "Posters", - "show_titles": "Show Titles", - "poster_size": "Poster Size", - "poster_corners": "Poster Corners", - "size_small": "Small", - "size_medium": "Medium", - "size_large": "Large", - "corners_square": "Square", - "corners_rounded": "Rounded", - "corners_pill": "Pill", - "about_these_settings": "ABOUT THESE SETTINGS", - "about_desc": "These settings control how content is displayed on your Home screen. Changes are applied immediately without requiring an app restart.", - "hero_catalogs": { - "title": "Hero Section Catalogs", - "select_all": "Select All", - "clear_all": "Clear All", - "info": "Select which catalogs to display in the hero section. If none are selected, all catalogs will be used. Don't forget to press Save when you're done.", - "settings_saved": "Settings Saved", - "error_load": "Failed to load catalogs", - "movies": "Movies", - "tv_shows": "TV Shows" - } - }, - "calendar": { - "title": "Calendar", - "loading": "Loading calendar...", - "no_scheduled_episodes": "No scheduled episodes", - "check_back_later": "Check back later", - "showing_episodes_for": "Showing episodes for {{date}}", - "show_all_episodes": "Show All Episodes", - "no_episodes_for": "No episodes for {{date}}", - "no_upcoming_found": "No upcoming episodes found", - "add_series_desc": "Add series to your library to see their upcoming episodes here" - }, - "mdblist": { - "title": "Rating Sources", - "status_disabled": "MDBList Disabled", - "status_active": "API Key Active", - "status_required": "API Key Required", - "status_disabled_desc": "MDBList functionality is currently disabled.", - "status_active_desc": "Ratings from MDBList are enabled.", - "status_required_desc": "Add your key below to enable ratings.", - "enable_toggle": "Enable MDBList", - "enable_toggle_desc": "Turn on/off all MDBList functionality", - "api_section": "API Key", - "placeholder": "Paste your MDBList API key", - "save": "Save", - "clear": "Clear Key", - "rating_providers": "Rating Providers", - "rating_providers_desc": "Choose which ratings to display in the app", - "how_to": "How to get an API key", - "step_1": "Log in on the", - "step_1_link": "MDBList website", - "step_2": "Go to", - "step_2_settings": "Settings", - "step_2_api": "API", - "step_2_end": "section.", - "step_3": "Generate a new key and copy it.", - "go_to_website": "Go to MDBList", - "alert_clear_title": "Clear API Key", - "alert_clear_msg": "Are you sure you want to remove the saved API key?", - "success_saved": "API key saved successfully.", - "error_empty": "API Key cannot be empty.", - "error_save": "An error occurred while saving. Please try again.", - "api_key_empty_error": "API Key cannot be empty.", - "success_cleared": "API key cleared successfully", - "error_clear": "Failed to clear API key" - }, - "notification": { - "title": "Notification Settings", - "section_general": "General", - "enable_notifications": "Enable Notifications", - "section_types": "Notification Types", - "new_episodes": "New Episodes", - "upcoming_shows": "Upcoming Shows", - "reminders": "Reminders", - "section_timing": "Notification Timing", - "timing_desc": "When should you be notified before an episode airs?", - "hours_1": "1 hour", - "hours_suffix": "hours", - "section_status": "Notification Status", - "stats_upcoming": "Upcoming", - "stats_this_week": "This Week", - "stats_total": "Total", - "sync_button": "Sync Library & Trakt", - "syncing": "Syncing...", - "sync_desc": "Automatically syncs notifications for all shows in your library and Trakt watchlist/collection.", - "section_advanced": "Advanced", - "reset_button": "Reset All Notifications", - "test_button": "Test Notification (5 sec)", - "test_notification_in": "Notification in {{seconds}}s...", - "test_notification_text": "Notification will appear in {{seconds}} seconds", - "alert_reset_title": "Reset Notifications", - "alert_reset_msg": "This will cancel all scheduled notifications, but will not remove anything from your saved library. Are you sure?", - "alert_reset_success": "All notifications have been reset", - "alert_sync_complete": "Sync Complete", - "alert_sync_msg": "Successfully synced notifications for your library and Trakt items.\n\nScheduled: {{upcoming}} upcoming episodes\nThis week: {{thisWeek}} episodes", - "alert_test_scheduled": "Test notification scheduled to fire instantly" - }, - "backup": { - "title": "Backup & Restore", - "options_title": "Backup Options", - "options_desc": "Choose what to include in your backups", - "section_core": "Core Data", - "section_addons": "Addons & Integrations", - "section_settings": "Settings & Preferences", - "library_label": "Library", - "library_desc": "Your saved movies and TV shows", - "watch_progress_label": "Watch Progress", - "watch_progress_desc": "Continue watching positions", - "addons_label": "Addons", - "addons_desc": "Installed Stremio addons", - "plugins_label": "Plugins", - "plugins_desc": "Custom scraper configurations", - "trakt_label": "Trakt Integration", - "trakt_desc": "Sync data and authentication tokens", - "app_settings_label": "App Settings", - "app_settings_desc": "Theme, preferences, and configurations", - "user_prefs_label": "User Preferences", - "user_prefs_desc": "Addon order and UI settings", - "catalog_settings_label": "Catalog Settings", - "catalog_settings_desc": "Catalog filters and preferences", - "api_keys_label": "API Keys", - "api_keys_desc": "MDBList and OpenRouter keys", - "action_create": "Create Backup", - "action_restore": "Restore from Backup", - "section_info": "About Backups", - "info_text": "• Customize what gets backed up using the toggles above\n• Backup files are stored locally on your device\n• Share your backup to transfer data between devices\n• Restoring will overwrite your current data", - "alert_create_title": "Create Backup", - "alert_no_content": "No content selected for backup.\n\nPlease enable at least one option in the Backup Options section above.", - "alert_backup_created_title": "Backup Created", - "alert_backup_created_msg": "Your backup has been created and is ready to share.", - "alert_backup_failed_title": "Backup Failed", - "alert_restore_confirm_title": "Confirm Restore", - "alert_restore_confirm_msg": "This will restore your data from a backup created on {{date}}.\n\nThis action will overwrite your current data. Are you sure you want to continue?", - "alert_restore_complete_title": "Restore Complete", - "alert_restore_complete_msg": "Your data has been successfully restored. Please restart the app to see all changes.", - "alert_restore_failed_title": "Restore Failed", - "restart_app": "Restart App", - "alert_restart_failed_title": "Restart Failed", - "alert_restart_failed_msg": "Failed to restart the app. Please manually close and reopen the app to see your restored data." - }, - "updates": { - "title": "App Updates", - "status_checking": "Checking for updates...", - "status_available": "Update available!", - "status_downloading": "Downloading update...", - "status_installing": "Installing update...", - "status_success": "Update installed successfully!", - "status_error": "Update failed", - "status_ready": "Ready to check for updates", - "action_check": "Check for Updates", - "action_install": "Install Update", - "release_notes": "Release notes:", - "version": "Version:", - "last_checked": "Last checked:", - "current_version": "Current version:", - "current_release_notes": "Current release notes:", - "github_release": "GITHUB RELEASE", - "current": "Current:", - "latest": "Latest:", - "notes": "Notes:", - "view_release": "View Release", - "notification_settings": "NOTIFICATION SETTINGS", - "ota_alerts_label": "OTA Update Alerts", - "ota_alerts_desc": "Show notifications for over-the-air updates", - "major_alerts_label": "Major Update Alerts", - "major_alerts_desc": "Show notifications for new app versions on GitHub", - "alert_disable_ota_title": "Disable OTA Update Alerts?", - "alert_disable_ota_msg": "You will no longer receive automatic notifications for OTA updates.\n\n⚠️ Warning: Staying on the latest version is important for:\n• Bug fixes and stability improvements\n• New features and enhancements\n• Providing accurate feedback and crash reports\n\nYou can still manually check for updates in this screen.", - "alert_disable_major_title": "Disable Major Update Alerts?", - "alert_disable_major_msg": "You will no longer receive notifications for major app updates that require reinstallation.\n\n⚠️ Warning: Major updates often include:\n• Critical security patches\n• Breaking changes that require app reinstall\n• Important compatibility fixes\n\nYou can still check for updates manually.", - "warning_note": "Keeping alerts enabled ensures you receive bug fixes and can provide accurate crash reports.", - "disable": "Disable", - "alert_no_update_to_install": "No update available to install", - "alert_install_failed": "Failed to install update", - "alert_no_update_title": "No Update", - "alert_update_applied_msg": "Update will be applied on next app restart" - }, - "player": { - "title": "Video Player", - "section_selection": "PLAYER SELECTION", - "internal_title": "Built-in Player", - "internal_desc": "Use the app's default video player", - "vlc_title": "VLC", - "vlc_desc": "Open streams in VLC media player", - "infuse_title": "Infuse", - "infuse_desc": "Open streams in Infuse player", - "outplayer_title": "OutPlayer", - "outplayer_desc": "Open streams in OutPlayer", - "vidhub_title": "VidHub", - "vidhub_desc": "Open streams in VidHub player", - "infuse_live_title": "Infuse Livecontainer", - "infuse_live_desc": "Open streams in Infuse player LiveContainer", - "external_title": "External Player", - "external_desc": "Open streams in your preferred video player", - "section_playback": "PLAYBACK OPTIONS", - "skip_intro_settings_title": "Skip Intro", - "powered_by_introdb": "Powered by IntroDB", - "autoplay_title": "Auto-play First Stream", - "autoplay_desc": "Automatically start the first stream shown in the list.", - "resume_title": "Always Resume", - "resume_desc": "Skip the resume prompt and automatically continue where you left off (if less than 85% watched).", - "engine_title": "Video Player Engine", - "engine_desc": "Auto uses ExoPlayer with MPV fallback. Some formats like Dolby Vision and HDR may not be supported by MPV, so Auto is recommended for best compatibility.", - "decoder_title": "Decoder Mode", - "decoder_desc": "How video is decoded. Auto is recommended for best balance.", - "gpu_title": "GPU Rendering", - "gpu_desc": "GPU-Next offers better HDR and color management.", - "external_downloads_title": "External Player for Downloads", - "external_downloads_desc": "Play downloaded content in your preferred external player.", - "restart_required": "Restart Required", - "restart_msg_decoder": "Please restart the app for the decoder change to take effect.", - "restart_msg_gpu": "Please restart the app for the GPU mode change to take effect.", - "option_auto": "Auto", - "option_auto_desc_engine": "ExoPlayer + MPV fallback", - "option_mpv": "MPV", - "option_mpv_desc": "MPV only", - "option_auto_desc_decoder": "Best balance", - "option_sw": "SW", - "option_sw_desc": "Software", - "option_hw": "HW", - "option_hw_desc": "Hardware", - "option_hw_plus": "HW+", - "option_hw_plus_desc": "Full HW", - "option_gpu_desc": "Standard", - "option_gpu_next_desc": "Advanced" - }, - "plugins": { - "title": "Plugins", - "enable_title": "Enable Plugins", - "enable_desc": "Enable the plugin engine to resolve external media sources", - "repo_config_title": "Repository Configuration", - "repo_config_desc": "Manage external plugin repositories. Toggle each repository on or off below.", - "your_repos": "Repositories", - "your_repos_desc": "Configure external sources for plugins.", - "add_repo_button": "Add Repository", - "refresh": "Refresh", - "remove": "Remove", - "enabled": "Enabled", - "disabled": "Disabled", - "updating": "Updating...", - "success": "Success", - "error": "Error", - "alert_repo_added": "Repository added and plugins loaded successfully", - "alert_repo_saved": "Repository URL saved successfully", - "alert_repo_refreshed": "Repository refreshed successfully", - "alert_invalid_url": "Invalid URL Format", - "alert_plugins_cleared": "All plugins have been removed", - "alert_cache_cleared": "Repository cache cleared successfully", - "unknown": "Unknown", - "active": "Active", - "available": "Available", - "platform_disabled": "Platform Disabled", - "limited": "Limited", - "clear_all": "Clear All Plugins", - "clear_all_desc": "Are you sure you want to remove all installed plugins? This action cannot be undone.", - "clear_cache": "Clear Repository Cache", - "clear_cache_desc": "This will remove the saved repository URL and clear all cached plugin data. You will need to re-enter your repository URL.", - "add_new_repo": "Add New Repository", - "available_plugins": "Available Plugins ({{count}})", - "placeholder": "Search plugins...", - "all": "All", - "filter_all": "All Types", - "filter_movies": "Movies", - "filter_tv": "TV Shows", - "enable_all": "Enable All", - "disable_all": "Disable All", - "no_plugins_found": "No Plugins Found", - "no_plugins_available": "No Plugins Available", - "no_match_desc": "No plugins match \"{{query}}\". Try a different search term.", - "configure_repo_desc": "Configure a repository above to view available plugins.", - "clear_search": "Clear Search", - "no_external_player": "No external player", - "showbox_token": "ShowBox UI Token", - "showbox_placeholder": "Paste your ShowBox UI token", - "save": "Save", - "clear": "Clear", - "additional_settings": "Additional Settings", - "enable_url_validation": "Enable URL Validation", - "url_validation_desc": "Validate media URLs before returning them (may slow down results but improves reliability)", - "group_streams": "Group Plugin Sources", - "group_streams_desc": "When enabled, sources are grouped by repository. When disabled, each plugin shows as a separate provider.", - "sort_quality": "Sort by Quality First", - "sort_quality_desc": "When enabled, sources are sorted by quality first. Only available when grouping is enabled.", - "show_logos": "Show Plugin Logos", - "show_logos_desc": "Display plugin logos next to media links on the sources screen.", - "quality_filtering": "Quality Filtering", - "quality_filtering_desc": "Exclude specific video resolutions from search results. Tap on a quality to exclude it from plugin results.", - "excluded_qualities": "Excluded qualities:", - "language_filtering": "Language Filtering", - "language_filtering_desc": "Exclude specific languages from search results. Tap on a language to exclude it from plugin results.", - "note": "Note:", - "language_filtering_note": "This filter only applies to providers that include language information. It does not affect other providers.", - "excluded_languages": "Excluded languages:", - "about_title": "About Plugins", - "about_desc_1": "Plugins are modular components that adapt content from various external protocols. They run locally on your device and can be installed from trusted repositories.", - "about_desc_2": "Plugins marked as \"Limited\" may require specific external configurations.", - "help_title": "Plugin Setup", - "help_step_1": "1. **Enable Plugins** - Turn on the main switch", - "help_step_2": "2. **Add Repository** - Add a valid repository URL", - "help_step_3": "3. **Refresh Repository** - Fetch available plugins", - "help_step_4": "4. **Activate** - Enable the plugins you wish to use", - "got_it": "Got it!", - "repo_format_hint": "Format: https://raw.githubusercontent.com/username/repo/refs/heads/branch", - "cancel": "Cancel", - "add": "Add" - }, - "theme": { - "title": "App Themes", - "select_theme": "SELECT THEME", - "create_custom": "Create Custom Theme", - "options": "OPTIONS", - "use_dominant_color": "Use Dominant Color from Artwork", - "categories": { - "all": "All Themes", - "dark": "Dark Themes", - "colorful": "Colorful", - "custom": "My Themes" - }, - "editor": { - "theme_name_placeholder": "Theme name", - "save": "Save", - "primary": "Primary", - "secondary": "Secondary", - "background": "Background", - "invalid_name_title": "Invalid Name", - "invalid_name_msg": "Please enter a valid theme name" - }, - "alerts": { - "delete_title": "Delete Theme", - "delete_msg": "Are you sure you want to delete \"{{name}}\"?", - "ok": "OK", - "delete": "Delete", - "cancel": "Cancel", - "back": "Settings" - } - }, - "legal": { - "title": "Legal & Disclaimer", - "intro_title": "Nature of the Application", - "intro_text": "Nuvio is a media player and metadata management application. It acts solely as a client-side interface for browsing publicly available metadata (movies, TV shows, etc.) and playing media files provided by the user or third-party extensions. Nuvio itself does not host, store, distribute, or index any media content.", - "extensions_title": "Third-Party Plugins", - "extensions_text": "Nuvio uses an extensible architecture that allows users to install third-party plugins. These plugins are developed and maintained by independent developers not affiliated with Nuvio. We have no control over, and assume no responsibility for, the content, legality, or functionality of any third-party plugin.", - "user_resp_title": "User Responsibility", - "user_resp_text": "Users are solely responsible for the plugins they install and the content they access. By using this application, you agree to ensure that you have the legal right to access any content you view using Nuvio. The developers of Nuvio do not endorse or encourage copyright infringement.", - "dmca_title": "Copyright & DMCA", - "dmca_text": "We respect the intellectual property rights of others. Nuvio does not host media content. If you believe this project's code, assets, or interface infringes your rights, submit a notice through the official project contact channels listed on the website and repository.", - "warranty_title": "No Warranty", - "warranty_text": "This software is provided \"as is\", without warranty of any kind, express or implied. In no event shall the authors or copyright holders be liable for any claim, damages, or other liability arising from the use of this software." - }, - "plugin_tester": { - "title": "Plugin Tester", - "subtitle": "Run scrapers and inspect logs in real-time", - "tabs": { - "individual": "Individual", - "repo": "Repo Tester", - "code": "Code", - "logs": "Logs", - "results": "Results" - }, - "common": { - "error": "Error", - "success": "Success", - "movie": "Movie", - "tv": "TV", - "tmdb_id": "TMDB ID", - "season": "Season", - "episode": "Episode", - "running": "Running…", - "run_test": "Run Test", - "play": "Play", - "done": "Done", - "test": "Test", - "testing": "Testing…" - }, - "individual": { - "load_from_url": "Load from URL", - "load_from_url_desc": "Paste a raw GitHub URL or local IP and tap download.", - "enter_url_error": "Please enter a URL", - "code_loaded": "Code loaded from URL", - "fetch_error": "Failed to fetch: {{message}}", - "no_code_error": "No code to run", - "plugin_code": "Plugin Code", - "focus_editor": "Focus code editor", - "code_placeholder": "// Paste plugin code here...", - "test_parameters": "Test Parameters", - "no_logs": "No logs yet. Run a test to see output.", - "no_streams": "No streams found yet.", - "streams_found": "{{count}} Stream Found", - "streams_found_plural": "{{count}} Streams Found", - "tap_play_hint": "Tap Play to test a stream in the native player.", - "unnamed_stream": "Unnamed Stream", - "quality": "Quality: {{quality}}", - "size": "Size: {{size}}", - "url_label": "URL: {{url}}", - "headers_info": "Headers: {{count}} custom header(s)", - "find_placeholder": "Find in code…", - "edit_code_title": "Edit Code", - "no_url_stream_error": "No URL found for this stream" - }, - "repo": { - "title": "Repo Tester", - "description": "Fetch a repository (local URL or GitHub raw) and test each provider.", - "enter_repo_url_error": "Please enter a repository URL", - "invalid_url_title": "Invalid URL", - "invalid_url_msg": "Use a GitHub raw URL or a local http(s) URL.\n\nExample:\nhttps://raw.githubusercontent.com/tapframe/nuvio-providers/refs/heads/main", - "manifest_build_error": "Could not build a manifest URL from the input", - "manifest_fetch_error": "Failed to fetch manifest", - "repo_manifest_fetch_error": "Failed to fetch repository manifest", - "missing_filename": "Missing filename in manifest", - "scraper_build_error": "Could not build a scraper URL", - "download_scraper_error": "Failed to download scraper", - "test_failed": "Test failed", - "test_parameters": "Repo Test Parameters", - "test_parameters_desc": "These parameters are used only for Repo Tester.", - "using_info": "Using: {{mediaType}} • TMDB {{tmdbId}}", - "using_info_tv": "Using: {{mediaType}} • TMDB {{tmdbId}} • S{{season}}E{{episode}}", - "providers_title": "Providers", - "repository_default": "Repository", - "providers_count": "{{count}} providers", - "fetch_hint": "Fetch a repo to list providers.", - "test_all": "Test All", - "status_running": "RUNNING", - "status_ok": "OK ({{count}})", - "status_ok_empty": "OK (0)", - "status_failed": "FAILED", - "status_idle": "IDLE", - "tried_url": "Tried: {{url}}", - "provider_logs": "Provider Logs", - "no_logs_captured": "No logs captured." - } - } + "common": { + "loading": "Loading...", + "cancel": "Cancel", + "save": "Save", + "delete": "Delete", + "edit": "Edit", + "search": "Search", + "error": "Error", + "success": "Success", + "ok": "OK", + "unknown": "Unknown", + "clear": "Clear", + "retry": "Retry", + "try_again": "Try Again", + "go_back": "Go Back", + "settings": "Settings", + "close": "Close", + "enable": "Enable", + "disable": "Disable", + "show_more": "Show More", + "show_less": "Show Less", + "load_more": "Load More", + "unknown_date": "Unknown date", + "anonymous_user": "Anonymous User", + "time": { + "now": "Just now", + "minutes_ago": "{{count}}m ago", + "hours_ago": "{{count}}h ago", + "days_ago": "{{count}}d ago" + }, + "days_short": { + "sun": "Sun", + "mon": "Mon", + "tue": "Tue", + "wed": "Wed", + "thu": "Thu", + "fri": "Fri", + "sat": "Sat" + }, + "email": "Email", + "status": "Status" + }, + "home": { + "categories": { + "movies": "Movies", + "series": "Series", + "channels": "Channels" + }, + "movies": "Movies", + "tv_shows": "TV Shows", + "load_more_catalogs": "Load More Catalogs", + "no_content": "No content available", + "add_catalogs": "Add Catalogs", + "sign_in_available": "Sign In Available", + "sign_in_desc": "You can sign in anytime from Settings → Account", + "view_all": "View All", + "this_week": "This Week", + "upcoming": "Upcoming", + "recently_released": "Recently Released", + "no_scheduled_episodes": "Series with No Scheduled Episodes", + "check_back_later": "Check back later", + "continue_watching": "Continue Watching", + "up_next": "Up Next", + "up_next_caps": "UP NEXT", + "released": "Released", + "new": "New", + "tba": "TBA", + "new_episodes": "{{count}} New Episodes", + "season_short": "S{{season}}", + "episode_short": "E{{episode}}", + "season": "Season {{season}}", + "episode": "Episode {{episode}}", + "movie": "Movie", + "series": "Series", + "tv_show": "TV Show", + "percent_watched": "{{percent}}% watched", + "view_details": "View Details", + "remove": "Remove", + "play": "Play", + "play_now": "Play Now", + "resume": "Resume", + "info": "Info", + "more_info": "More Info", + "my_list": "My List", + "save": "Save", + "saved": "Saved", + "retry": "Retry", + "install_addons": "Install Addons", + "settings": "Settings", + "no_featured_content": "No Featured Content", + "couldnt_load_featured": "Couldn't load featured content", + "no_featured_desc": "Install addons with catalogs or change the content source in your settings.", + "load_error_desc": "There was a problem fetching featured content. Please check your connection and try again.", + "no_featured_available": "No featured content available", + "no_description": "No description available" + }, + "navigation": { + "home": "Home", + "library": "Library", + "search": "Search", + "downloads": "Downloads", + "settings": "Settings" + }, + "search": { + "title": "Search", + "recent_searches": "Recent Searches", + "discover": "Discover", + "movies": "Movies", + "tv_shows": "TV Shows", + "select_catalog": "Select Catalog", + "all_genres": "All Genres", + "discovering": "Discovering content...", + "show_more": "Show More ({{count}})", + "no_content_found": "No content found", + "try_different": "Try a different genre or catalog", + "select_catalog_desc": "Select a catalog to discover", + "tap_catalog_desc": "Tap the catalog chip above to get started", + "placeholder": "Search movies, shows...", + "keep_typing": "Keep typing...", + "type_characters": "Type at least 2 characters to search", + "no_results": "No results found", + "try_keywords": "Try different keywords or check your spelling", + "select_type": "Select Type", + "browse_movies": "Browse movie catalogs", + "browse_tv": "Browse TV series catalogs", + "select_genre": "Select Genre", + "show_all_content": "Show all content", + "genres_count": "{{count}} genres" + }, + "library": { + "title": "Library", + "watched": "Watched", + "continue": "Continue", + "watchlist": "Watchlist", + "collection": "Collection", + "rated": "Rated", + "items": "items", + "trakt_collections": "Trakt collections", + "trakt_collection": "Trakt Collection", + "no_trakt": "No Trakt collections", + "no_trakt_desc": "Your Trakt collections will appear here once you start using Trakt", + "load_collections": "Load Collections", + "empty_folder": "No content in {{folder}}", + "empty_folder_desc": "This collection is empty", + "refresh": "Refresh", + "no_movies": "No movies yet", + "no_series": "No TV shows yet", + "no_content": "No content yet", + "add_content_desc": "Add some content to your library to see it here", + "find_something": "Find something to watch", + "removed_from_library": "Removed from Library", + "item_removed": "Item removed from your library", + "failed_update_library": "Failed to update Library", + "unable_remove": "Unable to remove item from library", + "marked_watched": "Marked as Watched", + "marked_unwatched": "Marked as Unwatched", + "item_marked_watched": "Item marked as watched", + "item_marked_unwatched": "Item marked as unwatched", + "failed_update_watched": "Failed to update watched status", + "unable_update_watched": "Unable to update watched status", + "added_to_library": "Added to Library", + "item_added": "Added to your local library", + "add_to_library": "Add to Library", + "remove_from_library": "Remove from Library", + "mark_watched": "Mark as Watched", + "mark_unwatched": "Mark as Unwatched", + "share": "Share", + "add_to_watchlist": "Add to Trakt Watchlist", + "remove_from_watchlist": "Remove from Trakt Watchlist", + "added_to_watchlist": "Added to Watchlist", + "added_to_watchlist_desc": "Added to your Trakt watchlist", + "removed_from_watchlist": "Removed from Watchlist", + "removed_from_watchlist_desc": "Removed from your Trakt watchlist", + "add_to_collection": "Add to Trakt Collection", + "remove_from_collection": "Remove from Trakt Collection", + "added_to_collection": "Added to Collection", + "added_to_collection_desc": "Added to your Trakt collection", + "removed_from_collection": "Removed from Collection", + "removed_from_collection_desc": "Removed from your Trakt collection" + }, + "metadata": { + "unable_to_load": "Unable to Load Content", + "error_code": "Error Code: {{code}}", + "content_not_found": "Content not found", + "content_not_found_desc": "This content doesn't exist or may have been removed.", + "server_error": "Server error", + "server_error_desc": "The server is temporarily unavailable. Please try again later.", + "bad_gateway": "Bad gateway", + "bad_gateway_desc": "The server is experiencing issues. Please try again later.", + "service_unavailable": "Service unavailable", + "service_unavailable_desc": "The service is currently down for maintenance. Please try again later.", + "too_many_requests": "Too many requests", + "too_many_requests_desc": "You're making too many requests. Please wait a moment and try again.", + "request_timeout": "Request timeout", + "request_timeout_desc": "The request took too long. Please try again.", + "network_error": "Network error", + "network_error_desc": "Please check your internet connection and try again.", + "auth_error": "Authentication error", + "auth_error_desc": "Please check your account settings and try again.", + "access_denied": "Access denied", + "access_denied_desc": "You don't have permission to access this content.", + "connection_error": "Connection error", + "streams_unavailable": "Streams unavailable", + "streams_unavailable_desc": "Streaming sources are currently unavailable. Please try again later.", + "unknown_error": "Unknown error", + "something_went_wrong": "Something went wrong. Please try again.", + "cast": "Cast", + "more_like_this": "More Like This", + "collection": "Collection", + "episodes": "Episodes", + "seasons": "Seasons", + "posters": "Posters", + "banners": "Banners", + "specials": "Specials", + "season_number": "Season {{number}}", + "episode_count": "{{count}} Episode", + "episode_count_plural": "{{count}} Episodes", + "no_episodes": "No episodes available", + "no_episodes_for_season": "No episodes available for Season {{season}}", + "episodes_not_released": "Episodes may not be released yet", + "no_description": "No description available", + "episode_label": "EPISODE {{number}}", + "watch_again": "Watch Again", + "completed": "Completed", + "play_episode": "Play S{{season}}E{{episode}}", + "play": "Play", + "watched": "Watched", + "watched_on_trakt": "Watched on Trakt", + "synced_with_trakt": "Synced with Trakt", + "saved": "Saved", + "director": "Director", + "directors": "Directors", + "creator": "Creator", + "creators": "Creators", + "production": "Production", + "network": "Network", + "mark_watched": "Mark as Watched", + "mark_unwatched": "Mark as Unwatched", + "marking": "Marking...", + "removing": "Removing...", + "unmark_season": "Unmark Season {{season}}", + "mark_season": "Mark Season {{season}}", + "resume": "Resume", + "spoiler_warning": "Spoiler Warning", + "spoiler_warning_desc": "This comment contains spoilers. Are you sure you want to reveal it?", + "cancel": "Cancel", + "reveal_spoilers": "Reveal Spoilers", + "movie_details": "Movie Details", + "show_details": "Show Details", + "tagline": "Tagline", + "status": "Status", + "release_date": "Release Date", + "runtime": "Runtime", + "budget": "Budget", + "revenue": "Revenue", + "origin_country": "Origin Country", + "original_language": "Original Language", + "first_air_date": "First Air Date", + "last_air_date": "Last Air Date", + "total_episodes": "Total Episodes", + "episode_runtime": "Episode Runtime", + "created_by": "Created By", + "backdrop_gallery": "Backdrop Gallery", + "loading_episodes": "Loading episodes...", + "no_episodes_available": "No episodes available", + "play_next": "Play S{{season}}E{{episode}}", + "play_next_episode": "Play Next Episode", + "save": "Save", + "percent_watched": "{{percent}}% watched", + "percent_watched_trakt": "{{percent}}% watched ({{traktPercent}}% on Trakt)", + "synced_with_trakt_progress": "Synced with Trakt", + "using_trakt_progress": "Using Trakt progress", + "added_to_collection_hero": "Added to Collection", + "added_to_collection_desc_hero": "Added to your Trakt collection", + "removed_from_collection_hero": "Removed from Collection", + "removed_from_collection_desc_hero": "Removed from your Trakt collection", + "mark_as_watched": "Mark as Watched", + "mark_as_unwatched": "Mark as Unwatched" + }, + "cast": { + "biography": "Biography", + "known_for": "Known For", + "personal_info": "Personal Info", + "born_in": "Born in {{place}}", + "filmography": "Filmography", + "also_known_as": "Also Known As", + "no_info_available": "No additional information available", + "as_character": "as {{character}}", + "loading_details": "Loading details...", + "years_old": "{{age}} years old", + "view_filmography": "View Filmography", + "filter": "Filter", + "sort_by": "Sort By", + "sort_popular": "Popular", + "sort_latest": "Latest", + "sort_upcoming": "Upcoming", + "upcoming_badge": "UPCOMING", + "coming_soon": "Coming Soon", + "filmography_count": "Filmography • {{count}} titles", + "loading_filmography": "Loading filmography...", + "load_more_remaining": "Load More ({{count}} remaining)", + "alert_error_title": "Error", + "alert_error_message": "Unable to load \"{{title}}\". Please try again later.", + "alert_ok": "OK", + "no_upcoming": "No upcoming releases available for this actor", + "no_content": "No content available for this actor", + "no_movies": "No movies available for this actor", + "no_tv": "No TV shows available for this actor" + }, + "comments": { + "title": "Trakt Comments", + "spoiler_warning": "⚠️ This comment contains spoilers. Tap to reveal.", + "spoiler": "Spoiler", + "contains_spoilers": "Contains spoilers", + "reveal": "Reveal", + "vip": "VIP", + "unavailable": "Comments unavailable", + "no_comments": "No comments on Trakt yet", + "not_in_database": "This content may not be in Trakt's database yet", + "check_trakt": "Check Trakt" + }, + "trailers": { + "title": "Trailers", + "official_trailers": "Official Trailers", + "official_trailer": "Official Trailer", + "teasers": "Teasers", + "teaser": "Teaser", + "clips_scenes": "Clips & Scenes", + "clip": "Clip", + "featurettes": "Featurettes", + "featurette": "Featurette", + "behind_the_scenes": "Behind the Scenes", + "no_trailers": "No trailers available", + "unavailable": "Trailer Unavailable", + "unavailable_desc": "This trailer could not be loaded at this time. Please try again later.", + "unable_to_play": "Unable to play trailer. Please try again.", + "watch_on_youtube": "Watch on YouTube" + }, + "catalog": { + "no_content_found": "No content found", + "no_content_filters": "No content found for the selected filters", + "loading_content": "Loading content...", + "back": "Back", + "in_theaters": "In Theaters", + "all": "All", + "failed_tmdb": "Failed to load content from TMDB", + "movies": "Movies", + "tv_shows": "TV Shows", + "channels": "Channels" + }, + "streams": { + "back_to_episodes": "Back to Episodes", + "back_to_info": "Back to Info", + "fetching_from": "Fetching from:", + "no_sources_available": "No streaming sources available", + "add_sources_desc": "Please add streaming sources in settings", + "add_sources": "Add Sources", + "finding_streams": "Finding available streams...", + "finding_best_stream": "Finding best stream for autoplay...", + "still_fetching": "Still fetching streams…", + "no_streams_available": "No streams available", + "starting_best_stream": "Starting best stream...", + "loading_more_sources": "Loading more sources..." + }, + "player_ui": { + "via": "via {{name}}", + "audio_tracks": "Audio Tracks", + "no_audio_tracks": "No audio tracks available", + "playback_speed": "Playback Speed", + "on_hold": "On Hold", + "playback_error": "Playback Error", + "unknown_error": "An unknown error occurred during playback.", + "copy_error": "Copy error details", + "copied_to_clipboard": "Copied to clipboard", + "dismiss": "Dismiss", + "continue_watching": "Continue Watching", + "start_over": "Start Over", + "resume": "Resume", + "change_source": "Change Source", + "switching_source": "Switching source...", + "no_sources_found": "No sources found", + "sources": "Sources", + "finding_sources": "Finding sources...", + "unknown_source": "Unknown Source", + "sources_limited": "Sources might be limited due to provider errors.", + "episodes": "Episodes", + "specials": "Specials", + "season": "Season {{season}}", + "stream": "Stream {{number}}", + "subtitles": "Subtitles", + "built_in": "Built-in", + "addons": "Addons", + "style": "Style", + "none": "None", + "search_online_subtitles": "Search Online Subtitles", + "preview": "Preview", + "quick_presets": "Quick Presets", + "default": "Default", + "yellow": "Yellow", + "high_contrast": "High Contrast", + "large": "Large", + "core": "Core", + "font_size": "Font Size", + "show_background": "Show Background", + "advanced": "Advanced", + "position": "Position", + "text_color": "Text Color", + "align": "Align", + "bottom_offset": "Bottom Offset", + "background_opacity": "Background Opacity", + "text_shadow": "Text Shadow", + "on": "On", + "off": "Off", + "outline_color": "Outline Color", + "outline": "Outline", + "outline_width": "Outline Width", + "letter_spacing": "Letter Spacing", + "line_height": "Line Height", + "timing_offset": "Timing Offset (s)", + "visual_sync": "Visual Sync", + "timing_hint": "Nudge subtitles earlier (-) or later (+) to sync if needed.", + "reset_defaults": "Reset to defaults", + "mark_intro_start": "Mark Intro Start", + "mark_intro_end": "Mark Intro End", + "intro_start_marked": "Intro start marked", + "intro_submitted": "Intro submitted successfully", + "intro_submit_failed": "Failed to submit intro" + }, + "downloads": { + "title": "Downloads", + "no_downloads": "No Downloads Yet", + "no_downloads_desc": "Downloaded content will appear here for offline viewing", + "explore": "Explore Content", + "path_copied": "Path Copied", + "path_copied_desc": "Local file path copied to clipboard", + "copied": "Copied", + "incomplete": "Download Incomplete", + "incomplete_desc": "Download is not complete yet", + "not_available": "Not Available", + "not_available_desc": "The local file path is available only after the download is complete.", + "status_downloading": "Downloading", + "status_completed": "Completed", + "status_paused": "Paused", + "status_error": "Error", + "status_queued": "Queued", + "status_unknown": "Unknown", + "provider": "Provider", + "streaming_playlist_warning": "May not play - streaming playlist", + "remaining": "remaining", + "not_ready": "Download not ready", + "not_ready_desc": "Please wait until the download completes.", + "filter_all": "All", + "filter_active": "Active", + "filter_done": "Done", + "filter_paused": "Paused", + "no_filter_results": "No {{filter}} downloads", + "try_different_filter": "Try selecting a different filter", + "limitations_title": "Download Limitations", + "limitations_msg": "• Files smaller than 1MB are typically M3U8 streaming playlists and cannot be downloaded for offline viewing. These only work with online streaming and contain links to video segments, not the actual video content.", + "remove_title": "Remove Download", + "remove_confirm": "Remove \"{{title}}\"{{season_episode}}?", + "cancel": "Cancel", + "remove": "Remove" + }, + "addons": { + "title": "Addons", + "reorder_mode": "Reorder Mode", + "reorder_info": "Addons at the top have higher priority when loading content", + "add_addon_placeholder": "Addon URL", + "add_button": "Add Addon", + "my_addons": "My Addons", + "community_addons": "Community Addons", + "no_addons": "No addons installed", + "uninstall_title": "Uninstall Addon", + "uninstall_message": "Are you sure you want to uninstall {{name}}?", + "uninstall_button": "Uninstall", + "install_success": "Addon installed successfully", + "install_error": "Failed to install addon", + "load_error": "Failed to load addons", + "fetch_error": "Failed to fetch addon details", + "invalid_url": "Please enter an addon URL", + "configure": "Configure", + "version": "Version: {{version}}", + "installed_addons": "INSTALLED ADDONS", + "reorder_drag_title": "DRAG ADDONS TO REORDER", + "install": "Install", + "config_unavailable_title": "Configuration Unavailable", + "config_unavailable_msg": "Could not determine configuration URL for this addon.", + "cannot_open_config_title": "Cannot Open Configuration", + "cannot_open_config_msg": "The configuration URL ({{url}}) cannot be opened. The addon may not have a configuration page.", + "description": "Description", + "supported_types": "Supported Types", + "catalogs": "Catalogs", + "no_description": "No description available", + "overview": "OVERVIEW", + "no_categories": "No categories", + "pre_installed": "PRE-INSTALLED" + }, + "trakt": { + "title": "Trakt Settings", + "settings_title": "Trakt Settings", + "connect_title": "Connect with Trakt", + "connect_desc": "Sync your watch history, watchlist, and collection with Trakt.tv", + "sign_in": "Sign In with Trakt", + "sign_out": "Sign Out", + "sign_out_confirm": "Are you sure you want to sign out of your Trakt account?", + "joined": "Joined {{date}}", + "sync_settings_title": "Sync Settings", + "sync_info": "When connected to Trakt, full history is synced directly from the API and is not written to local storage. Your Continue Watching list reflects your global Trakt progress.", + "auto_sync_label": "Auto-sync playback progress", + "auto_sync_desc": "Automatically sync watch progress to Trakt", + "import_history_label": "Import watched history", + "import_history_desc": "Use \"Sync Now\" to import your watch history and progress from Trakt", + "sync_now_button": "Sync Now", + "display_settings_title": "Display Settings", + "show_comments_label": "Show Trakt Comments", + "show_comments_desc": "Display Trakt comments in metadata screens when available", + "maintenance_title": "Under Maintenance", + "maintenance_unavailable": "Trakt Unavailable", + "maintenance_desc": "The Trakt integration is temporarily paused for maintenance. All syncing and authentication is disabled until maintenance is complete.", + "maintenance_button": "Service Under Maintenance", + "auth_success_title": "Successfully Connected", + "auth_success_msg": "Your Trakt account has been connected successfully.", + "auth_error_title": "Authentication Error", + "auth_error_msg": "Failed to complete authentication with Trakt.", + "auth_error_generic": "An error occurred during authentication.", + "sign_out_error": "Failed to sign out of Trakt.", + "sync_complete_title": "Sync Complete", + "sync_success_msg": "Successfully synced your watch progress with Trakt.", + "sync_error_msg": "Sync failed. Please try again.", + "disclaimer": "This product uses the Trakt API but is not endorsed or certified by Trakt." + }, + "simkl": { + "title": "Simkl Settings", + "settings_title": "Simkl Settings", + "connect_title": "Connect with Simkl", + "connect_desc": "Sync your watch history and track what you're watching", + "sign_in": "Sign In with Simkl", + "sign_out": "Disconnect", + "sign_out_confirm": "Are you sure you want to disconnect from Simkl?", + "syncing_desc": "Your watched items are syncing with Simkl.", + "auth_success_title": "Successfully Connected", + "auth_success_msg": "Your Simkl account has been connected successfully.", + "auth_error_title": "Authentication Error", + "auth_error_msg": "Failed to complete authentication with Simkl.", + "auth_error_generic": "An error occurred during authentication.", + "sign_out_error": "Failed to disconnect from Simkl.", + "config_error_title": "Configuration Error", + "config_error_msg": "Simkl Client ID is missing in environment variables.", + "conflict_title": "Conflict", + "conflict_msg": "You cannot connect to Simkl while Trakt is connected. Please disconnect Trakt first.", + "disclaimer": "Nuvio is not affiliated with Simkl." + }, + "tmdb_settings": { + "title": "TMDb Settings", + "metadata_enrichment": "Metadata Enrichment", + "metadata_enrichment_desc": "Enhance your content metadata with TMDb data for better details and information.", + "enable_enrichment": "Enable Enrichment", + "enable_enrichment_desc": "Augments addon metadata with TMDb for cast, certification, logos/posters, and production info.", + "localized_text": "Localized Text", + "localized_text_desc": "Fetch titles and descriptions in your preferred language from TMDb.", + "language": "Language", + "change": "Change", + "logo_preview": "Logo Preview", + "logo_preview_desc": "Preview shows how localized logos will appear in the selected language.", + "example": "Example:", + "no_logo": "No logo available", + "enrichment_options": "Enrichment Options", + "enrichment_options_desc": "Control which data is fetched from TMDb. Disabled options will use addon data if available.", + "cast_crew": "Cast & Crew", + "cast_crew_desc": "Actors, directors, writers with profile photos", + "title_description": "Title & Description", + "title_description_desc": "Use TMDb localized title and overview text", + "title_logos": "Title Logos", + "title_logos_desc": "High-quality title treatment images", + "banners_backdrops": "Banners & Backdrops", + "banners_backdrops_desc": "High-resolution backdrop images", + "certification": "Content Certification", + "certification_desc": "Age ratings (PG-13, R, TV-MA, etc.)", + "recommendations": "Recommendations", + "recommendations_desc": "Similar content suggestions", + "episode_data": "Episode Data", + "episode_data_desc": "Episode thumbnails, info & fallbacks for TV shows", + "season_posters": "Season Posters", + "season_posters_desc": "Season-specific poster images", + "production_info": "Production Info", + "production_info_desc": "Networks & production companies with logos", + "movie_details": "Movie Details", + "movie_details_desc": "Budget, revenue, runtime, tagline", + "tv_details": "TV Show Details", + "tv_details_desc": "Status, seasons count, networks, creators", + "movie_collections": "Movie Collections", + "movie_collections_desc": "Franchise movies (Marvel, Star Wars, etc.)", + "api_configuration": "API Configuration", + "api_configuration_desc": "Configure your TMDb API access for enhanced functionality.", + "custom_api_key": "Custom API Key", + "custom_api_key_desc": "Use your own TMDb API key for better performance and dedicated rate limits.", + "custom_key_active": "Custom API key active", + "api_key_required": "API key required", + "api_key_placeholder": "Paste your TMDb API key (v3)", + "how_to_get_key": "How to get a TMDb API key?", + "built_in_key_msg": "Currently using built-in API key. Consider using your own key for better performance.", + "cache_size": "Cache Size", + "clear_cache": "Clear Cache", + "cache_days": "TMDB responses are cached for 7 days to improve performance", + "choose_language": "Choose Language", + "choose_language_desc": "Select your preferred language for TMDb content", + "search_language_placeholder": "Search languages...", + "popular": "Popular", + "all_languages": "All Languages", + "search_results": "Search Results", + "no_languages_found": "No languages found for ", + "clear_search": "Clear Search", + "clear_cache_title": "Clear TMDB Cache", + "clear_cache_msg": "This will clear all cached TMDB data ({{size}}). This may temporarily slow down loading until cache rebuilds.", + "clear_cache_success": "TMDB cache cleared successfully.", + "clear_cache_error": "Failed to clear cache.", + "clear_api_key_title": "Clear API Key", + "clear_api_key_msg": "Are you sure you want to remove your custom API key and revert to the default?", + "clear_api_key_success": "API key cleared successfully", + "clear_api_key_error": "Failed to clear API key", + "empty_api_key": "API Key cannot be empty.", + "invalid_api_key": "Invalid API key. Please check and try again.", + "save_error": "An error occurred while saving. Please try again.", + "using_builtin_key": "Now using the built-in TMDb API key.", + "using_custom_key": "Now using your custom TMDb API key.", + "enter_custom_key": "Please enter and save your custom TMDb API key.", + "key_verified": "API key verified and saved successfully.", + "disclaimer_part_one": "This product uses the TMDB API but is not", + "disclaimer_part_two": "endorsed or certified by TMDB.", + "done": "Done" + }, + "settings": { + "language": "Language", + "select_language": "Select Language", + "english": "English", + "portuguese": "Portuguese", + "portuguese_br": "Portuguese (Brazil)", + "portuguese_pt": "Portuguese (Portugal)", + "german": "German", + "arabic": "Arabic", + "spanish": "Spanish", + "french": "French", + "italian": "Italian", + "croatian": "Croatian", + "chinese": "Chinese (Simplified)", + "hindi": "Hindi", + "serbian": "Serbian", + "hebrew": "Hebrew", + "bulgarian": "Bulgarian", + "polish": "Polish", + "czech": "Czech", + "turkish": "Turkish", + "slovenian": "Slovenian", + "macedonian": "Macedonian", + "russian": "Russian", + "filipino": "Filipino", + "dutch_nl": "Dutch (Netherlands)", + "romanian": "Romanian", + "albanian": "Albanian", + "catalan": "Catalan", + "account": "Account", + "content_discovery": "Content & Discovery", + "appearance": "Appearance", + "integrations": "Integrations", + "playback": "Playback", + "backup_restore": "Backup & Restore", + "backup_restore_desc": "Create and restore app backups", + "updates": "Updates", + "about": "About", + "developer": "Developer", + "cache": "Cache", + "title": "Settings", + "settings_title": "Settings", + "sign_in_sync": "Sign in to sync", + "add_catalogs_sources": "Addons, catalogs, and sources", + "player_trailers_downloads": "Player, trailers, downloads", + "mdblist_tmdb_ai": "MDBList, TMDB, AI", + "check_updates": "Check for updates", + "clear_mdblist_cache": "Clear MDBList Cache", + "cache_management": "CACHE MANAGEMENT", + "downloads_counter": "downloads and counting", + "made_with_love": "Made with ❤️ by Tapframe and friends", + "sections": { + "information": "INFORMATION", + "account": "ACCOUNT", + "theme": "THEME", + "layout": "LAYOUT", + "sources": "SOURCES", + "catalogs": "CATALOGS", + "discovery": "DISCOVERY", + "metadata": "METADATA", + "ai_assistant": "AI ASSISTANT", + "video_player": "VIDEO PLAYER", + "audio_subtitles": "AUDIO & SUBTITLES", + "media": "MEDIA", + "notifications": "NOTIFICATIONS", + "testing": "TESTING", + "danger_zone": "DANGER ZONE" + }, + "items": { + "legal": "Legal & Disclaimer", + "privacy_policy": "Privacy Policy", + "report_issue": "Report Issue", + "version": "Version", + "contributors": "Contributors", + "view_contributors": "View all contributors", + "theme": "Theme", + "episode_layout": "Episode Layout", + "streams_backdrop": "Streams Backdrop", + "streams_backdrop_desc": "Show blurred backdrop on mobile streams", + "addons": "Addons", + "installed": "installed", + "debrid_integration": "Debrid Integration", + "debrid_desc": "Connect Torbox", + "plugins": "Plugins", + "plugins_desc": "Manage plugins and repositories", + "catalogs": "Catalogs", + "active": "active", + "home_screen": "Home Screen", + "home_screen_desc": "Layout and content", + "continue_watching": "Continue Watching", + "continue_watching_desc": "Cache and playback behavior", + "show_discover": "Show Discover Section", + "show_discover_desc": "Display discover content in Search", + "mdblist": "MDBList", + "mdblist_connected": "Connected", + "mdblist_desc": "Enable to add ratings & reviews", + "simkl": "Simkl", + "simkl_connected": "Connected", + "simkl_desc": "Track what you watch", + "tmdb": "TMDB", + "tmdb_desc": "Metadata & logo source provider", + "openrouter": "OpenRouter API", + "openrouter_connected": "Connected", + "openrouter_desc": "Add your API key to enable AI chat", + "video_player": "Video Player", + "built_in": "Built-in", + "external": "External", + "preferred_audio": "Preferred Audio Language", + "preferred_subtitle": "Preferred Subtitle Language", + "subtitle_source": "Subtitle Source Priority", + "auto_select_subs": "Auto-Select Subtitles", + "auto_select_subs_desc": "Automatically select subtitles matching your preferences", + "show_trailers": "Show Trailers", + "show_trailers_desc": "Display trailers in hero section", + "enable_downloads": "Enable Downloads", + "enable_downloads_desc": "Show Downloads tab and enable saving streams", + "notifications": "Notifications", + "notifications_desc": "Episode reminders", + "developer_tools": "Developer Tools", + "developer_tools_desc": "Testing and debug options", + "test_onboarding": "Test Onboarding", + "reset_onboarding": "Reset Onboarding", + "test_announcement": "Test Announcement", + "test_announcement_desc": "Show what's new overlay", + "reset_campaigns": "Reset Campaigns", + "reset_campaigns_desc": "Clear campaign impressions", + "clear_all_data": "Clear All Data", + "clear_all_data_desc": "Reset all settings and cached data" + }, + "options": { + "horizontal": "Horizontal", + "vertical": "Vertical", + "internal_first": "Internal First", + "internal_first_desc": "Prefer embedded subtitles, then external", + "external_first": "External First", + "external_first_desc": "Prefer addon subtitles, then embedded", + "any_available": "Any Available", + "any_available_desc": "Use first available subtitle track" + }, + "clear_data_desc": "This will reset all settings and clear all cached data. Are you sure?", + "app_updates": "App Updates", + "about_nuvio": "About Nuvio", + "cloud_sync": { + "title": "Nuvio Sync", + "description": "Sync data across your Nuvio devices", + "hero_title": "Cloud Sync", + "hero_subtitle": "Keep your addons, progress, and library aligned across all devices.", + "auth": { + "account": "Account", + "not_configured": "Supabase not configured", + "not_authenticated": "Not authenticated", + "email_session": "Email session", + "signed_in_as": "Signed in as {{email}}", + "not_signed_in": "Not signed in", + "effective_owner": "Effective owner: {{id}}" + }, + "stats": { + "title": "Database Statistics", + "plugins": "Plugins", + "addons": "Addons", + "watch_progress": "Watch Progress", + "library_items": "Library Items", + "watched_items": "Watched Items", + "signin_required": "Sign in to load remote data counts." + }, + "actions": { + "title": "Actions", + "description": "Pull to update this device from the cloud, or push from this device as the latest source.", + "pull_btn": "Pull from Cloud", + "push_btn": "Push from Device", + "manage_account": "Manage Account", + "sign_out": "Sign Out", + "sign_in_up": "Sign In / Up" + }, + "alerts": { + "pull_success_title": "Cloud Data Pulled", + "pull_success_msg": "The latest cloud data has been downloaded to this device.", + "pull_failed_title": "Pull Failed", + "pull_failed_msg": "Failed to download data from the cloud", + "push_success_title": "Push Completed", + "push_success_msg": "Device data has been uploaded to the cloud.", + "push_failed_title": "Push Failed", + "push_failed_msg": "Failed to upload local data", + "sign_out_failed": "Sign Out Failed", + "sign_out_failed_title": "Logout Error" + }, + "external_sync": { + "title": "External Sync Priority", + "active_msg": "{{services}} is active. Watch progress and library updates are managed by these services instead of Nuvio cloud database.", + "inactive_msg": "If Trakt or Simkl sync is enabled, watch progress and library updates will use those services instead of Nuvio cloud database." + }, + "pre_auth": { + "title": "Before Syncing", + "description": "Sign in to start cloud sync and keep your data consistent across devices.", + "point_1": "• Addons and plugins settings", + "point_2": "• Watch progress and library", + "env_warning": "Set EXPO_PUBLIC_SUPABASE_URL and EXPO_PUBLIC_SUPABASE_ANON_KEY to enable sync." + }, + "connection": "Connection" + } + }, + "privacy": { + "title": "Privacy & Data", + "settings_desc": "Control telemetry and data collection", + "info_title": "Your Privacy Matters", + "info_description": "Control what data is collected and shared. Analytics are off by default and crash reports are anonymous by default.", + "analytics_enabled_title": "Analytics Enabled", + "analytics_enabled_message": "Usage data will be collected to help improve the app. You can disable this at any time.", + "disable_error_reporting_title": "Disable Error Reporting?", + "disable_error_reporting_message": "Disabling error reporting means we won’t be notified of crashes or issues you experience. This may affect our ability to fix bugs.", + "enable_session_replay_title": "Enable Session Replay?", + "enable_session_replay_message": "Session replay records your screen when errors occur to help us understand what happened. This may capture visible content on your screen.", + "enable_pii_title": "Enable PII Collection?", + "enable_pii_message": "This allows collection of personally identifiable information like IP address and device details. This data helps diagnose issues but increases privacy exposure.", + "disable_all_title": "Disable All Telemetry?", + "disable_all_message": "This will disable all analytics, error reporting, and session replay. We won’t receive any data about app usage or crashes.", + "disable_all_button": "Disable All", + "all_disabled_title": "All Telemetry Disabled", + "all_disabled_message": "All data collection has been disabled. Changes take effect on next app restart.", + "reset_title": "Reset to Recommended", + "reset_message": "Privacy settings have been reset to recommended defaults (error reporting enabled, analytics disabled).", + "section_analytics": "ANALYTICS", + "analytics_title": "Usage Analytics", + "analytics_description": "Collect anonymous usage patterns and screen views", + "section_error_reporting": "ERROR REPORTING", + "error_reporting_title": "Crash Reports", + "error_reporting_description": "Send anonymous crash reports to improve stability", + "session_replay_title": "Session Replay", + "session_replay_description": "Record screen when errors occur", + "pii_title": "Include Device Info", + "pii_description": "Send IP address and device details with reports", + "section_quick_actions": "QUICK ACTIONS", + "disable_all": "Disable All Telemetry", + "disable_all_desc": "Turn off all data collection", + "reset_recommended": "Reset to Recommended", + "reset_recommended_desc": "Privacy-first defaults with error reporting", + "section_learn_more": "LEARN MORE", + "privacy_policy": "Privacy Policy", + "current_settings": "Current Settings Summary", + "summary_analytics": "Analytics", + "summary_errors": "Error Reports", + "summary_replay": "Session Replay", + "summary_pii": "Device Info", + "restart_note_detailed": "* Analytics and error reporting changes take effect immediately. Session replay and PII settings require app restart." + }, + "ai_settings": { + "title": "AI Assistant", + "info_title": "AI-Powered Chat", + "info_desc": "Ask questions about any movie or TV show episode using advanced AI. Get insights about plot, characters, themes, trivia, and more - all powered by comprehensive TMDB data.", + "feature_1": "Episode-specific context and analysis", + "feature_2": "Plot explanations and character insights", + "feature_3": "Behind-the-scenes trivia and facts", + "feature_4": "Your own free OpenRouter API key", + "api_key_section": "OPENROUTER API KEY", + "api_key_label": "API Key", + "api_key_desc": "Enter your OpenRouter API key to enable AI chat features", + "save_api_key": "Save API Key", + "saving": "Saving...", + "update": "Update", + "remove": "Remove", + "get_free_key": "Get Free API Key from OpenRouter", + "enable_chat": "Enable AI Chat", + "enable_chat_desc": "When enabled, the Ask AI button will appear on content pages.", + "chat_enabled": "AI Chat Enabled", + "chat_enabled_desc": "You can now ask questions about movies and TV shows. Look for the \"Ask AI\" button on content pages!", + "how_it_works": "How it works", + "how_it_works_desc": "• OpenRouter provides access to multiple AI models\n• Your API key stays private and secure\n• Free tier includes generous usage limits\n• Chat with context about specific episodes/movies\n• Get detailed analysis and explanations", + "error_invalid_key": "Please enter a valid API key", + "error_key_format": "OpenRouter API keys should start with \"sk-or-\"", + "success_saved": "OpenRouter API key saved successfully!", + "error_save": "Failed to save API key", + "confirm_remove_title": "Remove API Key", + "confirm_remove_msg": "Are you sure you want to remove your OpenRouter API key? This will disable AI chat features.", + "success_removed": "API key removed successfully", + "error_remove": "Failed to remove API key" + }, + "catalog_settings": { + "title": "Catalogs", + "layout_phone": "LAYOUT CATALOGSCREEN (PHONE)", + "posters_per_row": "Posters per row", + "auto": "Auto", + "show_titles": "Show Poster Titles", + "show_titles_desc": "Display title text below each poster", + "phone_only_hint": "Applies to phones only. Tablets keep adaptive layout.", + "catalogs_group": "Catalogs", + "enabled_count": "{{enabled}} of {{total}} enabled", + "rename_hint": "Long-press a catalog to rename", + "rename_modal_title": "Rename Catalog", + "rename_placeholder": "Enter new catalog name", + "error_save_name": "Could not save the custom name." + }, + "continue_watching_settings": { + "title": "Continue Watching", + "playback_behavior": "PLAYBACK BEHAVIOR", + "use_cached": "Use Cached Streams", + "use_cached_desc": "When enabled, clicking Continue Watching items will open the player directly using previously played streams. When disabled, opens a content screen instead.", + "open_metadata": "Open Metadata Screen", + "open_metadata_desc": "When cached streams are disabled, open the Metadata screen instead of the Streams screen. This shows content details and allows manual stream selection.", + "card_appearance": "CARD APPEARANCE", + "card_style": "Card Style", + "card_style_desc": "Choose how Continue Watching items appear on the home screen", + "wide": "Wide", + "poster": "Poster", + "cache_settings": "CACHE SETTINGS", + "cache_duration": "Stream Cache Duration", + "cache_duration_desc": "How long to keep cached stream links before they expire", + "important_note": "Important Note", + "important_note_text": "Not all stream links may remain active for the full cache duration. Longer cache times may result in expired links. If a cached link fails, the app will fall back to fetching fresh streams.", + "how_it_works": "How it works", + "how_it_works_cached": "• Streams are cached for your selected duration after playing\n• Cached streams are validated before use\n• If cache is invalid or expired, falls back to content screen\n• \"Use Cached Streams\" controls direct player vs screen navigation\n• \"Open Metadata Screen\" appears only when cached streams are disabled", + "how_it_works_uncached": "• When cached streams are disabled, clicking Continue Watching items opens content screens\n• \"Open Metadata Screen\" option controls which screen to open\n• Metadata screen shows content details and allows manual stream selection\n• Streams screen shows available streams for immediate playback", + "changes_saved": "Changes saved", + "min": "min", + "hour": "hour", + "hours": "hours" + }, + "contributors": { + "title": "Contributors", + "special_mentions": "Special Mentions", + "tab_contributors": "Contributors", + "tab_special": "Special Mentions", + "tab_donors": "Donors", + "manager_role": "Community Manager", + "manager_desc": "Manages the Discord & Reddit communities for Nuvio", + "sponsor_role": "Server Sponsor", + "sponsor_desc": "Sponsored the server infrastructure for Nuvio", + "mod_role": "Discord Mod", + "mod_desc": "Helps moderate the Nuvio Discord community", + "loading": "Loading...", + "discord_user": "Discord User", + "contributions": "contributions", + "gratitude_title": "We're grateful for every contribution", + "gratitude_desc": "Each line of code, bug report, and suggestion helps make Nuvio better for everyone", + "special_thanks_title": "Special Thanks", + "special_thanks_desc": "These amazing people help keep the Nuvio community running and the servers online", + "donors_desc": "Thank you for believing in what we're building. Your support keeps Nuvio free and constantly improving.", + "latest_donations": "Latest", + "leaderboard": "Leaderboard", + "loading_donors": "Loading donors…", + "no_donors": "No donors yet", + "error_rate_limit": "GitHub API rate limit exceeded. Please try again later or pull to refresh.", + "error_failed": "Failed to load contributors. Please check your internet connection.", + "retry": "Try Again", + "no_contributors": "No contributors found", + "loading_contributors": "Loading contributors..." + }, + "debrid": { + "title": "Debrid Integration", + "description_torbox": "Connect Torbox to use your account-based source preferences. Enter your API key below to configure the integration.", + "description_torrentio": "Configure Torrentio as an external source integration. A compatible debrid account may be required depending on your setup.", + "tab_torbox": "TorBox", + "tab_torrentio": "Torrentio", + "status_connected": "Connected", + "status_disconnected": "Disconnected", + "enable_addon": "Enable Addon", + "disconnect_button": "Disconnect & Remove", + "disconnect_loading": "Disconnecting...", + "account_info": "Account Information", + "plan": "Plan", + "plan_free": "Free", + "plan_essential": "Essential ($3/mo)", + "plan_pro": "Pro ($10/mo)", + "plan_standard": "Standard ($5/mo)", + "plan_unknown": "Unknown", + "expires": "Expires", + "downloaded": "Downloaded", + "status_active": "Active", + "connected_title": "✓ Connected to TorBox", + "connected_desc": "Your TorBox addon is active and providing premium streams.", + "configure_title": "Configure Addon", + "configure_desc": "Customize your streaming experience. Sort by quality, filter file sizes, and manage other integration settings.", + "open_settings": "Open Settings", + "what_is_debrid": "What is a Debrid Service?", + "enter_api_key": "Enter your API Key", + "connect_button": "Connect & Install", + "connecting": "Connecting...", + "unlock_speeds_title": "Optional Torbox Subscription", + "unlock_speeds_desc": "Torbox offers account tiers with enhanced performance and availability features.", + "get_subscription": "Get Subscription", + "powered_by": "Powered by", + "disclaimer_torbox": "Nuvio is not affiliated with Torbox in any way.", + "disclaimer_torrentio": "Nuvio is not affiliated with Torrentio in any way.", + "installed_badge": "✓ INSTALLED", + "promo_title": "⚡ Need a Debrid Service?", + "promo_desc": "Use TorBox if you want account-managed performance features for supported integrations.", + "promo_button": "Get TorBox Subscription", + "service_label": "Debrid Service *", + "api_key_label": "API Key *", + "sorting_label": "Sorting", + "exclude_qualities": "Exclude Qualities", + "priority_languages": "Priority Languages", + "max_results": "Max Results", + "additional_options": "Additional Options", + "no_download_links": "Don't show download links", + "no_debrid_catalog": "Don't show debrid catalog", + "install_button": "Install Torrentio", + "installing": "Installing...", + "update_button": "Update Configuration", + "updating": "Updating...", + "remove_button": "Remove Torrentio", + "error_api_required": "API Key Required", + "error_api_required_desc": "Please enter your debrid service API key to install Torrentio.", + "success_installed": "Torrentio addon installed successfully!", + "success_removed": "Torrentio addon removed successfully", + "alert_disconnect_title": "Disconnect Torbox", + "alert_disconnect_msg": "Are you sure you want to disconnect Torbox? This will remove the addon and clear your saved API key." + }, + "home_screen": { + "title": "Home Screen Settings", + "changes_applied": "Changes Applied", + "display_options": "DISPLAY OPTIONS", + "show_hero": "Show Hero Section", + "show_hero_desc": "Featured content at the top", + "show_this_week": "Show This Week Section", + "show_this_week_desc": "New episodes from current week", + "select_catalogs": "Select Catalogs", + "all_catalogs": "All catalogs", + "selected": "selected", + "prefer_external_meta": "Prefer External Meta Addon", + "prefer_external_meta_desc": "Use external metadata on detail page", + "hero_layout": "Hero Layout", + "layout_legacy": "Legacy", + "layout_carousel": "Carousel", + "layout_appletv": "Apple TV", + "layout_desc": "Full-width banner, swipeable cards, or Apple TV style", + "featured_source": "Featured Source", + "using_catalogs": "Using Catalogs", + "manage_selected_catalogs": "Manage selected catalogs", + "dynamic_bg": "Dynamic Hero Background", + "dynamic_bg_desc": "Blurred banner behind carousel", + "performance_note": "May impact performance on low-end devices.", + "posters": "Posters", + "show_titles": "Show Titles", + "poster_size": "Poster Size", + "poster_corners": "Poster Corners", + "size_small": "Small", + "size_medium": "Medium", + "size_large": "Large", + "corners_square": "Square", + "corners_rounded": "Rounded", + "corners_pill": "Pill", + "about_these_settings": "ABOUT THESE SETTINGS", + "about_desc": "These settings control how content is displayed on your Home screen. Changes are applied immediately without requiring an app restart.", + "hero_catalogs": { + "title": "Hero Section Catalogs", + "select_all": "Select All", + "clear_all": "Clear All", + "info": "Select which catalogs to display in the hero section. If none are selected, all catalogs will be used. Don't forget to press Save when you're done.", + "settings_saved": "Settings Saved", + "error_load": "Failed to load catalogs", + "movies": "Movies", + "tv_shows": "TV Shows" + } + }, + "calendar": { + "title": "Calendar", + "loading": "Loading calendar...", + "no_scheduled_episodes": "No scheduled episodes", + "check_back_later": "Check back later", + "showing_episodes_for": "Showing episodes for {{date}}", + "show_all_episodes": "Show All Episodes", + "no_episodes_for": "No episodes for {{date}}", + "no_upcoming_found": "No upcoming episodes found", + "add_series_desc": "Add series to your library to see their upcoming episodes here" + }, + "mdblist": { + "title": "Rating Sources", + "status_disabled": "MDBList Disabled", + "status_active": "API Key Active", + "status_required": "API Key Required", + "status_disabled_desc": "MDBList functionality is currently disabled.", + "status_active_desc": "Ratings from MDBList are enabled.", + "status_required_desc": "Add your key below to enable ratings.", + "enable_toggle": "Enable MDBList", + "enable_toggle_desc": "Turn on/off all MDBList functionality", + "api_section": "API Key", + "placeholder": "Paste your MDBList API key", + "save": "Save", + "clear": "Clear Key", + "rating_providers": "Rating Providers", + "rating_providers_desc": "Choose which ratings to display in the app", + "how_to": "How to get an API key", + "step_1": "Log in on the", + "step_1_link": "MDBList website", + "step_2": "Go to", + "step_2_settings": "Settings", + "step_2_api": "API", + "step_2_end": "section.", + "step_3": "Generate a new key and copy it.", + "go_to_website": "Go to MDBList", + "alert_clear_title": "Clear API Key", + "alert_clear_msg": "Are you sure you want to remove the saved API key?", + "success_saved": "API key saved successfully.", + "error_empty": "API Key cannot be empty.", + "error_save": "An error occurred while saving. Please try again.", + "api_key_empty_error": "API Key cannot be empty.", + "success_cleared": "API key cleared successfully", + "error_clear": "Failed to clear API key" + }, + "notification": { + "title": "Notification Settings", + "section_general": "General", + "enable_notifications": "Enable Notifications", + "section_types": "Notification Types", + "new_episodes": "New Episodes", + "upcoming_shows": "Upcoming Shows", + "reminders": "Reminders", + "section_timing": "Notification Timing", + "timing_desc": "When should you be notified before an episode airs?", + "hours_1": "1 hour", + "hours_suffix": "hours", + "section_status": "Notification Status", + "stats_upcoming": "Upcoming", + "stats_this_week": "This Week", + "stats_total": "Total", + "sync_button": "Sync Library & Trakt", + "syncing": "Syncing...", + "sync_desc": "Automatically syncs notifications for all shows in your library and Trakt watchlist/collection.", + "section_advanced": "Advanced", + "reset_button": "Reset All Notifications", + "test_button": "Test Notification (5 sec)", + "test_notification_in": "Notification in {{seconds}}s...", + "test_notification_text": "Notification will appear in {{seconds}} seconds", + "alert_reset_title": "Reset Notifications", + "alert_reset_msg": "This will cancel all scheduled notifications, but will not remove anything from your saved library. Are you sure?", + "alert_reset_success": "All notifications have been reset", + "alert_sync_complete": "Sync Complete", + "alert_sync_msg": "Successfully synced notifications for your library and Trakt items.\n\nScheduled: {{upcoming}} upcoming episodes\nThis week: {{thisWeek}} episodes", + "alert_test_scheduled": "Test notification scheduled to fire instantly" + }, + "backup": { + "title": "Backup & Restore", + "options_title": "Backup Options", + "options_desc": "Choose what to include in your backups", + "section_core": "Core Data", + "section_addons": "Addons & Integrations", + "section_settings": "Settings & Preferences", + "library_label": "Library", + "library_desc": "Your saved movies and TV shows", + "watch_progress_label": "Watch Progress", + "watch_progress_desc": "Continue watching positions", + "addons_label": "Addons", + "addons_desc": "Installed Stremio addons", + "plugins_label": "Plugins", + "plugins_desc": "Custom scraper configurations", + "trakt_label": "Trakt Integration", + "trakt_desc": "Sync data and authentication tokens", + "app_settings_label": "App Settings", + "app_settings_desc": "Theme, preferences, and configurations", + "user_prefs_label": "User Preferences", + "user_prefs_desc": "Addon order and UI settings", + "catalog_settings_label": "Catalog Settings", + "catalog_settings_desc": "Catalog filters and preferences", + "api_keys_label": "API Keys", + "api_keys_desc": "MDBList and OpenRouter keys", + "action_create": "Create Backup", + "action_restore": "Restore from Backup", + "section_info": "About Backups", + "info_text": "• Customize what gets backed up using the toggles above\n• Backup files are stored locally on your device\n• Share your backup to transfer data between devices\n• Restoring will overwrite your current data", + "alert_create_title": "Create Backup", + "alert_no_content": "No content selected for backup.\n\nPlease enable at least one option in the Backup Options section above.", + "alert_backup_created_title": "Backup Created", + "alert_backup_created_msg": "Your backup has been created and is ready to share.", + "alert_backup_failed_title": "Backup Failed", + "alert_restore_confirm_title": "Confirm Restore", + "alert_restore_confirm_msg": "This will restore your data from a backup created on {{date}}.\n\nThis action will overwrite your current data. Are you sure you want to continue?", + "alert_restore_complete_title": "Restore Complete", + "alert_restore_complete_msg": "Your data has been successfully restored. Please restart the app to see all changes.", + "alert_restore_failed_title": "Restore Failed", + "restart_app": "Restart App", + "alert_restart_failed_title": "Restart Failed", + "alert_restart_failed_msg": "Failed to restart the app. Please manually close and reopen the app to see your restored data." + }, + "updates": { + "title": "App Updates", + "status_checking": "Checking for updates...", + "status_available": "Update available!", + "status_downloading": "Downloading update...", + "status_installing": "Installing update...", + "status_success": "Update installed successfully!", + "status_error": "Update failed", + "status_ready": "Ready to check for updates", + "action_check": "Check for Updates", + "action_install": "Install Update", + "release_notes": "Release notes:", + "version": "Version:", + "last_checked": "Last checked:", + "current_version": "Current version:", + "current_release_notes": "Current release notes:", + "github_release": "GITHUB RELEASE", + "current": "Current:", + "latest": "Latest:", + "notes": "Notes:", + "view_release": "View Release", + "notification_settings": "NOTIFICATION SETTINGS", + "ota_alerts_label": "OTA Update Alerts", + "ota_alerts_desc": "Show notifications for over-the-air updates", + "major_alerts_label": "Major Update Alerts", + "major_alerts_desc": "Show notifications for new app versions on GitHub", + "alert_disable_ota_title": "Disable OTA Update Alerts?", + "alert_disable_ota_msg": "You will no longer receive automatic notifications for OTA updates.\n\n⚠️ Warning: Staying on the latest version is important for:\n• Bug fixes and stability improvements\n• New features and enhancements\n• Providing accurate feedback and crash reports\n\nYou can still manually check for updates in this screen.", + "alert_disable_major_title": "Disable Major Update Alerts?", + "alert_disable_major_msg": "You will no longer receive notifications for major app updates that require reinstallation.\n\n⚠️ Warning: Major updates often include:\n• Critical security patches\n• Breaking changes that require app reinstall\n• Important compatibility fixes\n\nYou can still check for updates manually.", + "warning_note": "Keeping alerts enabled ensures you receive bug fixes and can provide accurate crash reports.", + "disable": "Disable", + "alert_no_update_to_install": "No update available to install", + "alert_install_failed": "Failed to install update", + "alert_no_update_title": "No Update", + "alert_update_applied_msg": "Update will be applied on next app restart" + }, + "player": { + "title": "Video Player", + "section_selection": "PLAYER SELECTION", + "internal_title": "Built-in Player", + "internal_desc": "Use the app's default video player", + "vlc_title": "VLC", + "vlc_desc": "Open streams in VLC media player", + "infuse_title": "Infuse", + "infuse_desc": "Open streams in Infuse player", + "outplayer_title": "OutPlayer", + "outplayer_desc": "Open streams in OutPlayer", + "vidhub_title": "VidHub", + "vidhub_desc": "Open streams in VidHub player", + "infuse_live_title": "Infuse Livecontainer", + "infuse_live_desc": "Open streams in Infuse player LiveContainer", + "external_title": "External Player", + "external_desc": "Open streams in your preferred video player", + "section_playback": "PLAYBACK OPTIONS", + "skip_intro_settings_title": "Skip Intro", + "powered_by_introdb": "Powered by IntroDB", + "autoplay_title": "Auto-play First Stream", + "autoplay_desc": "Automatically start the first stream shown in the list.", + "resume_title": "Always Resume", + "resume_desc": "Skip the resume prompt and automatically continue where you left off (if less than 85% watched).", + "engine_title": "Video Player Engine", + "engine_desc": "Auto uses ExoPlayer with MPV fallback. Some formats like Dolby Vision and HDR may not be supported by MPV, so Auto is recommended for best compatibility.", + "decoder_title": "Decoder Mode", + "decoder_desc": "How video is decoded. Auto is recommended for best balance.", + "gpu_title": "GPU Rendering", + "gpu_desc": "GPU-Next offers better HDR and color management.", + "external_downloads_title": "External Player for Downloads", + "external_downloads_desc": "Play downloaded content in your preferred external player.", + "restart_required": "Restart Required", + "restart_msg_decoder": "Please restart the app for the decoder change to take effect.", + "restart_msg_gpu": "Please restart the app for the GPU mode change to take effect.", + "option_auto": "Auto", + "option_auto_desc_engine": "ExoPlayer + MPV fallback", + "option_mpv": "MPV", + "option_mpv_desc": "MPV only", + "option_auto_desc_decoder": "Best balance", + "option_sw": "SW", + "option_sw_desc": "Software", + "option_hw": "HW", + "option_hw_desc": "Hardware", + "option_hw_plus": "HW+", + "option_hw_plus_desc": "Full HW", + "option_gpu_desc": "Standard", + "option_gpu_next_desc": "Advanced" + }, + "plugins": { + "title": "Plugins", + "enable_title": "Enable Plugins", + "enable_desc": "Enable the plugin engine to resolve external media sources", + "repo_config_title": "Repository Configuration", + "repo_config_desc": "Manage external plugin repositories. Toggle each repository on or off below.", + "your_repos": "Repositories", + "your_repos_desc": "Configure external sources for plugins.", + "add_repo_button": "Add Repository", + "refresh": "Refresh", + "remove": "Remove", + "enabled": "Enabled", + "disabled": "Disabled", + "updating": "Updating...", + "success": "Success", + "error": "Error", + "alert_repo_added": "Repository added and plugins loaded successfully", + "alert_repo_saved": "Repository URL saved successfully", + "alert_repo_refreshed": "Repository refreshed successfully", + "alert_invalid_url": "Invalid URL Format", + "alert_plugins_cleared": "All plugins have been removed", + "alert_cache_cleared": "Repository cache cleared successfully", + "unknown": "Unknown", + "active": "Active", + "available": "Available", + "platform_disabled": "Platform Disabled", + "limited": "Limited", + "clear_all": "Clear All Plugins", + "clear_all_desc": "Are you sure you want to remove all installed plugins? This action cannot be undone.", + "clear_cache": "Clear Repository Cache", + "clear_cache_desc": "This will remove the saved repository URL and clear all cached plugin data. You will need to re-enter your repository URL.", + "add_new_repo": "Add New Repository", + "available_plugins": "Available Plugins ({{count}})", + "placeholder": "Search plugins...", + "all": "All", + "filter_all": "All Types", + "filter_movies": "Movies", + "filter_tv": "TV Shows", + "enable_all": "Enable All", + "disable_all": "Disable All", + "no_plugins_found": "No Plugins Found", + "no_plugins_available": "No Plugins Available", + "no_match_desc": "No plugins match \"{{query}}\". Try a different search term.", + "configure_repo_desc": "Configure a repository above to view available plugins.", + "clear_search": "Clear Search", + "no_external_player": "No external player", + "showbox_token": "ShowBox UI Token", + "showbox_placeholder": "Paste your ShowBox UI token", + "save": "Save", + "clear": "Clear", + "additional_settings": "Additional Settings", + "enable_url_validation": "Enable URL Validation", + "url_validation_desc": "Validate media URLs before returning them (may slow down results but improves reliability)", + "group_streams": "Group Plugin Sources", + "group_streams_desc": "When enabled, sources are grouped by repository. When disabled, each plugin shows as a separate provider.", + "sort_quality": "Sort by Quality First", + "sort_quality_desc": "When enabled, sources are sorted by quality first. Only available when grouping is enabled.", + "show_logos": "Show Plugin Logos", + "show_logos_desc": "Display plugin logos next to media links on the sources screen.", + "quality_filtering": "Quality Filtering", + "quality_filtering_desc": "Exclude specific video resolutions from search results. Tap on a quality to exclude it from plugin results.", + "excluded_qualities": "Excluded qualities:", + "language_filtering": "Language Filtering", + "language_filtering_desc": "Exclude specific languages from search results. Tap on a language to exclude it from plugin results.", + "note": "Note:", + "language_filtering_note": "This filter only applies to providers that include language information. It does not affect other providers.", + "excluded_languages": "Excluded languages:", + "about_title": "About Plugins", + "about_desc_1": "Plugins are modular components that adapt content from various external protocols. They run locally on your device and can be installed from trusted repositories.", + "about_desc_2": "Plugins marked as \"Limited\" may require specific external configurations.", + "help_title": "Plugin Setup", + "help_step_1": "1. **Enable Plugins** - Turn on the main switch", + "help_step_2": "2. **Add Repository** - Add a valid repository URL", + "help_step_3": "3. **Refresh Repository** - Fetch available plugins", + "help_step_4": "4. **Activate** - Enable the plugins you wish to use", + "got_it": "Got it!", + "repo_format_hint": "Format: https://raw.githubusercontent.com/username/repo/refs/heads/branch", + "cancel": "Cancel", + "add": "Add" + }, + "theme": { + "title": "App Themes", + "select_theme": "SELECT THEME", + "create_custom": "Create Custom Theme", + "options": "OPTIONS", + "use_dominant_color": "Use Dominant Color from Artwork", + "categories": { + "all": "All Themes", + "dark": "Dark Themes", + "colorful": "Colorful", + "custom": "My Themes" + }, + "editor": { + "theme_name_placeholder": "Theme name", + "save": "Save", + "primary": "Primary", + "secondary": "Secondary", + "background": "Background", + "invalid_name_title": "Invalid Name", + "invalid_name_msg": "Please enter a valid theme name" + }, + "alerts": { + "delete_title": "Delete Theme", + "delete_msg": "Are you sure you want to delete \"{{name}}\"?", + "ok": "OK", + "delete": "Delete", + "cancel": "Cancel", + "back": "Settings" + } + }, + "legal": { + "title": "Legal & Disclaimer", + "intro_title": "Nature of the Application", + "intro_text": "Nuvio is a media player and metadata management application. It acts solely as a client-side interface for browsing publicly available metadata (movies, TV shows, etc.) and playing media files provided by the user or third-party extensions. Nuvio itself does not host, store, distribute, or index any media content.", + "extensions_title": "Third-Party Plugins", + "extensions_text": "Nuvio uses an extensible architecture that allows users to install third-party plugins. These plugins are developed and maintained by independent developers not affiliated with Nuvio. We have no control over, and assume no responsibility for, the content, legality, or functionality of any third-party plugin.", + "user_resp_title": "User Responsibility", + "user_resp_text": "Users are solely responsible for the plugins they install and the content they access. By using this application, you agree to ensure that you have the legal right to access any content you view using Nuvio. The developers of Nuvio do not endorse or encourage copyright infringement.", + "dmca_title": "Copyright & DMCA", + "dmca_text": "We respect the intellectual property rights of others. Nuvio does not host media content. If you believe this project's code, assets, or interface infringes your rights, submit a notice through the official project contact channels listed on the website and repository.", + "warranty_title": "No Warranty", + "warranty_text": "This software is provided \"as is\", without warranty of any kind, express or implied. In no event shall the authors or copyright holders be liable for any claim, damages, or other liability arising from the use of this software." + }, + "plugin_tester": { + "title": "Plugin Tester", + "subtitle": "Run scrapers and inspect logs in real-time", + "tabs": { + "individual": "Individual", + "repo": "Repo Tester", + "code": "Code", + "logs": "Logs", + "results": "Results" + }, + "common": { + "error": "Error", + "success": "Success", + "movie": "Movie", + "tv": "TV", + "tmdb_id": "TMDB ID", + "season": "Season", + "episode": "Episode", + "running": "Running…", + "run_test": "Run Test", + "play": "Play", + "done": "Done", + "test": "Test", + "testing": "Testing…" + }, + "individual": { + "load_from_url": "Load from URL", + "load_from_url_desc": "Paste a raw GitHub URL or local IP and tap download.", + "enter_url_error": "Please enter a URL", + "code_loaded": "Code loaded from URL", + "fetch_error": "Failed to fetch: {{message}}", + "no_code_error": "No code to run", + "plugin_code": "Plugin Code", + "focus_editor": "Focus code editor", + "code_placeholder": "// Paste plugin code here...", + "test_parameters": "Test Parameters", + "no_logs": "No logs yet. Run a test to see output.", + "no_streams": "No streams found yet.", + "streams_found": "{{count}} Stream Found", + "streams_found_plural": "{{count}} Streams Found", + "tap_play_hint": "Tap Play to test a stream in the native player.", + "unnamed_stream": "Unnamed Stream", + "quality": "Quality: {{quality}}", + "size": "Size: {{size}}", + "url_label": "URL: {{url}}", + "headers_info": "Headers: {{count}} custom header(s)", + "find_placeholder": "Find in code…", + "edit_code_title": "Edit Code", + "no_url_stream_error": "No URL found for this stream" + }, + "repo": { + "title": "Repo Tester", + "description": "Fetch a repository (local URL or GitHub raw) and test each provider.", + "enter_repo_url_error": "Please enter a repository URL", + "invalid_url_title": "Invalid URL", + "invalid_url_msg": "Use a GitHub raw URL or a local http(s) URL.\n\nExample:\nhttps://raw.githubusercontent.com/tapframe/nuvio-providers/refs/heads/main", + "manifest_build_error": "Could not build a manifest URL from the input", + "manifest_fetch_error": "Failed to fetch manifest", + "repo_manifest_fetch_error": "Failed to fetch repository manifest", + "missing_filename": "Missing filename in manifest", + "scraper_build_error": "Could not build a scraper URL", + "download_scraper_error": "Failed to download scraper", + "test_failed": "Test failed", + "test_parameters": "Repo Test Parameters", + "test_parameters_desc": "These parameters are used only for Repo Tester.", + "using_info": "Using: {{mediaType}} • TMDB {{tmdbId}}", + "using_info_tv": "Using: {{mediaType}} • TMDB {{tmdbId}} • S{{season}}E{{episode}}", + "providers_title": "Providers", + "repository_default": "Repository", + "providers_count": "{{count}} providers", + "fetch_hint": "Fetch a repo to list providers.", + "test_all": "Test All", + "status_running": "RUNNING", + "status_ok": "OK ({{count}})", + "status_ok_empty": "OK (0)", + "status_failed": "FAILED", + "status_idle": "IDLE", + "tried_url": "Tried: {{url}}", + "provider_logs": "Provider Logs", + "no_logs_captured": "No logs captured." + } + } } diff --git a/src/i18n/locales/it.json b/src/i18n/locales/it.json index 3115c2e4..1ad95852 100644 --- a/src/i18n/locales/it.json +++ b/src/i18n/locales/it.json @@ -1,1491 +1,1497 @@ { - "common": { - "loading": "Caricamento...", - "cancel": "Annulla", - "save": "Salva", - "delete": "Elimina", - "edit": "Modifica", - "search": "Cerca", - "error": "Errore", - "success": "Successo", - "ok": "OK", - "unknown": "Sconosciuto", - "retry": "Riprova", - "try_again": "Riprova", - "go_back": "Torna indietro", - "settings": "Impostazioni", - "close": "Chiudi", - "enable": "Abilita", - "disable": "Disabilita", - "show_more": "Mostra altro", - "show_less": "Mostra meno", - "load_more": "Carica altro", - "unknown_date": "Data sconosciuta", - "anonymous_user": "Utente anonimo", - "time": { - "now": "Proprio ora", - "minutes_ago": "{{count}}m fa", - "hours_ago": "{{count}}o fa", - "days_ago": "{{count}}g fa" - }, - "days_short": { - "sun": "Dom", - "mon": "Lun", - "tue": "Mar", - "wed": "Mer", - "thu": "Gio", - "fri": "Ven", - "sat": "Sab" - }, - "email": "Email", - "status": "Stato" - }, - "home": { - "categories": { - "movies": "Film", - "series": "Serie TV", - "channels": "Canali" - }, - "movies": "Film", - "tv_shows": "Serie TV", - "load_more_catalogs": "Carica altri cataloghi", - "no_content": "Nessun contenuto disponibile", - "add_catalogs": "Aggiungi cataloghi", - "sign_in_available": "Accesso disponibile", - "sign_in_desc": "Puoi accedere in qualsiasi momento da Impostazioni → Account", - "view_all": "Vedi tutto", - "this_week": "Questa settimana", - "upcoming": "In arrivo", - "recently_released": "Rilasciati di recente", - "no_scheduled_episodes": "Serie TV senza episodi programmati", - "check_back_later": "Torna a controllare più tardi", - "continue_watching": "Continua a guardare", - "up_next": "Prossimi titoli", - "up_next_caps": "PROSSIMI TITOLI", - "released": "Rilasciato", - "new": "Nuovo", - "tba": "Da annunciare", - "new_episodes": "{{count}} Nuovi episodi", - "season_short": "S{{season}}", - "episode_short": "E{{episode}}", - "season": "Stagione {{season}}", - "episode": "Episodio {{episode}}", - "movie": "Film", - "series": "Serie TV", - "tv_show": "Serie TV", - "percent_watched": "{{percent}}% guardato", - "view_details": "Visualizza dettagli", - "remove": "Rimuovi", - "play": "Riproduci", - "play_now": "Riproduci ora", - "resume": "Riprendi", - "info": "Info", - "more_info": "Più informazioni", - "my_list": "La mia lista", - "save": "Salva", - "saved": "Salvato", - "retry": "Riprova", - "install_addons": "Installa Addon", - "settings": "Impostazioni", - "no_featured_content": "Nessun contenuto in evidenza", - "couldnt_load_featured": "Impossibile caricare i contenuti in evidenza", - "no_featured_desc": "Installa addon con cataloghi o cambia la sorgente dei contenuti nelle impostazioni.", - "load_error_desc": "Si è verificato un problema nel recupero dei contenuti in evidenza. Controlla la tua connessione e riprova.", - "no_featured_available": "Nessun contenuto in evidenza disponibile", - "no_description": "Nessuna descrizione disponibile" - }, - "navigation": { - "home": "Home", - "library": "Libreria", - "search": "Cerca", - "downloads": "Download", - "settings": "Impostazioni" - }, - "search": { - "title": "Cerca", - "recent_searches": "Ricerche recenti", - "discover": "Scopri", - "movies": "Film", - "tv_shows": "Serie TV", - "select_catalog": "Seleziona catalogo", - "all_genres": "Tutti i generi", - "discovering": "Scoperta contenuti in corso...", - "show_more": "Mostra altro ({{count}})", - "no_content_found": "Nessun contenuto trovato", - "try_different": "Prova un genere o un catalogo differente", - "select_catalog_desc": "Seleziona un catalogo da esplorare", - "tap_catalog_desc": "Tocca il catalogo qui sopra per iniziare", - "placeholder": "Cerca film, Serie TV...", - "keep_typing": "Continua a scrivere...", - "type_characters": "Scrivi almeno 2 caratteri per cercare", - "no_results": "Nessun risultato trovato", - "try_keywords": "Prova parole chiave diverse o controlla l'ortografia", - "select_type": "Seleziona tipo", - "browse_movies": "Sfoglia i cataloghi dei film", - "browse_tv": "Sfoglia i cataloghi delle Serie TV", - "select_genre": "Seleziona genere", - "show_all_content": "Mostra tutti i contenuti", - "genres_count": "{{count}} generi" - }, - "library": { - "title": "Libreria", - "watched": "Visti", - "continue": "Continua", - "watchlist": "Watchlist", - "collection": "Collezione", - "rated": "Valutati", - "items": "elementi", - "trakt_collections": "Collezioni Trakt", - "trakt_collection": "Collezione Trakt", - "no_trakt": "Nessuna collezione Trakt", - "no_trakt_desc": "Le tue collezioni Trakt appariranno qui non appena inizierai a usare Trakt", - "load_collections": "Carica collezioni", - "empty_folder": "Nessun contenuto in {{folder}}", - "empty_folder_desc": "Questa collezione è vuota", - "refresh": "Aggiorna", - "no_movies": "Ancora nessun film", - "no_series": "Ancora nessuna Serie TV", - "no_content": "Ancora nessun contenuto", - "add_content_desc": "Aggiungi dei contenuti alla tua libreria per vederli qui", - "find_something": "Trova qualcosa da guardare", - "removed_from_library": "Rimosso dalla Libreria", - "item_removed": "Elemento rimosso dalla tua libreria", - "failed_update_library": "Aggiornamento Libreria fallito", - "unable_remove": "Impossibile rimuovere l'elemento dalla libreria", - "marked_watched": "Segnato come Visto", - "marked_unwatched": "Segnato come Non Visto", - "item_marked_watched": "Elemento segnato come visto", - "item_marked_unwatched": "Elemento segnato come non visto", - "failed_update_watched": "Aggiornamento stato visione fallito", - "unable_update_watched": "Impossibile aggiornare lo stato di visione", - "added_to_library": "Aggiunto alla Libreria", - "item_added": "Aggiunto alla tua libreria locale", - "add_to_library": "Aggiungi alla Libreria", - "remove_from_library": "Rimuovi dalla Libreria", - "mark_watched": "Segna come visto", - "mark_unwatched": "Segna come non visto", - "share": "Condividi", - "add_to_watchlist": "Aggiungi alla Watchlist Trakt", - "remove_from_watchlist": "Rimuovi dalla Watchlist Trakt", - "added_to_watchlist": "Aggiunto alla Watchlist", - "added_to_watchlist_desc": "Aggiunto alla tua watchlist Trakt", - "removed_from_watchlist": "Rimosso dalla Watchlist", - "removed_from_watchlist_desc": "Rimosso dalla tua watchlist Trakt", - "add_to_collection": "Aggiungi alla Collezione Trakt", - "remove_from_collection": "Rimuovi dalla Collezione Trakt", - "added_to_collection": "Aggiunto alla Collezione", - "added_to_collection_desc": "Aggiunto alla tua collezione Trakt", - "removed_from_collection": "Rimosso dalla Collezione", - "removed_from_collection_desc": "Rimosso dalla tua collezione Trakt" - }, - "metadata": { - "unable_to_load": "Impossibile caricare il contenuto", - "error_code": "Codice errore: {{code}}", - "content_not_found": "Contenuto non trovato", - "content_not_found_desc": "Questo contenuto non esiste o potrebbe essere stato rimosso.", - "server_error": "Errore del server", - "server_error_desc": "Il server è temporaneamente non disponibile. Riprova più tardi.", - "bad_gateway": "Bad gateway", - "bad_gateway_desc": "Il server sta riscontrando dei problemi. Riprova più tardi.", - "service_unavailable": "Servizio non disponibile", - "service_unavailable_desc": "Il servizio è attualmente in manutenzione. Riprova più tardi.", - "too_many_requests": "Troppe richieste", - "too_many_requests_desc": "Stai effettuando troppe richieste. Attendi un momento e riprova.", - "request_timeout": "Richiesta scaduta", - "request_timeout_desc": "La richiesta ha impiegato troppo tempo. Riprova.", - "network_error": "Errore di rete", - "network_error_desc": "Controlla la tua connessione internet e riprova.", - "auth_error": "Errore di autenticazione", - "auth_error_desc": "Controlla le impostazioni del tuo account e riprova.", - "access_denied": "Accesso negato", - "access_denied_desc": "Non hai i permessi per accedere a questo contenuto.", - "connection_error": "Errore di connessione", - "streams_unavailable": "Streaming non disponibili", - "streams_unavailable_desc": "Le sorgenti di streaming sono attualmente non disponibili. Riprova più tardi.", - "unknown_error": "Errore sconosciuto", - "something_went_wrong": "Qualcosa è andato storto. Riprova.", - "cast": "Cast", - "more_like_this": "Altri titoli simili", - "collection": "Collezione", - "episodes": "Episodi", - "seasons": "Stagioni", - "posters": "Poster", - "banners": "Banner", - "specials": "Speciali", - "season_number": "Stagione {{number}}", - "episode_count": "{{count}} Episodio", - "episode_count_plural": "{{count}} Episodi", - "no_episodes": "Nessun episodio disponibile", - "no_episodes_for_season": "Nessun episodio disponibile per la Stagione {{season}}", - "episodes_not_released": "Gli episodi potrebbero non essere ancora stati rilasciati", - "no_description": "Nessuna descrizione disponibile", - "episode_label": "EPISODIO {{number}}", - "watch_again": "Guarda di nuovo", - "completed": "Completato", - "play_episode": "Riproduci {{season}}x{{episode}}", - "play": "Riproduci", - "watched": "Visto", - "watched_on_trakt": "Visto su Trakt", - "synced_with_trakt": "Sincronizzato con Trakt", - "saved": "Salvato", - "director": "Regista", - "directors": "Registi", - "creator": "Creatore", - "creators": "Creatori", - "production": "Produzione", - "network": "Rete", - "mark_watched": "Segna come visto", - "mark_unwatched": "Segna come non visto", - "marking": "Segnatura...", - "removing": "Rimozione...", - "unmark_season": "Deseleziona Stagione {{season}}", - "mark_season": "Segna Stagione {{season}} come vista", - "resume": "Riprendi", - "spoiler_warning": "Avviso Spoiler", - "spoiler_warning_desc": "Questo commento contiene spoiler. Sei sicuro di volerlo visualizzare?", - "cancel": "Annulla", - "reveal_spoilers": "Mostra Spoiler", - "movie_details": "Dettagli Film", - "show_details": "Dettagli Serie TV", - "tagline": "Slogan", - "status": "Stato", - "release_date": "Data di rilascio", - "runtime": "Durata", - "budget": "Budget", - "revenue": "Incasso", - "origin_country": "Paese d'origine", - "original_language": "Lingua originale", - "first_air_date": "Data prima messa in onda", - "last_air_date": "Data ultima messa in onda", - "total_episodes": "Episodi totali", - "episode_runtime": "Durata episodio", - "created_by": "Creato da", - "backdrop_gallery": "Galleria sfondi", - "loading_episodes": "Caricamento episodi...", - "no_episodes_available": "Nessun episodio disponibile", - "play_next": "Riproduci S{{season}}E{{episode}}", - "play_next_episode": "Riproduci prossimo episodio", - "save": "Salva", - "percent_watched": "{{percent}}% guardato", - "percent_watched_trakt": "{{percent}}% guardato ({{traktPercent}}% su Trakt)", - "synced_with_trakt_progress": "Sincronizzato con Trakt", - "using_trakt_progress": "Utilizzo progressi Trakt", - "added_to_collection_hero": "Aggiunto alla collezione", - "added_to_collection_desc_hero": "Aggiunto alla tua collezione Trakt", - "removed_from_collection_hero": "Rimosso dalla collezione", - "removed_from_collection_desc_hero": "Rimosso dalla tua collezione Trakt", - "mark_as_watched": "Segna come visto", - "mark_as_unwatched": "Segna come non visto" - }, - "cast": { - "biography": "Biografia", - "known_for": "Conosciuto per", - "personal_info": "Informazioni personali", - "born_in": "Nato a {{place}}", - "filmography": "Filmografia", - "also_known_as": "Conosciuto anche come", - "no_info_available": "Nessuna informazione aggiuntiva disponibile", - "as_character": "nel ruolo di {{character}}", - "loading_details": "Caricamento dettagli...", - "years_old": "{{age}} anni", - "view_filmography": "Visualizza filmografia", - "filter": "Filtra", - "sort_by": "Ordina per", - "sort_popular": "Popolarità", - "sort_latest": "Ultimi prodotti", - "sort_upcoming": "In arrivo", - "upcoming_badge": "IN ARRIVO", - "coming_soon": "Prossimamente", - "filmography_count": "Filmografia • {{count}} titoli", - "loading_filmography": "Caricamento filmografia...", - "load_more_remaining": "Carica altri ({{count}} rimanenti)", - "alert_error_title": "Errore", - "alert_error_message": "Impossibile caricare \"{{title}}\". Riprova più tardi.", - "alert_ok": "OK", - "no_upcoming": "Nessuna uscita imminente disponibile per questo attore", - "no_content": "Nessun contenuto disponibile per questo attore", - "no_movies": "Nessun film disponibile per questo attore", - "no_tv": "Nessuna Serie TV disponibile per questo attore" - }, - "comments": { - "title": "Commenti Trakt", - "spoiler_warning": "⚠️ Questo commento contiene spoiler. Tocca per visualizzare.", - "spoiler": "Spoiler", - "contains_spoilers": "Contiene spoiler", - "reveal": "Mostra", - "vip": "VIP", - "unavailable": "Commenti non disponibili", - "no_comments": "Ancora nessun commento su Trakt", - "not_in_database": "Questo contenuto potrebbe non essere ancora nel database di Trakt", - "check_trakt": "Controlla su Trakt" - }, - "trailers": { - "title": "Trailer", - "official_trailers": "Trailer ufficiali", - "official_trailer": "Trailer ufficiale", - "teasers": "Teaser", - "teaser": "Teaser", - "clips_scenes": "Clip e scene", - "clip": "Clip", - "featurettes": "Featurette", - "featurette": "Featurette", - "behind_the_scenes": "Dietro le quinte", - "no_trailers": "Nessun trailer disponibile", - "unavailable": "Trailer non disponibile", - "unavailable_desc": "Impossibile caricare questo trailer al momento. Riprova più tardi.", - "unable_to_play": "Impossibile riprodurre il trailer. Riprova.", - "watch_on_youtube": "Guarda su YouTube" - }, - "catalog": { - "no_content_found": "Nessun contenuto trovato", - "no_content_filters": "Nessun contenuto trovato per i filtri selezionati", - "loading_content": "Caricamento contenuti...", - "back": "Indietro", - "in_theaters": "Al cinema", - "all": "Tutti", - "failed_tmdb": "Impossibile caricare i contenuti da TMDB", - "movies": "Film", - "tv_shows": "Serie TV", - "channels": "Canali" - }, - "streams": { - "back_to_episodes": "Torna agli episodi", - "back_to_info": "Torna alle info", - "fetching_from": "Recupero da:", - "no_sources_available": "Nessuna sorgente streaming disponibile", - "add_sources_desc": "Aggiungi sorgenti streaming nelle impostazioni", - "add_sources": "Aggiungi sorgenti", - "finding_streams": "Ricerca streaming disponibili...", - "finding_best_stream": "Ricerca del miglior streaming per l'avvio automatico...", - "still_fetching": "Recupero streaming in corso...", - "no_streams_available": "Nessuno streaming disponibile", - "starting_best_stream": "Avvio del miglior streaming...", - "loading_more_sources": "Caricamento di altre sorgenti..." - }, - "player_ui": { - "via": "via {{name}}", - "audio_tracks": "Tracce audio", - "no_audio_tracks": "Nessuna traccia audio disponibile", - "playback_speed": "Velocità di riproduzione", - "on_hold": "In attesa", - "playback_error": "Errore di riproduzione", - "unknown_error": "Si è verificato un errore sconosciuto durante la riproduzione.", - "copy_error": "Copia dettagli errore", - "copied_to_clipboard": "Copiato negli appunti", - "dismiss": "Chiudi", - "continue_watching": "Continua a guardare", - "start_over": "Ricomincia dall'inizio", - "resume": "Riprendi", - "change_source": "Cambia sorgente", - "switching_source": "Cambio sorgente in corso...", - "no_sources_found": "Nessuna sorgente trovata", - "sources": "Sorgenti", - "finding_sources": "Ricerca sorgenti...", - "unknown_source": "Sorgente sconosciuta", - "sources_limited": "Le sorgenti potrebbero essere limitate a causa di errori del provider.", - "episodes": "Episodi", - "specials": "Speciali", - "season": "Stagione {{season}}", - "stream": "Streaming {{number}}", - "subtitles": "Sottotitoli", - "built_in": "Integrati", - "addons": "Addon", - "style": "Stile", - "none": "Nessuno", - "search_online_subtitles": "Cerca sottotitoli online", - "preview": "Anteprima", - "quick_presets": "Predefiniti rapidi", - "default": "Predefinito", - "yellow": "Giallo", - "high_contrast": "Contrasto elevato", - "large": "Grande", - "core": "Base", - "font_size": "Dimensione carattere", - "show_background": "Mostra sfondo", - "advanced": "Avanzate", - "position": "Posizione", - "text_color": "Colore testo", - "align": "Allineamento", - "bottom_offset": "Distanza dal basso", - "background_opacity": "Opacità sfondo", - "text_shadow": "Ombra testo", - "on": "Attivo", - "off": "Disattivo", - "outline_color": "Colore contorno", - "outline": "Contorno", - "outline_width": "Larghezza contorno", - "letter_spacing": "Spaziatura lettere", - "line_height": "Altezza riga", - "timing_offset": "Ritardo sincronizzazione (s)", - "visual_sync": "Sincronizzazione visiva", - "timing_hint": "Anticipa (-) o ritarda (+) i sottotitoli per sincronizzarli se necessario.", - "reset_defaults": "Ripristina predefiniti", - "mark_intro_start": "Segna Inizio Intro", - "mark_intro_end": "Segna Fine Intro", - "intro_start_marked": "Inizio intro segnato", - "intro_submitted": "Intro registrata con successo", - "intro_submit_failed": "Errore Registrazione Intro" - }, - "downloads": { - "title": "Download", - "no_downloads": "Ancora nessun download", - "no_downloads_desc": "I contenuti scaricati appariranno qui per la visione offline", - "explore": "Esplora contenuti", - "path_copied": "Percorso copiato", - "path_copied_desc": "Percorso del file locale copiato negli appunti", - "copied": "Copiato", - "incomplete": "Download incompleto", - "incomplete_desc": "Il download non è ancora terminato", - "not_available": "Non disponibile", - "not_available_desc": "Il percorso del file locale è disponibile solo al termine del download.", - "status_downloading": "Download in corso", - "status_completed": "Completato", - "status_paused": "In pausa", - "status_error": "Errore", - "status_queued": "In coda", - "status_unknown": "Sconosciuto", - "provider": "Provider", - "streaming_playlist_warning": "Potrebbe non funzionare - playlist di streaming", - "remaining": "rimanenti", - "not_ready": "Download non pronto", - "not_ready_desc": "Attendi il completamento del download.", - "filter_all": "Tutti", - "filter_active": "Attivi", - "filter_done": "Completati", - "filter_paused": "In pausa", - "no_filter_results": "Nessun download per il filtro {{filter}}", - "try_different_filter": "Prova a selezionare un filtro diverso", - "limitations_title": "Limitazioni del download", - "limitations_msg": "• I file più piccoli di 1MB sono solitamente playlist streaming M3U8 e non possono essere scaricati per la visione offline. Funzionano solo con lo streaming online e contengono link ai segmenti video, non il contenuto video effettivo.", - "remove_title": "Rimuovi download", - "remove_confirm": "Rimuovere \"{{title}}\"{{season_episode}}?", - "cancel": "Annulla", - "remove": "Rimuovi" - }, - "addons": { - "title": "Addon", - "reorder_mode": "Modalità riordino", - "reorder_info": "Gli Addon in alto hanno priorità maggiore durante il caricamento dei contenuti", - "add_addon_placeholder": "URL Addon", - "add_button": "Aggiungi Addon", - "my_addons": "I miei Addon", - "community_addons": "Addon della community", - "no_addons": "Nessun Addon installato", - "uninstall_title": "Disinstalla Addon", - "uninstall_message": "Sei sicuro di voler disinstallare {{name}}?", - "uninstall_button": "Disinstalla", - "install_success": "Addon installato con successo", - "install_error": "Installazione Addon fallita", - "load_error": "Caricamento Addon fallito", - "fetch_error": "Impossibile recuperare i dettagli dell'Addon", - "invalid_url": "Inserisci un URL Addon valido", - "configure": "Configura", - "version": "Versione: {{version}}", - "installed_addons": "ADDON INSTALLATI", - "reorder_drag_title": "TRASCINA GLI ADDON PER RIORDINARLI", - "install": "Installa", - "config_unavailable_title": "Configurazione non disponibile", - "config_unavailable_msg": "Impossibile determinare l'URL di configurazione per questo Addon.", - "cannot_open_config_title": "Impossibile aprire la configurazione", - "cannot_open_config_msg": "L'URL di configurazione ({{url}}) non può essere aperto. L'Addon potrebbe non avere una pagina di configurazione.", - "description": "Descrizione", - "supported_types": "Tipi supportati", - "catalogs": "Cataloghi", - "no_description": "Nessuna descrizione disponibile", - "overview": "PANORAMICA", - "no_categories": "Nessuna categoria", - "pre_installed": "PRE-INSTALLATO" - }, - "trakt": { - "title": "Impostazioni Trakt", - "settings_title": "Impostazioni Trakt", - "connect_title": "Connettiti a Trakt", - "connect_desc": "Sincronizza cronologia, watchlist e collezione con Trakt.tv", - "sign_in": "Accedi con Trakt", - "sign_out": "Esci", - "sign_out_confirm": "Sei sicuro di voler uscire dal tuo account Trakt?", - "joined": "Iscritto dal {{date}}", - "sync_settings_title": "Impostazioni Sincronizzazione", - "sync_info": "Quando sei connesso a Trakt, l'intera cronologia viene sincronizzata direttamente dall'API e non viene salvata localmente. La tua lista 'Continua a guardare' riflette i tuoi progressi globali su Trakt.", - "auto_sync_label": "Sincronizzazione automatica progressi", - "auto_sync_desc": "Sincronizza automaticamente i progressi di visione su Trakt", - "import_history_label": "Importa cronologia visioni", - "import_history_desc": "Usa \"Sincronizza ora\" per importare la cronologia e i progressi da Trakt", - "sync_now_button": "Sincronizza ora", - "display_settings_title": "Impostazioni Visualizzazione", - "show_comments_label": "Mostra commenti Trakt", - "show_comments_desc": "Visualizza i commenti di Trakt nelle schede dei contenuti quando disponibili", - "maintenance_title": "In manutenzione", - "maintenance_unavailable": "Trakt non disponibile", - "maintenance_desc": "L'integrazione con Trakt è temporaneamente sospesa per manutenzione. Tutte le funzioni di sincronizzazione e autenticazione sono disabilitate fino al termine dei lavori.", - "maintenance_button": "Servizio in manutenzione", - "auth_success_title": "Connessione riuscita", - "auth_success_msg": "Il tuo account Trakt è stato collegato correttamente.", - "auth_error_title": "Errore di autenticazione", - "auth_error_msg": "Impossibile completare l'autenticazione con Trakt.", - "auth_error_generic": "Si è verificato un errore durante l'autenticazione.", - "sign_out_error": "Impossibile uscire da Trakt.", - "sync_complete_title": "Sincronizzazione completata", - "sync_success_msg": "Progressi di visione sincronizzati con successo con Trakt.", - "sync_error_msg": "Sincronizzazione fallita. Riprova." - }, - "simkl": { - "title": "Impostazioni Simkl", - "settings_title": "Impostazioni Simkl", - "connect_title": "Connetti con Simkl", - "connect_desc": "Sincronizza la tua cronologia di visione e traccia ciò che guardi", - "sign_in": "Accedi con Simkl", - "sign_out": "Disconnetti", - "sign_out_confirm": "Sei sicuro di voler disconnettere da Simkl?", - "syncing_desc": "I tuoi elementi guardati sono in sincronizzazione con Simkl.", - "auth_success_title": "Connesso con successo", - "auth_success_msg": "Il tuo account Simkl è stato connesso con successo.", - "auth_error_title": "Errore di autenticazione", - "auth_error_msg": "Impossibile completare l'autenticazione con Simkl.", - "auth_error_generic": "Si è verificato un errore durante l'autenticazione.", - "sign_out_error": "Impossibile disconnettere da Simkl.", - "config_error_title": "Errore di configurazione", - "config_error_msg": "L'ID client Simkl manca nelle variabili d'ambiente.", - "conflict_title": "Conflitto", - "conflict_msg": "Non puoi connettere Simkl mentre Trakt è connesso. Disconnetti prima Trakt.", - "disclaimer": "Nuvio non è affiliato con Simkl." - }, - "tmdb_settings": { - "title": "Impostazioni TMDb", - "metadata_enrichment": "Arricchimento metadati", - "metadata_enrichment_desc": "Migliora i metadati dei contenuti con i dati di TMDb per maggiori dettagli e informazioni.", - "enable_enrichment": "Abilita arricchimento", - "enable_enrichment_desc": "Integra i metadati degli Addon con TMDb per cast, certificazioni, loghi/poster e info di produzione.", - "localized_text": "Testo localizzato", - "localized_text_desc": "Recupera titoli e descrizioni nella tua lingua preferita da TMDb.", - "language": "Lingua", - "change": "Cambia", - "logo_preview": "Anteprima Logo", - "logo_preview_desc": "L'anteprima mostra come appariranno i loghi localizzati nella lingua selezionata.", - "example": "Esempio:", - "no_logo": "Nessun logo disponibile", - "enrichment_options": "Opzioni di arricchimento", - "enrichment_options_desc": "Controlla quali dati recuperare da TMDb. Le opzioni disabilitate useranno i dati dell'Addon, se disponibili.", - "cast_crew": "Cast e Troupe", - "cast_crew_desc": "Attori, registi, sceneggiatori con foto profilo", - "title_description": "Titolo e Descrizione", - "title_description_desc": "Usa titolo e panoramica localizzati di TMDb", - "title_logos": "Loghi Titolo", - "title_logos_desc": "Immagini di alta qualità per il titolo", - "banners_backdrops": "Banner e Sfondi", - "banners_backdrops_desc": "Immagini di sfondo ad alta risoluzione", - "certification": "Certificazione contenuti", - "certification_desc": "Classificazione per età (PG-13, R, TV-MA, ecc.)", - "recommendations": "Consigliati", - "recommendations_desc": "Suggerimenti per contenuti simili", - "episode_data": "Dati episodi", - "episode_data_desc": "Miniature episodi, info e fallback per le Serie TV", - "season_posters": "Poster stagioni", - "season_posters_desc": "Immagini poster specifiche per le stagioni", - "production_info": "Info di produzione", - "production_info_desc": "Reti e case di produzione con loghi", - "movie_details": "Dettagli Film", - "movie_details_desc": "Budget, incassi, durata, slogan", - "tv_details": "Dettagli Serie TV", - "tv_details_desc": "Stato, numero stagioni, reti, creatori", - "movie_collections": "Collezioni Film", - "movie_collections_desc": "Franchise cinematografici (Marvel, Star Wars, ecc.)", - "api_configuration": "Configurazione API", - "api_configuration_desc": "Configura il tuo accesso alle API di TMDb per funzionalità avanzate.", - "custom_api_key": "Chiave API personalizzata", - "custom_api_key_desc": "Usa la tua chiave API TMDb per prestazioni migliori e limiti di frequenza dedicati.", - "custom_key_active": "Chiave API personalizzata attiva", - "api_key_required": "Chiave API richiesta", - "api_key_placeholder": "Incolla la tua chiave API TMDb (v3)", - "how_to_get_key": "Come ottenere una chiave API TMDb?", - "built_in_key_msg": "Attualmente in uso la chiave API integrata. Considera l'uso di una chiave tua per prestazioni migliori.", - "cache_size": "Dimensione Cache", - "clear_cache": "Cancella Cache", - "cache_days": "Le risposte TMDB vengono memorizzate per 7 giorni per migliorare le prestazioni", - "choose_language": "Scegli Lingua", - "choose_language_desc": "Seleziona la lingua preferita per i contenuti TMDb", - "popular": "Popolari", - "all_languages": "Tutte le lingue", - "search_results": "Risultati di ricerca", - "no_languages_found": "Nessuna lingua trovata per \"{{query}}\"", - "clear_search": "Cancella ricerca", - "clear_cache_title": "Cancella Cache TMDB", - "clear_cache_msg": "Questo cancellerà tutti i dati TMDB memorizzati ({{size}}). Il caricamento potrebbe rallentare temporaneamente fino alla ricostruzione della cache.", - "clear_cache_success": "Cache TMDB cancellata con successo.", - "clear_cache_error": "Impossibile cancellare la cache.", - "clear_api_key_title": "Rimuovi Chiave API", - "clear_api_key_msg": "Sei sicuro di voler rimuovere la tua chiave API personalizzata e tornare a quella predefinita?", - "clear_api_key_success": "Chiave API rimossa con successo", - "clear_api_key_error": "Impossibile rimuovere la chiave API", - "empty_api_key": "La chiave API non può essere vuota.", - "invalid_api_key": "Chiave API non valida. Controlla e riprova.", - "save_error": "Si è verificato un errore durante il salvataggio. Riprova.", - "using_builtin_key": "Ora in uso la chiave API TMDb integrata.", - "using_custom_key": "Ora in uso la tua chiave API TMDb personalizzata.", - "enter_custom_key": "Inserisci e salva la tua chiave API TMDb personalizzata.", - "key_verified": "Chiave API verificata e salvata con successo." - }, - "settings": { - "language": "Lingua", - "select_language": "Seleziona Lingua", - "english": "Inglese", - "portuguese": "Portoghese", - "portuguese_br": "Portoghese (Brasile)", - "portuguese_pt": "Portoghese (Portogallo)", - "german": "Tedesco", - "arabic": "Arabo", - "spanish": "Spagnolo", - "french": "Francese", - "italian": "Italiano", - "croatian": "Croato", - "chinese": "Cinese (Semplificato)", - "hindi": "Hindi", - "serbian": "Serbo", - "hebrew": "Ebraico", - "bulgarian": "Bulgaro", - "polish": "Polacco", - "czech": "Ceco", - "turkish": "Turco", - "slovenian": "Sloveno", - "macedonian": "Macedone", - "russian": "Russo", - "filipino": "Filippino", - "dutch_nl": "Olandese (Paesi Bassi)", - "romanian": "Rumeno", - "albanian": "Albanese", - "catalan": "Catalano", - "account": "Account", - "content_discovery": "Contenuti e Scoperta", - "appearance": "Aspetto", - "integrations": "Integrazioni", - "playback": "Riproduzione", - "backup_restore": "Backup e Ripristino", - "updates": "Aggiornamenti", - "about": "Informazioni", - "developer": "Sviluppatore", - "cache": "Cache", - "title": "Impostazioni", - "settings_title": "Impostazioni", - "sign_in_sync": "Accedi per sincronizzare", - "add_catalogs_sources": "Addon, cataloghi e sorgenti", - "player_trailers_downloads": "Player, trailer, download", - "mdblist_tmdb_ai": "MDBList, TMDB, IA", - "check_updates": "Controlla aggiornamenti", - "clear_mdblist_cache": "Cancella Cache MDBList", - "cache_management": "GESTIONE CACHE", - "downloads_counter": "download e oltre", - "made_with_love": "Fatto con ❤️ da Tapframe e amici", - "sections": { - "information": "INFORMAZIONI", - "account": "ACCOUNT", - "theme": "TEMA", - "layout": "LAYOUT", - "sources": "SORGENTI", - "catalogs": "CATALOGHI", - "discovery": "SCOPERTA", - "metadata": "METADATI", - "ai_assistant": "ASSISTENTE IA", - "video_player": "VIDEO PLAYER", - "audio_subtitles": "AUDIO E SOTTOTITOLI", - "media": "MEDIA", - "notifications": "NOTIFICHE", - "testing": "TEST", - "danger_zone": "ZONA PERICOLOSA" - }, - "items": { - "legal": "Note Legali & Disclaimer", - "privacy_policy": "Informativa sulla privacy", - "report_issue": "Segnala un problema", - "version": "Versione", - "contributors": "Collaboratori", - "view_contributors": "Vedi tutti i collaboratori", - "theme": "Tema", - "episode_layout": "Layout episodi", - "streams_backdrop": "Sfondo streaming", - "streams_backdrop_desc": "Mostra sfondo sfocato durante lo streaming su mobile", - "addons": "Addon", - "installed": "installati", - "debrid_integration": "Integrazione Debrid", - "debrid_desc": "Connetti Torbox", - "plugins": "Plugin", - "plugins_desc": "Gestisci plugin e repository", - "catalogs": "Cataloghi", - "active": "attivi", - "home_screen": "Schermata Home", - "home_screen_desc": "Layout e contenuti", - "continue_watching": "Continua a guardare", - "continue_watching_desc": "Comportamento cache e riproduzione", - "show_discover": "Mostra sezione Scopri", - "show_discover_desc": "Visualizza contenuti scopri nella Ricerca", - "mdblist": "MDBList", - "mdblist_connected": "Connesso", - "mdblist_desc": "Abilita per aggiungere voti e recensioni", - "simkl": "Simkl", - "simkl_connected": "Connesso", - "simkl_desc": "Traccia ciò che guardi", - "tmdb": "TMDB", - "tmdb_desc": "Sorgente metadati e loghi", - "openrouter": "API OpenRouter", - "openrouter_connected": "Connesso", - "openrouter_desc": "Aggiungi la tua chiave API per abilitare la chat IA", - "video_player": "Video Player", - "built_in": "Integrato", - "external": "Esterno", - "preferred_audio": "Lingua audio preferita", - "preferred_subtitle": "Lingua sottotitoli preferita", - "subtitle_source": "Priorità sorgente sottotitoli", - "auto_select_subs": "Selezione automatica sottotitoli", - "auto_select_subs_desc": "Seleziona automaticamente i sottotitoli in base alle tue preferenze", - "show_trailers": "Mostra Trailer", - "show_trailers_desc": "Visualizza i trailer nella sezione principale", - "enable_downloads": "Abilita Download", - "enable_downloads_desc": "Mostra la scheda Download e abilita il salvataggio degli streaming", - "notifications": "Notifiche", - "notifications_desc": "Promemoria episodi", - "developer_tools": "Strumenti di Sviluppo", - "developer_tools_desc": "Opzioni di test e debug", - "test_onboarding": "Test Introduzione", - "reset_onboarding": "Ripristina Introduzione", - "test_announcement": "Test Annuncio", - "test_announcement_desc": "Mostra l'overlay 'novità'", - "reset_campaigns": "Ripristina Campagne", - "reset_campaigns_desc": "Cancella le impressioni delle campagne", - "clear_all_data": "Cancella tutti i dati", - "clear_all_data_desc": "Ripristina tutte le impostazioni e i dati memorizzati" - }, - "options": { - "horizontal": "Orizzontale", - "vertical": "Verticale", - "internal_first": "Prima interni", - "internal_first_desc": "Preferisci i sottotitoli integrati, poi quelli esterni", - "external_first": "Prima esterni", - "external_first_desc": "Preferisci i sottotitoli degli Addon, poi quelli integrati", - "any_available": "Qualsiasi disponibile", - "any_available_desc": "Usa la prima traccia di sottotitoli disponibile" - }, - "clear_data_desc": "Questo ripristinerà tutte le impostazioni e cancellerà tutti i dati in cache. Sei sicuro?", - "app_updates": "Aggiornamenti App", - "about_nuvio": "Informazioni su Nuvio", - "cloud_sync": { - "title": "Nuvio Sync", - "description": "Sicronizza i dati sui tuoi dispositivi con Nuvio", - "hero_title": "Sincronizzazione su Cloud", - "hero_subtitle": "Mantieni i tuoi addons, progressi e libreria allineati su tutti i tuoi dispositivi.", - "auth": { - "account": "Account", - "not_configured": "Supabase non configurato", - "not_authenticated": "Non Autenticato", - "email_session": "Sessione Email", - "signed_in_as": "Login effettuato come {{email}}", - "not_signed_in": "Non Registrato", - "effective_owner": "Proprietario effettivo: {{id}}" - }, - "stats": { - "title": "Statistiche Database", - "plugins": "Plugins", - "addons": "Addons", - "watch_progress": "Watch Progress", - "library_items": "Oggetti della Libreria", - "watched_items": "Oggetti guardati", - "signin_required": "Accedi per caricare gli ultimi conteggi." - }, - "actions": { - "title": "Azioni", - "description": "Aggiorna dal cloud (pull) o usa le impostazioni di questo dispositivo come sorgente principale (push).", - "pull_btn": "Pull dal Cloud", - "push_btn": "Push dal Dispositivo", - "manage_account": "Gestiscci Account", - "sign_out": "Disconnetti", - "sign_in_up": "Accedi / Registrati" - }, - "alerts": { - "pull_success_title": "Dati Cloud recuperati", - "pull_success_msg": "Gli ultimi dati del Cloud sono stati scaricati su questo dispositivo.", - "pull_failed_title": "Pull Fallito", - "pull_failed_msg": "Download dei dati dal cloud fallito", - "push_success_title": "Push Completato", - "push_success_msg": "Le impostazioni di questo dispositivo sono stati caricati sul Cloud.", - "push_failed_title": "Push Fallito", - "push_failed_msg": "Upload dei dati locali fallito", - "sign_out_failed": "Logout fallito", - "sign_out_failed_title": "Errore Logout" - }, - "external_sync": { - "title": "Priorità sincronizzazione esterna", - "active_msg": "{{services}} è attivo. Il 'Continua a Guardare' e gli aggiornamenti della libreria sono gestiti da questi servizi piuttosto che dal Database Cloud di Nuvio.", - "inactive_msg": "Se la sincronizzazione di Trakt o Simkl è abilitata, il 'Continua a Guardare' e gli aggiornamenti della libreria saranno gestiti da quei servizi piuttosto che dal Database Cloud di Nuvio." - }, - "pre_auth": { - "title": "Prima della Sincronizzazione", - "description": "Accedi per abilitare la sincronizzazione Cloud e mantenere i dati sincronizzati fra tutti i dispositivi.", - "point_1": "• Imostazioni Addons e plugins ", - "point_2": "• Avanzamento della visione e libreria", - "env_warning": "Imposta EXPO_PUBLIC_SUPABASE_URL e EXPO_PUBLIC_SUPABASE_ANON_KEY per abilitare la sincronizzazione." - }, - "connection": "Connessione" - } - }, - "privacy": { - "title": "Privacy e Dati", - "settings_desc": "Controlla la telemetria e la raccolta di dati", - "info_title": "La Tua Privacy è Importante per Noi", - "info_description": "Controlla quali dati vengono raccolti e condivisi. L'analisi è disabilitata per impostazione predefinita e i rapporti di arresto sono anonimi per impostazione predefinita.", - "analytics_enabled_title": "Analisi Abilitata", - "analytics_enabled_message": "I dati di utilizzo verranno raccolti per aiutare a migliorare l'app. Puoi disabilitarlo in qualsiasi momento.", - "disable_error_reporting_title": "Disabilitare la Segnalazione di Errori?", - "disable_error_reporting_message": "Disabilitare la segnalazione di errori significa che non riceveremo notifiche di arresti anomali o problemi che riscontri. Questo potrebbe influire sulla nostra capacità di correggere i bug.", - "enable_session_replay_title": "Abilitare la Riproduzione Sessione?", - "enable_session_replay_message": "La riproduzione della sessione registra il tuo schermo quando si verificano errori per aiutarci a capire cosa è successo. Questo potrebbe catturare contenuti visibili sullo schermo.", - "enable_pii_title": "Abilitare la Raccolta PII?", - "enable_pii_message": "Ciò consente la raccolta di informazioni di identificazione personale come indirizzo IP e dettagli del dispositivo. Questi dati aiutano a diagnosticare i problemi ma aumentano l'esposizione della privacy.", - "disable_all_title": "Disabilitare Tutta la Telemetria?", - "disable_all_message": "Questo disabiliterà tutta l'analisi, la segnalazione di errori e la riproduzione della sessione. Non riceveremo alcun dato sull'utilizzo dell'app o gli arresti.", - "disable_all_button": "Disabilita Tutto", - "all_disabled_title": "Tutta la Telemetria Disabilitata", - "all_disabled_message": "Tutta la raccolta di dati è stata disabilitata. Le modifiche avranno effetto al prossimo riavvio dell'app.", - "reset_title": "Ripristina ai Valori Consigliati", - "reset_message": "Le impostazioni di privacy sono state ripristinate ai valori predefiniti consigliati (segnalazione di errori abilitata, analisi disabilitata).", - "section_analytics": "ANALISI", - "analytics_title": "Analisi di Utilizzo", - "analytics_description": "Raccogli schemi di utilizzo anonimi e visualizzazioni dello schermo", - "section_error_reporting": "SEGNALAZIONE DI ERRORI", - "error_reporting_title": "Rapporti di Arresto", - "error_reporting_description": "Invia rapporti di arresto anonimi per migliorare la stabilità", - "session_replay_title": "Riproduzione Sessione", - "session_replay_description": "Registra lo schermo quando si verificano errori", - "pii_title": "Includi Informazioni Dispositivo", - "pii_description": "Invia indirizzo IP e dettagli del dispositivo con i rapporti", - "section_quick_actions": "AZIONI RAPIDE", - "disable_all": "Disabilitare Tutta la Telemetria", - "disable_all_desc": "Disattiva tutta la raccolta di dati", - "reset_recommended": "Ripristina ai Valori Consigliati", - "reset_recommended_desc": "Impostazioni predefinite incentrate sulla privacy con segnalazione di errori", - "section_learn_more": "SCOPRI DI PIÙ", - "privacy_policy": "Informativa sulla Privacy", - "current_settings": "Riepilogo delle Impostazioni Attuali", - "summary_analytics": "Analisi", - "summary_errors": "Rapporti di Errore", - "summary_replay": "Riproduzione Sessione", - "summary_pii": "Informazioni Dispositivo", - "restart_note_detailed": "* Le modifiche all'analisi e alla segnalazione di errori hanno effetto immediato. La riproduzione della sessione e le impostazioni PII richiedono il riavvio dell'app." - }, - "ai_settings": { - "title": "Assistente IA", - "info_title": "Chat potenziata dall'IA", - "info_desc": "Fai domande su qualsiasi film o episodio di Serie TV usando l'IA avanzata. Ottieni approfondimenti su trama, personaggi, temi, curiosità e altro ancora, tutto basato sui dati completi di TMDB.", - "feature_1": "Contesto e analisi specifici per ogni episodio", - "feature_2": "Spiegazioni della trama e analisi dei personaggi", - "feature_3": "Curiosità e fatti dietro le quinte", - "feature_4": "Usa la tua chiave API gratuita di OpenRouter", - "api_key_section": "CHIAVE API OPENROUTER", - "api_key_label": "Chiave API", - "api_key_desc": "Inserisci la tua chiave API OpenRouter per abilitare le funzioni di chat IA", - "save_api_key": "Salva Chiave API", - "saving": "Salvataggio...", - "update": "Aggiorna", - "remove": "Rimuovi", - "get_free_key": "Ottieni una chiave API gratuita da OpenRouter", - "enable_chat": "Abilita Chat IA", - "enable_chat_desc": "Se abilitato, il pulsante 'Chiedi all'IA' apparirà nelle pagine dei contenuti.", - "chat_enabled": "Chat IA Abilitata", - "chat_enabled_desc": "Ora puoi fare domande su film e Serie TV. Cerca il pulsante \"Chiedi all'IA\" nelle pagine dei contenuti!", - "how_it_works": "Come funziona", - "how_it_works_desc": "• OpenRouter fornisce l'accesso a diversi modelli IA\n• La tua chiave API rimane privata e sicura\n• Il piano gratuito include limiti di utilizzo generosi\n• Chatta con contesto su film/episodi specifici\n• Ottieni analisi dettagliate e spiegazioni", - "error_invalid_key": "Inserisci una chiave API valida", - "error_key_format": "Le chiavi API OpenRouter devono iniziare con \"sk-or-\"", - "success_saved": "Chiave API OpenRouter salvata con successo!", - "error_save": "Impossibile salvare la chiave API", - "confirm_remove_title": "Rimuovi Chiave API", - "confirm_remove_msg": "Sei sicuro di voler rimuovere la tua chiave API OpenRouter? Questo disabiliterà le funzioni di chat IA.", - "success_removed": "Chiave API rimossa con successo", - "error_remove": "Impossibile rimuovere la chiave API" - }, - "catalog_settings": { - "title": "Cataloghi", - "layout_phone": "LAYOUT SCHERMATA CATALOGO (TELEFONO)", - "posters_per_row": "Poster per riga", - "auto": "Auto", - "show_titles": "Mostra titoli poster", - "show_titles_desc": "Visualizza il testo del titolo sotto ogni poster", - "phone_only_hint": "Si applica solo ai telefoni. I tablet mantengono il layout adattivo.", - "catalogs_group": "Cataloghi", - "enabled_count": "{{enabled}} su {{total}} abilitati", - "rename_hint": "Premi a lungo su un catalogo per rinominarlo", - "rename_modal_title": "Rinomina Catalogo", - "rename_placeholder": "Inserisci il nuovo nome del catalogo", - "error_save_name": "Impossibile salvare il nome personalizzato." - }, - "continue_watching_settings": { - "title": "Continua a guardare", - "playback_behavior": "COMPORTAMENTO RIPRODUZIONE", - "use_cached": "Usa stream in cache", - "use_cached_desc": "Se abilitato, cliccando sugli elementi in 'Continua a guardare' si aprirà direttamente il player usando gli stream riprodotti in precedenza. Se disabilitato, aprirà la schermata dei contenuti.", - "open_metadata": "Apri schermata metadati", - "open_metadata_desc": "Quando gli stream in cache sono disabilitati, apre la schermata dei metadati invece di quella degli stream. Mostra i dettagli del contenuto e permette la selezione manuale dello stream.", - "card_appearance": "ASPETTO SCHEDA", - "card_style": "Stile scheda", - "card_style_desc": "Scegli come appaiono gli elementi di 'Continua a guardare' nella home", - "wide": "Ampia", - "poster": "Poster", - "cache_settings": "IMPOSTAZIONI CACHE", - "cache_duration": "Durata cache stream", - "cache_duration_desc": "Per quanto tempo conservare i link degli stream in cache prima della scadenza", - "important_note": "Nota importante", - "important_note_text": "Non tutti i link degli stream rimangono attivi per l'intera durata della cache. Tempi di cache più lunghi potrebbero causare link scaduti. Se un link in cache fallisce, l'app tornerà a recuperare nuovi stream.", - "how_it_works": "Come funziona", - "how_it_works_cached": "• Gli stream vengono salvati in cache per la durata selezionata dopo la riproduzione\n• Gli stream in cache vengono convalidati prima dell'uso\n• Se la cache non è valida o è scaduta, si torna alla schermata dei contenuti\n• 'Usa stream in cache' controlla l'apertura diretta del player rispetto alla navigazione\n• 'Apri schermata metadati' appare solo quando gli stream in cache sono disabilitati", - "how_it_works_uncached": "• Quando gli stream in cache sono disabilitati, cliccare su 'Continua a guardare' apre la schermata dei contenuti\n• L'opzione 'Apri schermata metadati' controlla quale schermata aprire\n• La schermata metadati mostra i dettagli e permette la selezione manuale\n• La schermata degli stream mostra gli stream disponibili per la riproduzione immediata", - "changes_saved": "Modifiche salvate", - "min": "min", - "hour": "ora", - "hours": "ore" - }, - "contributors": { - "title": "Collaboratori", - "special_mentions": "Menzioni speciali", - "tab_contributors": "Collaboratori", - "tab_special": "Menzioni speciali", - "tab_donors": "Donatori", - "manager_role": "Community Manager", - "manager_desc": "Gestisce le community Discord e Reddit per Nuvio", - "sponsor_role": "Sponsor del Server", - "sponsor_desc": "Ha sponsorizzato l'infrastruttura del server per Nuvio", - "mod_role": "Moderatore Discord", - "mod_desc": "Aiuta a moderare la community Discord di Nuvio", - "loading": "Caricamento...", - "discord_user": "Utente Discord", - "contributions": "contributi", - "gratitude_title": "Siamo grati per ogni contributo", - "gratitude_desc": "Ogni riga di codice, segnalazione di bug e suggerimento aiuta a rendere Nuvio migliore per tutti", - "special_thanks_title": "Ringraziamenti speciali", - "special_thanks_desc": "Queste persone fantastiche aiutano a mantenere attiva la community di Nuvio e i server online", - "donors_desc": "Grazie per credere in quello che stiamo costruendo. Il vostro supporto mantiene Nuvio gratuito e in continuo miglioramento.", - "latest_donations": "Recenti", - "leaderboard": "Classifica", - "loading_donors": "Caricamento donatori…", - "no_donors": "Nessun donatore ancora", - "error_rate_limit": "Limite di frequenza API GitHub superato. Riprova più tardi o trascina per aggiornare.", - "error_failed": "Impossibile caricare i collaboratori. Controlla la tua connessione internet.", - "retry": "Riprova", - "no_contributors": "Nessun collaboratore trovato", - "loading_contributors": "Caricamento collaboratori..." - }, - "debrid": { - "title": "Integrazione Debrid", - "description_torbox": "Sblocca stream 4K di alta qualità e velocità fulminee integrando Torbox. Inserisci la tua chiave API qui sotto per potenziare istantaneamente la tua esperienza di streaming.", - "description_torrentio": "Configura Torrentio per ottenere stream torrent per film e Serie TV. È necessario un servizio debrid per riprodurre i contenuti.", - "tab_torbox": "TorBox", - "tab_torrentio": "Torrentio", - "status_connected": "Connesso", - "status_disconnected": "Disconnesso", - "enable_addon": "Abilita Addon", - "disconnect_button": "Disconnetti e Rimuovi", - "disconnect_loading": "Disconnessione in corso...", - "account_info": "Informazioni Account", - "plan": "Piano", - "plan_free": "Gratuito", - "plan_essential": "Essential ($3/mese)", - "plan_pro": "Pro ($10/mese)", - "plan_standard": "Standard ($5/mese)", - "plan_unknown": "Sconosciuto", - "expires": "Scade il", - "downloaded": "Scaricati", - "status_active": "Attivo", - "connected_title": "✓ Connesso a TorBox", - "connected_desc": "Il tuo Addon TorBox è attivo e fornisce stream premium.", - "configure_title": "Configura Addon", - "configure_desc": "Personalizza la tua esperienza di streaming. Ordina per qualità, filtra le dimensioni dei file e gestisci altre impostazioni di integrazione.", - "open_settings": "Apri Impostazioni", - "what_is_debrid": "Cos'è un servizio Debrid?", - "enter_api_key": "Inserisci la tua chiave API", - "connect_button": "Connetti e Installa", - "connecting": "Connessione in corso...", - "unlock_speeds_title": "Sblocca velocità Premium", - "unlock_speeds_desc": "Abbonati a Torbox per accedere a stream di alta qualità in cache con zero buffering.", - "get_subscription": "Abbonati ora", - "powered_by": "Offerto da", - "disclaimer_torbox": "Nuvio non è affiliato a Torbox in alcun modo.", - "disclaimer_torrentio": "Nuvio non è affiliato a Torrentio in alcun modo.", - "installed_badge": "✓ INSTALLATO", - "promo_title": "⚡ Serve un servizio Debrid?", - "promo_desc": "Scegli TorBox per streaming 4K velocissimo senza buffering. Torrent in cache premium e download istantanei.", - "promo_button": "Ottieni abbonamento TorBox", - "service_label": "Servizio Debrid *", - "api_key_label": "Chiave API *", - "sorting_label": "Ordinamento", - "exclude_qualities": "Escludi qualità", - "priority_languages": "Lingue prioritarie", - "max_results": "Risultati massimi", - "additional_options": "Opzioni aggiuntive", - "no_download_links": "Non mostrare link di download", - "no_debrid_catalog": "Non mostrare catalogo debrid", - "install_button": "Installa Torrentio", - "installing": "Installazione...", - "update_button": "Aggiorna configurazione", - "updating": "Aggiornamento...", - "remove_button": "Rimuovi Torrentio", - "error_api_required": "Chiave API richiesta", - "error_api_required_desc": "Inserisci la chiave API del tuo servizio debrid per installare Torrentio.", - "success_installed": "Addon Torrentio installato con successo!", - "success_removed": "Addon Torrentio rimosso con successo", - "alert_disconnect_title": "Disconnetti Torbox", - "alert_disconnect_msg": "Sei sicuro di voler disconnettere Torbox? Questo rimuoverà l'Addon e cancellerà la chiave API salvata." - }, - "home_screen": { - "title": "Impostazioni Schermata Home", - "changes_applied": "Modifiche applicate", - "display_options": "OPZIONI DI VISUALIZZAZIONE", - "show_hero": "Mostra sezione Hero", - "show_hero_desc": "Contenuti in evidenza in alto", - "show_this_week": "Mostra sezione 'Questa settimana'", - "show_this_week_desc": "Nuovi episodi della settimana corrente", - "select_catalogs": "Seleziona cataloghi", - "all_catalogs": "Tutti i cataloghi", - "selected": "selezionati", - "prefer_external_meta": "Preferisci Metadati Estrerni", - "prefer_external_meta_desc": "Usa i metadati esterni nella pagina dei dettagli", - "hero_layout": "Layout Hero", - "layout_legacy": "Classico", - "layout_carousel": "Carosello", - "layout_appletv": "Apple TV", - "layout_desc": "Banner a tutta larghezza, schede scorrevoli o stile Apple TV", - "featured_source": "Sorgente in evidenza", - "using_catalogs": "Utilizzo cataloghi", - "manage_selected_catalogs": "Gestisci cataloghi selezionati", - "dynamic_bg": "Sfondo Hero dinamico", - "dynamic_bg_desc": "Banner sfocato dietro il carosello", - "performance_note": "Potrebbe influire sulle prestazioni dei dispositivi meno potenti.", - "posters": "Poster", - "show_titles": "Mostra titoli", - "poster_size": "Dimensione poster", - "poster_corners": "Angoli poster", - "size_small": "Piccolo", - "size_medium": "Medio", - "size_large": "Grande", - "corners_square": "Squadrati", - "corners_rounded": "Arrotondati", - "corners_pill": "Pillola", - "about_these_settings": "INFORMAZIONI SU QUESTE IMPOSTAZIONI", - "about_desc": "Queste impostazioni controllano come i contenuti vengono visualizzati nella Home. Le modifiche vengono applicate immediatamente senza riavviare l'app.", - "hero_catalogs": { - "title": "Cataloghi sezione Hero", - "select_all": "Seleziona tutto", - "clear_all": "Cancella tutto", - "info": "Seleziona quali cataloghi visualizzare nella sezione hero. Se non ne viene selezionato nessuno, verranno usati tutti. Non dimenticare di salvare al termine.", - "settings_saved": "Impostazioni salvate", - "error_load": "Impossibile caricare i cataloghi", - "movies": "Film", - "tv_shows": "Serie TV" - } - }, - "calendar": { - "title": "Calendario", - "loading": "Caricamento calendario...", - "no_scheduled_episodes": "Nessun episodio in programma", - "check_back_later": "Torna più tardi", - "showing_episodes_for": "Episodi per il {{date}}", - "show_all_episodes": "Mostra tutti gli episodi", - "no_episodes_for": "Nessun episodio per il {{date}}", - "no_upcoming_found": "Nessun episodio imminente trovato", - "add_series_desc": "Aggiungi Serie TV alla tua libreria per vedere qui i prossimi episodi" - }, - "mdblist": { - "title": "Sorgenti valutazione", - "status_disabled": "MDBList disabilitato", - "status_active": "Chiave API attiva", - "status_required": "Chiave API richiesta", - "status_disabled_desc": "La funzionalità MDBList è attualmente disabilitata.", - "status_active_desc": "Le valutazioni di MDBList sono abilitate.", - "status_required_desc": "Aggiungi la tua chiave qui sotto per abilitare i voti.", - "enable_toggle": "Abilita MDBList", - "enable_toggle_desc": "Attiva/disattiva tutte le funzioni MDBList", - "api_section": "Chiave API", - "placeholder": "Incolla la tua chiave API MDBList", - "save": "Salva", - "clear": "Rimuovi chiave", - "rating_providers": "Fornitori valutazioni", - "rating_providers_desc": "Scegli quali voti visualizzare nell'app", - "how_to": "Come ottenere una chiave API", - "step_1": "Accedi al", - "step_1_link": "sito web MDBList", - "step_2": "Vai alla sezione", - "step_2_settings": "Impostazioni", - "step_2_api": "API", - "step_2_end": ".", - "step_3": "Genera una nuova chiave e copiala.", - "go_to_website": "Vai su MDBList", - "alert_clear_title": "Rimuovi chiave API", - "alert_clear_msg": "Sei sicuro di voler rimuovere la chiave API salvata?", - "success_saved": "Chiave API salvata con successo.", - "error_empty": "La chiave API non può essere vuota.", - "error_save": "Si è verificato un errore durante il salvataggio. Riprova.", - "api_key_empty_error": "La chiave API non può essere vuota.", - "success_cleared": "Chiave API rimossa con successo", - "error_clear": "Impossibile rimuovere la chiave API" - }, - "notification": { - "title": "Impostazioni Notifiche", - "section_general": "Generali", - "enable_notifications": "Abilita Notifiche", - "section_types": "Tipi di Notifica", - "new_episodes": "Nuovi Episodi", - "upcoming_shows": "Serie in Arrivo", - "reminders": "Promemoria", - "section_timing": "Tempistica Notifiche", - "timing_desc": "Quanto tempo prima della messa in onda vuoi ricevere la notifica?", - "hours_1": "1 ora", - "hours_suffix": "ore", - "section_status": "Stato Notifiche", - "stats_upcoming": "In arrivo", - "stats_this_week": "Questa settimana", - "stats_total": "Totale", - "sync_button": "Sincronizza Libreria e Trakt", - "syncing": "Sincronizzazione...", - "sync_desc": "Sincronizza automaticamente le notifiche per tutte le serie nella tua libreria e nella watchlist/collezione di Trakt.", - "section_advanced": "Avanzate", - "reset_button": "Ripristina tutte le notifiche", - "test_button": "Test Notifica (5 sec)", - "test_notification_in": "Notifica tra {{seconds}}s...", - "test_notification_text": "La notifica apparirà tra {{seconds}} secondi", - "alert_reset_title": "Ripristina Notifiche", - "alert_reset_msg": "Questo cancellerà tutte le notifiche programmate, ma non rimuoverà nulla dalla tua libreria salvata. Sei sicuro?", - "alert_reset_success": "Tutte le notifiche sono state ripristinate", - "alert_sync_complete": "Sincronizzazione Completata", - "alert_sync_msg": "Sincronizzazione notifiche completata con successo per la libreria e gli elementi Trakt.\n\nProgrammati: {{upcoming}} episodi in arrivo\nQuesta settimana: {{thisWeek}} episodi", - "alert_test_scheduled": "Notifica di test programmata per l'invio immediato" - }, - "backup": { - "title": "Backup e Ripristino", - "options_title": "Opzioni Backup", - "options_desc": "Scegli cosa includere nei tuoi backup", - "section_core": "Dati Principali", - "section_addons": "Addon e Integrazioni", - "section_settings": "Impostazioni e Preferenze", - "library_label": "Libreria", - "library_desc": "I tuoi film e Serie TV salvati", - "watch_progress_label": "Progressi di Visione", - "watch_progress_desc": "Posizioni di 'Continua a guardare'", - "addons_label": "Addon", - "addons_desc": "Addon di Stremio installati", - "plugins_label": "Plugin", - "plugins_desc": "Configurazioni scraper personalizzate", - "trakt_label": "Integrazione Trakt", - "trakt_desc": "Dati di sincronizzazione e token di autenticazione", - "app_settings_label": "Impostazioni App", - "app_settings_desc": "Tema, preferenze e configurazioni", - "user_prefs_label": "Preferenze Utente", - "user_prefs_desc": "Ordine degli Addon e impostazioni UI", - "catalog_settings_label": "Impostazioni Catalogo", - "catalog_settings_desc": "Filtri catalogo e preferenze", - "api_keys_label": "Chiavi API", - "api_keys_desc": "Chiavi per MDBList e OpenRouter", - "action_create": "Crea Backup", - "action_restore": "Ripristina da Backup", - "section_info": "Informazioni sui Backup", - "info_text": "• Personalizza gli elementi del backup usando i selettori sopra\n• I file di backup sono salvati localmente sul tuo dispositivo\n• Condividi il backup per trasferire i dati tra dispositivi\n• Il ripristino sovrascriverà i dati attuali", - "alert_create_title": "Crea Backup", - "alert_no_content": "Nessun contenuto selezionato per il backup.\n\nAbilita almeno un'opzione nella sezione Opzioni Backup sopra.", - "alert_backup_created_title": "Backup Creato", - "alert_backup_created_msg": "Il tuo backup è stato creato ed è pronto per essere condiviso.", - "alert_backup_failed_title": "Backup Fallito", - "alert_restore_confirm_title": "Conferma Ripristino", - "alert_restore_confirm_msg": "Questo ripristinerà i tuoi dati da un backup creato il {{date}}.\n\nQuesta azione sovrascriverà i dati attuali. Sei sicuro di voler continuare?", - "alert_restore_complete_title": "Ripristino Completato", - "alert_restore_complete_msg": "I tuoi dati sono stati ripristinati con successo. Riavvia l'app per applicare tutte le modifiche.", - "alert_restore_failed_title": "Ripristino Fallito", - "restart_app": "Riavvia App", - "alert_restart_failed_title": "Riavvio Fallito", - "alert_restart_failed_msg": "Impossibile riavviare l'app. Chiudi e riapri manualmente per vedere i dati ripristinati." - }, - "updates": { - "title": "Aggiornamenti App", - "status_checking": "Controllo aggiornamenti...", - "status_available": "Aggiornamento disponibile!", - "status_downloading": "Download aggiornamento...", - "status_installing": "Installazione aggiornamento...", - "status_success": "Aggiornamento installato con successo!", - "status_error": "Aggiornamento fallito", - "status_ready": "Pronto per controllare gli aggiornamenti", - "action_check": "Controlla Aggiornamenti", - "action_install": "Installa Aggiornamento", - "release_notes": "Note di rilascio:", - "version": "Versione:", - "last_checked": "Ultimo controllo:", - "current_version": "Versione attuale:", - "current_release_notes": "Note di rilascio attuali:", - "github_release": "RELEASE GITHUB", - "current": "Attuale:", - "latest": "Ultima:", - "notes": "Note:", - "view_release": "Vedi Release", - "notification_settings": "IMPOSTAZIONI NOTIFICHE", - "ota_alerts_label": "Avvisi Aggiornamenti OTA", - "ota_alerts_desc": "Mostra notifiche per aggiornamenti over-the-air", - "major_alerts_label": "Avvisi Aggiornamenti Maggiori", - "major_alerts_desc": "Mostra notifiche per nuove versioni dell'app su GitHub", - "alert_disable_ota_title": "Disabilitare Avvisi OTA?", - "alert_disable_ota_msg": "Non riceverai più notifiche automatiche per gli aggiornamenti OTA.\n\n⚠️ Attenzione: Rimanere all'ultima versione è importante per:\n• Correzioni di bug e stabilità\n• Nuove funzioni e miglioramenti\n• Feedback e report sui crash accurati\n\nPuoi comunque controllare manualmente gli aggiornamenti in questa schermata.", - "alert_disable_major_title": "Disabilitare Avvisi Major?", - "alert_disable_major_msg": "Non riceverai più notifiche per gli aggiornamenti maggiori che richiedono la reinstallazione.\n\n⚠️ Attenzione: Gli aggiornamenti maggiori includono spesso:\n• Patch di sicurezza critiche\n• Cambiamenti strutturali che richiedono la reinstallazione\n• Correzioni di compatibilità importanti\n\nPuoi comunque controllare manualmente.", - "warning_note": "Mantenere gli avvisi abilitati garantisce la ricezione delle correzioni e report di crash accurati.", - "disable": "Disabilita", - "alert_no_update_to_install": "Nessun aggiornamento disponibile da installare", - "alert_install_failed": "Installazione aggiornamento fallita", - "alert_no_update_title": "Nessun Aggiornamento", - "alert_update_applied_msg": "L'aggiornamento sarà applicato al prossimo riavvio dell'app" - }, - "player": { - "title": "Video Player", - "section_selection": "SELEZIONE PLAYER", - "internal_title": "Player Integrato", - "internal_desc": "Usa il video player predefinito dell'app", - "vlc_title": "VLC", - "vlc_desc": "Apri gli stream in VLC media player", - "infuse_title": "Infuse", - "infuse_desc": "Apri gli stream nel player Infuse", - "outplayer_title": "OutPlayer", - "outplayer_desc": "Apri gli stream in OutPlayer", - "vidhub_title": "VidHub", - "vidhub_desc": "Apri gli stream nel player VidHub", - "infuse_live_title": "Infuse LiveContainer", - "infuse_live_desc": "Apri gli stream in Infuse tramite LiveContainer", - "external_title": "Player Esterno", - "external_desc": "Apri gli stream nel tuo video player preferito", - "section_playback": "OPZIONI RIPRODUZIONE", - "skip_intro_settings_title": "Salta intro", - "powered_by_introdb": "Offerto da IntroDB", - "autoplay_title": "Auto-play Primo Stream", - "autoplay_desc": "Avvia automaticamente il primo stream mostrato nella lista.", - "resume_title": "Riprendi Sempre", - "resume_desc": "Salta la richiesta di ripresa e continua automaticamente da dove avevi interrotto (se visto meno dell'85%).", - "engine_title": "Motore Video Player", - "engine_desc": "Auto utilizza ExoPlayer con MPV come ripiego. Alcuni formati come Dolby Vision e HDR potrebbero non essere supportati da MPV, quindi Auto è raccomandato.", - "decoder_title": "Modalità Decodificatore", - "decoder_desc": "Come viene decodificato il video. Auto è raccomandato per il miglior bilanciamento.", - "gpu_title": "Rendering GPU", - "gpu_desc": "GPU-Next offre una migliore gestione di HDR e colori.", - "external_downloads_title": "Player Esterno per Download", - "external_downloads_desc": "Riproduci i contenuti scaricati nel tuo player esterno preferito.", - "restart_required": "Riavvio Richiesto", - "restart_msg_decoder": "Riavvia l'app affinché le modifiche al decodificatore abbiano effetto.", - "restart_msg_gpu": "Riavvia l'app affinché le modifiche alla modalità GPU abbiano effetto.", - "option_auto": "Auto", - "option_auto_desc_engine": "ExoPlayer + MPV fallback", - "option_mpv": "MPV", - "option_mpv_desc": "Solo MPV", - "option_auto_desc_decoder": "Miglior bilanciamento", - "option_sw": "SW", - "option_sw_desc": "Software", - "option_hw": "HW", - "option_hw_desc": "Hardware", - "option_hw_plus": "HW+", - "option_hw_plus_desc": "HW Completo", - "option_gpu_desc": "Standard", - "option_gpu_next_desc": "Avanzato" - }, - "plugins": { - "title": "Estensioni", - "enable_title": "Abilita Estensioni", - "enable_desc": "Permetti all'app di usare estensioni installate per trovare fonti multimediali", - "repo_config_title": "Configurazione Repository", - "repo_config_desc": "Gestisci repository di estensioni esterni. Attiva o disattiva ogni repository qui sotto.", - "your_repos": "Repository", - "your_repos_desc": "Configura sorgenti esterne per le estensioni.", - "add_repo_button": "Aggiungi Repository", - "refresh": "Aggiorna", - "remove": "Rimuovi", - "enabled": "Abilitato", - "disabled": "Disabilitato", - "updating": "Aggiornamento...", - "success": "Successo", - "error": "Errore", - "alert_repo_added": "Repository aggiunto ed estensioni caricate con successo", - "alert_repo_saved": "URL del repository salvato con successo", - "alert_repo_refreshed": "Repository aggiornato con successo", - "alert_invalid_url": "Formato URL Non Valido", - "alert_plugins_cleared": "Tutte le estensioni sono state rimosse", - "alert_cache_cleared": "Cache del repository cancellata con successo", - "unknown": "Sconosciuto", - "active": "Attivo", - "available": "Disponibile", - "platform_disabled": "Piattaforma Disabilitata", - "limited": "Limitato", - "clear_all": "Rimuovi Tutte le Estensioni", - "clear_all_desc": "Sei sicuro di voler rimuovere tutte le estensioni installate? L'azione è irreversibile.", - "clear_cache": "Cancella Cache Repository", - "clear_cache_desc": "Questo rimuoverà l'URL salvato e tutti i dati delle estensioni in cache.", - "add_new_repo": "Aggiungi Nuovo Repository", - "available_plugins": "Estensioni Disponibili ({{count}})", - "placeholder": "Cerca estensioni...", - "all": "Tutti", - "filter_all": "Tutti i Tipi", - "filter_movies": "Film", - "filter_tv": "Serie TV", - "enable_all": "Abilita Tutti", - "disable_all": "Disabilita Tutti", - "no_plugins_found": "Nessuna Estensione Trovata", - "no_plugins_available": "Nessuna Estensione Disponibile", - "no_match_desc": "Nessuna estensione corrisponde a \"{{query}}\".", - "configure_repo_desc": "Configura un repository sopra per visualizzare le estensioni disponibili.", - "clear_search": "Cancella Ricerca", - "no_external_player": "Nessun player esterno", - "showbox_token": "Token UI ShowBox", - "showbox_placeholder": "Incolla il tuo token UI ShowBox", - "save": "Salva", - "clear": "Cancella", - "additional_settings": "Impostazioni Aggiuntive", - "enable_url_validation": "Abilita Validazione URL", - "url_validation_desc": "Valida gli URL prima di mostrarli (può rallentare i risultati ma migliora l'affidabilità)", - "group_streams": "Raggruppa Fonti Estensioni", - "group_streams_desc": "Se abilitato, le fonti sono raggruppate per repository. Se disabilitato, ogni estensione appare come provider separato.", - "sort_quality": "Ordina Prima per Qualità", - "sort_quality_desc": "Se abilitato, le fonti sono ordinate prima per qualità. Solo disponibile con il raggruppamento attivo.", - "show_logos": "Mostra Loghi Estensioni", - "show_logos_desc": "Visualizza i loghi delle estensioni accanto ai link multimediali.", - "quality_filtering": "Filtraggio Qualità", - "quality_filtering_desc": "Escludi specifiche qualità video dai risultati. Tocca una qualità per escluderla dalle estensioni.", - "excluded_qualities": "Qualità escluse:", - "language_filtering": "Filtraggio Lingua", - "language_filtering_desc": "Escludi specifiche lingue dai risultati. Tocca una lingua per escluderla dalle estensioni.", - "note": "Nota:", - "language_filtering_note": "Questo filtro si applica solo ai provider che includono informazioni sulla lingua.", - "excluded_languages": "Lingue escluse:", - "about_title": "Informazioni sulle Estensioni", - "about_desc_1": "Le estensioni sono moduli che adattano contenuti da vari protocolli esterni. Girano localmente e sono installate da repository fidati.", - "about_desc_2": "Le estensioni contrassegnate come \"Limitate\" richiedono configurazioni esterne specifiche.", - "help_title": "Configurazione Estensioni", - "help_step_1": "1. **Abilita Estensioni** - Attiva l'interruttore principale", - "help_step_2": "2. **Aggiungi Repository** - Inserisci un URL di repository valido", - "help_step_3": "3. **Aggiorna Repository** - Scarica le estensioni disponibili", - "help_step_4": "4. **Abilita** - Attiva le estensioni che desideri usare", - "got_it": "Capito!", - "repo_format_hint": "Formato: https://raw.githubusercontent.com/user/repo/refs/heads/branch", - "cancel": "Annulla", - "add": "Aggiungi" - }, - "theme": { - "title": "Temi App", - "select_theme": "SELEZIONA TEMA", - "create_custom": "Crea Tema Personalizado", - "options": "OPZIONI", - "use_dominant_color": "Usa colore dominante dall'artwork", - "categories": { - "all": "Tutti i Temi", - "dark": "Temi Scuri", - "colorful": "Colorati", - "custom": "I Miei Temi" - }, - "editor": { - "theme_name_placeholder": "Nome tema", - "save": "Salva", - "primary": "Primario", - "secondary": "Secondario", - "background": "Sfondo", - "invalid_name_title": "Nome Non Valido", - "invalid_name_msg": "Inserisci un nome valido per il tema" - }, - "alerts": { - "delete_title": "Elimina Tema", - "delete_msg": "Sei sicuro di voler eliminare \"{{name}}\"?", - "ok": "OK", - "delete": "Elimina", - "cancel": "Annulla", - "back": "Impostazioni" - } - }, - "legal": { - "title": "Note Legali & Disclaimer", - "intro_title": "Natura dell'Applicazione", - "intro_text": "Nuvio è un lettore multimediale e un'applicazione per la gestione dei metadati. Agisce unicamente come interfaccia lato client per sfogliare metadati pubblicamente disponibili (film, serie TV, ecc.) e riprodurre file multimediali forniti dall'utente o da estensioni di terze parti. Nuvio non ospita, archivia, distribuisce né indicizza alcun contenuto multimediale autonomamente.", - "extensions_title": "Estensioni di Terze Parti", - "extensions_text": "Nuvio utilizza un'architettura estensibile che consente agli utenti di installare plugin di terze parti (estensioni). Queste estensioni sono sviluppate e mantenute da sviluppatori indipendenti non affiliati a Nuvio. Non abbiamo alcun controllo su, e non ci assumiamo alcuna responsabilità per, il contenuto, la legalità o la funzionalità di qualsiasi estensione di terze parti.", - "user_resp_title": "Responsabilità dell'Utente", - "user_resp_text": "Gli utenti sono gli unici responsabili delle estensioni che installano e dei contenuti a cui accedono. Utilizzando questa applicazione, accetti di garantire di avere il diritto legale di accedere a qualsiasi contenuto visualizzato utilizzando Nuvio. Gli sviluppatori di Nuvio non sostengono né incoraggiano la violazione del copyright.", - "dmca_title": "Copyright e DMCA", - "dmca_text": "Rispettiamo i diritti di proprietà intellettuale altrui. Poiché Nuvio non ospita alcun contenuto, non possiamo rimuovere contenuti da Internet. Tuttavia, se ritieni che l'interfaccia stessa dell'applicazione violi i tuoi diritti, ti preghiamo di contattarci.", - "warranty_title": "Nessuna Garanzia", - "warranty_text": "Questo software è fornito \"così com'è\", senza garanzia di alcun tipo, espressa o implicita. In nessun caso gli autori o i detentori del copyright saranno responsabili per qualsiasi reclamo, danno o altra responsabilità derivante dall'uso di questo software." - }, - "plugin_tester": { - "title": "Tester Plugin", - "subtitle": "Esegui scraper e ispeziona log in tempo reale", - "tabs": { - "individual": "Individuale", - "repo": "Tester Repo", - "code": "Codice", - "logs": "Log", - "results": "Risultati" - }, - "common": { - "error": "Errore", - "success": "Successo", - "movie": "Film", - "tv": "Serie TV", - "tmdb_id": "ID TMDB", - "season": "Stagione", - "episode": "Episodio", - "running": "In esecuzione…", - "run_test": "Esegui Test", - "play": "Riproduci", - "done": "Fatto", - "test": "Test", - "testing": "Test in corso…" - }, - "individual": { - "load_from_url": "Carica da URL", - "load_from_url_desc": "Incolla un URL GitHub raw o IP locale e tocca scarica.", - "enter_url_error": "Inserisci un URL", - "code_loaded": "Codice caricato da URL", - "fetch_error": "Caricamento fallito: {{message}}", - "no_code_error": "Nessun codice da eseguire", - "plugin_code": "Codice Plugin", - "focus_editor": "Focus editor", - "code_placeholder": "// Incolla il codice del plugin qui...", - "test_parameters": "Parametri di Test", - "no_logs": "Nessun log. Esegui un test per vedere l'output.", - "no_streams": "Nessun stream trovato.", - "streams_found": "{{count}} Stream Trovato", - "streams_found_plural": "{{count}} Stream Trovati", - "tap_play_hint": "Tocca Riproduci per testare uno stream nel player nativo.", - "unnamed_stream": "Stream Senza Nome", - "quality": "Qualità: {{quality}}", - "size": "Dimensione: {{size}}", - "url_label": "URL: {{url}}", - "headers_info": "Header: {{count}} header personalizzati", - "find_placeholder": "Cerca nel codice…", - "edit_code_title": "Modifica Codice", - "no_url_stream_error": "Nessun URL trovato per questo stream" - }, - "repo": { - "title": "Tester Repo", - "description": "Recupera un repository (URL locale o GitHub raw) e testa ogni provider.", - "enter_repo_url_error": "Inserisci un URL del repository", - "invalid_url_title": "URL Non Valido", - "invalid_url_msg": "Usa un URL GitHub raw o un URL locale http(s).\n\nEsempio:\nhttps://raw.githubusercontent.com/tapframe/nuvio-providers/refs/heads/main", - "manifest_build_error": "Impossibile creare un URL manifest dall'input", - "manifest_fetch_error": "Caricamento manifest fallito", - "repo_manifest_fetch_error": "Caricamento manifest repo fallito", - "missing_filename": "Nome file mancante nel manifest", - "scraper_build_error": "Impossibile creare un URL scraper", - "download_scraper_error": "Download scraper fallito", - "test_failed": "Test fallito", - "test_parameters": "Parametri Test Repo", - "test_parameters_desc": "Questi parametri sono usati solo per il Tester Repo.", - "using_info": "Uso: {{mediaType}} • TMDB {{tmdbId}}", - "using_info_tv": "Uso: {{mediaType}} • TMDB {{tmdbId}} • S{{season}}E{{episode}}", - "providers_title": "Provider", - "repository_default": "Repository", - "providers_count": "{{count}} provider", - "fetch_hint": "Recupera un repo per elencare i provider.", - "test_all": "Testa Tutto", - "status_running": "IN ESECUZIONE", - "status_ok": "OK ({{count}})", - "status_ok_empty": "OK (0)", - "status_failed": "FALLITO", - "status_idle": "INATTIVO", - "tried_url": "Provato: {{url}}", - "provider_logs": "Log Provider", - "no_logs_captured": "Nessun log catturato." - } - } + "common": { + "loading": "Caricamento...", + "cancel": "Annulla", + "save": "Salva", + "delete": "Elimina", + "edit": "Modifica", + "search": "Cerca", + "error": "Errore", + "success": "Successo", + "ok": "OK", + "unknown": "Sconosciuto", + "retry": "Riprova", + "try_again": "Riprova", + "go_back": "Torna indietro", + "settings": "Impostazioni", + "close": "Chiudi", + "enable": "Abilita", + "disable": "Disabilita", + "show_more": "Mostra altro", + "show_less": "Mostra meno", + "load_more": "Carica altro", + "clear": "Cancella", + "unknown_date": "Data sconosciuta", + "anonymous_user": "Utente anonimo", + "time": { + "now": "Proprio ora", + "minutes_ago": "{{count}}m fa", + "hours_ago": "{{count}}o fa", + "days_ago": "{{count}}g fa" + }, + "days_short": { + "sun": "Dom", + "mon": "Lun", + "tue": "Mar", + "wed": "Mer", + "thu": "Gio", + "fri": "Ven", + "sat": "Sab" + }, + "email": "Email", + "status": "Stato" + }, + "home": { + "categories": { + "movies": "Film", + "series": "Serie TV", + "channels": "Canali" + }, + "movies": "Film", + "tv_shows": "Serie TV", + "load_more_catalogs": "Carica altri cataloghi", + "no_content": "Nessun contenuto disponibile", + "add_catalogs": "Aggiungi cataloghi", + "sign_in_available": "Accesso disponibile", + "sign_in_desc": "Puoi accedere in qualsiasi momento da Impostazioni → Account", + "view_all": "Vedi tutto", + "this_week": "Questa settimana", + "upcoming": "In arrivo", + "recently_released": "Rilasciati di recente", + "no_scheduled_episodes": "Serie TV senza episodi programmati", + "check_back_later": "Torna a controllare più tardi", + "continue_watching": "Continua a guardare", + "up_next": "Prossimi titoli", + "up_next_caps": "PROSSIMI TITOLI", + "released": "Rilasciato", + "new": "Nuovo", + "tba": "Da annunciare", + "new_episodes": "{{count}} Nuovi episodi", + "season_short": "S{{season}}", + "episode_short": "E{{episode}}", + "season": "Stagione {{season}}", + "episode": "Episodio {{episode}}", + "movie": "Film", + "series": "Serie TV", + "tv_show": "Serie TV", + "percent_watched": "{{percent}}% guardato", + "view_details": "Visualizza dettagli", + "remove": "Rimuovi", + "play": "Riproduci", + "play_now": "Riproduci ora", + "resume": "Riprendi", + "info": "Info", + "more_info": "Più informazioni", + "my_list": "La mia lista", + "save": "Salva", + "saved": "Salvato", + "retry": "Riprova", + "install_addons": "Installa Addon", + "settings": "Impostazioni", + "no_featured_content": "Nessun contenuto in evidenza", + "couldnt_load_featured": "Impossibile caricare i contenuti in evidenza", + "no_featured_desc": "Installa addon con cataloghi o cambia la sorgente dei contenuti nelle impostazioni.", + "load_error_desc": "Si è verificato un problema nel recupero dei contenuti in evidenza. Controlla la tua connessione e riprova.", + "no_featured_available": "Nessun contenuto in evidenza disponibile", + "no_description": "Nessuna descrizione disponibile" + }, + "navigation": { + "home": "Home", + "library": "Libreria", + "search": "Cerca", + "downloads": "Download", + "settings": "Impostazioni" + }, + "search": { + "title": "Cerca", + "recent_searches": "Ricerche recenti", + "discover": "Scopri", + "movies": "Film", + "tv_shows": "Serie TV", + "select_catalog": "Seleziona catalogo", + "all_genres": "Tutti i generi", + "discovering": "Scoperta contenuti in corso...", + "show_more": "Mostra altro ({{count}})", + "no_content_found": "Nessun contenuto trovato", + "try_different": "Prova un genere o un catalogo differente", + "select_catalog_desc": "Seleziona un catalogo da esplorare", + "tap_catalog_desc": "Tocca il catalogo qui sopra per iniziare", + "placeholder": "Cerca film, Serie TV...", + "keep_typing": "Continua a scrivere...", + "type_characters": "Scrivi almeno 2 caratteri per cercare", + "no_results": "Nessun risultato trovato", + "try_keywords": "Prova parole chiave diverse o controlla l'ortografia", + "select_type": "Seleziona tipo", + "browse_movies": "Sfoglia i cataloghi dei film", + "browse_tv": "Sfoglia i cataloghi delle Serie TV", + "select_genre": "Seleziona genere", + "show_all_content": "Mostra tutti i contenuti", + "genres_count": "{{count}} generi" + }, + "library": { + "title": "Libreria", + "watched": "Visti", + "continue": "Continua", + "watchlist": "Watchlist", + "collection": "Collezione", + "rated": "Valutati", + "items": "elementi", + "trakt_collections": "Collezioni Trakt", + "trakt_collection": "Collezione Trakt", + "no_trakt": "Nessuna collezione Trakt", + "no_trakt_desc": "Le tue collezioni Trakt appariranno qui non appena inizierai a usare Trakt", + "load_collections": "Carica collezioni", + "empty_folder": "Nessun contenuto in {{folder}}", + "empty_folder_desc": "Questa collezione è vuota", + "refresh": "Aggiorna", + "no_movies": "Ancora nessun film", + "no_series": "Ancora nessuna Serie TV", + "no_content": "Ancora nessun contenuto", + "add_content_desc": "Aggiungi dei contenuti alla tua libreria per vederli qui", + "find_something": "Trova qualcosa da guardare", + "removed_from_library": "Rimosso dalla Libreria", + "item_removed": "Elemento rimosso dalla tua libreria", + "failed_update_library": "Aggiornamento Libreria fallito", + "unable_remove": "Impossibile rimuovere l'elemento dalla libreria", + "marked_watched": "Segnato come Visto", + "marked_unwatched": "Segnato come Non Visto", + "item_marked_watched": "Elemento segnato come visto", + "item_marked_unwatched": "Elemento segnato come non visto", + "failed_update_watched": "Aggiornamento stato visione fallito", + "unable_update_watched": "Impossibile aggiornare lo stato di visione", + "added_to_library": "Aggiunto alla Libreria", + "item_added": "Aggiunto alla tua libreria locale", + "add_to_library": "Aggiungi alla Libreria", + "remove_from_library": "Rimuovi dalla Libreria", + "mark_watched": "Segna come visto", + "mark_unwatched": "Segna come non visto", + "share": "Condividi", + "add_to_watchlist": "Aggiungi alla Watchlist Trakt", + "remove_from_watchlist": "Rimuovi dalla Watchlist Trakt", + "added_to_watchlist": "Aggiunto alla Watchlist", + "added_to_watchlist_desc": "Aggiunto alla tua watchlist Trakt", + "removed_from_watchlist": "Rimosso dalla Watchlist", + "removed_from_watchlist_desc": "Rimosso dalla tua watchlist Trakt", + "add_to_collection": "Aggiungi alla Collezione Trakt", + "remove_from_collection": "Rimuovi dalla Collezione Trakt", + "added_to_collection": "Aggiunto alla Collezione", + "added_to_collection_desc": "Aggiunto alla tua collezione Trakt", + "removed_from_collection": "Rimosso dalla Collezione", + "removed_from_collection_desc": "Rimosso dalla tua collezione Trakt" + }, + "metadata": { + "unable_to_load": "Impossibile caricare il contenuto", + "error_code": "Codice errore: {{code}}", + "content_not_found": "Contenuto non trovato", + "content_not_found_desc": "Questo contenuto non esiste o potrebbe essere stato rimosso.", + "server_error": "Errore del server", + "server_error_desc": "Il server è temporaneamente non disponibile. Riprova più tardi.", + "bad_gateway": "Bad gateway", + "bad_gateway_desc": "Il server sta riscontrando dei problemi. Riprova più tardi.", + "service_unavailable": "Servizio non disponibile", + "service_unavailable_desc": "Il servizio è attualmente in manutenzione. Riprova più tardi.", + "too_many_requests": "Troppe richieste", + "too_many_requests_desc": "Stai effettuando troppe richieste. Attendi un momento e riprova.", + "request_timeout": "Richiesta scaduta", + "request_timeout_desc": "La richiesta ha impiegato troppo tempo. Riprova.", + "network_error": "Errore di rete", + "network_error_desc": "Controlla la tua connessione internet e riprova.", + "auth_error": "Errore di autenticazione", + "auth_error_desc": "Controlla le impostazioni del tuo account e riprova.", + "access_denied": "Accesso negato", + "access_denied_desc": "Non hai i permessi per accedere a questo contenuto.", + "connection_error": "Errore di connessione", + "streams_unavailable": "Streaming non disponibili", + "streams_unavailable_desc": "Le sorgenti di streaming sono attualmente non disponibili. Riprova più tardi.", + "unknown_error": "Errore sconosciuto", + "something_went_wrong": "Qualcosa è andato storto. Riprova.", + "cast": "Cast", + "more_like_this": "Altri titoli simili", + "collection": "Collezione", + "episodes": "Episodi", + "seasons": "Stagioni", + "posters": "Poster", + "banners": "Banner", + "specials": "Speciali", + "season_number": "Stagione {{number}}", + "episode_count": "{{count}} Episodio", + "episode_count_plural": "{{count}} Episodi", + "no_episodes": "Nessun episodio disponibile", + "no_episodes_for_season": "Nessun episodio disponibile per la Stagione {{season}}", + "episodes_not_released": "Gli episodi potrebbero non essere ancora stati rilasciati", + "no_description": "Nessuna descrizione disponibile", + "episode_label": "EPISODIO {{number}}", + "watch_again": "Guarda di nuovo", + "completed": "Completato", + "play_episode": "Riproduci {{season}}x{{episode}}", + "play": "Riproduci", + "watched": "Visto", + "watched_on_trakt": "Visto su Trakt", + "synced_with_trakt": "Sincronizzato con Trakt", + "saved": "Salvato", + "director": "Regista", + "directors": "Registi", + "creator": "Creatore", + "creators": "Creatori", + "production": "Produzione", + "network": "Rete", + "mark_watched": "Segna come visto", + "mark_unwatched": "Segna come non visto", + "marking": "Segnatura...", + "removing": "Rimozione...", + "unmark_season": "Deseleziona Stagione {{season}}", + "mark_season": "Segna Stagione {{season}} come vista", + "resume": "Riprendi", + "spoiler_warning": "Avviso Spoiler", + "spoiler_warning_desc": "Questo commento contiene spoiler. Sei sicuro di volerlo visualizzare?", + "cancel": "Annulla", + "reveal_spoilers": "Mostra Spoiler", + "movie_details": "Dettagli Film", + "show_details": "Dettagli Serie TV", + "tagline": "Slogan", + "status": "Stato", + "release_date": "Data di rilascio", + "runtime": "Durata", + "budget": "Budget", + "revenue": "Incasso", + "origin_country": "Paese d'origine", + "original_language": "Lingua originale", + "first_air_date": "Data prima messa in onda", + "last_air_date": "Data ultima messa in onda", + "total_episodes": "Episodi totali", + "episode_runtime": "Durata episodio", + "created_by": "Creato da", + "backdrop_gallery": "Galleria sfondi", + "loading_episodes": "Caricamento episodi...", + "no_episodes_available": "Nessun episodio disponibile", + "play_next": "Riproduci S{{season}}E{{episode}}", + "play_next_episode": "Riproduci prossimo episodio", + "save": "Salva", + "percent_watched": "{{percent}}% guardato", + "percent_watched_trakt": "{{percent}}% guardato ({{traktPercent}}% su Trakt)", + "synced_with_trakt_progress": "Sincronizzato con Trakt", + "using_trakt_progress": "Utilizzo progressi Trakt", + "added_to_collection_hero": "Aggiunto alla collezione", + "added_to_collection_desc_hero": "Aggiunto alla tua collezione Trakt", + "removed_from_collection_hero": "Rimosso dalla collezione", + "removed_from_collection_desc_hero": "Rimosso dalla tua collezione Trakt", + "mark_as_watched": "Segna come visto", + "mark_as_unwatched": "Segna come non visto" + }, + "cast": { + "biography": "Biografia", + "known_for": "Conosciuto per", + "personal_info": "Informazioni personali", + "born_in": "Nato a {{place}}", + "filmography": "Filmografia", + "also_known_as": "Conosciuto anche come", + "no_info_available": "Nessuna informazione aggiuntiva disponibile", + "as_character": "nel ruolo di {{character}}", + "loading_details": "Caricamento dettagli...", + "years_old": "{{age}} anni", + "view_filmography": "Visualizza filmografia", + "filter": "Filtra", + "sort_by": "Ordina per", + "sort_popular": "Popolarità", + "sort_latest": "Ultimi prodotti", + "sort_upcoming": "In arrivo", + "upcoming_badge": "IN ARRIVO", + "coming_soon": "Prossimamente", + "filmography_count": "Filmografia • {{count}} titoli", + "loading_filmography": "Caricamento filmografia...", + "load_more_remaining": "Carica altri ({{count}} rimanenti)", + "alert_error_title": "Errore", + "alert_error_message": "Impossibile caricare \"{{title}}\". Riprova più tardi.", + "alert_ok": "OK", + "no_upcoming": "Nessuna uscita imminente disponibile per questo attore", + "no_content": "Nessun contenuto disponibile per questo attore", + "no_movies": "Nessun film disponibile per questo attore", + "no_tv": "Nessuna Serie TV disponibile per questo attore" + }, + "comments": { + "title": "Commenti Trakt", + "spoiler_warning": "⚠️ Questo commento contiene spoiler. Tocca per visualizzare.", + "spoiler": "Spoiler", + "contains_spoilers": "Contiene spoiler", + "reveal": "Mostra", + "vip": "VIP", + "unavailable": "Commenti non disponibili", + "no_comments": "Ancora nessun commento su Trakt", + "not_in_database": "Questo contenuto potrebbe non essere ancora nel database di Trakt", + "check_trakt": "Controlla su Trakt" + }, + "trailers": { + "title": "Trailer", + "official_trailers": "Trailer ufficiali", + "official_trailer": "Trailer ufficiale", + "teasers": "Teaser", + "teaser": "Teaser", + "clips_scenes": "Clip e scene", + "clip": "Clip", + "featurettes": "Featurette", + "featurette": "Featurette", + "behind_the_scenes": "Dietro le quinte", + "no_trailers": "Nessun trailer disponibile", + "unavailable": "Trailer non disponibile", + "unavailable_desc": "Impossibile caricare questo trailer al momento. Riprova più tardi.", + "unable_to_play": "Impossibile riprodurre il trailer. Riprova.", + "watch_on_youtube": "Guarda su YouTube" + }, + "catalog": { + "no_content_found": "Nessun contenuto trovato", + "no_content_filters": "Nessun contenuto trovato per i filtri selezionati", + "loading_content": "Caricamento contenuti...", + "back": "Indietro", + "in_theaters": "Al cinema", + "all": "Tutti", + "failed_tmdb": "Impossibile caricare i contenuti da TMDB", + "movies": "Film", + "tv_shows": "Serie TV", + "channels": "Canali" + }, + "streams": { + "back_to_episodes": "Torna agli episodi", + "back_to_info": "Torna alle info", + "fetching_from": "Recupero da:", + "no_sources_available": "Nessuna sorgente streaming disponibile", + "add_sources_desc": "Aggiungi sorgenti streaming nelle impostazioni", + "add_sources": "Aggiungi sorgenti", + "finding_streams": "Ricerca streaming disponibili...", + "finding_best_stream": "Ricerca del miglior streaming per l'avvio automatico...", + "still_fetching": "Recupero streaming in corso...", + "no_streams_available": "Nessuno streaming disponibile", + "starting_best_stream": "Avvio del miglior streaming...", + "loading_more_sources": "Caricamento di altre sorgenti..." + }, + "player_ui": { + "via": "via {{name}}", + "audio_tracks": "Tracce audio", + "no_audio_tracks": "Nessuna traccia audio disponibile", + "playback_speed": "Velocità di riproduzione", + "on_hold": "In attesa", + "playback_error": "Errore di riproduzione", + "unknown_error": "Si è verificato un errore sconosciuto durante la riproduzione.", + "copy_error": "Copia dettagli errore", + "copied_to_clipboard": "Copiato negli appunti", + "dismiss": "Chiudi", + "continue_watching": "Continua a guardare", + "start_over": "Ricomincia dall'inizio", + "resume": "Riprendi", + "change_source": "Cambia sorgente", + "switching_source": "Cambio sorgente in corso...", + "no_sources_found": "Nessuna sorgente trovata", + "sources": "Sorgenti", + "finding_sources": "Ricerca sorgenti...", + "unknown_source": "Sorgente sconosciuta", + "sources_limited": "Le sorgenti potrebbero essere limitate a causa di errori del provider.", + "episodes": "Episodi", + "specials": "Speciali", + "season": "Stagione {{season}}", + "stream": "Streaming {{number}}", + "subtitles": "Sottotitoli", + "built_in": "Integrati", + "addons": "Addon", + "style": "Stile", + "none": "Nessuno", + "search_online_subtitles": "Cerca sottotitoli online", + "preview": "Anteprima", + "quick_presets": "Predefiniti rapidi", + "default": "Predefinito", + "yellow": "Giallo", + "high_contrast": "Contrasto elevato", + "large": "Grande", + "core": "Base", + "font_size": "Dimensione carattere", + "show_background": "Mostra sfondo", + "advanced": "Avanzate", + "position": "Posizione", + "text_color": "Colore testo", + "align": "Allineamento", + "bottom_offset": "Distanza dal basso", + "background_opacity": "Opacità sfondo", + "text_shadow": "Ombra testo", + "on": "Attivo", + "off": "Disattivo", + "outline_color": "Colore contorno", + "outline": "Contorno", + "outline_width": "Larghezza contorno", + "letter_spacing": "Spaziatura lettere", + "line_height": "Altezza riga", + "timing_offset": "Ritardo sincronizzazione (s)", + "visual_sync": "Sincronizzazione visiva", + "timing_hint": "Anticipa (-) o ritarda (+) i sottotitoli per sincronizzarli se necessario.", + "reset_defaults": "Ripristina predefiniti", + "mark_intro_start": "Segna Inizio Intro", + "mark_intro_end": "Segna Fine Intro", + "intro_start_marked": "Inizio intro segnato", + "intro_submitted": "Intro registrata con successo", + "intro_submit_failed": "Errore Registrazione Intro" + }, + "downloads": { + "title": "Download", + "no_downloads": "Ancora nessun download", + "no_downloads_desc": "I contenuti scaricati appariranno qui per la visione offline", + "explore": "Esplora contenuti", + "path_copied": "Percorso copiato", + "path_copied_desc": "Percorso del file locale copiato negli appunti", + "copied": "Copiato", + "incomplete": "Download incompleto", + "incomplete_desc": "Il download non è ancora terminato", + "not_available": "Non disponibile", + "not_available_desc": "Il percorso del file locale è disponibile solo al termine del download.", + "status_downloading": "Download in corso", + "status_completed": "Completato", + "status_paused": "In pausa", + "status_error": "Errore", + "status_queued": "In coda", + "status_unknown": "Sconosciuto", + "provider": "Provider", + "streaming_playlist_warning": "Potrebbe non funzionare - playlist di streaming", + "remaining": "rimanenti", + "not_ready": "Download non pronto", + "not_ready_desc": "Attendi il completamento del download.", + "filter_all": "Tutti", + "filter_active": "Attivi", + "filter_done": "Completati", + "filter_paused": "In pausa", + "no_filter_results": "Nessun download per il filtro {{filter}}", + "try_different_filter": "Prova a selezionare un filtro diverso", + "limitations_title": "Limitazioni del download", + "limitations_msg": "• I file più piccoli di 1MB sono solitamente playlist streaming M3U8 e non possono essere scaricati per la visione offline. Funzionano solo con lo streaming online e contengono link ai segmenti video, non il contenuto video effettivo.", + "remove_title": "Rimuovi download", + "remove_confirm": "Rimuovere \"{{title}}\"{{season_episode}}?", + "cancel": "Annulla", + "remove": "Rimuovi" + }, + "addons": { + "title": "Addon", + "reorder_mode": "Modalità riordino", + "reorder_info": "Gli Addon in alto hanno priorità maggiore durante il caricamento dei contenuti", + "add_addon_placeholder": "URL Addon", + "add_button": "Aggiungi Addon", + "my_addons": "I miei Addon", + "community_addons": "Addon della community", + "no_addons": "Nessun Addon installato", + "uninstall_title": "Disinstalla Addon", + "uninstall_message": "Sei sicuro di voler disinstallare {{name}}?", + "uninstall_button": "Disinstalla", + "install_success": "Addon installato con successo", + "install_error": "Installazione Addon fallita", + "load_error": "Caricamento Addon fallito", + "fetch_error": "Impossibile recuperare i dettagli dell'Addon", + "invalid_url": "Inserisci un URL Addon valido", + "configure": "Configura", + "version": "Versione: {{version}}", + "installed_addons": "ADDON INSTALLATI", + "reorder_drag_title": "TRASCINA GLI ADDON PER RIORDINARLI", + "install": "Installa", + "config_unavailable_title": "Configurazione non disponibile", + "config_unavailable_msg": "Impossibile determinare l'URL di configurazione per questo Addon.", + "cannot_open_config_title": "Impossibile aprire la configurazione", + "cannot_open_config_msg": "L'URL di configurazione ({{url}}) non può essere aperto. L'Addon potrebbe non avere una pagina di configurazione.", + "description": "Descrizione", + "supported_types": "Tipi supportati", + "catalogs": "Cataloghi", + "no_description": "Nessuna descrizione disponibile", + "overview": "PANORAMICA", + "no_categories": "Nessuna categoria", + "pre_installed": "PRE-INSTALLATO" + }, + "trakt": { + "title": "Impostazioni Trakt", + "settings_title": "Impostazioni Trakt", + "connect_title": "Connettiti a Trakt", + "connect_desc": "Sincronizza cronologia, watchlist e collezione con Trakt.tv", + "sign_in": "Accedi con Trakt", + "sign_out": "Esci", + "sign_out_confirm": "Sei sicuro di voler uscire dal tuo account Trakt?", + "joined": "Iscritto dal {{date}}", + "sync_settings_title": "Impostazioni Sincronizzazione", + "sync_info": "Quando sei connesso a Trakt, l'intera cronologia viene sincronizzata direttamente dall'API e non viene salvata localmente. La tua lista 'Continua a guardare' riflette i tuoi progressi globali su Trakt.", + "auto_sync_label": "Sincronizzazione automatica progressi", + "auto_sync_desc": "Sincronizza automaticamente i progressi di visione su Trakt", + "import_history_label": "Importa cronologia visioni", + "import_history_desc": "Usa \"Sincronizza ora\" per importare la cronologia e i progressi da Trakt", + "sync_now_button": "Sincronizza ora", + "display_settings_title": "Impostazioni Visualizzazione", + "show_comments_label": "Mostra commenti Trakt", + "show_comments_desc": "Visualizza i commenti di Trakt nelle schede dei contenuti quando disponibili", + "maintenance_title": "In manutenzione", + "maintenance_unavailable": "Trakt non disponibile", + "maintenance_desc": "L'integrazione con Trakt è temporaneamente sospesa per manutenzione. Tutte le funzioni di sincronizzazione e autenticazione sono disabilitate fino al termine dei lavori.", + "maintenance_button": "Servizio in manutenzione", + "auth_success_title": "Connessione riuscita", + "auth_success_msg": "Il tuo account Trakt è stato collegato correttamente.", + "auth_error_title": "Errore di autenticazione", + "auth_error_msg": "Impossibile completare l'autenticazione con Trakt.", + "auth_error_generic": "Si è verificato un errore durante l'autenticazione.", + "sign_out_error": "Impossibile uscire da Trakt.", + "sync_complete_title": "Sincronizzazione completata", + "sync_success_msg": "Progressi di visione sincronizzati con successo con Trakt.", + "sync_error_msg": "Sincronizzazione fallita. Riprova.", + "disclaimer": "Questo prodotto usa l'API di Trakt ma non è sponsorizzato nè certificato da Trakt." + }, + "simkl": { + "title": "Impostazioni Simkl", + "settings_title": "Impostazioni Simkl", + "connect_title": "Connetti con Simkl", + "connect_desc": "Sincronizza la tua cronologia di visione e traccia ciò che guardi", + "sign_in": "Accedi con Simkl", + "sign_out": "Disconnetti", + "sign_out_confirm": "Sei sicuro di voler disconnettere da Simkl?", + "syncing_desc": "I tuoi elementi guardati sono in sincronizzazione con Simkl.", + "auth_success_title": "Connesso con successo", + "auth_success_msg": "Il tuo account Simkl è stato connesso con successo.", + "auth_error_title": "Errore di autenticazione", + "auth_error_msg": "Impossibile completare l'autenticazione con Simkl.", + "auth_error_generic": "Si è verificato un errore durante l'autenticazione.", + "sign_out_error": "Impossibile disconnettere da Simkl.", + "config_error_title": "Errore di configurazione", + "config_error_msg": "L'ID client Simkl manca nelle variabili d'ambiente.", + "conflict_title": "Conflitto", + "conflict_msg": "Non puoi connettere Simkl mentre Trakt è connesso. Disconnetti prima Trakt.", + "disclaimer": "Nuvio non è affiliato con Simkl." + }, + "tmdb_settings": { + "title": "Impostazioni TMDB", + "metadata_enrichment": "Arricchimento metadati", + "metadata_enrichment_desc": "Migliora i metadati dei contenuti con i dati di TMDB per maggiori dettagli e informazioni.", + "enable_enrichment": "Abilita arricchimento", + "enable_enrichment_desc": "Integra i metadati degli Addon con TMDB per cast, certificazioni, loghi/poster e info di produzione.", + "localized_text": "Testo localizzato", + "localized_text_desc": "Recupera titoli e descrizioni nella tua lingua preferita da TMDB.", + "language": "Lingua", + "change": "Cambia", + "logo_preview": "Anteprima Logo", + "logo_preview_desc": "L'anteprima mostra come appariranno i loghi localizzati nella lingua selezionata.", + "example": "Esempio:", + "no_logo": "Nessun logo disponibile", + "enrichment_options": "Opzioni di arricchimento", + "enrichment_options_desc": "Controlla quali dati recuperare da TMDB. Le opzioni disabilitate useranno i dati dell'Addon, se disponibili.", + "cast_crew": "Cast e Troupe", + "cast_crew_desc": "Attori, registi, sceneggiatori con foto profilo", + "title_description": "Titolo e Descrizione", + "title_description_desc": "Usa titolo e panoramica localizzati di TMDB", + "title_logos": "Loghi Titolo", + "title_logos_desc": "Immagini di alta qualità per il titolo", + "banners_backdrops": "Banner e Sfondi", + "banners_backdrops_desc": "Immagini di sfondo ad alta risoluzione", + "certification": "Certificazione contenuti", + "certification_desc": "Classificazione per età (PG-13, R, TV-MA, ecc.)", + "recommendations": "Consigliati", + "recommendations_desc": "Suggerimenti per contenuti simili", + "episode_data": "Dati episodi", + "episode_data_desc": "Miniature episodi, info e fallback per le Serie TV", + "season_posters": "Poster stagioni", + "season_posters_desc": "Immagini poster specifiche per le stagioni", + "production_info": "Info di produzione", + "production_info_desc": "Reti e case di produzione con loghi", + "movie_details": "Dettagli Film", + "movie_details_desc": "Budget, incassi, durata, slogan", + "tv_details": "Dettagli Serie TV", + "tv_details_desc": "Stato, numero stagioni, reti, creatori", + "movie_collections": "Collezioni Film", + "movie_collections_desc": "Franchise cinematografici (Marvel, Star Wars, ecc.)", + "api_configuration": "Configurazione API", + "api_configuration_desc": "Configura il tuo accesso all'API di TMDB per funzionalità avanzate.", + "custom_api_key": "Chiave API personalizzata", + "custom_api_key_desc": "Usa la tua chiave di TMDB per prestazioni migliori e limiti di frequenza dedicati.", + "custom_key_active": "Chiave API personalizzata attiva", + "api_key_required": "Chiave API richiesta", + "api_key_placeholder": "Incolla la tua chiave API TMDB (v3)", + "how_to_get_key": "Come ottenere una chiave API TMDB?", + "built_in_key_msg": "Attualmente in uso la chiave API integrata. Considera l'uso di una chiave tua per prestazioni migliori.", + "cache_size": "Dimensione Cache", + "clear_cache": "Cancella Cache", + "cache_days": "Le risposte TMDB vengono memorizzate per 7 giorni per migliorare le prestazioni", + "choose_language": "Scegli Lingua", + "choose_language_desc": "Seleziona la lingua preferita per i contenuti TMDB", + "search_language_placeholder": "Ricerca lingue...", + "popular": "Popolari", + "all_languages": "Tutte le lingue", + "search_results": "Risultati di ricerca", + "no_languages_found": "Nessuna lingua trovata per ", + "clear_search": "Cancella ricerca", + "clear_cache_title": "Cancella Cache TMDB", + "clear_cache_msg": "Questo cancellerà tutti i dati TMDB memorizzati ({{size}}). Il caricamento potrebbe rallentare temporaneamente fino alla ricostruzione della cache.", + "clear_cache_success": "Cache TMDB cancellata con successo.", + "clear_cache_error": "Impossibile cancellare la cache.", + "clear_api_key_title": "Rimuovi Chiave API", + "clear_api_key_msg": "Sei sicuro di voler rimuovere la tua chiave API personalizzata e tornare a quella predefinita?", + "clear_api_key_success": "Chiave API rimossa con successo", + "clear_api_key_error": "Impossibile rimuovere la chiave API", + "empty_api_key": "La chiave API non può essere vuota.", + "invalid_api_key": "Chiave API non valida. Controlla e riprova.", + "save_error": "Si è verificato un errore durante il salvataggio. Riprova.", + "using_builtin_key": "Ora in uso la chiave API TMDb integrata.", + "using_custom_key": "Ora in uso la tua chiave API TMDb personalizzata.", + "enter_custom_key": "Inserisci e salva la tua chiave API TMDb personalizzata.", + "key_verified": "Chiave API verificata e salvata con successo.", + "disclaimer_part_one": "Questo prodotto usa l'API di TMDB ", + "disclaimer_part_two": "ma non è sponsorizzato nè certificato da TMDB.", + "done":"Fatto" + }, + "settings": { + "language": "Lingua", + "select_language": "Seleziona Lingua", + "english": "Inglese", + "portuguese": "Portoghese", + "portuguese_br": "Portoghese (Brasile)", + "portuguese_pt": "Portoghese (Portogallo)", + "german": "Tedesco", + "arabic": "Arabo", + "spanish": "Spagnolo", + "french": "Francese", + "italian": "Italiano", + "croatian": "Croato", + "chinese": "Cinese (Semplificato)", + "hindi": "Hindi", + "serbian": "Serbo", + "hebrew": "Ebraico", + "bulgarian": "Bulgaro", + "polish": "Polacco", + "czech": "Ceco", + "turkish": "Turco", + "slovenian": "Sloveno", + "macedonian": "Macedone", + "russian": "Russo", + "filipino": "Filippino", + "dutch_nl": "Olandese (Paesi Bassi)", + "romanian": "Rumeno", + "albanian": "Albanese", + "catalan": "Catalano", + "account": "Account", + "content_discovery": "Contenuti e Scoperta", + "appearance": "Aspetto", + "integrations": "Integrazioni", + "playback": "Riproduzione", + "backup_restore": "Backup e Ripristino", + "updates": "Aggiornamenti", + "about": "Informazioni", + "developer": "Sviluppatore", + "cache": "Cache", + "title": "Impostazioni", + "settings_title": "Impostazioni", + "sign_in_sync": "Accedi per sincronizzare", + "add_catalogs_sources": "Addon, cataloghi e sorgenti", + "player_trailers_downloads": "Player, trailer, download", + "mdblist_tmdb_ai": "MDBList, TMDB, IA", + "check_updates": "Controlla aggiornamenti", + "clear_mdblist_cache": "Cancella Cache MDBList", + "cache_management": "GESTIONE CACHE", + "downloads_counter": "download e oltre", + "made_with_love": "Fatto con ❤️ da Tapframe e amici", + "sections": { + "information": "INFORMAZIONI", + "account": "ACCOUNT", + "theme": "TEMA", + "layout": "LAYOUT", + "sources": "SORGENTI", + "catalogs": "CATALOGHI", + "discovery": "SCOPERTA", + "metadata": "METADATI", + "ai_assistant": "ASSISTENTE IA", + "video_player": "VIDEO PLAYER", + "audio_subtitles": "AUDIO E SOTTOTITOLI", + "media": "MEDIA", + "notifications": "NOTIFICHE", + "testing": "TEST", + "danger_zone": "ZONA PERICOLOSA" + }, + "items": { + "legal": "Note Legali & Disclaimer", + "privacy_policy": "Informativa sulla privacy", + "report_issue": "Segnala un problema", + "version": "Versione", + "contributors": "Collaboratori", + "view_contributors": "Vedi tutti i collaboratori", + "theme": "Tema", + "episode_layout": "Layout episodi", + "streams_backdrop": "Sfondo streaming", + "streams_backdrop_desc": "Mostra sfondo sfocato durante lo streaming su mobile", + "addons": "Addon", + "installed": "installati", + "debrid_integration": "Integrazione Debrid", + "debrid_desc": "Connetti Torbox", + "plugins": "Plugin", + "plugins_desc": "Gestisci plugin e repository", + "catalogs": "Cataloghi", + "active": "attivi", + "home_screen": "Schermata Home", + "home_screen_desc": "Layout e contenuti", + "continue_watching": "Continua a guardare", + "continue_watching_desc": "Comportamento cache e riproduzione", + "show_discover": "Mostra sezione Scopri", + "show_discover_desc": "Visualizza contenuti scopri nella Ricerca", + "mdblist": "MDBList", + "mdblist_connected": "Connesso", + "mdblist_desc": "Abilita per aggiungere voti e recensioni", + "simkl": "Simkl", + "simkl_connected": "Connesso", + "simkl_desc": "Traccia ciò che guardi", + "tmdb": "TMDB", + "tmdb_desc": "Sorgente metadati e loghi", + "openrouter": "API OpenRouter", + "openrouter_connected": "Connesso", + "openrouter_desc": "Aggiungi la tua chiave API per abilitare la chat IA", + "video_player": "Video Player", + "built_in": "Integrato", + "external": "Esterno", + "preferred_audio": "Lingua audio preferita", + "preferred_subtitle": "Lingua sottotitoli preferita", + "subtitle_source": "Priorità sorgente sottotitoli", + "auto_select_subs": "Selezione automatica sottotitoli", + "auto_select_subs_desc": "Seleziona automaticamente i sottotitoli in base alle tue preferenze", + "show_trailers": "Mostra Trailer", + "show_trailers_desc": "Visualizza i trailer nella sezione principale", + "enable_downloads": "Abilita Download", + "enable_downloads_desc": "Mostra la scheda Download e abilita il salvataggio degli streaming", + "notifications": "Notifiche", + "notifications_desc": "Promemoria episodi", + "developer_tools": "Strumenti di Sviluppo", + "developer_tools_desc": "Opzioni di test e debug", + "test_onboarding": "Test Introduzione", + "reset_onboarding": "Ripristina Introduzione", + "test_announcement": "Test Annuncio", + "test_announcement_desc": "Mostra l'overlay 'novità'", + "reset_campaigns": "Ripristina Campagne", + "reset_campaigns_desc": "Cancella le impressioni delle campagne", + "clear_all_data": "Cancella tutti i dati", + "clear_all_data_desc": "Ripristina tutte le impostazioni e i dati memorizzati" + }, + "options": { + "horizontal": "Orizzontale", + "vertical": "Verticale", + "internal_first": "Prima interni", + "internal_first_desc": "Preferisci i sottotitoli integrati, poi quelli esterni", + "external_first": "Prima esterni", + "external_first_desc": "Preferisci i sottotitoli degli Addon, poi quelli integrati", + "any_available": "Qualsiasi disponibile", + "any_available_desc": "Usa la prima traccia di sottotitoli disponibile" + }, + "clear_data_desc": "Questo ripristinerà tutte le impostazioni e cancellerà tutti i dati in cache. Sei sicuro?", + "app_updates": "Aggiornamenti App", + "about_nuvio": "Informazioni su Nuvio", + "cloud_sync": { + "title": "Nuvio Sync", + "description": "Sicronizza i dati sui tuoi dispositivi con Nuvio", + "hero_title": "Sincronizzazione su Cloud", + "hero_subtitle": "Mantieni i tuoi addons, progressi e libreria allineati su tutti i tuoi dispositivi.", + "auth": { + "account": "Account", + "not_configured": "Supabase non configurato", + "not_authenticated": "Non Autenticato", + "email_session": "Sessione Email", + "signed_in_as": "Login effettuato come {{email}}", + "not_signed_in": "Non Registrato", + "effective_owner": "Proprietario effettivo: {{id}}" + }, + "stats": { + "title": "Statistiche Database", + "plugins": "Plugins", + "addons": "Addons", + "watch_progress": "Watch Progress", + "library_items": "Oggetti della Libreria", + "watched_items": "Oggetti guardati", + "signin_required": "Accedi per caricare gli ultimi conteggi." + }, + "actions": { + "title": "Azioni", + "description": "Aggiorna dal cloud (pull) o usa le impostazioni di questo dispositivo come sorgente principale (push).", + "pull_btn": "Pull dal Cloud", + "push_btn": "Push dal Dispositivo", + "manage_account": "Gestiscci Account", + "sign_out": "Disconnetti", + "sign_in_up": "Accedi / Registrati" + }, + "alerts": { + "pull_success_title": "Dati Cloud recuperati", + "pull_success_msg": "Gli ultimi dati del Cloud sono stati scaricati su questo dispositivo.", + "pull_failed_title": "Pull Fallito", + "pull_failed_msg": "Download dei dati dal cloud fallito", + "push_success_title": "Push Completato", + "push_success_msg": "Le impostazioni di questo dispositivo sono stati caricati sul Cloud.", + "push_failed_title": "Push Fallito", + "push_failed_msg": "Upload dei dati locali fallito", + "sign_out_failed": "Logout fallito", + "sign_out_failed_title": "Errore Logout" + }, + "external_sync": { + "title": "Priorità sincronizzazione esterna", + "active_msg": "{{services}} è attivo. Il 'Continua a Guardare' e gli aggiornamenti della libreria sono gestiti da questi servizi piuttosto che dal Database Cloud di Nuvio.", + "inactive_msg": "Se la sincronizzazione di Trakt o Simkl è abilitata, il 'Continua a Guardare' e gli aggiornamenti della libreria saranno gestiti da quei servizi piuttosto che dal Database Cloud di Nuvio." + }, + "pre_auth": { + "title": "Prima della Sincronizzazione", + "description": "Accedi per abilitare la sincronizzazione Cloud e mantenere i dati sincronizzati fra tutti i dispositivi.", + "point_1": "• Imostazioni Addons e plugins ", + "point_2": "• Avanzamento della visione e libreria", + "env_warning": "Imposta EXPO_PUBLIC_SUPABASE_URL e EXPO_PUBLIC_SUPABASE_ANON_KEY per abilitare la sincronizzazione." + }, + "connection": "Connessione" + } + }, + "privacy": { + "title": "Privacy e Dati", + "settings_desc": "Controlla la telemetria e la raccolta di dati", + "info_title": "La Tua Privacy è Importante per Noi", + "info_description": "Controlla quali dati vengono raccolti e condivisi. L'analisi è disabilitata per impostazione predefinita e i rapporti di arresto sono anonimi per impostazione predefinita.", + "analytics_enabled_title": "Analisi Abilitata", + "analytics_enabled_message": "I dati di utilizzo verranno raccolti per aiutare a migliorare l'app. Puoi disabilitarlo in qualsiasi momento.", + "disable_error_reporting_title": "Disabilitare la Segnalazione di Errori?", + "disable_error_reporting_message": "Disabilitare la segnalazione di errori significa che non riceveremo notifiche di arresti anomali o problemi che riscontri. Questo potrebbe influire sulla nostra capacità di correggere i bug.", + "enable_session_replay_title": "Abilitare la Riproduzione Sessione?", + "enable_session_replay_message": "La riproduzione della sessione registra il tuo schermo quando si verificano errori per aiutarci a capire cosa è successo. Questo potrebbe catturare contenuti visibili sullo schermo.", + "enable_pii_title": "Abilitare la Raccolta PII?", + "enable_pii_message": "Ciò consente la raccolta di informazioni di identificazione personale come indirizzo IP e dettagli del dispositivo. Questi dati aiutano a diagnosticare i problemi ma aumentano l'esposizione della privacy.", + "disable_all_title": "Disabilitare Tutta la Telemetria?", + "disable_all_message": "Questo disabiliterà tutta l'analisi, la segnalazione di errori e la riproduzione della sessione. Non riceveremo alcun dato sull'utilizzo dell'app o gli arresti.", + "disable_all_button": "Disabilita Tutto", + "all_disabled_title": "Tutta la Telemetria Disabilitata", + "all_disabled_message": "Tutta la raccolta di dati è stata disabilitata. Le modifiche avranno effetto al prossimo riavvio dell'app.", + "reset_title": "Ripristina ai Valori Consigliati", + "reset_message": "Le impostazioni di privacy sono state ripristinate ai valori predefiniti consigliati (segnalazione di errori abilitata, analisi disabilitata).", + "section_analytics": "ANALISI", + "analytics_title": "Analisi di Utilizzo", + "analytics_description": "Raccogli schemi di utilizzo anonimi e visualizzazioni dello schermo", + "section_error_reporting": "SEGNALAZIONE DI ERRORI", + "error_reporting_title": "Rapporti di Arresto", + "error_reporting_description": "Invia rapporti di arresto anonimi per migliorare la stabilità", + "session_replay_title": "Riproduzione Sessione", + "session_replay_description": "Registra lo schermo quando si verificano errori", + "pii_title": "Includi Informazioni Dispositivo", + "pii_description": "Invia indirizzo IP e dettagli del dispositivo con i rapporti", + "section_quick_actions": "AZIONI RAPIDE", + "disable_all": "Disabilitare Tutta la Telemetria", + "disable_all_desc": "Disattiva tutta la raccolta di dati", + "reset_recommended": "Ripristina ai Valori Consigliati", + "reset_recommended_desc": "Impostazioni predefinite incentrate sulla privacy con segnalazione di errori", + "section_learn_more": "SCOPRI DI PIÙ", + "privacy_policy": "Informativa sulla Privacy", + "current_settings": "Riepilogo delle Impostazioni Attuali", + "summary_analytics": "Analisi", + "summary_errors": "Rapporti di Errore", + "summary_replay": "Riproduzione Sessione", + "summary_pii": "Informazioni Dispositivo", + "restart_note_detailed": "* Le modifiche all'analisi e alla segnalazione di errori hanno effetto immediato. La riproduzione della sessione e le impostazioni PII richiedono il riavvio dell'app." + }, + "ai_settings": { + "title": "Assistente IA", + "info_title": "Chat potenziata dall'IA", + "info_desc": "Fai domande su qualsiasi film o episodio di Serie TV usando l'IA avanzata. Ottieni approfondimenti su trama, personaggi, temi, curiosità e altro ancora, tutto basato sui dati completi di TMDB.", + "feature_1": "Contesto e analisi specifici per ogni episodio", + "feature_2": "Spiegazioni della trama e analisi dei personaggi", + "feature_3": "Curiosità e fatti dietro le quinte", + "feature_4": "Usa la tua chiave API gratuita di OpenRouter", + "api_key_section": "CHIAVE API OPENROUTER", + "api_key_label": "Chiave API", + "api_key_desc": "Inserisci la tua chiave API OpenRouter per abilitare le funzioni di chat IA", + "save_api_key": "Salva Chiave API", + "saving": "Salvataggio...", + "update": "Aggiorna", + "remove": "Rimuovi", + "get_free_key": "Ottieni una chiave API gratuita da OpenRouter", + "enable_chat": "Abilita Chat IA", + "enable_chat_desc": "Se abilitato, il pulsante 'Chiedi all'IA' apparirà nelle pagine dei contenuti.", + "chat_enabled": "Chat IA Abilitata", + "chat_enabled_desc": "Ora puoi fare domande su film e Serie TV. Cerca il pulsante \"Chiedi all'IA\" nelle pagine dei contenuti!", + "how_it_works": "Come funziona", + "how_it_works_desc": "• OpenRouter fornisce l'accesso a diversi modelli IA\n• La tua chiave API rimane privata e sicura\n• Il piano gratuito include limiti di utilizzo generosi\n• Chatta con contesto su film/episodi specifici\n• Ottieni analisi dettagliate e spiegazioni", + "error_invalid_key": "Inserisci una chiave API valida", + "error_key_format": "Le chiavi API OpenRouter devono iniziare con \"sk-or-\"", + "success_saved": "Chiave API OpenRouter salvata con successo!", + "error_save": "Impossibile salvare la chiave API", + "confirm_remove_title": "Rimuovi Chiave API", + "confirm_remove_msg": "Sei sicuro di voler rimuovere la tua chiave API OpenRouter? Questo disabiliterà le funzioni di chat IA.", + "success_removed": "Chiave API rimossa con successo", + "error_remove": "Impossibile rimuovere la chiave API" + }, + "catalog_settings": { + "title": "Cataloghi", + "layout_phone": "LAYOUT SCHERMATA CATALOGO (TELEFONO)", + "posters_per_row": "Poster per riga", + "auto": "Auto", + "show_titles": "Mostra titoli poster", + "show_titles_desc": "Visualizza il testo del titolo sotto ogni poster", + "phone_only_hint": "Si applica solo ai telefoni. I tablet mantengono il layout adattivo.", + "catalogs_group": "Cataloghi", + "enabled_count": "{{enabled}} su {{total}} abilitati", + "rename_hint": "Premi a lungo su un catalogo per rinominarlo", + "rename_modal_title": "Rinomina Catalogo", + "rename_placeholder": "Inserisci il nuovo nome del catalogo", + "error_save_name": "Impossibile salvare il nome personalizzato." + }, + "continue_watching_settings": { + "title": "Continua a guardare", + "playback_behavior": "COMPORTAMENTO RIPRODUZIONE", + "use_cached": "Usa stream in cache", + "use_cached_desc": "Se abilitato, cliccando sugli elementi in 'Continua a guardare' si aprirà direttamente il player usando gli stream riprodotti in precedenza. Se disabilitato, aprirà la schermata dei contenuti.", + "open_metadata": "Apri schermata metadati", + "open_metadata_desc": "Quando gli stream in cache sono disabilitati, apre la schermata dei metadati invece di quella degli stream. Mostra i dettagli del contenuto e permette la selezione manuale dello stream.", + "card_appearance": "ASPETTO SCHEDA", + "card_style": "Stile scheda", + "card_style_desc": "Scegli come appaiono gli elementi di 'Continua a guardare' nella home", + "wide": "Ampia", + "poster": "Poster", + "cache_settings": "IMPOSTAZIONI CACHE", + "cache_duration": "Durata cache stream", + "cache_duration_desc": "Per quanto tempo conservare i link degli stream in cache prima della scadenza", + "important_note": "Nota importante", + "important_note_text": "Non tutti i link degli stream rimangono attivi per l'intera durata della cache. Tempi di cache più lunghi potrebbero causare link scaduti. Se un link in cache fallisce, l'app tornerà a recuperare nuovi stream.", + "how_it_works": "Come funziona", + "how_it_works_cached": "• Gli stream vengono salvati in cache per la durata selezionata dopo la riproduzione\n• Gli stream in cache vengono convalidati prima dell'uso\n• Se la cache non è valida o è scaduta, si torna alla schermata dei contenuti\n• 'Usa stream in cache' controlla l'apertura diretta del player rispetto alla navigazione\n• 'Apri schermata metadati' appare solo quando gli stream in cache sono disabilitati", + "how_it_works_uncached": "• Quando gli stream in cache sono disabilitati, cliccare su 'Continua a guardare' apre la schermata dei contenuti\n• L'opzione 'Apri schermata metadati' controlla quale schermata aprire\n• La schermata metadati mostra i dettagli e permette la selezione manuale\n• La schermata degli stream mostra gli stream disponibili per la riproduzione immediata", + "changes_saved": "Modifiche salvate", + "min": "min", + "hour": "ora", + "hours": "ore" + }, + "contributors": { + "title": "Collaboratori", + "special_mentions": "Menzioni speciali", + "tab_contributors": "Collaboratori", + "tab_special": "Menzioni speciali", + "tab_donors": "Donatori", + "manager_role": "Community Manager", + "manager_desc": "Gestisce le community Discord e Reddit per Nuvio", + "sponsor_role": "Sponsor del Server", + "sponsor_desc": "Ha sponsorizzato l'infrastruttura del server per Nuvio", + "mod_role": "Moderatore Discord", + "mod_desc": "Aiuta a moderare la community Discord di Nuvio", + "loading": "Caricamento...", + "discord_user": "Utente Discord", + "contributions": "contributi", + "gratitude_title": "Siamo grati per ogni contributo", + "gratitude_desc": "Ogni riga di codice, segnalazione di bug e suggerimento aiuta a rendere Nuvio migliore per tutti", + "special_thanks_title": "Ringraziamenti speciali", + "special_thanks_desc": "Queste persone fantastiche aiutano a mantenere attiva la community di Nuvio e i server online", + "donors_desc": "Grazie per credere in quello che stiamo costruendo. Il vostro supporto mantiene Nuvio gratuito e in continuo miglioramento.", + "latest_donations": "Recenti", + "leaderboard": "Classifica", + "loading_donors": "Caricamento donatori…", + "no_donors": "Nessun donatore ancora", + "error_rate_limit": "Limite di frequenza API GitHub superato. Riprova più tardi o trascina per aggiornare.", + "error_failed": "Impossibile caricare i collaboratori. Controlla la tua connessione internet.", + "retry": "Riprova", + "no_contributors": "Nessun collaboratore trovato", + "loading_contributors": "Caricamento collaboratori..." + }, + "debrid": { + "title": "Integrazione Debrid", + "description_torbox": "Sblocca stream 4K di alta qualità e velocità fulminee integrando Torbox. Inserisci la tua chiave API qui sotto per potenziare istantaneamente la tua esperienza di streaming.", + "description_torrentio": "Configura Torrentio per ottenere stream torrent per film e Serie TV. È necessario un servizio debrid per riprodurre i contenuti.", + "tab_torbox": "TorBox", + "tab_torrentio": "Torrentio", + "status_connected": "Connesso", + "status_disconnected": "Disconnesso", + "enable_addon": "Abilita Addon", + "disconnect_button": "Disconnetti e Rimuovi", + "disconnect_loading": "Disconnessione in corso...", + "account_info": "Informazioni Account", + "plan": "Piano", + "plan_free": "Gratuito", + "plan_essential": "Essential ($3/mese)", + "plan_pro": "Pro ($10/mese)", + "plan_standard": "Standard ($5/mese)", + "plan_unknown": "Sconosciuto", + "expires": "Scade il", + "downloaded": "Scaricati", + "status_active": "Attivo", + "connected_title": "✓ Connesso a TorBox", + "connected_desc": "Il tuo Addon TorBox è attivo e fornisce stream premium.", + "configure_title": "Configura Addon", + "configure_desc": "Personalizza la tua esperienza di streaming. Ordina per qualità, filtra le dimensioni dei file e gestisci altre impostazioni di integrazione.", + "open_settings": "Apri Impostazioni", + "what_is_debrid": "Cos'è un servizio Debrid?", + "enter_api_key": "Inserisci la tua chiave API", + "connect_button": "Connetti e Installa", + "connecting": "Connessione in corso...", + "unlock_speeds_title": "Sblocca velocità Premium", + "unlock_speeds_desc": "Abbonati a Torbox per accedere a stream di alta qualità in cache con zero buffering.", + "get_subscription": "Abbonati ora", + "powered_by": "Offerto da", + "disclaimer_torbox": "Nuvio non è affiliato a Torbox in alcun modo.", + "disclaimer_torrentio": "Nuvio non è affiliato a Torrentio in alcun modo.", + "installed_badge": "✓ INSTALLATO", + "promo_title": "⚡ Serve un servizio Debrid?", + "promo_desc": "Scegli TorBox per streaming 4K velocissimo senza buffering. Torrent in cache premium e download istantanei.", + "promo_button": "Ottieni abbonamento TorBox", + "service_label": "Servizio Debrid *", + "api_key_label": "Chiave API *", + "sorting_label": "Ordinamento", + "exclude_qualities": "Escludi qualità", + "priority_languages": "Lingue prioritarie", + "max_results": "Risultati massimi", + "additional_options": "Opzioni aggiuntive", + "no_download_links": "Non mostrare link di download", + "no_debrid_catalog": "Non mostrare catalogo debrid", + "install_button": "Installa Torrentio", + "installing": "Installazione...", + "update_button": "Aggiorna configurazione", + "updating": "Aggiornamento...", + "remove_button": "Rimuovi Torrentio", + "error_api_required": "Chiave API richiesta", + "error_api_required_desc": "Inserisci la chiave API del tuo servizio debrid per installare Torrentio.", + "success_installed": "Addon Torrentio installato con successo!", + "success_removed": "Addon Torrentio rimosso con successo", + "alert_disconnect_title": "Disconnetti Torbox", + "alert_disconnect_msg": "Sei sicuro di voler disconnettere Torbox? Questo rimuoverà l'Addon e cancellerà la chiave API salvata." + }, + "home_screen": { + "title": "Impostazioni Schermata Home", + "changes_applied": "Modifiche applicate", + "display_options": "OPZIONI DI VISUALIZZAZIONE", + "show_hero": "Mostra sezione Hero", + "show_hero_desc": "Contenuti in evidenza in alto", + "show_this_week": "Mostra sezione 'Questa settimana'", + "show_this_week_desc": "Nuovi episodi della settimana corrente", + "select_catalogs": "Seleziona cataloghi", + "all_catalogs": "Tutti i cataloghi", + "selected": "selezionati", + "prefer_external_meta": "Preferisci Metadati Estrerni", + "prefer_external_meta_desc": "Usa i metadati esterni nella pagina dei dettagli", + "hero_layout": "Layout Hero", + "layout_legacy": "Classico", + "layout_carousel": "Carosello", + "layout_appletv": "Apple TV", + "layout_desc": "Banner a tutta larghezza, schede scorrevoli o stile Apple TV", + "featured_source": "Sorgente in evidenza", + "using_catalogs": "Utilizzo cataloghi", + "manage_selected_catalogs": "Gestisci cataloghi selezionati", + "dynamic_bg": "Sfondo Hero dinamico", + "dynamic_bg_desc": "Banner sfocato dietro il carosello", + "performance_note": "Potrebbe influire sulle prestazioni dei dispositivi meno potenti.", + "posters": "Poster", + "show_titles": "Mostra titoli", + "poster_size": "Dimensione poster", + "poster_corners": "Angoli poster", + "size_small": "Piccolo", + "size_medium": "Medio", + "size_large": "Grande", + "corners_square": "Squadrati", + "corners_rounded": "Arrotondati", + "corners_pill": "Pillola", + "about_these_settings": "INFORMAZIONI SU QUESTE IMPOSTAZIONI", + "about_desc": "Queste impostazioni controllano come i contenuti vengono visualizzati nella Home. Le modifiche vengono applicate immediatamente senza riavviare l'app.", + "hero_catalogs": { + "title": "Cataloghi sezione Hero", + "select_all": "Seleziona tutto", + "clear_all": "Cancella tutto", + "info": "Seleziona quali cataloghi visualizzare nella sezione hero. Se non ne viene selezionato nessuno, verranno usati tutti. Non dimenticare di salvare al termine.", + "settings_saved": "Impostazioni salvate", + "error_load": "Impossibile caricare i cataloghi", + "movies": "Film", + "tv_shows": "Serie TV" + } + }, + "calendar": { + "title": "Calendario", + "loading": "Caricamento calendario...", + "no_scheduled_episodes": "Nessun episodio in programma", + "check_back_later": "Torna più tardi", + "showing_episodes_for": "Episodi per il {{date}}", + "show_all_episodes": "Mostra tutti gli episodi", + "no_episodes_for": "Nessun episodio per il {{date}}", + "no_upcoming_found": "Nessun episodio imminente trovato", + "add_series_desc": "Aggiungi Serie TV alla tua libreria per vedere qui i prossimi episodi" + }, + "mdblist": { + "title": "Sorgenti valutazione", + "status_disabled": "MDBList disabilitato", + "status_active": "Chiave API attiva", + "status_required": "Chiave API richiesta", + "status_disabled_desc": "La funzionalità MDBList è attualmente disabilitata.", + "status_active_desc": "Le valutazioni di MDBList sono abilitate.", + "status_required_desc": "Aggiungi la tua chiave qui sotto per abilitare i voti.", + "enable_toggle": "Abilita MDBList", + "enable_toggle_desc": "Attiva/disattiva tutte le funzioni MDBList", + "api_section": "Chiave API", + "placeholder": "Incolla la tua chiave API MDBList", + "save": "Salva", + "clear": "Rimuovi chiave", + "rating_providers": "Fornitori valutazioni", + "rating_providers_desc": "Scegli quali voti visualizzare nell'app", + "how_to": "Come ottenere una chiave API", + "step_1": "Accedi al", + "step_1_link": "sito web MDBList", + "step_2": "Vai alla sezione", + "step_2_settings": "Impostazioni", + "step_2_api": "API", + "step_2_end": ".", + "step_3": "Genera una nuova chiave e copiala.", + "go_to_website": "Vai su MDBList", + "alert_clear_title": "Rimuovi chiave API", + "alert_clear_msg": "Sei sicuro di voler rimuovere la chiave API salvata?", + "success_saved": "Chiave API salvata con successo.", + "error_empty": "La chiave API non può essere vuota.", + "error_save": "Si è verificato un errore durante il salvataggio. Riprova.", + "api_key_empty_error": "La chiave API non può essere vuota.", + "success_cleared": "Chiave API rimossa con successo", + "error_clear": "Impossibile rimuovere la chiave API" + }, + "notification": { + "title": "Impostazioni Notifiche", + "section_general": "Generali", + "enable_notifications": "Abilita Notifiche", + "section_types": "Tipi di Notifica", + "new_episodes": "Nuovi Episodi", + "upcoming_shows": "Serie in Arrivo", + "reminders": "Promemoria", + "section_timing": "Tempistica Notifiche", + "timing_desc": "Quanto tempo prima della messa in onda vuoi ricevere la notifica?", + "hours_1": "1 ora", + "hours_suffix": "ore", + "section_status": "Stato Notifiche", + "stats_upcoming": "In arrivo", + "stats_this_week": "Questa settimana", + "stats_total": "Totale", + "sync_button": "Sincronizza Libreria e Trakt", + "syncing": "Sincronizzazione...", + "sync_desc": "Sincronizza automaticamente le notifiche per tutte le serie nella tua libreria e nella watchlist/collezione di Trakt.", + "section_advanced": "Avanzate", + "reset_button": "Ripristina tutte le notifiche", + "test_button": "Test Notifica (5 sec)", + "test_notification_in": "Notifica tra {{seconds}}s...", + "test_notification_text": "La notifica apparirà tra {{seconds}} secondi", + "alert_reset_title": "Ripristina Notifiche", + "alert_reset_msg": "Questo cancellerà tutte le notifiche programmate, ma non rimuoverà nulla dalla tua libreria salvata. Sei sicuro?", + "alert_reset_success": "Tutte le notifiche sono state ripristinate", + "alert_sync_complete": "Sincronizzazione Completata", + "alert_sync_msg": "Sincronizzazione notifiche completata con successo per la libreria e gli elementi Trakt.\n\nProgrammati: {{upcoming}} episodi in arrivo\nQuesta settimana: {{thisWeek}} episodi", + "alert_test_scheduled": "Notifica di test programmata per l'invio immediato" + }, + "backup": { + "title": "Backup e Ripristino", + "options_title": "Opzioni Backup", + "options_desc": "Scegli cosa includere nei tuoi backup", + "section_core": "Dati Principali", + "section_addons": "Addon e Integrazioni", + "section_settings": "Impostazioni e Preferenze", + "library_label": "Libreria", + "library_desc": "I tuoi film e Serie TV salvati", + "watch_progress_label": "Progressi di Visione", + "watch_progress_desc": "Posizioni di 'Continua a guardare'", + "addons_label": "Addon", + "addons_desc": "Addon di Stremio installati", + "plugins_label": "Plugin", + "plugins_desc": "Configurazioni scraper personalizzate", + "trakt_label": "Integrazione Trakt", + "trakt_desc": "Dati di sincronizzazione e token di autenticazione", + "app_settings_label": "Impostazioni App", + "app_settings_desc": "Tema, preferenze e configurazioni", + "user_prefs_label": "Preferenze Utente", + "user_prefs_desc": "Ordine degli Addon e impostazioni UI", + "catalog_settings_label": "Impostazioni Catalogo", + "catalog_settings_desc": "Filtri catalogo e preferenze", + "api_keys_label": "Chiavi API", + "api_keys_desc": "Chiavi per MDBList e OpenRouter", + "action_create": "Crea Backup", + "action_restore": "Ripristina da Backup", + "section_info": "Informazioni sui Backup", + "info_text": "• Personalizza gli elementi del backup usando i selettori sopra\n• I file di backup sono salvati localmente sul tuo dispositivo\n• Condividi il backup per trasferire i dati tra dispositivi\n• Il ripristino sovrascriverà i dati attuali", + "alert_create_title": "Crea Backup", + "alert_no_content": "Nessun contenuto selezionato per il backup.\n\nAbilita almeno un'opzione nella sezione Opzioni Backup sopra.", + "alert_backup_created_title": "Backup Creato", + "alert_backup_created_msg": "Il tuo backup è stato creato ed è pronto per essere condiviso.", + "alert_backup_failed_title": "Backup Fallito", + "alert_restore_confirm_title": "Conferma Ripristino", + "alert_restore_confirm_msg": "Questo ripristinerà i tuoi dati da un backup creato il {{date}}.\n\nQuesta azione sovrascriverà i dati attuali. Sei sicuro di voler continuare?", + "alert_restore_complete_title": "Ripristino Completato", + "alert_restore_complete_msg": "I tuoi dati sono stati ripristinati con successo. Riavvia l'app per applicare tutte le modifiche.", + "alert_restore_failed_title": "Ripristino Fallito", + "restart_app": "Riavvia App", + "alert_restart_failed_title": "Riavvio Fallito", + "alert_restart_failed_msg": "Impossibile riavviare l'app. Chiudi e riapri manualmente per vedere i dati ripristinati." + }, + "updates": { + "title": "Aggiornamenti App", + "status_checking": "Controllo aggiornamenti...", + "status_available": "Aggiornamento disponibile!", + "status_downloading": "Download aggiornamento...", + "status_installing": "Installazione aggiornamento...", + "status_success": "Aggiornamento installato con successo!", + "status_error": "Aggiornamento fallito", + "status_ready": "Pronto per controllare gli aggiornamenti", + "action_check": "Controlla Aggiornamenti", + "action_install": "Installa Aggiornamento", + "release_notes": "Note di rilascio:", + "version": "Versione:", + "last_checked": "Ultimo controllo:", + "current_version": "Versione attuale:", + "current_release_notes": "Note di rilascio attuali:", + "github_release": "RELEASE GITHUB", + "current": "Attuale:", + "latest": "Ultima:", + "notes": "Note:", + "view_release": "Vedi Release", + "notification_settings": "IMPOSTAZIONI NOTIFICHE", + "ota_alerts_label": "Avvisi Aggiornamenti OTA", + "ota_alerts_desc": "Mostra notifiche per aggiornamenti over-the-air", + "major_alerts_label": "Avvisi Aggiornamenti Maggiori", + "major_alerts_desc": "Mostra notifiche per nuove versioni dell'app su GitHub", + "alert_disable_ota_title": "Disabilitare Avvisi OTA?", + "alert_disable_ota_msg": "Non riceverai più notifiche automatiche per gli aggiornamenti OTA.\n\n⚠️ Attenzione: Rimanere all'ultima versione è importante per:\n• Correzioni di bug e stabilità\n• Nuove funzioni e miglioramenti\n• Feedback e report sui crash accurati\n\nPuoi comunque controllare manualmente gli aggiornamenti in questa schermata.", + "alert_disable_major_title": "Disabilitare Avvisi Major?", + "alert_disable_major_msg": "Non riceverai più notifiche per gli aggiornamenti maggiori che richiedono la reinstallazione.\n\n⚠️ Attenzione: Gli aggiornamenti maggiori includono spesso:\n• Patch di sicurezza critiche\n• Cambiamenti strutturali che richiedono la reinstallazione\n• Correzioni di compatibilità importanti\n\nPuoi comunque controllare manualmente.", + "warning_note": "Mantenere gli avvisi abilitati garantisce la ricezione delle correzioni e report di crash accurati.", + "disable": "Disabilita", + "alert_no_update_to_install": "Nessun aggiornamento disponibile da installare", + "alert_install_failed": "Installazione aggiornamento fallita", + "alert_no_update_title": "Nessun Aggiornamento", + "alert_update_applied_msg": "L'aggiornamento sarà applicato al prossimo riavvio dell'app" + }, + "player": { + "title": "Video Player", + "section_selection": "SELEZIONE PLAYER", + "internal_title": "Player Integrato", + "internal_desc": "Usa il video player predefinito dell'app", + "vlc_title": "VLC", + "vlc_desc": "Apri gli stream in VLC media player", + "infuse_title": "Infuse", + "infuse_desc": "Apri gli stream nel player Infuse", + "outplayer_title": "OutPlayer", + "outplayer_desc": "Apri gli stream in OutPlayer", + "vidhub_title": "VidHub", + "vidhub_desc": "Apri gli stream nel player VidHub", + "infuse_live_title": "Infuse LiveContainer", + "infuse_live_desc": "Apri gli stream in Infuse tramite LiveContainer", + "external_title": "Player Esterno", + "external_desc": "Apri gli stream nel tuo video player preferito", + "section_playback": "OPZIONI RIPRODUZIONE", + "skip_intro_settings_title": "Salta intro", + "powered_by_introdb": "Offerto da IntroDB", + "autoplay_title": "Auto-play Primo Stream", + "autoplay_desc": "Avvia automaticamente il primo stream mostrato nella lista.", + "resume_title": "Riprendi Sempre", + "resume_desc": "Salta la richiesta di ripresa e continua automaticamente da dove avevi interrotto (se visto meno dell'85%).", + "engine_title": "Motore Video Player", + "engine_desc": "Auto utilizza ExoPlayer con MPV come ripiego. Alcuni formati come Dolby Vision e HDR potrebbero non essere supportati da MPV, quindi Auto è raccomandato.", + "decoder_title": "Modalità Decodificatore", + "decoder_desc": "Come viene decodificato il video. Auto è raccomandato per il miglior bilanciamento.", + "gpu_title": "Rendering GPU", + "gpu_desc": "GPU-Next offre una migliore gestione di HDR e colori.", + "external_downloads_title": "Player Esterno per Download", + "external_downloads_desc": "Riproduci i contenuti scaricati nel tuo player esterno preferito.", + "restart_required": "Riavvio Richiesto", + "restart_msg_decoder": "Riavvia l'app affinché le modifiche al decodificatore abbiano effetto.", + "restart_msg_gpu": "Riavvia l'app affinché le modifiche alla modalità GPU abbiano effetto.", + "option_auto": "Auto", + "option_auto_desc_engine": "ExoPlayer + MPV fallback", + "option_mpv": "MPV", + "option_mpv_desc": "Solo MPV", + "option_auto_desc_decoder": "Miglior bilanciamento", + "option_sw": "SW", + "option_sw_desc": "Software", + "option_hw": "HW", + "option_hw_desc": "Hardware", + "option_hw_plus": "HW+", + "option_hw_plus_desc": "HW Completo", + "option_gpu_desc": "Standard", + "option_gpu_next_desc": "Avanzato" + }, + "plugins": { + "title": "Estensioni", + "enable_title": "Abilita Estensioni", + "enable_desc": "Permetti all'app di usare estensioni installate per trovare fonti multimediali", + "repo_config_title": "Configurazione Repository", + "repo_config_desc": "Gestisci repository di estensioni esterni. Attiva o disattiva ogni repository qui sotto.", + "your_repos": "Repository", + "your_repos_desc": "Configura sorgenti esterne per le estensioni.", + "add_repo_button": "Aggiungi Repository", + "refresh": "Aggiorna", + "remove": "Rimuovi", + "enabled": "Abilitato", + "disabled": "Disabilitato", + "updating": "Aggiornamento...", + "success": "Successo", + "error": "Errore", + "alert_repo_added": "Repository aggiunto ed estensioni caricate con successo", + "alert_repo_saved": "URL del repository salvato con successo", + "alert_repo_refreshed": "Repository aggiornato con successo", + "alert_invalid_url": "Formato URL Non Valido", + "alert_plugins_cleared": "Tutte le estensioni sono state rimosse", + "alert_cache_cleared": "Cache del repository cancellata con successo", + "unknown": "Sconosciuto", + "active": "Attivo", + "available": "Disponibile", + "platform_disabled": "Piattaforma Disabilitata", + "limited": "Limitato", + "clear_all": "Rimuovi Tutte le Estensioni", + "clear_all_desc": "Sei sicuro di voler rimuovere tutte le estensioni installate? L'azione è irreversibile.", + "clear_cache": "Cancella Cache Repository", + "clear_cache_desc": "Questo rimuoverà l'URL salvato e tutti i dati delle estensioni in cache.", + "add_new_repo": "Aggiungi Nuovo Repository", + "available_plugins": "Estensioni Disponibili ({{count}})", + "placeholder": "Cerca estensioni...", + "all": "Tutti", + "filter_all": "Tutti i Tipi", + "filter_movies": "Film", + "filter_tv": "Serie TV", + "enable_all": "Abilita Tutti", + "disable_all": "Disabilita Tutti", + "no_plugins_found": "Nessuna Estensione Trovata", + "no_plugins_available": "Nessuna Estensione Disponibile", + "no_match_desc": "Nessuna estensione corrisponde a \"{{query}}\".", + "configure_repo_desc": "Configura un repository sopra per visualizzare le estensioni disponibili.", + "clear_search": "Cancella Ricerca", + "no_external_player": "Nessun player esterno", + "showbox_token": "Token UI ShowBox", + "showbox_placeholder": "Incolla il tuo token UI ShowBox", + "save": "Salva", + "clear": "Cancella", + "additional_settings": "Impostazioni Aggiuntive", + "enable_url_validation": "Abilita Validazione URL", + "url_validation_desc": "Valida gli URL prima di mostrarli (può rallentare i risultati ma migliora l'affidabilità)", + "group_streams": "Raggruppa Fonti Estensioni", + "group_streams_desc": "Se abilitato, le fonti sono raggruppate per repository. Se disabilitato, ogni estensione appare come provider separato.", + "sort_quality": "Ordina Prima per Qualità", + "sort_quality_desc": "Se abilitato, le fonti sono ordinate prima per qualità. Solo disponibile con il raggruppamento attivo.", + "show_logos": "Mostra Loghi Estensioni", + "show_logos_desc": "Visualizza i loghi delle estensioni accanto ai link multimediali.", + "quality_filtering": "Filtraggio Qualità", + "quality_filtering_desc": "Escludi specifiche qualità video dai risultati. Tocca una qualità per escluderla dalle estensioni.", + "excluded_qualities": "Qualità escluse:", + "language_filtering": "Filtraggio Lingua", + "language_filtering_desc": "Escludi specifiche lingue dai risultati. Tocca una lingua per escluderla dalle estensioni.", + "note": "Nota:", + "language_filtering_note": "Questo filtro si applica solo ai provider che includono informazioni sulla lingua.", + "excluded_languages": "Lingue escluse:", + "about_title": "Informazioni sulle Estensioni", + "about_desc_1": "Le estensioni sono moduli che adattano contenuti da vari protocolli esterni. Girano localmente e sono installate da repository fidati.", + "about_desc_2": "Le estensioni contrassegnate come \"Limitate\" richiedono configurazioni esterne specifiche.", + "help_title": "Configurazione Estensioni", + "help_step_1": "1. **Abilita Estensioni** - Attiva l'interruttore principale", + "help_step_2": "2. **Aggiungi Repository** - Inserisci un URL di repository valido", + "help_step_3": "3. **Aggiorna Repository** - Scarica le estensioni disponibili", + "help_step_4": "4. **Abilita** - Attiva le estensioni che desideri usare", + "got_it": "Capito!", + "repo_format_hint": "Formato: https://raw.githubusercontent.com/user/repo/refs/heads/branch", + "cancel": "Annulla", + "add": "Aggiungi" + }, + "theme": { + "title": "Temi App", + "select_theme": "SELEZIONA TEMA", + "create_custom": "Crea Tema Personalizado", + "options": "OPZIONI", + "use_dominant_color": "Usa colore dominante dall'artwork", + "categories": { + "all": "Tutti i Temi", + "dark": "Temi Scuri", + "colorful": "Colorati", + "custom": "I Miei Temi" + }, + "editor": { + "theme_name_placeholder": "Nome tema", + "save": "Salva", + "primary": "Primario", + "secondary": "Secondario", + "background": "Sfondo", + "invalid_name_title": "Nome Non Valido", + "invalid_name_msg": "Inserisci un nome valido per il tema" + }, + "alerts": { + "delete_title": "Elimina Tema", + "delete_msg": "Sei sicuro di voler eliminare \"{{name}}\"?", + "ok": "OK", + "delete": "Elimina", + "cancel": "Annulla", + "back": "Impostazioni" + } + }, + "legal": { + "title": "Note Legali & Disclaimer", + "intro_title": "Natura dell'Applicazione", + "intro_text": "Nuvio è un lettore multimediale e un'applicazione per la gestione dei metadati. Agisce unicamente come interfaccia lato client per sfogliare metadati pubblicamente disponibili (film, serie TV, ecc.) e riprodurre file multimediali forniti dall'utente o da estensioni di terze parti. Nuvio non ospita, archivia, distribuisce né indicizza alcun contenuto multimediale autonomamente.", + "extensions_title": "Estensioni di Terze Parti", + "extensions_text": "Nuvio utilizza un'architettura estensibile che consente agli utenti di installare plugin di terze parti (estensioni). Queste estensioni sono sviluppate e mantenute da sviluppatori indipendenti non affiliati a Nuvio. Non abbiamo alcun controllo su, e non ci assumiamo alcuna responsabilità per, il contenuto, la legalità o la funzionalità di qualsiasi estensione di terze parti.", + "user_resp_title": "Responsabilità dell'Utente", + "user_resp_text": "Gli utenti sono gli unici responsabili delle estensioni che installano e dei contenuti a cui accedono. Utilizzando questa applicazione, accetti di garantire di avere il diritto legale di accedere a qualsiasi contenuto visualizzato utilizzando Nuvio. Gli sviluppatori di Nuvio non sostengono né incoraggiano la violazione del copyright.", + "dmca_title": "Copyright e DMCA", + "dmca_text": "Rispettiamo i diritti di proprietà intellettuale altrui. Poiché Nuvio non ospita alcun contenuto, non possiamo rimuovere contenuti da Internet. Tuttavia, se ritieni che l'interfaccia stessa dell'applicazione violi i tuoi diritti, ti preghiamo di contattarci.", + "warranty_title": "Nessuna Garanzia", + "warranty_text": "Questo software è fornito \"così com'è\", senza garanzia di alcun tipo, espressa o implicita. In nessun caso gli autori o i detentori del copyright saranno responsabili per qualsiasi reclamo, danno o altra responsabilità derivante dall'uso di questo software." + }, + "plugin_tester": { + "title": "Tester Plugin", + "subtitle": "Esegui scraper e ispeziona log in tempo reale", + "tabs": { + "individual": "Individuale", + "repo": "Tester Repo", + "code": "Codice", + "logs": "Log", + "results": "Risultati" + }, + "common": { + "error": "Errore", + "success": "Successo", + "movie": "Film", + "tv": "Serie TV", + "tmdb_id": "ID TMDB", + "season": "Stagione", + "episode": "Episodio", + "running": "In esecuzione…", + "run_test": "Esegui Test", + "play": "Riproduci", + "done": "Fatto", + "test": "Test", + "testing": "Test in corso…" + }, + "individual": { + "load_from_url": "Carica da URL", + "load_from_url_desc": "Incolla un URL GitHub raw o IP locale e tocca scarica.", + "enter_url_error": "Inserisci un URL", + "code_loaded": "Codice caricato da URL", + "fetch_error": "Caricamento fallito: {{message}}", + "no_code_error": "Nessun codice da eseguire", + "plugin_code": "Codice Plugin", + "focus_editor": "Focus editor", + "code_placeholder": "// Incolla il codice del plugin qui...", + "test_parameters": "Parametri di Test", + "no_logs": "Nessun log. Esegui un test per vedere l'output.", + "no_streams": "Nessun stream trovato.", + "streams_found": "{{count}} Stream Trovato", + "streams_found_plural": "{{count}} Stream Trovati", + "tap_play_hint": "Tocca Riproduci per testare uno stream nel player nativo.", + "unnamed_stream": "Stream Senza Nome", + "quality": "Qualità: {{quality}}", + "size": "Dimensione: {{size}}", + "url_label": "URL: {{url}}", + "headers_info": "Header: {{count}} header personalizzati", + "find_placeholder": "Cerca nel codice…", + "edit_code_title": "Modifica Codice", + "no_url_stream_error": "Nessun URL trovato per questo stream" + }, + "repo": { + "title": "Tester Repo", + "description": "Recupera un repository (URL locale o GitHub raw) e testa ogni provider.", + "enter_repo_url_error": "Inserisci un URL del repository", + "invalid_url_title": "URL Non Valido", + "invalid_url_msg": "Usa un URL GitHub raw o un URL locale http(s).\n\nEsempio:\nhttps://raw.githubusercontent.com/tapframe/nuvio-providers/refs/heads/main", + "manifest_build_error": "Impossibile creare un URL manifest dall'input", + "manifest_fetch_error": "Caricamento manifest fallito", + "repo_manifest_fetch_error": "Caricamento manifest repo fallito", + "missing_filename": "Nome file mancante nel manifest", + "scraper_build_error": "Impossibile creare un URL scraper", + "download_scraper_error": "Download scraper fallito", + "test_failed": "Test fallito", + "test_parameters": "Parametri Test Repo", + "test_parameters_desc": "Questi parametri sono usati solo per il Tester Repo.", + "using_info": "Uso: {{mediaType}} • TMDB {{tmdbId}}", + "using_info_tv": "Uso: {{mediaType}} • TMDB {{tmdbId}} • S{{season}}E{{episode}}", + "providers_title": "Provider", + "repository_default": "Repository", + "providers_count": "{{count}} provider", + "fetch_hint": "Recupera un repo per elencare i provider.", + "test_all": "Testa Tutto", + "status_running": "IN ESECUZIONE", + "status_ok": "OK ({{count}})", + "status_ok_empty": "OK (0)", + "status_failed": "FALLITO", + "status_idle": "INATTIVO", + "tried_url": "Provato: {{url}}", + "provider_logs": "Log Provider", + "no_logs_captured": "Nessun log catturato." + } + } } diff --git a/src/screens/SettingsScreen.tsx b/src/screens/SettingsScreen.tsx index 1e8f5bd6..1ff811e6 100644 --- a/src/screens/SettingsScreen.tsx +++ b/src/screens/SettingsScreen.tsx @@ -3,17 +3,22 @@ import React, { useCallback, useState, useEffect, useRef } from 'react'; import { useRealtimeConfig } from '../hooks/useRealtimeConfig'; import { - View, - Text, - StyleSheet, - TouchableOpacity, - ScrollView, - StatusBar, - Platform, - Dimensions, - FlatList, + View, + Text, + StyleSheet, + TouchableOpacity, + ScrollView, + StatusBar, + Platform, + Dimensions, + FlatList, } from 'react-native'; -import { BottomSheetModal, BottomSheetView, BottomSheetBackdrop, BottomSheetScrollView } from '@gorhom/bottom-sheet'; +import { + BottomSheetModal, + BottomSheetView, + BottomSheetBackdrop, + BottomSheetScrollView, +} from '@gorhom/bottom-sheet'; import { useTranslation } from 'react-i18next'; import { mmkvStorage } from '../services/mmkvStorage'; import { useNavigation } from '@react-navigation/native'; @@ -42,9 +47,17 @@ import { PlaybackSettingsContent } from './settings/PlaybackSettingsScreen'; import { ContentDiscoverySettingsContent } from './settings/ContentDiscoverySettingsScreen'; import { AppearanceSettingsContent } from './settings/AppearanceSettingsScreen'; import { IntegrationsSettingsContent } from './settings/IntegrationsSettingsScreen'; -import { AboutSettingsContent, AboutFooter } from './settings/AboutSettingsScreen'; +import { + AboutSettingsContent, + AboutFooter, +} from './settings/AboutSettingsScreen'; import { PrivacySettingsContent } from './settings/PrivacySettingsScreen'; -import { SettingsCard, SettingItem, ChevronRight, CustomSwitch } from './settings/SettingsComponents'; +import { + SettingsCard, + SettingItem, + ChevronRight, + CustomSwitch, +} from './settings/SettingsComponents'; import { useBottomSheetBackHandler } from '../hooks/useBottomSheetBackHandler'; import { LOCALES } from '../constants/locales'; import { useSimklIntegration } from '../hooks/useSimklIntegration'; @@ -56,1154 +69,1301 @@ const isTablet = width >= 768; // Settings categories for tablet sidebar // Settings categories moved inside component for translation - // Tablet Sidebar Component interface SidebarProps { - selectedCategory: string; - onCategorySelect: (category: string) => void; - currentTheme: any; - categories: any[]; - extraTopPadding?: number; + selectedCategory: string; + onCategorySelect: (category: string) => void; + currentTheme: any; + categories: any[]; + extraTopPadding?: number; } -const Sidebar: React.FC = ({ selectedCategory, onCategorySelect, currentTheme, categories, extraTopPadding = 0 }) => { - return ( - - - - Settings - - +const Sidebar: React.FC = ({ + selectedCategory, + onCategorySelect, + currentTheme, + categories, + extraTopPadding = 0, +}) => { + return ( + + + + Settings + + - - {categories.map((category) => ( - onCategorySelect(category.id)} - activeOpacity={0.6} - > - - - - - {category.title} - - - ))} - - - ); + + {categories.map((category) => ( + onCategorySelect(category.id)} + activeOpacity={0.6} + > + + + + + {category.title} + + + ))} + + + ); }; const SettingsScreen: React.FC = () => { - const { t, i18n } = useTranslation(); + const { t, i18n } = useTranslation(); - const SETTINGS_CATEGORIES = [ - { id: 'account', title: t('settings.account'), icon: 'user' }, - { id: 'content', title: t('settings.content_discovery'), icon: 'compass' }, - { id: 'appearance', title: t('settings.appearance'), icon: 'sliders' }, - { id: 'integrations', title: t('settings.integrations'), icon: 'layers' }, - { id: 'playback', title: t('settings.playback'), icon: 'play-circle' }, - { id: 'backup', title: t('settings.backup_restore'), icon: 'archive' }, - { id: 'updates', title: t('settings.updates'), icon: 'refresh-ccw' }, - { id: 'privacy', title: t('privacy.title'), icon: 'shield' }, - { id: 'about', title: t('settings.about'), icon: 'info' }, - { id: 'developer', title: t('settings.developer'), icon: 'code' }, - { id: 'cache', title: t('settings.cache'), icon: 'database' }, - ]; - const { settings, updateSetting } = useSettings(); - const [hasUpdateBadge, setHasUpdateBadge] = useState(false); - const languageSheetRef = useRef(null); - const { onChange, onDismiss } = useBottomSheetBackHandler(); - const insets = useSafeAreaInsets(); + const SETTINGS_CATEGORIES = [ + { id: 'account', title: t('settings.account'), icon: 'user' }, + { id: 'content', title: t('settings.content_discovery'), icon: 'compass' }, + { id: 'appearance', title: t('settings.appearance'), icon: 'sliders' }, + { id: 'integrations', title: t('settings.integrations'), icon: 'layers' }, + { id: 'playback', title: t('settings.playback'), icon: 'play-circle' }, + { id: 'backup', title: t('settings.backup_restore'), icon: 'archive' }, + { id: 'updates', title: t('settings.updates'), icon: 'refresh-ccw' }, + { id: 'privacy', title: t('privacy.title'), icon: 'shield' }, + { id: 'about', title: t('settings.about'), icon: 'info' }, + { id: 'developer', title: t('settings.developer'), icon: 'code' }, + { id: 'cache', title: t('settings.cache'), icon: 'database' }, + ]; + const { settings, updateSetting } = useSettings(); + const [hasUpdateBadge, setHasUpdateBadge] = useState(false); + const languageSheetRef = useRef(null); + const { onChange, onDismiss } = useBottomSheetBackHandler(); + const insets = useSafeAreaInsets(); - // Render backdrop for bottom sheet - const renderBackdrop = useCallback( - (props: any) => ( - - ), - [] - ); + // Render backdrop for bottom sheet + const renderBackdrop = useCallback( + (props: any) => ( + + ), + [], + ); - // CustomAlert state - const [alertVisible, setAlertVisible] = useState(false); - const [alertTitle, setAlertTitle] = useState(''); - const [alertMessage, setAlertMessage] = useState(''); - const [alertActions, setAlertActions] = useState void; style?: object }>>([]); + // CustomAlert state + const [alertVisible, setAlertVisible] = useState(false); + const [alertTitle, setAlertTitle] = useState(''); + const [alertMessage, setAlertMessage] = useState(''); + const [alertActions, setAlertActions] = useState< + Array<{ label: string; onPress: () => void; style?: object }> + >([]); - const openAlert = ( - title: string, - message: string, - actions?: Array<{ label: string; onPress: () => void; style?: object }> - ) => { - setAlertTitle(title); - setAlertMessage(message); - setAlertActions(actions && actions.length > 0 ? actions : [{ label: 'OK', onPress: () => { } }]); - setAlertVisible(true); - }; + const openAlert = ( + title: string, + message: string, + actions?: Array<{ label: string; onPress: () => void; style?: object }>, + ) => { + setAlertTitle(title); + setAlertMessage(message); + setAlertActions( + actions && actions.length > 0 + ? actions + : [{ label: 'OK', onPress: () => {} }], + ); + setAlertVisible(true); + }; - useEffect(() => { - if (Platform.OS !== 'android') return; - let mounted = true; - (async () => { - try { - const flag = await mmkvStorage.getItem('@update_badge_pending'); - if (mounted) setHasUpdateBadge(flag === 'true'); - } catch { } - })(); - return () => { mounted = false; }; - }, []); + useEffect(() => { + if (Platform.OS !== 'android') return; + let mounted = true; + (async () => { + try { + const flag = await mmkvStorage.getItem('@update_badge_pending'); + if (mounted) setHasUpdateBadge(flag === 'true'); + } catch {} + })(); + return () => { + mounted = false; + }; + }, []); - const navigation = useNavigation>(); - const { lastUpdate } = useCatalogContext(); - const { isAuthenticated, userProfile, refreshAuthStatus } = useTraktContext(); - const { isAuthenticated: isSimklAuthenticated } = useSimklIntegration(); - const { currentTheme } = useTheme(); + const navigation = useNavigation>(); + const { lastUpdate } = useCatalogContext(); + const { isAuthenticated, userProfile, refreshAuthStatus } = useTraktContext(); + const { isAuthenticated: isSimklAuthenticated } = useSimklIntegration(); + const { currentTheme } = useTheme(); - // Tablet-specific state - const [selectedCategory, setSelectedCategory] = useState('account'); + // Tablet-specific state + const [selectedCategory, setSelectedCategory] = useState('account'); - // States for dynamic content - const [mdblistKeySet, setMdblistKeySet] = useState(false); - const [developerModeEnabled, setDeveloperModeEnabled] = useState(false); - const [totalDownloads, setTotalDownloads] = useState(0); - const [displayDownloads, setDisplayDownloads] = useState(null); + // States for dynamic content + const [mdblistKeySet, setMdblistKeySet] = useState(false); + const [developerModeEnabled, setDeveloperModeEnabled] = + useState(false); + const [totalDownloads, setTotalDownloads] = useState(0); + const [displayDownloads, setDisplayDownloads] = useState(null); - // Use Realtime Config Hook - const settingsConfig = useRealtimeConfig(); + // Use Realtime Config Hook + const settingsConfig = useRealtimeConfig(); - // Load developer mode state - useEffect(() => { - const loadDevModeState = async () => { - try { - const devModeEnabled = await mmkvStorage.getItem('developer_mode_enabled'); - setDeveloperModeEnabled(devModeEnabled === 'true'); - } catch (error) { - if (__DEV__) console.error('Failed to load developer mode state:', error); - } - }; - loadDevModeState(); - }, []); + // Load developer mode state + useEffect(() => { + const loadDevModeState = async () => { + try { + const devModeEnabled = await mmkvStorage.getItem('developer_mode_enabled'); + setDeveloperModeEnabled(devModeEnabled === 'true'); + } catch (error) { + if (__DEV__) console.error('Failed to load developer mode state:', error); + } + }; + loadDevModeState(); + }, []); - // Scroll to top ref and handler - const mobileScrollViewRef = useRef(null); - const tabletScrollViewRef = useRef(null); + // Scroll to top ref and handler + const mobileScrollViewRef = useRef(null); + const tabletScrollViewRef = useRef(null); - const scrollToTop = useCallback(() => { - mobileScrollViewRef.current?.scrollTo({ y: 0, animated: true }); - tabletScrollViewRef.current?.scrollTo({ y: 0, animated: true }); - }, []); + const scrollToTop = useCallback(() => { + mobileScrollViewRef.current?.scrollTo({ y: 0, animated: true }); + tabletScrollViewRef.current?.scrollTo({ y: 0, animated: true }); + }, []); - useScrollToTop('Settings', scrollToTop); + useScrollToTop('Settings', scrollToTop); - // Refresh Trakt auth status on focus - useEffect(() => { - const unsubscribe = navigation.addListener('focus', () => { - refreshAuthStatus(); - }); - return unsubscribe; - }, [navigation, refreshAuthStatus]); + // Refresh Trakt auth status on focus + useEffect(() => { + const unsubscribe = navigation.addListener('focus', () => { + refreshAuthStatus(); + }); + return unsubscribe; + }, [navigation, refreshAuthStatus]); - const loadData = useCallback(async () => { - try { - // Check MDBList API key status - const mdblistKey = await mmkvStorage.getItem('mdblist_api_key'); - setMdblistKeySet(!!mdblistKey); + const loadData = useCallback(async () => { + try { + // Check MDBList API key status + const mdblistKey = await mmkvStorage.getItem('mdblist_api_key'); + setMdblistKeySet(!!mdblistKey); - // Check developer mode status - const devModeEnabled = await mmkvStorage.getItem('developer_mode_enabled'); - setDeveloperModeEnabled(devModeEnabled === 'true'); + // Check developer mode status + const devModeEnabled = await mmkvStorage.getItem('developer_mode_enabled'); + setDeveloperModeEnabled(devModeEnabled === 'true'); - // Load GitHub total downloads - const downloads = await fetchTotalDownloads(); - if (downloads !== null) { - setTotalDownloads(downloads); - setDisplayDownloads(downloads); - } - } catch (error) { - if (__DEV__) console.error('Error loading settings data:', error); - } - }, []); + // Load GitHub total downloads + const downloads = await fetchTotalDownloads(); + if (downloads !== null) { + setTotalDownloads(downloads); + setDisplayDownloads(downloads); + } + } catch (error) { + if (__DEV__) console.error('Error loading settings data:', error); + } + }, []); - useEffect(() => { - loadData(); - }, [loadData, lastUpdate]); + useEffect(() => { + loadData(); + }, [loadData, lastUpdate]); - useEffect(() => { - const unsubscribe = navigation.addListener('focus', () => { - loadData(); - }); - return unsubscribe; - }, [navigation, loadData]); + useEffect(() => { + const unsubscribe = navigation.addListener('focus', () => { + loadData(); + }); + return unsubscribe; + }, [navigation, loadData]); - // Poll GitHub downloads - useEffect(() => { - const shouldPoll = isTablet ? selectedCategory === 'about' : true; - if (!shouldPoll) return; + // Poll GitHub downloads + useEffect(() => { + const shouldPoll = isTablet ? selectedCategory === 'about' : true; + if (!shouldPoll) return; - const pollInterval = setInterval(async () => { - try { - const downloads = await fetchTotalDownloads(); - if (downloads !== null && downloads !== totalDownloads) { - setTotalDownloads(downloads); - } - } catch (error) { - if (__DEV__) console.error('Error polling downloads:', error); - } - }, 3600000); + const pollInterval = setInterval(async () => { + try { + const downloads = await fetchTotalDownloads(); + if (downloads !== null && downloads !== totalDownloads) { + setTotalDownloads(downloads); + } + } catch (error) { + if (__DEV__) console.error('Error polling downloads:', error); + } + }, 3600000); - return () => clearInterval(pollInterval); - }, [selectedCategory, totalDownloads]); + return () => clearInterval(pollInterval); + }, [selectedCategory, totalDownloads]); - // Animate counting up when totalDownloads changes - useEffect(() => { - if (totalDownloads === null || displayDownloads === null) return; - if (totalDownloads === displayDownloads) return; + // Animate counting up when totalDownloads changes + useEffect(() => { + if (totalDownloads === null || displayDownloads === null) return; + if (totalDownloads === displayDownloads) return; - const start = displayDownloads; - const end = totalDownloads; - const duration = 2000; - const startTime = Date.now(); + const start = displayDownloads; + const end = totalDownloads; + const duration = 2000; + const startTime = Date.now(); - const animate = () => { - const now = Date.now(); - const elapsed = now - startTime; - const progress = Math.min(elapsed / duration, 1); - const easeProgress = 1 - Math.pow(1 - progress, 2); - const current = Math.floor(start + (end - start) * easeProgress); + const animate = () => { + const now = Date.now(); + const elapsed = now - startTime; + const progress = Math.min(elapsed / duration, 1); + const easeProgress = 1 - Math.pow(1 - progress, 2); + const current = Math.floor(start + (end - start) * easeProgress); - setDisplayDownloads(current); + setDisplayDownloads(current); - if (progress < 1) { - requestAnimationFrame(animate); - } else { - setDisplayDownloads(end); - } - }; + if (progress < 1) { + requestAnimationFrame(animate); + } else { + setDisplayDownloads(end); + } + }; - requestAnimationFrame(animate); - }, [totalDownloads]); + requestAnimationFrame(animate); + }, [totalDownloads]); - const handleClearMDBListCache = () => { - openAlert( - 'Clear MDBList Cache', - 'Are you sure you want to clear all cached MDBList data? This cannot be undone.', - [ - { label: 'Cancel', onPress: () => { } }, - { - label: 'Clear', - onPress: async () => { - try { - await mmkvStorage.removeItem('mdblist_cache'); - openAlert('Success', 'MDBList cache has been cleared.'); - } catch (error) { - openAlert('Error', 'Could not clear MDBList cache.'); - if (__DEV__) console.error('Error clearing MDBList cache:', error); - } - } - } - ] - ); - }; + const handleClearMDBListCache = () => { + openAlert( + 'Clear MDBList Cache', + 'Are you sure you want to clear all cached MDBList data? This cannot be undone.', + [ + { label: 'Cancel', onPress: () => {} }, + { + label: 'Clear', + onPress: async () => { + try { + await mmkvStorage.removeItem('mdblist_cache'); + openAlert('Success', 'MDBList cache has been cleared.'); + } catch (error) { + openAlert('Error', 'Could not clear MDBList cache.'); + if (__DEV__) console.error('Error clearing MDBList cache:', error); + } + }, + }, + ], + ); + }; - // Helper to check item visibility - const isItemVisible = (itemId: string) => { - if (!settingsConfig?.items) return true; - const item = settingsConfig.items[itemId]; - if (item && item.visible === false) return false; - return true; - }; - const showTraktItem = isItemVisible('trakt'); - const showSimklItem = isItemVisible('simkl'); - const showCloudSyncItem = isItemVisible('cloud_sync'); + // Helper to check item visibility + const isItemVisible = (itemId: string) => { + if (!settingsConfig?.items) return true; + const item = settingsConfig.items[itemId]; + if (item && item.visible === false) return false; + return true; + }; + const showTraktItem = isItemVisible('trakt'); + const showSimklItem = isItemVisible('simkl'); + const showCloudSyncItem = isItemVisible('cloud_sync'); - // Filter categories based on conditions - const visibleCategories = SETTINGS_CATEGORIES.filter(category => { - if (settingsConfig?.categories?.[category.id]?.visible === false) return false; - if (category.id === 'developer' && !__DEV__ && !developerModeEnabled) return false; - if (category.id === 'cache' && !mdblistKeySet) return false; - return true; - }); + // Filter categories based on conditions + const visibleCategories = SETTINGS_CATEGORIES.filter((category) => { + if (settingsConfig?.categories?.[category.id]?.visible === false) + return false; + if (category.id === 'developer' && !__DEV__ && !developerModeEnabled) + return false; + if (category.id === 'cache' && !mdblistKeySet) return false; + return true; + }); - // Render tablet category content using reusable components - const renderCategoryContent = (categoryId: string) => { - switch (categoryId) { - case 'account': - return ( - - {showCloudSyncItem && ( - - } - renderControl={() => } - onPress={() => (navigation as any).navigate('SyncSettings')} - isLast={!showTraktItem && !showSimklItem} - isTablet={isTablet} - /> - )} - {showTraktItem && ( - } - renderControl={() => } - onPress={() => navigation.navigate('TraktSettings')} - isLast={!showSimklItem} - isTablet={isTablet} - /> - )} - {showSimklItem && ( - } - renderControl={() => } - onPress={() => navigation.navigate('SimklSettings')} - isLast={true} - isTablet={isTablet} - /> - )} - - ); + // Render tablet category content using reusable components + const renderCategoryContent = (categoryId: string) => { + switch (categoryId) { + case 'account': + return ( + + {showCloudSyncItem && ( + + } + renderControl={() => } + onPress={() => (navigation as any).navigate('SyncSettings')} + isLast={!showTraktItem && !showSimklItem} + isTablet={isTablet} + /> + )} + {showTraktItem && ( + } + renderControl={() => } + onPress={() => navigation.navigate('TraktSettings')} + isLast={!showSimklItem} + isTablet={isTablet} + /> + )} + {showSimklItem && ( + } + renderControl={() => } + onPress={() => navigation.navigate('SimklSettings')} + isLast={true} + isTablet={isTablet} + /> + )} + + ); - case 'content': - return ; + case 'content': + return ; - case 'appearance': - return ( - <> - - l.code === i18n.language)?.key}`)} - icon="globe" - renderControl={() => } - onPress={() => languageSheetRef.current?.present()} - isLast={true} - isTablet={isTablet} - /> - - - - ); + case 'appearance': + return ( + <> + + l.code === i18n.language)?.key}`, + )} + icon="globe" + renderControl={() => } + onPress={() => languageSheetRef.current?.present()} + isLast={true} + isTablet={isTablet} + /> + + + + ); - case 'integrations': - return ; + case 'integrations': + return ; - case 'playback': - return ; + case 'playback': + return ; - case 'about': - return ; + case 'about': + return ( + + ); - case 'privacy': - return ; + case 'privacy': + return ; - case 'developer': - return (__DEV__ || developerModeEnabled) ? ( - - navigation.navigate('Onboarding')} - renderControl={() => } - isTablet={isTablet} - /> - navigation.navigate('PluginTester')} - renderControl={() => } - isTablet={isTablet} - /> - { - try { - await mmkvStorage.removeItem('hasCompletedOnboarding'); - openAlert('Success', 'Onboarding has been reset. Restart the app to see the onboarding flow.'); - } catch (error) { - openAlert('Error', 'Failed to reset onboarding.'); - } - }} - renderControl={() => } - isTablet={isTablet} - /> - { - await campaignService.resetCampaigns(); - openAlert('Success', 'Campaign history reset. Restart app to see posters again.'); - }} - renderControl={() => } - isTablet={isTablet} - /> - { - openAlert( - t('settings.clear_data'), - t('settings.clear_data_desc'), - [ - { label: 'Cancel', onPress: () => { } }, - { - label: 'Clear', - onPress: async () => { - try { - await mmkvStorage.clear(); - openAlert('Success', 'All data cleared. Please restart the app.'); - } catch (error) { - openAlert('Error', 'Failed to clear data.'); - } - } - } - ] - ); - }} - isLast={true} - isTablet={isTablet} - /> - - ) : null; + case 'developer': + return __DEV__ || developerModeEnabled ? ( + + navigation.navigate('Onboarding')} + renderControl={() => } + isTablet={isTablet} + /> + navigation.navigate('PluginTester')} + renderControl={() => } + isTablet={isTablet} + /> + { + try { + await mmkvStorage.removeItem('hasCompletedOnboarding'); + openAlert( + 'Success', + 'Onboarding has been reset. Restart the app to see the onboarding flow.', + ); + } catch (error) { + openAlert('Error', 'Failed to reset onboarding.'); + } + }} + renderControl={() => } + isTablet={isTablet} + /> + { + await campaignService.resetCampaigns(); + openAlert( + 'Success', + 'Campaign history reset. Restart app to see posters again.', + ); + }} + renderControl={() => } + isTablet={isTablet} + /> + { + openAlert(t('settings.clear_data'), t('settings.clear_data_desc'), [ + { label: 'Cancel', onPress: () => {} }, + { + label: 'Clear', + onPress: async () => { + try { + await mmkvStorage.clear(); + openAlert('Success', 'All data cleared. Please restart the app.'); + } catch (error) { + openAlert('Error', 'Failed to clear data.'); + } + }, + }, + ]); + }} + isLast={true} + isTablet={isTablet} + /> + + ) : null; - case 'cache': - return mdblistKeySet ? ( - - - - ) : null; + case 'cache': + return mdblistKeySet ? ( + + + + ) : null; - case 'backup': - return ( - - } - onPress={() => navigation.navigate('Backup')} - isLast={true} - isTablet={isTablet} - /> - - ); + case 'backup': + return ( + + } + onPress={() => navigation.navigate('Backup')} + isLast={true} + isTablet={isTablet} + /> + + ); - case 'updates': - return ( - - } - badge={Platform.OS === 'android' && hasUpdateBadge ? 1 : undefined} - onPress={async () => { - if (Platform.OS === 'android') { - try { await mmkvStorage.removeItem('@update_badge_pending'); } catch { } - setHasUpdateBadge(false); - } - navigation.navigate('Update'); - }} - isLast={true} - isTablet={isTablet} - /> - - ); + case 'updates': + return ( + + } + badge={Platform.OS === 'android' && hasUpdateBadge ? 1 : undefined} + onPress={async () => { + if (Platform.OS === 'android') { + try { + await mmkvStorage.removeItem('@update_badge_pending'); + } catch {} + setHasUpdateBadge(false); + } + navigation.navigate('Update'); + }} + isLast={true} + isTablet={isTablet} + /> + + ); - default: - return null; - } - }; + default: + return null; + } + }; - // Keep headers below floating top navigator on tablets - const tabletNavOffset = isTablet ? 64 : 0; + // Keep headers below floating top navigator on tablets + const tabletNavOffset = isTablet ? 64 : 0; - // TABLET LAYOUT - if (isTablet) { - return ( - - - - + // TABLET LAYOUT + if (isTablet) { + return ( + + + + - - - {renderCategoryContent(selectedCategory)} + + + {renderCategoryContent(selectedCategory)} - {selectedCategory === 'about' && ( - - )} - - - - setAlertVisible(false)} - /> + {selectedCategory === 'about' && ( + + )} + + + + setAlertVisible(false)} + /> - - - - {t('settings.select_language')} - - languageSheetRef.current?.dismiss()}> - - - - - { - LOCALES.sort((a, b) => a.key.localeCompare(b.key)).map(l => - { - i18n.changeLanguage(l.code); - languageSheetRef.current?.dismiss(); - }} - > - - {t(`settings.${l.key}`)} - - {i18n.language === l.code && ( - - )} - - ) - } - - - - ); - } + + + + {t('settings.select_language')} + + languageSheetRef.current?.dismiss()}> + + + + + {LOCALES.sort((a, b) => a.key.localeCompare(b.key)).map((l) => ( + { + i18n.changeLanguage(l.code); + languageSheetRef.current?.dismiss(); + }} + > + + {t(`settings.${l.key}`)} + + {i18n.language === l.code && ( + + )} + + ))} + + + + ); + } - // MOBILE LAYOUT - Simplified navigation hub - return ( - - - - - - - {/* Account */} - {(settingsConfig?.categories?.['account']?.visible !== false) && (showTraktItem || showSimklItem || showCloudSyncItem) && ( - - {showCloudSyncItem && ( - - } - renderControl={() => } - onPress={() => (navigation as any).navigate('SyncSettings')} - isLast={!showTraktItem && !showSimklItem} - /> - )} - {showTraktItem && ( - } - renderControl={() => } - onPress={() => navigation.navigate('TraktSettings')} - isLast={!showSimklItem} - /> - )} - {showSimklItem && ( - } - renderControl={() => } - onPress={() => navigation.navigate('SimklSettings')} - isLast={true} - /> - )} - - )} + // MOBILE LAYOUT - Simplified navigation hub + return ( + + + + + + + {/* Account */} + {settingsConfig?.categories?.['account']?.visible !== false && + (showTraktItem || showSimklItem || showCloudSyncItem) && ( + + {showCloudSyncItem && ( + + } + renderControl={() => } + onPress={() => (navigation as any).navigate('SyncSettings')} + isLast={!showTraktItem && !showSimklItem} + /> + )} + {showTraktItem && ( + } + renderControl={() => } + onPress={() => navigation.navigate('TraktSettings')} + isLast={!showSimklItem} + /> + )} + {showSimklItem && ( + } + renderControl={() => } + onPress={() => navigation.navigate('SimklSettings')} + isLast={true} + /> + )} + + )} - {/* General Settings */} - {( - (settingsConfig?.categories?.['content']?.visible !== false) || - (settingsConfig?.categories?.['appearance']?.visible !== false) || - (settingsConfig?.categories?.['integrations']?.visible !== false) || - (settingsConfig?.categories?.['playback']?.visible !== false) - ) && ( - - l.code === i18n.language)?.key}`) - } - icon="globe" - renderControl={() => } - onPress={() => languageSheetRef.current?.present()} - /> - {(settingsConfig?.categories?.['content']?.visible !== false) && ( - } - onPress={() => navigation.navigate('ContentDiscoverySettings')} - /> - )} - {(settingsConfig?.categories?.['appearance']?.visible !== false) && ( - } - onPress={() => navigation.navigate('AppearanceSettings')} - /> - )} - {(settingsConfig?.categories?.['integrations']?.visible !== false) && ( - } - onPress={() => navigation.navigate('IntegrationsSettings')} - /> - )} - {(settingsConfig?.categories?.['playback']?.visible !== false) && ( - } - onPress={() => navigation.navigate('PlaybackSettings')} - isLast - /> - )} - - )} + {/* General Settings */} + {(settingsConfig?.categories?.['content']?.visible !== false || + settingsConfig?.categories?.['appearance']?.visible !== false || + settingsConfig?.categories?.['integrations']?.visible !== false || + settingsConfig?.categories?.['playback']?.visible !== false) && ( + + l.code === i18n.language)?.key}`, + )} + icon="globe" + renderControl={() => } + onPress={() => languageSheetRef.current?.present()} + /> + {settingsConfig?.categories?.['content']?.visible !== false && ( + } + onPress={() => navigation.navigate('ContentDiscoverySettings')} + /> + )} + {settingsConfig?.categories?.['appearance']?.visible !== false && ( + } + onPress={() => navigation.navigate('AppearanceSettings')} + /> + )} + {settingsConfig?.categories?.['integrations']?.visible !== false && ( + } + onPress={() => navigation.navigate('IntegrationsSettings')} + /> + )} + {settingsConfig?.categories?.['playback']?.visible !== false && ( + } + onPress={() => navigation.navigate('PlaybackSettings')} + isLast + /> + )} + + )} - {/* Data */} - {( - (settingsConfig?.categories?.['backup']?.visible !== false) || - (settingsConfig?.categories?.['updates']?.visible !== false) - ) && ( - - {(settingsConfig?.categories?.['backup']?.visible !== false) && ( - } - onPress={() => navigation.navigate('Backup')} - /> - )} - {(settingsConfig?.categories?.['updates']?.visible !== false) && ( - } - onPress={async () => { - if (Platform.OS === 'android') { - try { await mmkvStorage.removeItem('@update_badge_pending'); } catch { } - setHasUpdateBadge(false); - } - navigation.navigate('Update'); - }} - isLast - /> - )} - - )} + {/* Data */} + {(settingsConfig?.categories?.['backup']?.visible !== false || + settingsConfig?.categories?.['updates']?.visible !== false) && ( + + {settingsConfig?.categories?.['backup']?.visible !== false && ( + } + onPress={() => navigation.navigate('Backup')} + /> + )} + {settingsConfig?.categories?.['updates']?.visible !== false && ( + } + onPress={async () => { + if (Platform.OS === 'android') { + try { + await mmkvStorage.removeItem('@update_badge_pending'); + } catch {} + setHasUpdateBadge(false); + } + navigation.navigate('Update'); + }} + isLast + /> + )} + + )} - {/* Cache - only if MDBList is set */} - {mdblistKeySet && ( - - - - )} + {/* Cache - only if MDBList is set */} + {mdblistKeySet && ( + + + + )} - {/* About */} - - } - onPress={() => navigation.navigate('Contributors')} - /> - } - onPress={() => navigation.navigate('PrivacySettings')} - /> - } - onPress={() => navigation.navigate('AboutSettings')} - isLast - /> - + {/* About */} + + } + onPress={() => navigation.navigate('Contributors')} + /> + } + onPress={() => navigation.navigate('PrivacySettings')} + /> + } + onPress={() => navigation.navigate('AboutSettings')} + isLast + /> + - {/* Developer - visible in DEV mode or when developer mode is enabled */} - {(__DEV__ || developerModeEnabled) && ( - - } - onPress={() => navigation.navigate('DeveloperSettings')} - isLast - /> - - )} + {/* Developer - visible in DEV mode or when developer mode is enabled */} + {(__DEV__ || developerModeEnabled) && ( + + } + onPress={() => navigation.navigate('DeveloperSettings')} + isLast + /> + + )} - {/* Downloads Counter */} - {settingsConfig?.items?.['downloads_counter']?.visible !== false && displayDownloads !== null && ( - - - {displayDownloads.toLocaleString()} - - - {t('settings.downloads_counter')} - - - )} + {/* Downloads Counter */} + {settingsConfig?.items?.['downloads_counter']?.visible !== false && + displayDownloads !== null && ( + + + {displayDownloads.toLocaleString()} + + + {t('settings.downloads_counter')} + + + )} - {/* Support & Community Buttons */} - - WebBrowser.openBrowserAsync('https://ko-fi.com/tapframe', { - presentationStyle: Platform.OS === 'ios' ? WebBrowser.WebBrowserPresentationStyle.FORM_SHEET : WebBrowser.WebBrowserPresentationStyle.FORM_SHEET - })} - activeOpacity={0.7} - > - - + {/* Support & Community Buttons */} + + + WebBrowser.openBrowserAsync('https://ko-fi.com/tapframe', { + presentationStyle: + Platform.OS === 'ios' + ? WebBrowser.WebBrowserPresentationStyle.FORM_SHEET + : WebBrowser.WebBrowserPresentationStyle.FORM_SHEET, + }) + } + activeOpacity={0.7} + > + + + - + {/* Monkey Animation */} + + + - {/* Monkey Animation */} - - - + + + - - - + + + {t('settings.made_with_love')} + + - - - {t('settings.made_with_love')} - - + + + + + setAlertVisible(false)} + /> - - - - - setAlertVisible(false)} - /> - - - - - {t('settings.select_language')} - - languageSheetRef.current?.dismiss()}> - - - - - { - LOCALES.sort((a, b) => a.key.localeCompare(b.key)).map(l => - { - i18n.changeLanguage(l.code); - languageSheetRef.current?.dismiss(); - }} - > - - {t(`settings.${l.key}`)} - - {i18n.language === l.code && ( - - )} - - ) - } - - - - ); + + + + {t('settings.select_language')} + + languageSheetRef.current?.dismiss()}> + + + + + {LOCALES.sort((a, b) => a.key.localeCompare(b.key)).map((l) => ( + { + i18n.changeLanguage(l.code); + languageSheetRef.current?.dismiss(); + }} + > + + {t(`settings.${l.key}`)} + + {i18n.language === l.code && ( + + )} + + ))} + + + + ); }; const styles = StyleSheet.create({ - container: { - flex: 1, - }, - actionSheetContent: { - flex: 1, - }, - bottomSheetHeader: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - paddingHorizontal: 20, - paddingVertical: 16, - borderBottomWidth: 1, - borderBottomColor: 'rgba(255,255,255,0.1)', - }, - bottomSheetTitle: { - fontSize: 18, - fontWeight: '600', - }, - bottomSheetContent: { - paddingHorizontal: 16, - paddingTop: 8, - paddingBottom: 24, - }, - languageOption: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - paddingVertical: 14, - paddingHorizontal: 16, - borderRadius: 8, - marginBottom: 8, - }, - languageText: { - fontSize: 16, - }, - // Mobile styles - contentContainer: { - flex: 1, - zIndex: 1, - width: '100%', - }, - scrollView: { - flex: 1, - width: '100%', - }, - scrollContent: { - flexGrow: 1, - width: '100%', - paddingTop: 8, - paddingBottom: 32, - }, - // Tablet-specific styles - tabletContainer: { - flex: 1, - flexDirection: 'row', - }, - sidebar: { - width: 280, - borderRightWidth: 1, - }, - sidebarHeader: { - paddingHorizontal: 24, - paddingBottom: 20, - paddingTop: Platform.OS === 'android' ? (StatusBar.currentHeight || 0) + 24 : 48, - borderBottomWidth: 1, - }, - sidebarTitle: { - fontSize: 42, - fontWeight: '700', - letterSpacing: -0.3, - }, - sidebarContent: { - flex: 1, - paddingTop: 12, - paddingBottom: 24, - }, - sidebarItem: { - flexDirection: 'row', - alignItems: 'center', - paddingHorizontal: 16, - paddingVertical: 12, - marginHorizontal: 12, - marginVertical: 2, - borderRadius: 10, - }, - sidebarItemActive: { - borderRadius: 10, - }, - sidebarItemIconContainer: { - width: 32, - height: 32, - borderRadius: 8, - alignItems: 'center', - justifyContent: 'center', - }, - sidebarItemText: { - fontSize: 15, - marginLeft: 12, - }, - tabletContent: { - flex: 1, - paddingTop: Platform.OS === 'android' ? (StatusBar.currentHeight || 0) + 24 : 48, - }, - tabletScrollView: { - flex: 1, - paddingHorizontal: 40, - }, - tabletScrollContent: { - paddingTop: 8, - paddingBottom: 40, - }, - // Footer and social styles - footer: { - alignItems: 'center', - justifyContent: 'center', - marginTop: 0, - marginBottom: 48, - }, - footerText: { - fontSize: 13, - opacity: 0.5, - letterSpacing: 0.2, - }, - discordContainer: { - marginTop: 12, - marginBottom: 24, - alignItems: 'center', - }, - discordButton: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center', - paddingVertical: 10, - paddingHorizontal: 18, - borderRadius: 10, - maxWidth: 200, - }, - kofiImage: { - height: 34, - width: 155, - }, - downloadsContainer: { - marginTop: 32, - marginBottom: 16, - alignItems: 'center', - }, - downloadsNumber: { - fontSize: 36, - fontWeight: '800', - letterSpacing: 0.5, - marginBottom: 6, - }, - downloadsLabel: { - fontSize: 11, - fontWeight: '600', - opacity: 0.5, - letterSpacing: 1.5, - textTransform: 'uppercase', - }, - monkeyContainer: { - alignItems: 'center', - justifyContent: 'center', - marginTop: 0, - marginBottom: 16, - }, - monkeyAnimation: { - width: 180, - height: 180, - }, - syncLogoIcon: { - width: 20, - height: 20, - }, - syncLogoIconTablet: { - width: 24, - height: 24, - }, - brandLogoContainer: { - alignItems: 'center', - justifyContent: 'center', - marginTop: 0, - marginBottom: 16, - opacity: 0.8, - }, - brandLogo: { - width: 120, - height: 40, - }, + container: { + flex: 1, + }, + actionSheetContent: { + flex: 1, + }, + bottomSheetHeader: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + paddingHorizontal: 20, + paddingVertical: 16, + borderBottomWidth: 1, + borderBottomColor: 'rgba(255,255,255,0.1)', + }, + bottomSheetTitle: { + fontSize: 18, + fontWeight: '600', + }, + bottomSheetContent: { + paddingHorizontal: 16, + paddingTop: 8, + paddingBottom: 24, + }, + languageOption: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + paddingVertical: 14, + paddingHorizontal: 16, + borderRadius: 8, + marginBottom: 8, + }, + languageText: { + fontSize: 16, + }, + // Mobile styles + contentContainer: { + flex: 1, + zIndex: 1, + width: '100%', + }, + scrollView: { + flex: 1, + width: '100%', + }, + scrollContent: { + flexGrow: 1, + width: '100%', + paddingTop: 8, + paddingBottom: 32, + }, + // Tablet-specific styles + tabletContainer: { + flex: 1, + flexDirection: 'row', + }, + sidebar: { + width: 280, + borderRightWidth: 1, + }, + sidebarHeader: { + paddingHorizontal: 24, + paddingBottom: 20, + paddingTop: + Platform.OS === 'android' ? (StatusBar.currentHeight || 0) + 24 : 48, + borderBottomWidth: 1, + }, + sidebarTitle: { + fontSize: 42, + fontWeight: '700', + letterSpacing: -0.3, + }, + sidebarContent: { + flex: 1, + paddingTop: 12, + paddingBottom: 24, + }, + sidebarItem: { + flexDirection: 'row', + alignItems: 'center', + paddingHorizontal: 16, + paddingVertical: 12, + marginHorizontal: 12, + marginVertical: 2, + borderRadius: 10, + }, + sidebarItemActive: { + borderRadius: 10, + }, + sidebarItemIconContainer: { + width: 32, + height: 32, + borderRadius: 8, + alignItems: 'center', + justifyContent: 'center', + }, + sidebarItemText: { + fontSize: 15, + marginLeft: 12, + }, + tabletContent: { + flex: 1, + paddingTop: + Platform.OS === 'android' ? (StatusBar.currentHeight || 0) + 24 : 48, + }, + tabletScrollView: { + flex: 1, + paddingHorizontal: 40, + }, + tabletScrollContent: { + paddingTop: 8, + paddingBottom: 40, + }, + // Footer and social styles + footer: { + alignItems: 'center', + justifyContent: 'center', + marginTop: 0, + marginBottom: 48, + }, + footerText: { + fontSize: 13, + opacity: 0.5, + letterSpacing: 0.2, + }, + discordContainer: { + marginTop: 12, + marginBottom: 24, + alignItems: 'center', + }, + discordButton: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + paddingVertical: 10, + paddingHorizontal: 18, + borderRadius: 10, + maxWidth: 200, + }, + kofiImage: { + height: 34, + width: 155, + }, + downloadsContainer: { + marginTop: 32, + marginBottom: 16, + alignItems: 'center', + }, + downloadsNumber: { + fontSize: 36, + fontWeight: '800', + letterSpacing: 0.5, + marginBottom: 6, + }, + downloadsLabel: { + fontSize: 11, + fontWeight: '600', + opacity: 0.5, + letterSpacing: 1.5, + textTransform: 'uppercase', + }, + monkeyContainer: { + alignItems: 'center', + justifyContent: 'center', + marginTop: 0, + marginBottom: 16, + }, + monkeyAnimation: { + width: 180, + height: 180, + }, + syncLogoIcon: { + width: 20, + height: 20, + }, + syncLogoIconTablet: { + width: 24, + height: 24, + }, + brandLogoContainer: { + alignItems: 'center', + justifyContent: 'center', + marginTop: 0, + marginBottom: 16, + opacity: 0.8, + }, + brandLogo: { + width: 120, + height: 40, + }, }); export default SettingsScreen; diff --git a/src/screens/TMDBSettingsScreen.tsx b/src/screens/TMDBSettingsScreen.tsx index 73edcb69..5b37d1f5 100644 --- a/src/screens/TMDBSettingsScreen.tsx +++ b/src/screens/TMDBSettingsScreen.tsx @@ -1,22 +1,22 @@ import React, { useState, useEffect, useRef, useCallback } from 'react'; import { - View, - Text, - StyleSheet, - TouchableOpacity, - TextInput, - SafeAreaView, - StatusBar, - Platform, - ActivityIndicator, - Linking, - ScrollView, - Keyboard, - Clipboard, - Switch, - KeyboardAvoidingView, - TouchableWithoutFeedback, - Modal, + View, + Text, + StyleSheet, + TouchableOpacity, + TextInput, + SafeAreaView, + StatusBar, + Platform, + ActivityIndicator, + Linking, + ScrollView, + Keyboard, + Clipboard, + Switch, + KeyboardAvoidingView, + TouchableWithoutFeedback, + Modal, } from 'react-native'; import { useNavigation } from '@react-navigation/native'; import MaterialIcons from 'react-native-vector-icons/MaterialIcons'; @@ -37,1748 +37,2333 @@ const TMDB_API_KEY = '439c478a771f35c05022f9feabcca01c'; // Define example shows with their IMDB IDs and TMDB IDs const EXAMPLE_SHOWS = [ - { - name: 'Breaking Bad', - imdbId: 'tt0903747', - tmdbId: '1396', - type: 'tv' as const - }, - { - name: 'Friends', - imdbId: 'tt0108778', - tmdbId: '1668', - type: 'tv' as const - }, - { - name: 'Stranger Things', - imdbId: 'tt4574334', - tmdbId: '66732', - type: 'tv' as const - }, - { - name: 'Avatar', - imdbId: 'tt0499549', - tmdbId: '19995', - type: 'movie' as const - }, + { + name: 'Breaking Bad', + imdbId: 'tt0903747', + tmdbId: '1396', + type: 'tv' as const, + }, + { + name: 'Friends', + imdbId: 'tt0108778', + tmdbId: '1668', + type: 'tv' as const, + }, + { + name: 'Stranger Things', + imdbId: 'tt4574334', + tmdbId: '66732', + type: 'tv' as const, + }, + { + name: 'Avatar', + imdbId: 'tt0499549', + tmdbId: '19995', + type: 'movie' as const, + }, ]; const TMDBSettingsScreen = () => { - const { t } = useTranslation(); - const navigation = useNavigation(); - const [apiKey, setApiKey] = useState(''); - const [isLoading, setIsLoading] = useState(true); - const [isKeySet, setIsKeySet] = useState(false); - const [useCustomKey, setUseCustomKey] = useState(false); - const [testResult, setTestResult] = useState<{ success: boolean; message: string } | null>(null); - const [isInputFocused, setIsInputFocused] = useState(false); - const [alertVisible, setAlertVisible] = useState(false); - const [alertTitle, setAlertTitle] = useState(''); - const [alertMessage, setAlertMessage] = useState(''); - const [alertActions, setAlertActions] = useState void; style?: object }>>([ - { label: t('common.ok'), onPress: () => setAlertVisible(false) }, - ]); - const apiKeyInputRef = useRef(null); - const { currentTheme } = useTheme(); - const insets = useSafeAreaInsets(); - const { settings, updateSetting } = useSettings(); - const [languagePickerVisible, setLanguagePickerVisible] = useState(false); - const [languageSearch, setLanguageSearch] = useState(''); - - // Logo preview state - const [selectedShow, setSelectedShow] = useState(EXAMPLE_SHOWS[0]); - const [tmdbLogo, setTmdbLogo] = useState(null); - const [tmdbBanner, setTmdbBanner] = useState(null); - const [loadingLogos, setLoadingLogos] = useState(true); - const [previewLanguage, setPreviewLanguage] = useState(''); - const [isPreviewFallback, setIsPreviewFallback] = useState(false); - const [cacheSize, setCacheSize] = useState('0 KB'); - - const openAlert = ( - title: string, - message: string, - actions?: Array<{ label: string; onPress?: () => void; style?: object }> - ) => { - setAlertTitle(title); - setAlertMessage(message); - if (actions && actions.length > 0) { - setAlertActions( - actions.map(a => ({ - label: a.label, - style: a.style, - onPress: () => { a.onPress?.(); }, - })) - ); - } else { - setAlertActions([{ label: t('common.ok'), onPress: () => setAlertVisible(false) }]); - } - setAlertVisible(true); - }; - - useEffect(() => { - logger.log('[TMDBSettingsScreen] Component mounted'); - loadSettings(); - calculateCacheSize(); - return () => { - logger.log('[TMDBSettingsScreen] Component unmounted'); - }; - }, []); - - const calculateCacheSize = async () => { - try { - const keys = await mmkvStorage.getAllKeys(); - const tmdbKeys = keys.filter(key => key.startsWith('tmdb_cache_')); - - let totalSize = 0; - for (const key of tmdbKeys) { - const value = mmkvStorage.getString(key); - if (value) { - totalSize += value.length; - } - } - - // Convert to KB/MB - let sizeStr = ''; - if (totalSize < 1024) { - sizeStr = `${totalSize} B`; - } else if (totalSize < 1024 * 1024) { - sizeStr = `${(totalSize / 1024).toFixed(2)} KB`; - } else { - sizeStr = `${(totalSize / (1024 * 1024)).toFixed(2)} MB`; - } - - setCacheSize(sizeStr); - } catch (error) { - logger.error('[TMDBSettingsScreen] Error calculating cache size:', error); - setCacheSize('Unknown'); - } - }; - - const handleClearCache = () => { - openAlert( - t('tmdb_settings.clear_cache_title'), - t('tmdb_settings.clear_cache_msg', { size: cacheSize }), - [ - { - label: t('common.cancel'), - onPress: () => logger.log('[TMDBSettingsScreen] Clear cache cancelled'), - }, - { - label: t('tmdb_settings.clear_cache'), - onPress: async () => { - logger.log('[TMDBSettingsScreen] Proceeding with cache clear'); - try { - await tmdbService.clearAllCache(); - setCacheSize('0 KB'); - logger.log('[TMDBSettingsScreen] Cache cleared successfully'); - openAlert(t('common.success'), t('tmdb_settings.clear_cache_success')); - } catch (error) { - logger.error('[TMDBSettingsScreen] Failed to clear cache:', error); - openAlert(t('common.error'), t('tmdb_settings.clear_cache_error')); - } - }, - }, - ] - ); - }; - - const loadSettings = async () => { - logger.log('[TMDBSettingsScreen] Loading settings from storage'); - try { - const [savedKey, savedUseCustomKey] = await Promise.all([ - mmkvStorage.getItem(TMDB_API_KEY_STORAGE_KEY), - mmkvStorage.getItem(USE_CUSTOM_TMDB_API_KEY) - ]); - - logger.log('[TMDBSettingsScreen] API key status:', savedKey ? 'Found' : 'Not found'); - logger.log('[TMDBSettingsScreen] Use custom API setting:', savedUseCustomKey); - - if (savedKey) { - setApiKey(savedKey); - setIsKeySet(true); - } else { - setIsKeySet(false); - } - - setUseCustomKey(savedUseCustomKey === 'true'); - } catch (error) { - logger.error('[TMDBSettingsScreen] Failed to load settings:', error); - setIsKeySet(false); - setUseCustomKey(false); - } finally { - setIsLoading(false); - logger.log('[TMDBSettingsScreen] Finished loading settings'); - } - }; - - const saveApiKey = async () => { - logger.log('[TMDBSettingsScreen] Starting API key save'); - Keyboard.dismiss(); - - try { - const trimmedKey = apiKey.trim(); - if (!trimmedKey) { - logger.warn('[TMDBSettingsScreen] Empty API key provided'); - setTestResult({ success: false, message: t('tmdb_settings.empty_api_key') }); - return; - } - - // Test the API key to make sure it works - if (await testApiKey(trimmedKey)) { - logger.log('[TMDBSettingsScreen] API key test successful, saving key'); - await mmkvStorage.setItem(TMDB_API_KEY_STORAGE_KEY, trimmedKey); - await mmkvStorage.setItem(USE_CUSTOM_TMDB_API_KEY, 'true'); - setIsKeySet(true); - setUseCustomKey(true); - setTestResult({ success: true, message: t('tmdb_settings.key_verified') }); - logger.log('[TMDBSettingsScreen] API key saved successfully'); - } else { - logger.warn('[TMDBSettingsScreen] API key test failed'); - setTestResult({ success: false, message: t('tmdb_settings.invalid_api_key') }); - } - } catch (error) { - logger.error('[TMDBSettingsScreen] Error saving API key:', error); - setTestResult({ - success: false, - message: t('tmdb_settings.save_error') - }); - } - }; - - const testApiKey = async (key: string): Promise => { - try { - // Simple API call to test the key using the API key parameter method - const response = await fetch( - `https://api.themoviedb.org/3/configuration?api_key=${key}`, - { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - } - } - ); - return response.ok; - } catch (error) { - logger.error('[TMDBSettingsScreen] API key test error:', error); - return false; - } - }; - - const clearApiKey = async () => { - logger.log('[TMDBSettingsScreen] Clear API key requested'); - openAlert( - t('tmdb_settings.clear_api_key_title'), - t('tmdb_settings.clear_api_key_msg'), - [ - { - label: t('common.cancel'), - onPress: () => logger.log('[TMDBSettingsScreen] Clear API key cancelled'), - }, - { - label: t('mdblist.clear'), - onPress: async () => { - logger.log('[TMDBSettingsScreen] Proceeding with API key clear'); - try { - await mmkvStorage.removeItem(TMDB_API_KEY_STORAGE_KEY); - await mmkvStorage.setItem(USE_CUSTOM_TMDB_API_KEY, 'false'); - setApiKey(''); - setIsKeySet(false); - setUseCustomKey(false); - setTestResult(null); - logger.log('[TMDBSettingsScreen] API key cleared successfully'); - } catch (error) { - logger.error('[TMDBSettingsScreen] Failed to clear API key:', error); - openAlert(t('common.error'), t('tmdb_settings.clear_api_key_error')); - } - }, - }, - ] - ); - }; - - const toggleUseCustomKey = async (value: boolean) => { - logger.log('[TMDBSettingsScreen] Toggle use custom key:', value); - try { - await mmkvStorage.setItem(USE_CUSTOM_TMDB_API_KEY, value ? 'true' : 'false'); - setUseCustomKey(value); - - if (!value) { - // If switching to built-in key, show confirmation - logger.log('[TMDBSettingsScreen] Switching to built-in API key'); - setTestResult({ - success: true, - message: t('tmdb_settings.using_builtin_key') - }); - } else if (apiKey && isKeySet) { - // If switching to custom key and we have a key - logger.log('[TMDBSettingsScreen] Switching to custom API key'); - setTestResult({ - success: true, - message: t('tmdb_settings.using_custom_key') - }); - } else { - // If switching to custom key but don't have a key yet - logger.log('[TMDBSettingsScreen] No custom key available yet'); - setTestResult({ - success: false, - message: t('tmdb_settings.enter_custom_key') - }); - } - } catch (error) { - logger.error('[TMDBSettingsScreen] Failed to toggle custom key setting:', error); - } - }; - - const pasteFromClipboard = async () => { - logger.log('[TMDBSettingsScreen] Attempting to paste from clipboard'); - try { - const clipboardContent = await Clipboard.getString(); - if (clipboardContent) { - logger.log('[TMDBSettingsScreen] Content pasted from clipboard'); - setApiKey(clipboardContent); - setTestResult(null); - } else { - logger.warn('[TMDBSettingsScreen] No content in clipboard'); - } - } catch (error) { - logger.error('[TMDBSettingsScreen] Error pasting from clipboard:', error); - } - }; - - const openTMDBWebsite = () => { - logger.log('[TMDBSettingsScreen] Opening TMDb website'); - Linking.openURL('https://www.themoviedb.org/settings/api').catch(error => { - logger.error('[TMDBSettingsScreen] Error opening website:', error); - }); - }; - - // Logo preview functions - const fetchExampleLogos = async (show: typeof EXAMPLE_SHOWS[0]) => { - setLoadingLogos(true); - setTmdbLogo(null); - setTmdbBanner(null); - - try { - const tmdbId = show.tmdbId; - const contentType = show.type; - - logger.log(`[TMDBSettingsScreen] Fetching ${show.name} with TMDB ID: ${tmdbId}`); - - const preferredTmdbLanguage = settings.tmdbLanguagePreference || 'en'; - - const apiKey = TMDB_API_KEY; - const endpoint = contentType === 'tv' ? 'tv' : 'movie'; - const response = await fetch(`https://api.themoviedb.org/3/${endpoint}/${tmdbId}/images?api_key=${apiKey}`); - const imagesData = await response.json(); - - if (imagesData.logos && imagesData.logos.length > 0) { - let logoPath: string | null = null; - let logoLanguage = preferredTmdbLanguage; - - // Try to find logo in preferred language - const preferredLogo = imagesData.logos.find((logo: { iso_639_1: string; file_path: string }) => logo.iso_639_1 === preferredTmdbLanguage); - - if (preferredLogo) { - logoPath = preferredLogo.file_path; - logoLanguage = preferredTmdbLanguage; - setIsPreviewFallback(false); - } else { - // Fallback to English - const englishLogo = imagesData.logos.find((logo: { iso_639_1: string; file_path: string }) => logo.iso_639_1 === 'en'); - - if (englishLogo) { - logoPath = englishLogo.file_path; - logoLanguage = 'en'; - setIsPreviewFallback(true); - } else if (imagesData.logos[0]) { - // Fallback to first available - logoPath = imagesData.logos[0].file_path; - logoLanguage = imagesData.logos[0].iso_639_1 || 'unknown'; - setIsPreviewFallback(true); - } - } - - if (logoPath) { - setTmdbLogo(`https://image.tmdb.org/t/p/original${logoPath}`); - setPreviewLanguage(logoLanguage); - } else { - setPreviewLanguage(''); - setIsPreviewFallback(false); - } - } else { - setPreviewLanguage(''); - setIsPreviewFallback(false); - } - - // Get TMDB banner (backdrop) - if (imagesData.backdrops && imagesData.backdrops.length > 0) { - const backdropPath = imagesData.backdrops[0].file_path; - setTmdbBanner(`https://image.tmdb.org/t/p/original${backdropPath}`); - } else { - const detailsResponse = await fetch(`https://api.themoviedb.org/3/${endpoint}/${tmdbId}?api_key=${apiKey}`); - const details = await detailsResponse.json(); - - if (details.backdrop_path) { - setTmdbBanner(`https://image.tmdb.org/t/p/original${details.backdrop_path}`); - } - } - } catch (err) { - logger.error(`[TMDBSettingsScreen] Error fetching ${show.name} preview:`, err); - } finally { - setLoadingLogos(false); - } - }; - - const handleShowSelect = (show: typeof EXAMPLE_SHOWS[0]) => { - setSelectedShow(show); - try { - mmkvStorage.setItem('tmdb_settings_selected_show', show.imdbId); - } catch (e) { - if (__DEV__) console.error('Error saving selected show:', e); - } - }; - - const renderLogoExample = (logo: string | null, banner: string | null, isLoading: boolean) => { - if (isLoading) { - return ( - - - - ); - } - - return ( - - - - {logo && ( - - )} - {!logo && ( - - {t('tmdb_settings.no_logo')} - - )} - - ); - }; - - // Load example logos when show or language changes - useEffect(() => { - if (settings.enrichMetadataWithTMDB && settings.useTmdbLocalizedMetadata) { - fetchExampleLogos(selectedShow); - } - }, [selectedShow, settings.enrichMetadataWithTMDB, settings.useTmdbLocalizedMetadata, settings.tmdbLanguagePreference]); - - // Load selected show from AsyncStorage on mount - useEffect(() => { - const loadSelectedShow = async () => { - try { - const savedShowId = await mmkvStorage.getItem('tmdb_settings_selected_show'); - if (savedShowId) { - const foundShow = EXAMPLE_SHOWS.find(show => show.imdbId === savedShowId); - if (foundShow) { - setSelectedShow(foundShow); - } - } - } catch (e) { - if (__DEV__) console.error('Error loading selected show:', e); - } - }; - - loadSelectedShow(); - }, []); - - const headerBaseHeight = Platform.OS === 'android' ? 80 : 60; - const topSpacing = Platform.OS === 'android' ? (StatusBar.currentHeight || 0) : insets.top; - const headerHeight = headerBaseHeight + topSpacing; - - if (isLoading) { - return ( - - - - - {t('common.loading')} - - - ); - } - - return ( - - - - - navigation.goBack()} - > - - {t('settings.settings_title')} - - - - {t('tmdb_settings.title')} - - - - - {/* Metadata Enrichment Section */} - - - - {t('tmdb_settings.metadata_enrichment')} - - - {t('tmdb_settings.metadata_enrichment_desc')} - - - - - {t('tmdb_settings.enable_enrichment')} - - {t('tmdb_settings.enable_enrichment_desc')} - - - updateSetting('enrichMetadataWithTMDB', v)} - trackColor={{ false: 'rgba(255,255,255,0.1)', true: currentTheme.colors.primary }} - thumbColor={Platform.OS === 'android' ? (settings.enrichMetadataWithTMDB ? currentTheme.colors.white : currentTheme.colors.white) : ''} - ios_backgroundColor={'rgba(255,255,255,0.1)'} - /> - - - {settings.enrichMetadataWithTMDB && ( - <> - - - - - {t('tmdb_settings.localized_text')} - - {t('tmdb_settings.localized_text_desc')} - - - updateSetting('useTmdbLocalizedMetadata', v)} - trackColor={{ false: 'rgba(255,255,255,0.1)', true: currentTheme.colors.primary }} - thumbColor={Platform.OS === 'android' ? (settings.useTmdbLocalizedMetadata ? currentTheme.colors.white : currentTheme.colors.white) : ''} - ios_backgroundColor={'rgba(255,255,255,0.1)'} - /> - - - {settings.useTmdbLocalizedMetadata && ( - <> - - - - - {t('tmdb_settings.language')} - - Current: {(settings.tmdbLanguagePreference || 'en').toUpperCase()} - - - setLanguagePickerVisible(true)} - style={[styles.languageButton, { backgroundColor: currentTheme.colors.primary }]} - > - {t('tmdb_settings.change')} - - - - {/* Logo Preview */} - - - {t('tmdb_settings.logo_preview')} - - {t('tmdb_settings.logo_preview_desc')} - - - {/* Show selector */} - {t('tmdb_settings.example')} - - {EXAMPLE_SHOWS.map((show) => ( - handleShowSelect(show)} - activeOpacity={0.7} - > - - {show.name} - - - ))} - - - {/* Preview card */} - - {renderLogoExample(tmdbLogo, tmdbBanner, loadingLogos)} - {tmdbLogo && ( - - {`Language: ${(previewLanguage || '').toUpperCase() || 'N/A'}${isPreviewFallback ? ' (fallback to available)' : ''}`} - - )} - - - )} - - {/* Granular Enrichment Options */} - - - {t('tmdb_settings.enrichment_options')} - - {t('tmdb_settings.enrichment_options_desc')} - - - {/* Cast & Crew */} - - - {t('tmdb_settings.cast_crew')} - - {t('tmdb_settings.cast_crew_desc')} - - - updateSetting('tmdbEnrichCast', v)} - trackColor={{ false: 'rgba(255,255,255,0.1)', true: currentTheme.colors.primary }} - thumbColor={Platform.OS === 'android' ? currentTheme.colors.white : ''} - ios_backgroundColor={'rgba(255,255,255,0.1)'} - /> - - - {/* Title & Description */} - - - {t('tmdb_settings.title_description')} - - {t('tmdb_settings.title_description_desc')} - - - updateSetting('tmdbEnrichTitleDescription', v)} - trackColor={{ false: 'rgba(255,255,255,0.1)', true: currentTheme.colors.primary }} - thumbColor={Platform.OS === 'android' ? currentTheme.colors.white : ''} - ios_backgroundColor={'rgba(255,255,255,0.1)'} - /> - - - {/* Title Logos */} - - - {t('tmdb_settings.title_logos')} - - {t('tmdb_settings.title_logos_desc')} - - - updateSetting('tmdbEnrichLogos', v)} - trackColor={{ false: 'rgba(255,255,255,0.1)', true: currentTheme.colors.primary }} - thumbColor={Platform.OS === 'android' ? currentTheme.colors.white : ''} - ios_backgroundColor={'rgba(255,255,255,0.1)'} - /> - - - {/* Banners/Backdrops */} - - - {t('tmdb_settings.banners_backdrops')} - - {t('tmdb_settings.banners_backdrops_desc')} - - - updateSetting('tmdbEnrichBanners', v)} - trackColor={{ false: 'rgba(255,255,255,0.1)', true: currentTheme.colors.primary }} - thumbColor={Platform.OS === 'android' ? currentTheme.colors.white : ''} - ios_backgroundColor={'rgba(255,255,255,0.1)'} - /> - - - {/* Certification */} - - - {t('tmdb_settings.certification')} - - {t('tmdb_settings.certification_desc')} - - - updateSetting('tmdbEnrichCertification', v)} - trackColor={{ false: 'rgba(255,255,255,0.1)', true: currentTheme.colors.primary }} - thumbColor={Platform.OS === 'android' ? currentTheme.colors.white : ''} - ios_backgroundColor={'rgba(255,255,255,0.1)'} - /> - - - {/* Recommendations */} - - - {t('tmdb_settings.recommendations')} - - {t('tmdb_settings.recommendations_desc')} - - - updateSetting('tmdbEnrichRecommendations', v)} - trackColor={{ false: 'rgba(255,255,255,0.1)', true: currentTheme.colors.primary }} - thumbColor={Platform.OS === 'android' ? currentTheme.colors.white : ''} - ios_backgroundColor={'rgba(255,255,255,0.1)'} - /> - - - {/* Episode Data */} - - - {t('tmdb_settings.episode_data')} - - {t('tmdb_settings.episode_data_desc')} - - - updateSetting('tmdbEnrichEpisodes', v)} - trackColor={{ false: 'rgba(255,255,255,0.1)', true: currentTheme.colors.primary }} - thumbColor={Platform.OS === 'android' ? currentTheme.colors.white : ''} - ios_backgroundColor={'rgba(255,255,255,0.1)'} - /> - - - {/* Season Posters */} - - - {t('tmdb_settings.season_posters')} - - {t('tmdb_settings.season_posters_desc')} - - - updateSetting('tmdbEnrichSeasonPosters', v)} - trackColor={{ false: 'rgba(255,255,255,0.1)', true: currentTheme.colors.primary }} - thumbColor={Platform.OS === 'android' ? currentTheme.colors.white : ''} - ios_backgroundColor={'rgba(255,255,255,0.1)'} - /> - - - {/* Production Info */} - - - Production Info - - Networks & production companies with logos - - - updateSetting('tmdbEnrichProductionInfo', v)} - trackColor={{ false: 'rgba(255,255,255,0.1)', true: currentTheme.colors.primary }} - thumbColor={Platform.OS === 'android' ? currentTheme.colors.white : ''} - ios_backgroundColor={'rgba(255,255,255,0.1)'} - /> - - - {/* Movie Details */} - - - Movie Details - - Budget, revenue, runtime, tagline - - - updateSetting('tmdbEnrichMovieDetails', v)} - trackColor={{ false: 'rgba(255,255,255,0.1)', true: currentTheme.colors.primary }} - thumbColor={Platform.OS === 'android' ? currentTheme.colors.white : ''} - ios_backgroundColor={'rgba(255,255,255,0.1)'} - /> - - - {/* TV Details */} - - - TV Show Details - - Status, seasons count, networks, creators - - - updateSetting('tmdbEnrichTvDetails', v)} - trackColor={{ false: 'rgba(255,255,255,0.1)', true: currentTheme.colors.primary }} - thumbColor={Platform.OS === 'android' ? currentTheme.colors.white : ''} - ios_backgroundColor={'rgba(255,255,255,0.1)'} - /> - - - {/* Collections */} - - - Movie Collections - - Franchise movies (Marvel, Star Wars, etc.) - - - updateSetting('tmdbEnrichCollections', v)} - trackColor={{ false: 'rgba(255,255,255,0.1)', true: currentTheme.colors.primary }} - thumbColor={Platform.OS === 'android' ? currentTheme.colors.white : ''} - ios_backgroundColor={'rgba(255,255,255,0.1)'} - /> - - - )} - - - {/* API Configuration Section */} - - - - API Configuration - - - Configure your TMDb API access for enhanced functionality. - - - - - Custom API Key - - Use your own TMDb API key for better performance and dedicated rate limits. - - - - - - {useCustomKey && ( - <> - - - {/* API Key Status */} - - - - {isKeySet ? "Custom API key active" : "API key required"} - - - - {/* API Key Input */} - - - { - setApiKey(text); - if (testResult) setTestResult(null); - }} - placeholder="Paste your TMDb API key (v3)" - placeholderTextColor={currentTheme.colors.mediumEmphasis} - autoCapitalize="none" - autoCorrect={false} - spellCheck={false} - onFocus={() => setIsInputFocused(true)} - onBlur={() => setIsInputFocused(false)} - /> - - - - - - - - Save - - - {isKeySet && ( - - Clear - - )} - - - {testResult && ( - - - - {testResult.message} - - - )} - - - - - How to get a TMDb API key? - - - - - )} - - {!useCustomKey && ( - - - - Currently using built-in API key. Consider using your own key for better performance. - - - )} - - {/* Cache Management Section */} - - - - - Cache Size - - {cacheSize} - - - - - - - - Clear Cache - - - - - - - TMDB responses are cached for 7 days to improve performance - - - - - {/* TMDB Attribution */} - - - - - This product uses the TMDB API but is not - - - endorsed or certified by TMDB. - - - - - {/* Language Picker Modal */} - setLanguagePickerVisible(false)} - > - setLanguagePickerVisible(false)}> - - - - {/* Header */} - - - Choose Language - Select your preferred language for TMDb content - - - {/* Search Section */} - - - - - {languageSearch.length > 0 && ( - setLanguageSearch('')} style={styles.searchClearButton}> - - - )} - - - - {/* Popular Languages */} - {languageSearch.length === 0 && ( - - Popular - - {[ - { code: 'en', label: 'EN' }, - { code: 'ar', label: 'AR' }, - { code: 'es', label: 'ES' }, - { code: 'fr', label: 'FR' }, - { code: 'de', label: 'DE' }, - { code: 'tr', label: 'TR' }, - ].map(({ code, label }) => ( - { updateSetting('tmdbLanguagePreference', code); setLanguagePickerVisible(false); }} - style={[ - styles.popularChip, - settings.tmdbLanguagePreference === code && styles.selectedChip, - { - backgroundColor: settings.tmdbLanguagePreference === code ? currentTheme.colors.primary : currentTheme.colors.elevation1, - borderColor: settings.tmdbLanguagePreference === code ? currentTheme.colors.primary : 'rgba(255,255,255,0.1)', - } - ]} - > - - {label} - - - ))} - - - )} - - {/* All Languages */} - - 0 && styles.searchResultsTitle, - { color: languageSearch.length > 0 ? currentTheme.colors.text : currentTheme.colors.mediumEmphasis } - ]}> - {languageSearch.length > 0 ? 'Search Results' : 'All Languages'} - - - - {(() => { - const languages = [ - { code: 'en', label: 'English', native: 'English' }, - { code: 'ar', label: 'العربية', native: 'Arabic' }, - { code: 'es', label: 'Español', native: 'Spanish' }, - { code: 'fr', label: 'Français', native: 'French' }, - { code: 'de', label: 'Deutsch', native: 'German' }, - { code: 'it', label: 'Italiano', native: 'Italian' }, - { code: 'pt-BR', label: 'Português (Brasil)', native: 'Português (Brasil)' }, - { code: 'pt', label: 'Português (Portugal)', native: 'Português' }, - { code: 'ru', label: 'Русский', native: 'Russian' }, - { code: 'tr', label: 'Türkçe', native: 'Turkish' }, - { code: 'ja', label: '日本語', native: 'Japanese' }, - { code: 'ko', label: '한국어', native: 'Korean' }, - { code: 'zh', label: '中文', native: 'Chinese' }, - { code: 'hi', label: 'हिन्दी', native: 'Hindi' }, - { code: 'he', label: 'עברית', native: 'Hebrew' }, - { code: 'id', label: 'Bahasa Indonesia', native: 'Indonesian' }, - { code: 'nl', label: 'Nederlands', native: 'Dutch' }, - { code: 'sv', label: 'Svenska', native: 'Swedish' }, - { code: 'no', label: 'Norsk', native: 'Norwegian' }, - { code: 'da', label: 'Dansk', native: 'Danish' }, - { code: 'fi', label: 'Suomi', native: 'Finnish' }, - { code: 'pl', label: 'Polski', native: 'Polish' }, - { code: 'cs', label: 'Čeština', native: 'Czech' }, - { code: 'ro', label: 'Română', native: 'Romanian' }, - { code: 'uk', label: 'Українська', native: 'Ukrainian' }, - { code: 'vi', label: 'Tiếng Việt', native: 'Vietnamese' }, - { code: 'th', label: 'ไทย', native: 'Thai' }, - { code: 'hr', label: 'Hrvatski', native: 'Croatian' }, - { code: 'sr', label: 'Српски', native: 'Serbian' }, - { code: 'bg', label: 'български', native: 'Bulgarian' }, - { code: 'sl', label: 'Slovenščina', native: 'Slovenian' }, - { code: 'mk', label: 'Македонски', native: 'Macedonian' }, - { code: 'fil', label: 'Filipino', native: 'Filipino' }, - { code: 'sq', label: 'Shqipe', native: 'Albanian' }, - { code: 'ca', label: 'Català', native: 'Catalan' }, - ]; - - const filteredLanguages = languages.filter(({ label, code, native }) => - (languageSearch || '').length === 0 || - label.toLowerCase().includes(languageSearch.toLowerCase()) || - native.toLowerCase().includes(languageSearch.toLowerCase()) || - code.toLowerCase().includes(languageSearch.toLowerCase()) - ); - - return ( - <> - {filteredLanguages.map(({ code, label, native }) => ( - { updateSetting('tmdbLanguagePreference', code); setLanguagePickerVisible(false); }} - style={[ - styles.languageItem, - settings.tmdbLanguagePreference === code && styles.selectedLanguageItem - ]} - activeOpacity={0.7} - > - - - - {native} - - - {label} • {code.toUpperCase()} - - - {settings.tmdbLanguagePreference === code && ( - - - - )} - - - ))} - {languageSearch.length > 0 && filteredLanguages.length === 0 && ( - - - - No languages found for "{languageSearch}" - - setLanguageSearch('')} - style={[styles.clearSearchButton, { backgroundColor: currentTheme.colors.elevation1 }]} - > - Clear search - - - )} - - ); - })()} - - - - {/* Footer Actions */} - - setLanguagePickerVisible(false)} - style={styles.cancelButton} - > - Cancel - - setLanguagePickerVisible(false)} - style={[styles.doneButton, { backgroundColor: currentTheme.colors.primary }]} - > - Done - - - - - - - - - setAlertVisible(false)} - actions={alertActions} - /> - - ); + const { t } = useTranslation(); + const navigation = useNavigation(); + const [apiKey, setApiKey] = useState(''); + const [isLoading, setIsLoading] = useState(true); + const [isKeySet, setIsKeySet] = useState(false); + const [useCustomKey, setUseCustomKey] = useState(false); + const [testResult, setTestResult] = useState<{ + success: boolean; + message: string; + } | null>(null); + const [isInputFocused, setIsInputFocused] = useState(false); + const [alertVisible, setAlertVisible] = useState(false); + const [alertTitle, setAlertTitle] = useState(''); + const [alertMessage, setAlertMessage] = useState(''); + const [alertActions, setAlertActions] = useState< + Array<{ label: string; onPress: () => void; style?: object }> + >([{ label: t('common.ok'), onPress: () => setAlertVisible(false) }]); + const apiKeyInputRef = useRef(null); + const { currentTheme } = useTheme(); + const insets = useSafeAreaInsets(); + const { settings, updateSetting } = useSettings(); + const [languagePickerVisible, setLanguagePickerVisible] = useState(false); + const [languageSearch, setLanguageSearch] = useState(''); + + // Logo preview state + const [selectedShow, setSelectedShow] = useState(EXAMPLE_SHOWS[0]); + const [tmdbLogo, setTmdbLogo] = useState(null); + const [tmdbBanner, setTmdbBanner] = useState(null); + const [loadingLogos, setLoadingLogos] = useState(true); + const [previewLanguage, setPreviewLanguage] = useState(''); + const [isPreviewFallback, setIsPreviewFallback] = useState(false); + const [cacheSize, setCacheSize] = useState('0 KB'); + + const openAlert = ( + title: string, + message: string, + actions?: Array<{ label: string; onPress?: () => void; style?: object }>, + ) => { + setAlertTitle(title); + setAlertMessage(message); + if (actions && actions.length > 0) { + setAlertActions( + actions.map((a) => ({ + label: a.label, + style: a.style, + onPress: () => { + a.onPress?.(); + }, + })), + ); + } else { + setAlertActions([ + { label: t('common.ok'), onPress: () => setAlertVisible(false) }, + ]); + } + setAlertVisible(true); + }; + + useEffect(() => { + logger.log('[TMDBSettingsScreen] Component mounted'); + loadSettings(); + calculateCacheSize(); + return () => { + logger.log('[TMDBSettingsScreen] Component unmounted'); + }; + }, []); + + const calculateCacheSize = async () => { + try { + const keys = await mmkvStorage.getAllKeys(); + const tmdbKeys = keys.filter((key) => key.startsWith('tmdb_cache_')); + + let totalSize = 0; + for (const key of tmdbKeys) { + const value = mmkvStorage.getString(key); + if (value) { + totalSize += value.length; + } + } + + // Convert to KB/MB + let sizeStr = ''; + if (totalSize < 1024) { + sizeStr = `${totalSize} B`; + } else if (totalSize < 1024 * 1024) { + sizeStr = `${(totalSize / 1024).toFixed(2)} KB`; + } else { + sizeStr = `${(totalSize / (1024 * 1024)).toFixed(2)} MB`; + } + + setCacheSize(sizeStr); + } catch (error) { + logger.error('[TMDBSettingsScreen] Error calculating cache size:', error); + setCacheSize('Unknown'); + } + }; + + const handleClearCache = () => { + openAlert( + t('tmdb_settings.clear_cache_title'), + t('tmdb_settings.clear_cache_msg', { size: cacheSize }), + [ + { + label: t('common.cancel'), + onPress: () => logger.log('[TMDBSettingsScreen] Clear cache cancelled'), + }, + { + label: t('tmdb_settings.clear_cache'), + onPress: async () => { + logger.log('[TMDBSettingsScreen] Proceeding with cache clear'); + try { + await tmdbService.clearAllCache(); + setCacheSize('0 KB'); + logger.log('[TMDBSettingsScreen] Cache cleared successfully'); + openAlert(t('common.success'), t('tmdb_settings.clear_cache_success')); + } catch (error) { + logger.error('[TMDBSettingsScreen] Failed to clear cache:', error); + openAlert(t('common.error'), t('tmdb_settings.clear_cache_error')); + } + }, + }, + ], + ); + }; + + const loadSettings = async () => { + logger.log('[TMDBSettingsScreen] Loading settings from storage'); + try { + const [savedKey, savedUseCustomKey] = await Promise.all([ + mmkvStorage.getItem(TMDB_API_KEY_STORAGE_KEY), + mmkvStorage.getItem(USE_CUSTOM_TMDB_API_KEY), + ]); + + logger.log( + '[TMDBSettingsScreen] API key status:', + savedKey ? 'Found' : 'Not found', + ); + logger.log( + '[TMDBSettingsScreen] Use custom API setting:', + savedUseCustomKey, + ); + + if (savedKey) { + setApiKey(savedKey); + setIsKeySet(true); + } else { + setIsKeySet(false); + } + + setUseCustomKey(savedUseCustomKey === 'true'); + } catch (error) { + logger.error('[TMDBSettingsScreen] Failed to load settings:', error); + setIsKeySet(false); + setUseCustomKey(false); + } finally { + setIsLoading(false); + logger.log('[TMDBSettingsScreen] Finished loading settings'); + } + }; + + const saveApiKey = async () => { + logger.log('[TMDBSettingsScreen] Starting API key save'); + Keyboard.dismiss(); + + try { + const trimmedKey = apiKey.trim(); + if (!trimmedKey) { + logger.warn('[TMDBSettingsScreen] Empty API key provided'); + setTestResult({ + success: false, + message: t('tmdb_settings.empty_api_key'), + }); + return; + } + + // Test the API key to make sure it works + if (await testApiKey(trimmedKey)) { + logger.log('[TMDBSettingsScreen] API key test successful, saving key'); + await mmkvStorage.setItem(TMDB_API_KEY_STORAGE_KEY, trimmedKey); + await mmkvStorage.setItem(USE_CUSTOM_TMDB_API_KEY, 'true'); + setIsKeySet(true); + setUseCustomKey(true); + setTestResult({ success: true, message: t('tmdb_settings.key_verified') }); + logger.log('[TMDBSettingsScreen] API key saved successfully'); + } else { + logger.warn('[TMDBSettingsScreen] API key test failed'); + setTestResult({ + success: false, + message: t('tmdb_settings.invalid_api_key'), + }); + } + } catch (error) { + logger.error('[TMDBSettingsScreen] Error saving API key:', error); + setTestResult({ + success: false, + message: t('tmdb_settings.save_error'), + }); + } + }; + + const testApiKey = async (key: string): Promise => { + try { + // Simple API call to test the key using the API key parameter method + const response = await fetch( + `https://api.themoviedb.org/3/configuration?api_key=${key}`, + { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }, + ); + return response.ok; + } catch (error) { + logger.error('[TMDBSettingsScreen] API key test error:', error); + return false; + } + }; + + const clearApiKey = async () => { + logger.log('[TMDBSettingsScreen] Clear API key requested'); + openAlert( + t('tmdb_settings.clear_api_key_title'), + t('tmdb_settings.clear_api_key_msg'), + [ + { + label: t('common.cancel'), + onPress: () => logger.log('[TMDBSettingsScreen] Clear API key cancelled'), + }, + { + label: t('mdblist.clear'), + onPress: async () => { + logger.log('[TMDBSettingsScreen] Proceeding with API key clear'); + try { + await mmkvStorage.removeItem(TMDB_API_KEY_STORAGE_KEY); + await mmkvStorage.setItem(USE_CUSTOM_TMDB_API_KEY, 'false'); + setApiKey(''); + setIsKeySet(false); + setUseCustomKey(false); + setTestResult(null); + logger.log('[TMDBSettingsScreen] API key cleared successfully'); + } catch (error) { + logger.error('[TMDBSettingsScreen] Failed to clear API key:', error); + openAlert(t('common.error'), t('tmdb_settings.clear_api_key_error')); + } + }, + }, + ], + ); + }; + + const toggleUseCustomKey = async (value: boolean) => { + logger.log('[TMDBSettingsScreen] Toggle use custom key:', value); + try { + await mmkvStorage.setItem(USE_CUSTOM_TMDB_API_KEY, value ? 'true' : 'false'); + setUseCustomKey(value); + + if (!value) { + // If switching to built-in key, show confirmation + logger.log('[TMDBSettingsScreen] Switching to built-in API key'); + setTestResult({ + success: true, + message: t('tmdb_settings.using_builtin_key'), + }); + } else if (apiKey && isKeySet) { + // If switching to custom key and we have a key + logger.log('[TMDBSettingsScreen] Switching to custom API key'); + setTestResult({ + success: true, + message: t('tmdb_settings.using_custom_key'), + }); + } else { + // If switching to custom key but don't have a key yet + logger.log('[TMDBSettingsScreen] No custom key available yet'); + setTestResult({ + success: false, + message: t('tmdb_settings.enter_custom_key'), + }); + } + } catch (error) { + logger.error( + '[TMDBSettingsScreen] Failed to toggle custom key setting:', + error, + ); + } + }; + + const pasteFromClipboard = async () => { + logger.log('[TMDBSettingsScreen] Attempting to paste from clipboard'); + try { + const clipboardContent = await Clipboard.getString(); + if (clipboardContent) { + logger.log('[TMDBSettingsScreen] Content pasted from clipboard'); + setApiKey(clipboardContent); + setTestResult(null); + } else { + logger.warn('[TMDBSettingsScreen] No content in clipboard'); + } + } catch (error) { + logger.error('[TMDBSettingsScreen] Error pasting from clipboard:', error); + } + }; + + const openTMDBWebsite = () => { + logger.log('[TMDBSettingsScreen] Opening TMDb website'); + Linking.openURL('https://www.themoviedb.org/settings/api').catch((error) => { + logger.error('[TMDBSettingsScreen] Error opening website:', error); + }); + }; + + // Logo preview functions + const fetchExampleLogos = async (show: (typeof EXAMPLE_SHOWS)[0]) => { + setLoadingLogos(true); + setTmdbLogo(null); + setTmdbBanner(null); + + try { + const tmdbId = show.tmdbId; + const contentType = show.type; + + logger.log( + `[TMDBSettingsScreen] Fetching ${show.name} with TMDB ID: ${tmdbId}`, + ); + + const preferredTmdbLanguage = settings.tmdbLanguagePreference || 'en'; + + const apiKey = TMDB_API_KEY; + const endpoint = contentType === 'tv' ? 'tv' : 'movie'; + const response = await fetch( + `https://api.themoviedb.org/3/${endpoint}/${tmdbId}/images?api_key=${apiKey}`, + ); + const imagesData = await response.json(); + + if (imagesData.logos && imagesData.logos.length > 0) { + let logoPath: string | null = null; + let logoLanguage = preferredTmdbLanguage; + + // Try to find logo in preferred language + const preferredLogo = imagesData.logos.find( + (logo: { iso_639_1: string; file_path: string }) => + logo.iso_639_1 === preferredTmdbLanguage, + ); + + if (preferredLogo) { + logoPath = preferredLogo.file_path; + logoLanguage = preferredTmdbLanguage; + setIsPreviewFallback(false); + } else { + // Fallback to English + const englishLogo = imagesData.logos.find( + (logo: { iso_639_1: string; file_path: string }) => + logo.iso_639_1 === 'en', + ); + + if (englishLogo) { + logoPath = englishLogo.file_path; + logoLanguage = 'en'; + setIsPreviewFallback(true); + } else if (imagesData.logos[0]) { + // Fallback to first available + logoPath = imagesData.logos[0].file_path; + logoLanguage = imagesData.logos[0].iso_639_1 || 'unknown'; + setIsPreviewFallback(true); + } + } + + if (logoPath) { + setTmdbLogo(`https://image.tmdb.org/t/p/original${logoPath}`); + setPreviewLanguage(logoLanguage); + } else { + setPreviewLanguage(''); + setIsPreviewFallback(false); + } + } else { + setPreviewLanguage(''); + setIsPreviewFallback(false); + } + + // Get TMDB banner (backdrop) + if (imagesData.backdrops && imagesData.backdrops.length > 0) { + const backdropPath = imagesData.backdrops[0].file_path; + setTmdbBanner(`https://image.tmdb.org/t/p/original${backdropPath}`); + } else { + const detailsResponse = await fetch( + `https://api.themoviedb.org/3/${endpoint}/${tmdbId}?api_key=${apiKey}`, + ); + const details = await detailsResponse.json(); + + if (details.backdrop_path) { + setTmdbBanner( + `https://image.tmdb.org/t/p/original${details.backdrop_path}`, + ); + } + } + } catch (err) { + logger.error( + `[TMDBSettingsScreen] Error fetching ${show.name} preview:`, + err, + ); + } finally { + setLoadingLogos(false); + } + }; + + const handleShowSelect = (show: (typeof EXAMPLE_SHOWS)[0]) => { + setSelectedShow(show); + try { + mmkvStorage.setItem('tmdb_settings_selected_show', show.imdbId); + } catch (e) { + if (__DEV__) console.error('Error saving selected show:', e); + } + }; + + const renderLogoExample = ( + logo: string | null, + banner: string | null, + isLoading: boolean, + ) => { + if (isLoading) { + return ( + + + + ); + } + + return ( + + + + {logo && ( + + )} + {!logo && ( + + {t('tmdb_settings.no_logo')} + + )} + + ); + }; + + // Load example logos when show or language changes + useEffect(() => { + if (settings.enrichMetadataWithTMDB && settings.useTmdbLocalizedMetadata) { + fetchExampleLogos(selectedShow); + } + }, [ + selectedShow, + settings.enrichMetadataWithTMDB, + settings.useTmdbLocalizedMetadata, + settings.tmdbLanguagePreference, + ]); + + // Load selected show from AsyncStorage on mount + useEffect(() => { + const loadSelectedShow = async () => { + try { + const savedShowId = await mmkvStorage.getItem( + 'tmdb_settings_selected_show', + ); + if (savedShowId) { + const foundShow = EXAMPLE_SHOWS.find( + (show) => show.imdbId === savedShowId, + ); + if (foundShow) { + setSelectedShow(foundShow); + } + } + } catch (e) { + if (__DEV__) console.error('Error loading selected show:', e); + } + }; + + loadSelectedShow(); + }, []); + + const headerBaseHeight = Platform.OS === 'android' ? 80 : 60; + const topSpacing = + Platform.OS === 'android' ? StatusBar.currentHeight || 0 : insets.top; + const headerHeight = headerBaseHeight + topSpacing; + + if (isLoading) { + return ( + + + + + + {t('common.loading')} + + + + ); + } + + return ( + + + + + navigation.goBack()} + > + + + {t('settings.settings_title')} + + + + + {t('tmdb_settings.title')} + + + + + {/* Metadata Enrichment Section */} + + + + + {t('tmdb_settings.metadata_enrichment')} + + + + {t('tmdb_settings.metadata_enrichment_desc')} + + + + + + {t('tmdb_settings.enable_enrichment')} + + + {t('tmdb_settings.enable_enrichment_desc')} + + + updateSetting('enrichMetadataWithTMDB', v)} + trackColor={{ + false: 'rgba(255,255,255,0.1)', + true: currentTheme.colors.primary, + }} + thumbColor={ + Platform.OS === 'android' + ? settings.enrichMetadataWithTMDB + ? currentTheme.colors.white + : currentTheme.colors.white + : '' + } + ios_backgroundColor={'rgba(255,255,255,0.1)'} + /> + + + {settings.enrichMetadataWithTMDB && ( + <> + + + + + + {t('tmdb_settings.localized_text')} + + + {t('tmdb_settings.localized_text_desc')} + + + updateSetting('useTmdbLocalizedMetadata', v)} + trackColor={{ + false: 'rgba(255,255,255,0.1)', + true: currentTheme.colors.primary, + }} + thumbColor={ + Platform.OS === 'android' + ? settings.useTmdbLocalizedMetadata + ? currentTheme.colors.white + : currentTheme.colors.white + : '' + } + ios_backgroundColor={'rgba(255,255,255,0.1)'} + /> + + + {settings.useTmdbLocalizedMetadata && ( + <> + + + + + + {t('tmdb_settings.language')} + + + Current: {(settings.tmdbLanguagePreference || 'en').toUpperCase()} + + + setLanguagePickerVisible(true)} + style={[ + styles.languageButton, + { backgroundColor: currentTheme.colors.primary }, + ]} + > + + {t('tmdb_settings.change')} + + + + + {/* Logo Preview */} + + + + {t('tmdb_settings.logo_preview')} + + + {t('tmdb_settings.logo_preview_desc')} + + + {/* Show selector */} + + {t('tmdb_settings.example')} + + + {EXAMPLE_SHOWS.map((show) => ( + handleShowSelect(show)} + activeOpacity={0.7} + > + + {show.name} + + + ))} + + + {/* Preview card */} + + {renderLogoExample(tmdbLogo, tmdbBanner, loadingLogos)} + {tmdbLogo && ( + + {`Language: ${(previewLanguage || '').toUpperCase() || 'N/A'}${isPreviewFallback ? ' (fallback to available)' : ''}`} + + )} + + + )} + + {/* Granular Enrichment Options */} + + + + {t('tmdb_settings.enrichment_options')} + + + {t('tmdb_settings.enrichment_options_desc')} + + + {/* Cast & Crew */} + + + + {t('tmdb_settings.cast_crew')} + + + {t('tmdb_settings.cast_crew_desc')} + + + updateSetting('tmdbEnrichCast', v)} + trackColor={{ + false: 'rgba(255,255,255,0.1)', + true: currentTheme.colors.primary, + }} + thumbColor={Platform.OS === 'android' ? currentTheme.colors.white : ''} + ios_backgroundColor={'rgba(255,255,255,0.1)'} + /> + + + {/* Title & Description */} + + + + {t('tmdb_settings.title_description')} + + + {t('tmdb_settings.title_description_desc')} + + + updateSetting('tmdbEnrichTitleDescription', v)} + trackColor={{ + false: 'rgba(255,255,255,0.1)', + true: currentTheme.colors.primary, + }} + thumbColor={Platform.OS === 'android' ? currentTheme.colors.white : ''} + ios_backgroundColor={'rgba(255,255,255,0.1)'} + /> + + + {/* Title Logos */} + + + + {t('tmdb_settings.title_logos')} + + + {t('tmdb_settings.title_logos_desc')} + + + updateSetting('tmdbEnrichLogos', v)} + trackColor={{ + false: 'rgba(255,255,255,0.1)', + true: currentTheme.colors.primary, + }} + thumbColor={Platform.OS === 'android' ? currentTheme.colors.white : ''} + ios_backgroundColor={'rgba(255,255,255,0.1)'} + /> + + + {/* Banners/Backdrops */} + + + + {t('tmdb_settings.banners_backdrops')} + + + {t('tmdb_settings.banners_backdrops_desc')} + + + updateSetting('tmdbEnrichBanners', v)} + trackColor={{ + false: 'rgba(255,255,255,0.1)', + true: currentTheme.colors.primary, + }} + thumbColor={Platform.OS === 'android' ? currentTheme.colors.white : ''} + ios_backgroundColor={'rgba(255,255,255,0.1)'} + /> + + + {/* Certification */} + + + + {t('tmdb_settings.certification')} + + + {t('tmdb_settings.certification_desc')} + + + updateSetting('tmdbEnrichCertification', v)} + trackColor={{ + false: 'rgba(255,255,255,0.1)', + true: currentTheme.colors.primary, + }} + thumbColor={Platform.OS === 'android' ? currentTheme.colors.white : ''} + ios_backgroundColor={'rgba(255,255,255,0.1)'} + /> + + + {/* Recommendations */} + + + + {t('tmdb_settings.recommendations')} + + + {t('tmdb_settings.recommendations_desc')} + + + updateSetting('tmdbEnrichRecommendations', v)} + trackColor={{ + false: 'rgba(255,255,255,0.1)', + true: currentTheme.colors.primary, + }} + thumbColor={Platform.OS === 'android' ? currentTheme.colors.white : ''} + ios_backgroundColor={'rgba(255,255,255,0.1)'} + /> + + + {/* Episode Data */} + + + + {t('tmdb_settings.episode_data')} + + + {t('tmdb_settings.episode_data_desc')} + + + updateSetting('tmdbEnrichEpisodes', v)} + trackColor={{ + false: 'rgba(255,255,255,0.1)', + true: currentTheme.colors.primary, + }} + thumbColor={Platform.OS === 'android' ? currentTheme.colors.white : ''} + ios_backgroundColor={'rgba(255,255,255,0.1)'} + /> + + + {/* Season Posters */} + + + + {t('tmdb_settings.season_posters')} + + + {t('tmdb_settings.season_posters_desc')} + + + updateSetting('tmdbEnrichSeasonPosters', v)} + trackColor={{ + false: 'rgba(255,255,255,0.1)', + true: currentTheme.colors.primary, + }} + thumbColor={Platform.OS === 'android' ? currentTheme.colors.white : ''} + ios_backgroundColor={'rgba(255,255,255,0.1)'} + /> + + + {/* Production Info */} + + + + {t('tmdb_settings.production_info')} + + + {t('tmdb_settings.production_info_desc')} + + + updateSetting('tmdbEnrichProductionInfo', v)} + trackColor={{ + false: 'rgba(255,255,255,0.1)', + true: currentTheme.colors.primary, + }} + thumbColor={Platform.OS === 'android' ? currentTheme.colors.white : ''} + ios_backgroundColor={'rgba(255,255,255,0.1)'} + /> + + + {/* Movie Details */} + + + + {t('tmdb_settings.movie_details')} + + + {t('tmdb_settings.movie_details_desc')} + + + updateSetting('tmdbEnrichMovieDetails', v)} + trackColor={{ + false: 'rgba(255,255,255,0.1)', + true: currentTheme.colors.primary, + }} + thumbColor={Platform.OS === 'android' ? currentTheme.colors.white : ''} + ios_backgroundColor={'rgba(255,255,255,0.1)'} + /> + + + {/* TV Details */} + + + + {t('tmdb_settings.tv_details')} + + + {t('tmdb_settings.tv_details_desc')} + + + updateSetting('tmdbEnrichTvDetails', v)} + trackColor={{ + false: 'rgba(255,255,255,0.1)', + true: currentTheme.colors.primary, + }} + thumbColor={Platform.OS === 'android' ? currentTheme.colors.white : ''} + ios_backgroundColor={'rgba(255,255,255,0.1)'} + /> + + + {/* Collections */} + + + + {t('tmdb_settings.movie_collections')} + + + {t('tmdb_settings.movie_collections_desc')} + + + updateSetting('tmdbEnrichCollections', v)} + trackColor={{ + false: 'rgba(255,255,255,0.1)', + true: currentTheme.colors.primary, + }} + thumbColor={Platform.OS === 'android' ? currentTheme.colors.white : ''} + ios_backgroundColor={'rgba(255,255,255,0.1)'} + /> + + + )} + + + {/* API Configuration Section */} + + + + + {t('tmdb_settings.api_configuration')} + + + + {t('tmdb_settings.api_configuration_desc')} + + + + + + {t('tmdb_settings.custom_api_key')} + + + {t('tmdb_settings.custom_api_key_desc')} + + + + + + {useCustomKey && ( + <> + + + {/* API Key Status */} + + + + {isKeySet + ? t('tmdb_settings.custom_key_active') + : t('tmdb_settings.api_key_required')} + + + + {/* API Key Input */} + + + { + setApiKey(text); + if (testResult) setTestResult(null); + }} + placeholder={t('tmdb_settings.api_key_placeholder')} + placeholderTextColor={currentTheme.colors.mediumEmphasis} + autoCapitalize="none" + autoCorrect={false} + spellCheck={false} + onFocus={() => setIsInputFocused(true)} + onBlur={() => setIsInputFocused(false)} + /> + + + + + + + + + {t('common.save')} + + + + {isKeySet && ( + + + {t('common.clear')} + + + )} + + + {testResult && ( + + + + {testResult.message} + + + )} + + + + + {t('tmdb_settings.how_to_get_key')} + + + + + )} + + {!useCustomKey && ( + + + + {t('tmdb_settings.built_in_key_msg')} + + + )} + + {/* Cache Management Section */} + + + + + + {t('tmdb_settings.cache_size')} + + + {cacheSize} + + + + + + + + + {t('tmdb_settings.clear_cache')} + + + + + + + + {t('tmdb_settings.cache_days')} + + + + + {/* TMDB Attribution */} + + + + + {t('tmdb_settings.disclaimer_part_one')} + + + {t('tmdb_settings.disclaimer_part_two')} + + + + + {/* Language Picker Modal */} + setLanguagePickerVisible(false)} + > + setLanguagePickerVisible(false)}> + + + + {/* Header */} + + + + {t('tmdb_settings.choose_language')} + + + {t('tmdb_settings.choose_language_desc')} + + + + {/* Search Section */} + + + + + {languageSearch.length > 0 && ( + setLanguageSearch('')} + style={styles.searchClearButton} + > + + + )} + + + + {/* Popular Languages */} + {languageSearch.length === 0 && ( + + + {t('tmdb_settings.popular')} + + + {[ + { code: 'en', label: 'EN' }, + { code: 'ar', label: 'AR' }, + { code: 'it', label: 'IT' }, + { code: 'es', label: 'ES' }, + { code: 'fr', label: 'FR' }, + { code: 'de', label: 'DE' }, + { code: 'tr', label: 'TR' }, + ].map(({ code, label }) => ( + { + updateSetting('tmdbLanguagePreference', code); + setLanguagePickerVisible(false); + }} + style={[ + styles.popularChip, + settings.tmdbLanguagePreference === code && styles.selectedChip, + { + backgroundColor: + settings.tmdbLanguagePreference === code + ? currentTheme.colors.primary + : currentTheme.colors.elevation1, + borderColor: + settings.tmdbLanguagePreference === code + ? currentTheme.colors.primary + : 'rgba(255,255,255,0.1)', + }, + ]} + > + + {label} + + + ))} + + + )} + + {/* All Languages */} + + 0 && styles.searchResultsTitle, + { + color: + languageSearch.length > 0 + ? currentTheme.colors.text + : currentTheme.colors.mediumEmphasis, + }, + ]} + > + {languageSearch.length > 0 + ? t('tmdb_settings.search_results') + : t('tmdb_settings.all_languages')} + + + + {(() => { + const languages = [ + { code: 'en', label: 'English', native: 'English' }, + { code: 'ar', label: 'العربية', native: 'Arabic' }, + { code: 'es', label: 'Español', native: 'Spanish' }, + { code: 'fr', label: 'Français', native: 'French' }, + { code: 'de', label: 'Deutsch', native: 'German' }, + { code: 'it', label: 'Italiano', native: 'Italian' }, + { + code: 'pt-BR', + label: 'Português (Brasil)', + native: 'Português (Brasil)', + }, + { code: 'pt', label: 'Português (Portugal)', native: 'Português' }, + { code: 'ru', label: 'Русский', native: 'Russian' }, + { code: 'tr', label: 'Türkçe', native: 'Turkish' }, + { code: 'ja', label: '日本語', native: 'Japanese' }, + { code: 'ko', label: '한국어', native: 'Korean' }, + { code: 'zh', label: '中文', native: 'Chinese' }, + { code: 'hi', label: 'हिन्दी', native: 'Hindi' }, + { code: 'he', label: 'עברית', native: 'Hebrew' }, + { code: 'id', label: 'Bahasa Indonesia', native: 'Indonesian' }, + { code: 'nl', label: 'Nederlands', native: 'Dutch' }, + { code: 'sv', label: 'Svenska', native: 'Swedish' }, + { code: 'no', label: 'Norsk', native: 'Norwegian' }, + { code: 'da', label: 'Dansk', native: 'Danish' }, + { code: 'fi', label: 'Suomi', native: 'Finnish' }, + { code: 'pl', label: 'Polski', native: 'Polish' }, + { code: 'cs', label: 'Čeština', native: 'Czech' }, + { code: 'ro', label: 'Română', native: 'Romanian' }, + { code: 'uk', label: 'Українська', native: 'Ukrainian' }, + { code: 'vi', label: 'Tiếng Việt', native: 'Vietnamese' }, + { code: 'th', label: 'ไทย', native: 'Thai' }, + { code: 'hr', label: 'Hrvatski', native: 'Croatian' }, + { code: 'sr', label: 'Српски', native: 'Serbian' }, + { code: 'bg', label: 'български', native: 'Bulgarian' }, + { code: 'sl', label: 'Slovenščina', native: 'Slovenian' }, + { code: 'mk', label: 'Македонски', native: 'Macedonian' }, + { code: 'fil', label: 'Filipino', native: 'Filipino' }, + { code: 'sq', label: 'Shqipe', native: 'Albanian' }, + { code: 'ca', label: 'Català', native: 'Catalan' }, + ]; + + const filteredLanguages = languages.filter( + ({ label, code, native }) => + (languageSearch || '').length === 0 || + label.toLowerCase().includes(languageSearch.toLowerCase()) || + native.toLowerCase().includes(languageSearch.toLowerCase()) || + code.toLowerCase().includes(languageSearch.toLowerCase()), + ); + + return ( + <> + {filteredLanguages.map(({ code, label, native }) => ( + { + updateSetting('tmdbLanguagePreference', code); + setLanguagePickerVisible(false); + }} + style={[ + styles.languageItem, + settings.tmdbLanguagePreference === code && + styles.selectedLanguageItem, + ]} + activeOpacity={0.7} + > + + + + {native} + + + {label} • {code.toUpperCase()} + + + {settings.tmdbLanguagePreference === code && ( + + + + )} + + + ))} + {languageSearch.length > 0 && filteredLanguages.length === 0 && ( + + + + {t('tmdb_settings.no_languages_found')}"{languageSearch}" + + setLanguageSearch('')} + style={[ + styles.clearSearchButton, + { backgroundColor: currentTheme.colors.elevation1 }, + ]} + > + + {t('tmdb_settings.clear_search')} + + + + )} + + ); + })()} + + + + {/* Footer Actions */} + + setLanguagePickerVisible(false)} + style={styles.cancelButton} + > + + {t('common.cancel')} + + + setLanguagePickerVisible(false)} + style={[ + styles.doneButton, + { backgroundColor: currentTheme.colors.primary }, + ]} + > + + {t('tmdb_settings.done')} + + + + + + + + + + setAlertVisible(false)} + actions={alertActions} + /> + + ); }; const styles = StyleSheet.create({ - container: { - flex: 1, - }, - loadingContainer: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - }, - loadingText: { - marginTop: 12, - fontSize: 16, - }, - headerContainer: { - paddingHorizontal: 20, - paddingBottom: 8, - backgroundColor: 'transparent', - zIndex: 2, - }, - header: { - flexDirection: 'row', - alignItems: 'center', - marginBottom: 12, - }, - backButton: { - flexDirection: 'row', - alignItems: 'center', - }, - backText: { - fontSize: 16, - fontWeight: '500', - marginLeft: 4, - }, - headerTitle: { - fontSize: 32, - fontWeight: '800', - letterSpacing: 0.3, - paddingLeft: 4, - }, - scrollView: { - flex: 1, - zIndex: 1, - }, - scrollContent: { - paddingHorizontal: 16, - paddingBottom: 40, - }, - sectionCard: { - borderRadius: 16, - marginBottom: 20, - padding: 20, - shadowColor: '#000', - shadowOffset: { width: 0, height: 2 }, - shadowOpacity: 0.1, - shadowRadius: 4, - elevation: 3, - }, - sectionHeader: { - flexDirection: 'row', - alignItems: 'center', - marginBottom: 8, - }, - sectionTitle: { - fontSize: 18, - fontWeight: '700', - marginLeft: 8, - }, - sectionDescription: { - fontSize: 14, - lineHeight: 20, - marginBottom: 20, - }, - settingRow: { - flexDirection: 'row', - alignItems: 'flex-start', - marginBottom: 16, - }, - settingTextContainer: { - flex: 1, - marginRight: 16, - }, - settingTitle: { - fontSize: 16, - fontWeight: '600', - marginBottom: 4, - }, - settingDescription: { - fontSize: 14, - lineHeight: 20, - opacity: 0.8, - }, - languageButton: { - paddingHorizontal: 16, - paddingVertical: 8, - borderRadius: 8, - alignItems: 'center', - }, - languageButtonText: { - fontSize: 14, - fontWeight: '600', - }, - divider: { - height: 1, - backgroundColor: 'rgba(255,255,255,0.1)', - marginVertical: 16, - }, - statusRow: { - flexDirection: 'row', - alignItems: 'center', - marginBottom: 16, - }, - statusText: { - fontSize: 14, - fontWeight: '500', - marginLeft: 8, - }, - apiKeyContainer: { - marginTop: 16, - }, - infoContainer: { - flexDirection: 'row', - alignItems: 'flex-start', - marginTop: 16, - padding: 12, - backgroundColor: 'rgba(255,255,255,0.05)', - borderRadius: 8, - }, - inputContainer: { - flexDirection: 'row', - alignItems: 'center', - marginBottom: 16, - }, - input: { - flex: 1, - borderRadius: 12, - paddingHorizontal: 16, - paddingVertical: 14, - fontSize: 15, - borderWidth: 2, - }, - pasteButton: { - position: 'absolute', - right: 12, - padding: 8, - }, - buttonRow: { - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - }, - button: { - borderRadius: 12, - paddingVertical: 14, - paddingHorizontal: 20, - alignItems: 'center', - flex: 1, - marginRight: 8, - }, - buttonText: { - fontWeight: '600', - fontSize: 15, - }, - resultMessage: { - borderRadius: 12, - padding: 16, - marginTop: 16, - flexDirection: 'row', - alignItems: 'center', - }, - resultIcon: { - marginRight: 12, - }, - resultText: { - flex: 1, - fontSize: 14, - fontWeight: '500', - }, - helpLink: { - flexDirection: 'row', - alignItems: 'center', - marginTop: 16, - paddingVertical: 8, - }, - helpIcon: { - marginRight: 8, - }, - helpText: { - fontSize: 14, - fontWeight: '500', - }, - infoText: { - fontSize: 14, - flex: 1, - lineHeight: 20, - opacity: 0.8, - marginLeft: 8, - }, - clearButton: { - backgroundColor: 'transparent', - borderWidth: 2, - marginRight: 0, - marginLeft: 8, - flex: 0, - paddingHorizontal: 16, - }, + container: { + flex: 1, + }, + loadingContainer: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + }, + loadingText: { + marginTop: 12, + fontSize: 16, + }, + headerContainer: { + paddingHorizontal: 20, + paddingBottom: 8, + backgroundColor: 'transparent', + zIndex: 2, + }, + header: { + flexDirection: 'row', + alignItems: 'center', + marginBottom: 12, + }, + backButton: { + flexDirection: 'row', + alignItems: 'center', + }, + backText: { + fontSize: 16, + fontWeight: '500', + marginLeft: 4, + }, + headerTitle: { + fontSize: 32, + fontWeight: '800', + letterSpacing: 0.3, + paddingLeft: 4, + }, + scrollView: { + flex: 1, + zIndex: 1, + }, + scrollContent: { + paddingHorizontal: 16, + paddingBottom: 40, + }, + sectionCard: { + borderRadius: 16, + marginBottom: 20, + padding: 20, + shadowColor: '#000', + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.1, + shadowRadius: 4, + elevation: 3, + }, + sectionHeader: { + flexDirection: 'row', + alignItems: 'center', + marginBottom: 8, + }, + sectionTitle: { + fontSize: 18, + fontWeight: '700', + marginLeft: 8, + }, + sectionDescription: { + fontSize: 14, + lineHeight: 20, + marginBottom: 20, + }, + settingRow: { + flexDirection: 'row', + alignItems: 'flex-start', + marginBottom: 16, + }, + settingTextContainer: { + flex: 1, + marginRight: 16, + }, + settingTitle: { + fontSize: 16, + fontWeight: '600', + marginBottom: 4, + }, + settingDescription: { + fontSize: 14, + lineHeight: 20, + opacity: 0.8, + }, + languageButton: { + paddingHorizontal: 16, + paddingVertical: 8, + borderRadius: 8, + alignItems: 'center', + }, + languageButtonText: { + fontSize: 14, + fontWeight: '600', + }, + divider: { + height: 1, + backgroundColor: 'rgba(255,255,255,0.1)', + marginVertical: 16, + }, + statusRow: { + flexDirection: 'row', + alignItems: 'center', + marginBottom: 16, + }, + statusText: { + fontSize: 14, + fontWeight: '500', + marginLeft: 8, + }, + apiKeyContainer: { + marginTop: 16, + }, + infoContainer: { + flexDirection: 'row', + alignItems: 'flex-start', + marginTop: 16, + padding: 12, + backgroundColor: 'rgba(255,255,255,0.05)', + borderRadius: 8, + }, + inputContainer: { + flexDirection: 'row', + alignItems: 'center', + marginBottom: 16, + }, + input: { + flex: 1, + borderRadius: 12, + paddingHorizontal: 16, + paddingVertical: 14, + fontSize: 15, + borderWidth: 2, + }, + pasteButton: { + position: 'absolute', + right: 12, + padding: 8, + }, + buttonRow: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + }, + button: { + borderRadius: 12, + paddingVertical: 14, + paddingHorizontal: 20, + alignItems: 'center', + flex: 1, + marginRight: 8, + }, + buttonText: { + fontWeight: '600', + fontSize: 15, + }, + resultMessage: { + borderRadius: 12, + padding: 16, + marginTop: 16, + flexDirection: 'row', + alignItems: 'center', + }, + resultIcon: { + marginRight: 12, + }, + resultText: { + flex: 1, + fontSize: 14, + fontWeight: '500', + }, + helpLink: { + flexDirection: 'row', + alignItems: 'center', + marginTop: 16, + paddingVertical: 8, + }, + helpIcon: { + marginRight: 8, + }, + helpText: { + fontSize: 14, + fontWeight: '500', + }, + infoText: { + fontSize: 14, + flex: 1, + lineHeight: 20, + opacity: 0.8, + marginLeft: 8, + }, + clearButton: { + backgroundColor: 'transparent', + borderWidth: 2, + marginRight: 0, + marginLeft: 8, + flex: 0, + paddingHorizontal: 16, + }, - // Modal Styles - modalOverlay: { - flex: 1, - backgroundColor: 'rgba(0,0,0,0.6)', - justifyContent: 'flex-end', - }, - modalContent: { - borderTopLeftRadius: 24, - borderTopRightRadius: 24, - maxHeight: '85%', - minHeight: '70%', // Increased minimum height - flex: 1, - }, - modalHeader: { - alignItems: 'center', - paddingTop: 12, - paddingBottom: 16, - borderBottomWidth: 1, - borderBottomColor: 'rgba(255,255,255,0.1)', - }, - dragHandle: { - width: 40, - height: 4, - borderRadius: 2, - marginBottom: 12, - }, - modalTitle: { - fontSize: 20, - fontWeight: '700', - marginBottom: 4, - }, - modalSubtitle: { - fontSize: 14, - textAlign: 'center', - }, - searchSection: { - paddingHorizontal: 20, - paddingVertical: 16, - }, - searchContainer: { - flexDirection: 'row', - alignItems: 'center', - borderRadius: 12, - paddingHorizontal: 16, - paddingVertical: 12, - }, - searchIcon: { - marginRight: 12, - }, - searchInput: { - flex: 1, - fontSize: 16, - paddingVertical: 0, - }, - searchClearButton: { - padding: 4, - marginLeft: 8, - }, - popularSection: { - paddingHorizontal: 20, - paddingBottom: 16, - }, - searchResultsTitle: { - color: '#FFFFFF', - }, - popularChips: { - paddingVertical: 2, - }, - popularChip: { - paddingHorizontal: 16, - paddingVertical: 8, - borderRadius: 20, - marginRight: 8, - borderWidth: 1, - borderColor: 'rgba(255,255,255,0.1)', - }, - selectedChip: { - // Border color handled by inline styles - }, - popularChipText: { - fontSize: 14, - fontWeight: '500', - }, - selectedChipText: { - color: '#FFFFFF', - }, - languagesSection: { - flex: 1, - paddingHorizontal: 20, - }, - languageList: { - flex: 1, - }, - languageItem: { - paddingVertical: 16, - paddingHorizontal: 16, - borderRadius: 12, - marginBottom: 4, - minHeight: 60, - }, - selectedLanguageItem: { - backgroundColor: 'rgba(255,255,255,0.08)', - borderWidth: 1, - borderColor: 'rgba(255,255,255,0.15)', - }, - languageContent: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - }, - languageInfo: { - flex: 1, - marginRight: 12, - }, - languageName: { - fontSize: 16, - fontWeight: '500', - marginBottom: 2, - }, - selectedLanguageName: { - fontWeight: '600', - }, - languageCode: { - fontSize: 12, - }, - selectedLanguageCode: { - }, - checkmarkContainer: { - width: 32, - height: 32, - borderRadius: 16, - backgroundColor: 'rgba(255,255,255,0.1)', - alignItems: 'center', - justifyContent: 'center', - }, - noResultsContainer: { - alignItems: 'center', - justifyContent: 'center', - paddingVertical: 40, - }, - noResultsText: { - fontSize: 16, - marginTop: 12, - textAlign: 'center', - }, - clearSearchButton: { - marginTop: 16, - paddingHorizontal: 20, - paddingVertical: 10, - borderRadius: 8, - }, - clearSearchButtonText: { - fontSize: 14, - fontWeight: '600', - }, - modalFooter: { - flexDirection: 'row', - paddingHorizontal: 20, - paddingVertical: 20, - paddingBottom: Platform.OS === 'ios' ? 40 : 20, - gap: 12, - }, - cancelButton: { - flex: 1, - paddingVertical: 14, - alignItems: 'center', - borderRadius: 12, - backgroundColor: 'rgba(255,255,255,0.1)', - }, - cancelButtonText: { - fontSize: 16, - fontWeight: '600', - }, - doneButton: { - flex: 1, - paddingVertical: 14, - alignItems: 'center', - borderRadius: 12, - }, - doneButtonText: { - fontSize: 16, - fontWeight: '700', - }, - - // Logo Source Styles - selectorLabel: { - fontSize: 13, - marginBottom: 8, - marginTop: 4, - }, - showsScrollView: { - marginBottom: 16, - }, - showsScrollContent: { - paddingRight: 16, - paddingVertical: 2, - }, - showItem: { - paddingHorizontal: 12, - paddingVertical: 6, - borderRadius: 16, - marginRight: 6, - borderWidth: 1, - borderColor: 'transparent', - }, - selectedShowItem: { - borderWidth: 2, - }, - showItemText: { - fontSize: 13, - }, - selectedShowItemText: { - fontWeight: '600', - }, - logoPreviewCard: { - borderRadius: 12, - padding: 12, - marginTop: 12, - }, - exampleImage: { - height: 60, - width: '100%', - backgroundColor: 'rgba(0,0,0,0.5)', - borderRadius: 8, - }, - bannerContainer: { - height: 80, - width: '100%', - borderRadius: 8, - overflow: 'hidden', - position: 'relative', - marginTop: 4, - }, - bannerImage: { - ...StyleSheet.absoluteFillObject, - }, - bannerOverlay: { - ...StyleSheet.absoluteFillObject, - backgroundColor: 'rgba(0,0,0,0.4)', - }, - logoOverBanner: { - position: 'absolute', - width: '80%', - height: '70%', - alignSelf: 'center', - top: '15%', - }, - noLogoContainer: { - position: 'absolute', - top: 0, - left: 0, - right: 0, - bottom: 0, - justifyContent: 'center', - alignItems: 'center', - }, - noLogoText: { - color: '#fff', - fontSize: 13, - backgroundColor: 'rgba(0,0,0,0.5)', - paddingHorizontal: 12, - paddingVertical: 6, - borderRadius: 4, - }, - logoSourceLabel: { - fontSize: 11, - marginTop: 6, - }, - attributionContainer: { - alignItems: 'center', - marginBottom: 24, - marginTop: 8, - paddingHorizontal: 24, - width: '100%', - }, - tmdbLogo: { - width: 80, - height: 60, - marginBottom: 8, - }, - attributionText: { - fontSize: 11, - textAlign: 'center', - lineHeight: 16, - opacity: 0.7, - }, + // Modal Styles + modalOverlay: { + flex: 1, + backgroundColor: 'rgba(0,0,0,0.6)', + justifyContent: 'flex-end', + }, + modalContent: { + borderTopLeftRadius: 24, + borderTopRightRadius: 24, + maxHeight: '85%', + minHeight: '70%', // Increased minimum height + flex: 1, + }, + modalHeader: { + alignItems: 'center', + paddingTop: 12, + paddingBottom: 16, + borderBottomWidth: 1, + borderBottomColor: 'rgba(255,255,255,0.1)', + }, + dragHandle: { + width: 40, + height: 4, + borderRadius: 2, + marginBottom: 12, + }, + modalTitle: { + fontSize: 20, + fontWeight: '700', + marginBottom: 4, + }, + modalSubtitle: { + fontSize: 14, + textAlign: 'center', + }, + searchSection: { + paddingHorizontal: 20, + paddingVertical: 16, + }, + searchContainer: { + flexDirection: 'row', + alignItems: 'center', + borderRadius: 12, + paddingHorizontal: 16, + paddingVertical: 12, + }, + searchIcon: { + marginRight: 12, + }, + searchInput: { + flex: 1, + fontSize: 16, + paddingVertical: 0, + }, + searchClearButton: { + padding: 4, + marginLeft: 8, + }, + popularSection: { + paddingHorizontal: 20, + paddingBottom: 16, + }, + searchResultsTitle: { + color: '#FFFFFF', + }, + popularChips: { + paddingVertical: 2, + }, + popularChip: { + paddingHorizontal: 16, + paddingVertical: 8, + borderRadius: 20, + marginRight: 8, + borderWidth: 1, + borderColor: 'rgba(255,255,255,0.1)', + }, + selectedChip: { + // Border color handled by inline styles + }, + popularChipText: { + fontSize: 14, + fontWeight: '500', + }, + selectedChipText: { + color: '#FFFFFF', + }, + languagesSection: { + flex: 1, + paddingHorizontal: 20, + }, + languageList: { + flex: 1, + }, + languageItem: { + paddingVertical: 16, + paddingHorizontal: 16, + borderRadius: 12, + marginBottom: 4, + minHeight: 60, + }, + selectedLanguageItem: { + backgroundColor: 'rgba(255,255,255,0.08)', + borderWidth: 1, + borderColor: 'rgba(255,255,255,0.15)', + }, + languageContent: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + }, + languageInfo: { + flex: 1, + marginRight: 12, + }, + languageName: { + fontSize: 16, + fontWeight: '500', + marginBottom: 2, + }, + selectedLanguageName: { + fontWeight: '600', + }, + languageCode: { + fontSize: 12, + }, + selectedLanguageCode: {}, + checkmarkContainer: { + width: 32, + height: 32, + borderRadius: 16, + backgroundColor: 'rgba(255,255,255,0.1)', + alignItems: 'center', + justifyContent: 'center', + }, + noResultsContainer: { + alignItems: 'center', + justifyContent: 'center', + paddingVertical: 40, + }, + noResultsText: { + fontSize: 16, + marginTop: 12, + textAlign: 'center', + }, + clearSearchButton: { + marginTop: 16, + paddingHorizontal: 20, + paddingVertical: 10, + borderRadius: 8, + }, + clearSearchButtonText: { + fontSize: 14, + fontWeight: '600', + }, + modalFooter: { + flexDirection: 'row', + paddingHorizontal: 20, + paddingVertical: 20, + paddingBottom: Platform.OS === 'ios' ? 40 : 20, + gap: 12, + }, + cancelButton: { + flex: 1, + paddingVertical: 14, + alignItems: 'center', + borderRadius: 12, + backgroundColor: 'rgba(255,255,255,0.1)', + }, + cancelButtonText: { + fontSize: 16, + fontWeight: '600', + }, + doneButton: { + flex: 1, + paddingVertical: 14, + alignItems: 'center', + borderRadius: 12, + }, + doneButtonText: { + fontSize: 16, + fontWeight: '700', + }, + // Logo Source Styles + selectorLabel: { + fontSize: 13, + marginBottom: 8, + marginTop: 4, + }, + showsScrollView: { + marginBottom: 16, + }, + showsScrollContent: { + paddingRight: 16, + paddingVertical: 2, + }, + showItem: { + paddingHorizontal: 12, + paddingVertical: 6, + borderRadius: 16, + marginRight: 6, + borderWidth: 1, + borderColor: 'transparent', + }, + selectedShowItem: { + borderWidth: 2, + }, + showItemText: { + fontSize: 13, + }, + selectedShowItemText: { + fontWeight: '600', + }, + logoPreviewCard: { + borderRadius: 12, + padding: 12, + marginTop: 12, + }, + exampleImage: { + height: 60, + width: '100%', + backgroundColor: 'rgba(0,0,0,0.5)', + borderRadius: 8, + }, + bannerContainer: { + height: 80, + width: '100%', + borderRadius: 8, + overflow: 'hidden', + position: 'relative', + marginTop: 4, + }, + bannerImage: { + ...StyleSheet.absoluteFillObject, + }, + bannerOverlay: { + ...StyleSheet.absoluteFillObject, + backgroundColor: 'rgba(0,0,0,0.4)', + }, + logoOverBanner: { + position: 'absolute', + width: '80%', + height: '70%', + alignSelf: 'center', + top: '15%', + }, + noLogoContainer: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + justifyContent: 'center', + alignItems: 'center', + }, + noLogoText: { + color: '#fff', + fontSize: 13, + backgroundColor: 'rgba(0,0,0,0.5)', + paddingHorizontal: 12, + paddingVertical: 6, + borderRadius: 4, + }, + logoSourceLabel: { + fontSize: 11, + marginTop: 6, + }, + attributionContainer: { + alignItems: 'center', + marginBottom: 24, + marginTop: 8, + paddingHorizontal: 24, + width: '100%', + }, + tmdbLogo: { + width: 80, + height: 60, + marginBottom: 8, + }, + attributionText: { + fontSize: 11, + textAlign: 'center', + lineHeight: 16, + opacity: 0.7, + }, }); -export default TMDBSettingsScreen; \ No newline at end of file +export default TMDBSettingsScreen; diff --git a/src/screens/TraktSettingsScreen.tsx b/src/screens/TraktSettingsScreen.tsx index 81b57bb6..d7b5d3b2 100644 --- a/src/screens/TraktSettingsScreen.tsx +++ b/src/screens/TraktSettingsScreen.tsx @@ -595,7 +595,7 @@ const TraktSettingsScreen: React.FC = () => { )} - This product uses the Trakt API but is not endorsed or certified by Trakt. + {t('trakt.disclaimer')}