From c48670fa7487fc698f7d84966d9523f4c4ababe5 Mon Sep 17 00:00:00 2001 From: Eazvy <57739965+Eazvy@users.noreply.github.com> Date: Tue, 6 Jan 2026 18:13:04 -0500 Subject: [PATCH 1/5] fix escape key crashing on macOS just adds an ignore listener so it doesn't crash, nor do anything --- ios/KSPlayerView.swift | 41 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/ios/KSPlayerView.swift b/ios/KSPlayerView.swift index 818aae79..91a8f217 100644 --- a/ios/KSPlayerView.swift +++ b/ios/KSPlayerView.swift @@ -19,6 +19,7 @@ class KSPlayerView: UIView { weak var viewManager: KSPlayerViewManager? // Event blocks for Fabric + @objc var onExitFullscreen: RCTDirectEventBlock? @objc var onLoad: RCTDirectEventBlock? @objc var onProgress: RCTDirectEventBlock? @objc var onBuffering: RCTDirectEventBlock? @@ -101,6 +102,17 @@ class KSPlayerView: UIView { setupPlayerView() } + override var canBecomeFirstResponder: Bool { + return true + } + + override func didMoveToWindow() { + super.didMoveToWindow() + becomeFirstResponder() + } + + + private func setupPlayerView() { playerView = IOSVideoPlayerView() playerView.translatesAutoresizingMaskIntoConstraints = false @@ -237,10 +249,10 @@ class KSPlayerView: UIView { options.registerRemoteControll = false // PERFORMANCE OPTIMIZATION: Buffer durations for smooth high bitrate playback - // preferredForwardBufferDuration = 5.0s: Increased to prevent stalling on network hiccups - options.preferredForwardBufferDuration = 5.0 - // maxBufferDuration = 300.0s: Increased to allow 5 minutes of cache ahead - options.maxBufferDuration = 300.0 + // preferredForwardBufferDuration = 3.0s: Slightly increased to reduce rebuffering during playback + options.preferredForwardBufferDuration = 1.0 + // maxBufferDuration = 120.0s: Increased to allow the player to cache more content ahead of time (2 minutes) + options.maxBufferDuration = 120.0 // Enable "second open" to relax startup/seek buffering thresholds (already enabled) options.isSecondOpen = true @@ -315,6 +327,22 @@ class KSPlayerView: UIView { } } + override var keyCommands: [UIKeyCommand]? { + return [ + UIKeyCommand( + input: UIKeyCommand.inputEscape, + modifierFlags: [], + action: #selector(handleEscapeKey), + discoverabilityTitle: "Exit Fullscreen" + ) + ] + } + + @objc func handleEscapeKey() { + print("KSPlayerView: ESC pressed") + sendEvent("onExitFullscreen", [:]) + } + func setVolume(_ volume: Float) { currentVolume = volume playerView.playerLayer?.player.playbackVolume = volume @@ -979,3 +1007,8 @@ extension KSPlayerView { } } +extension IOSVideoPlayerView { + @objc func handleEscapeKey() { + self.next?.perform(#selector(handleEscapeKey)) + } +} From 0f9f6bbe5d2aa1c7771b002e0ae0d3cfa4721a33 Mon Sep 17 00:00:00 2001 From: Eazvy <57739965+Eazvy@users.noreply.github.com> Date: Tue, 6 Jan 2026 18:23:01 -0500 Subject: [PATCH 2/5] fix accidental override of buffering --- ios/KSPlayerView.swift | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/ios/KSPlayerView.swift b/ios/KSPlayerView.swift index 91a8f217..9b8cd457 100644 --- a/ios/KSPlayerView.swift +++ b/ios/KSPlayerView.swift @@ -19,13 +19,13 @@ class KSPlayerView: UIView { weak var viewManager: KSPlayerViewManager? // Event blocks for Fabric - @objc var onExitFullscreen: RCTDirectEventBlock? @objc var onLoad: RCTDirectEventBlock? @objc var onProgress: RCTDirectEventBlock? @objc var onBuffering: RCTDirectEventBlock? @objc var onEnd: RCTDirectEventBlock? @objc var onError: RCTDirectEventBlock? @objc var onBufferingProgress: RCTDirectEventBlock? + @objc var onExitFullscreen: RCTDirectEventBlock? // Property setters that React Native will call @objc var source: NSDictionary? { @@ -102,17 +102,6 @@ class KSPlayerView: UIView { setupPlayerView() } - override var canBecomeFirstResponder: Bool { - return true - } - - override func didMoveToWindow() { - super.didMoveToWindow() - becomeFirstResponder() - } - - - private func setupPlayerView() { playerView = IOSVideoPlayerView() playerView.translatesAutoresizingMaskIntoConstraints = false @@ -249,10 +238,10 @@ class KSPlayerView: UIView { options.registerRemoteControll = false // PERFORMANCE OPTIMIZATION: Buffer durations for smooth high bitrate playback - // preferredForwardBufferDuration = 3.0s: Slightly increased to reduce rebuffering during playback - options.preferredForwardBufferDuration = 1.0 - // maxBufferDuration = 120.0s: Increased to allow the player to cache more content ahead of time (2 minutes) - options.maxBufferDuration = 120.0 + // preferredForwardBufferDuration = 5.0s: Increased to prevent stalling on network hiccups + options.preferredForwardBufferDuration = 5.0 + // maxBufferDuration = 300.0s: Increased to allow 5 minutes of cache ahead + options.maxBufferDuration = 300.0 // Enable "second open" to relax startup/seek buffering thresholds (already enabled) options.isSecondOpen = true @@ -325,7 +314,7 @@ class KSPlayerView: UIView { } else { playerView.play() } - } + } override var keyCommands: [UIKeyCommand]? { return [ From e76b44cff1543eb42f8c3d439de743f1f5f1afde Mon Sep 17 00:00:00 2001 From: albyalex96 <72573722+albyalex96@users.noreply.github.com> Date: Wed, 7 Jan 2026 01:03:24 +0100 Subject: [PATCH 3/5] Created it locale --- src/i18n/locales/it.json | 1196 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 1196 insertions(+) create mode 100644 src/i18n/locales/it.json diff --git a/src/i18n/locales/it.json b/src/i18n/locales/it.json new file mode 100644 index 00000000..3728196e --- /dev/null +++ b/src/i18n/locales/it.json @@ -0,0 +1,1196 @@ +{ + "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", + "close": "Chiudi", + "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" + } + }, + "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 S{{season}}E{{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_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" + }, + "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." + }, + "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", + "arabic": "Arabo", + "spanish": "Spagnolo", + "french": "Francese", + "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", + "developer_tools": "Opzioni di test e debug", + "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": { + "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 per streaming premium", + "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", + "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 (Beta)", + "enable_downloads_desc": "Mostra la scheda Download e abilita il salvataggio degli streaming", + "notifications": "Notifiche", + "notifications_desc": "Promemoria episodi", + "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" + }, + "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", + "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", + "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", + "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", + "autoplay_title": "Auto-play Miglior Stream", + "autoplay_desc": "Avvia automaticamente lo stream alla massima qualità disponibile.", + "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": "Plugin", + "enable_title": "Abilita Plugin", + "enable_desc": "Permetti all'app di usare i plugin installati per trovare gli stream", + "repo_config_title": "Configurazione Repository", + "repo_config_desc": "Abilita più repository per combinare plugin da diverse sorgenti. Attiva o disattiva ogni repository qui sotto.", + "your_repos": "I Tuoi Repository", + "your_repos_desc": "Abilita più repository per combinare plugin da diverse sorgenti.", + "add_repo_button": "Aggiungi Repository", + "refresh": "Aggiorna", + "remove": "Rimuovi", + "enabled": "Abilitato", + "disabled": "Disabilitato", + "updating": "Aggiornamento...", + "success": "Successo", + "error": "Errore", + "alert_repo_added": "Repository aggiunto e plugin caricati con successo", + "alert_repo_saved": "URL del repository salvato con successo", + "alert_repo_refreshed": "Repository aggiornato con successo con i file più recenti", + "alert_invalid_url": "Formato URL Non Valido", + "alert_plugins_cleared": "Tutti i plugin sono stati rimossi", + "alert_cache_cleared": "Cache del repository cancellata con successo", + "unknown": "Sconosciuto", + "active": "Attivo", + "available": "Disponibile", + "platform_disabled": "Piattaforma Disabilitata", + "limited": "Limitato", + "clear_all": "Rimuovi Tutti i Plugin", + "clear_all_desc": "Sei sicuro di voler rimuovere tutti i plugin installati? L'azione è irreversibile.", + "clear_cache": "Cancella Cache Repository", + "clear_cache_desc": "Questo rimuoverà l'URL del repository salvato e tutti i dati dei plugin in cache. Dovrai reinserire l'URL del repository.", + "add_new_repo": "Aggiungi Nuovo Repository", + "available_plugins": "Plugin Disponibili ({{count}})", + "placeholder": "Cerca plugin...", + "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": "Nessun Plugin Trovato", + "no_plugins_available": "Nessun Plugin Disponibile", + "no_match_desc": "Nessun plugin corrisponde a \"{{query}}\". Prova un altro termine.", + "configure_repo_desc": "Configura un repository sopra per visualizzare i plugin 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 di streaming prima di mostrarli (può rallentare i risultati ma migliora l'affidabilità)", + "group_streams": "Raggruppa Stream dei Plugin", + "group_streams_desc": "Se abilitato, gli stream dei plugin sono raggruppati per repository. Se disabilitato, ogni plugin appare come provider separato.", + "sort_quality": "Ordina Prima per Qualità", + "sort_quality_desc": "Se abilitato, gli stream sono ordinati prima per qualità, poi per plugin. Solo disponibile con il raggruppamento attivo.", + "show_logos": "Mostra Loghi Plugin", + "show_logos_desc": "Visualizza i loghi dei plugin accanto ai link di streaming nella schermata degli stream.", + "quality_filtering": "Filtraggio Qualità", + "quality_filtering_desc": "Escludi specifiche qualità video dai risultati. Tocca una qualità per escluderla dai risultati dei plugin.", + "excluded_qualities": "Qualità escluse:", + "language_filtering": "Filtraggio Lingua", + "language_filtering_desc": "Escludi specifiche lingue dai risultati. Tocca una lingua per escluderla dai risultati dei plugin.", + "note": "Nota:", + "language_filtering_note": "Questo filtro si applica solo ai provider che includono informazioni sulla lingua nel nome dello stream.", + "excluded_languages": "Lingue escluse:", + "about_title": "Informazioni sui Plugin", + "about_desc_1": "I plugin sono moduli JavaScript che cercano link di streaming da varie sorgenti. Girano localmente sul tuo dispositivo e possono essere installati da repository fidati.", + "about_desc_2": "I provider contrassegnati come \"Limitati\" dipendono da API esterne che potrebbero smettere di funzionare senza preavviso.", + "help_title": "Guida ai Plugin", + "help_step_1": "1. **Abilita Plugin** - Attiva l'interruttore principale", + "help_step_2": "2. **Aggiungi Repository** - Inserisci un URL GitHub raw o usa quello predefinito", + "help_step_3": "3. **Aggiorna Repository** - Scarica i plugin disponibili dal repository", + "help_step_4": "4. **Abilita Plugin** - Attiva i singoli plugin che desideri usare", + "got_it": "Capito!", + "repo_format_hint": "Formato: https://raw.githubusercontent.com/user/repo/refs/heads/branch", + "cancel": "Annulla", + "add": "Aggiungi" + } +} From 9b2b6191215af4dc327586cc37870bfac36b21be Mon Sep 17 00:00:00 2001 From: tapframe Date: Wed, 7 Jan 2026 12:37:22 +0530 Subject: [PATCH 4/5] added italian language to UI. --- src/i18n/locales/ar.json | 1 + src/i18n/locales/en.json | 1 + src/i18n/locales/es.json | 1 + src/i18n/locales/fr.json | 1 + src/i18n/locales/it.json | 3 +- src/i18n/locales/pt.json | 1 + src/i18n/resources.ts | 2 + src/screens/SettingsScreen.tsx | 245 ++++++++++++++++++--------------- 8 files changed, 143 insertions(+), 112 deletions(-) diff --git a/src/i18n/locales/ar.json b/src/i18n/locales/ar.json index f29ab802..880bafd5 100644 --- a/src/i18n/locales/ar.json +++ b/src/i18n/locales/ar.json @@ -601,6 +601,7 @@ "arabic": "العربية", "spanish": "الإسبانية", "french": "الفرنسية", + "italian": "الإيطالية", "account": "الحساب", "content_discovery": "المحتوى والاكتشاف", "appearance": "المظهر", diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 98bd590b..7493f969 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -601,6 +601,7 @@ "arabic": "Arabic", "spanish": "Spanish", "french": "French", + "italian": "Italian", "account": "Account", "content_discovery": "Content & Discovery", "appearance": "Appearance", diff --git a/src/i18n/locales/es.json b/src/i18n/locales/es.json index 7728c44b..0f44c643 100644 --- a/src/i18n/locales/es.json +++ b/src/i18n/locales/es.json @@ -601,6 +601,7 @@ "arabic": "Árabe", "spanish": "Español", "french": "Francés", + "italian": "Italiano", "account": "Cuenta", "content_discovery": "Contenido y descubrimiento", "appearance": "Apariencia", diff --git a/src/i18n/locales/fr.json b/src/i18n/locales/fr.json index 519c2207..3af333bf 100644 --- a/src/i18n/locales/fr.json +++ b/src/i18n/locales/fr.json @@ -601,6 +601,7 @@ "arabic": "Arabe", "spanish": "Espagnol", "french": "Français", + "italian": "Italien", "account": "Compte", "content_discovery": "Contenu et découverte", "appearance": "Apparence", diff --git a/src/i18n/locales/it.json b/src/i18n/locales/it.json index 3728196e..b3dc370d 100644 --- a/src/i18n/locales/it.json +++ b/src/i18n/locales/it.json @@ -601,6 +601,7 @@ "arabic": "Arabo", "spanish": "Spagnolo", "french": "Francese", + "italian": "Italiano", "account": "Account", "content_discovery": "Contenuti e Scoperta", "appearance": "Aspetto", @@ -1193,4 +1194,4 @@ "cancel": "Annulla", "add": "Aggiungi" } -} +} \ No newline at end of file diff --git a/src/i18n/locales/pt.json b/src/i18n/locales/pt.json index 454dc94c..3d4a7575 100644 --- a/src/i18n/locales/pt.json +++ b/src/i18n/locales/pt.json @@ -579,6 +579,7 @@ "arabic": "Árabe", "spanish": "Espanhol", "french": "Francês", + "italian": "Italiano", "account": "Conta", "content_discovery": "Conteúdo e Descoberta", "appearance": "Aparência", diff --git a/src/i18n/resources.ts b/src/i18n/resources.ts index ab901df9..bb8c4e58 100644 --- a/src/i18n/resources.ts +++ b/src/i18n/resources.ts @@ -3,6 +3,7 @@ import pt from './locales/pt.json'; import ar from './locales/ar.json'; import es from './locales/es.json'; import fr from './locales/fr.json'; +import it from './locales/it.json'; export const resources = { en: { translation: en }, @@ -10,4 +11,5 @@ export const resources = { ar: { translation: ar }, es: { translation: es }, fr: { translation: fr }, + it: { translation: it }, }; diff --git a/src/screens/SettingsScreen.tsx b/src/screens/SettingsScreen.tsx index 2fb3c581..45f10c30 100644 --- a/src/screens/SettingsScreen.tsx +++ b/src/screens/SettingsScreen.tsx @@ -601,11 +601,12 @@ const SettingsScreen: React.FC = () => { } @@ -855,116 +856,138 @@ const SettingsScreen: React.FC = () => { style={{ backgroundColor: currentTheme.colors.darkGray || '#0A0C0C' }} contentContainerStyle={styles.bottomSheetContent} > - { - i18n.changeLanguage('en'); - languageSheetRef.current?.close(); - }} - > - - {t('settings.english')} - - {i18n.language === 'en' && ( - - )} - + { + i18n.changeLanguage('en'); + languageSheetRef.current?.close(); + }} + > + + {t('settings.english')} + + {i18n.language === 'en' && ( + + )} + - { - i18n.changeLanguage('pt'); - languageSheetRef.current?.close(); - }} - > - - {t('settings.portuguese')} - - {i18n.language === 'pt' && ( - - )} - + { + i18n.changeLanguage('pt'); + languageSheetRef.current?.close(); + }} + > + + {t('settings.portuguese')} + + {i18n.language === 'pt' && ( + + )} + - { - i18n.changeLanguage('ar'); - languageSheetRef.current?.close(); - }} - > - - {t('settings.arabic')} - - {i18n.language === 'ar' && ( - - )} - + { + i18n.changeLanguage('ar'); + languageSheetRef.current?.close(); + }} + > + + {t('settings.arabic')} + + {i18n.language === 'ar' && ( + + )} + - { - i18n.changeLanguage('es'); - languageSheetRef.current?.close(); - }} - > - - {t('settings.spanish')} - - {i18n.language === 'es' && ( - - )} - + { + i18n.changeLanguage('es'); + languageSheetRef.current?.close(); + }} + > + + {t('settings.spanish')} + + {i18n.language === 'es' && ( + + )} + - { - i18n.changeLanguage('fr'); - languageSheetRef.current?.close(); - }} - > - - {t('settings.french')} - - {i18n.language === 'fr' && ( - - )} - - + { + i18n.changeLanguage('fr'); + languageSheetRef.current?.close(); + }} + > + + {t('settings.french')} + + {i18n.language === 'fr' && ( + + )} + + + { + i18n.changeLanguage('it'); + languageSheetRef.current?.close(); + }} + > + + {t('settings.italian')} + + {i18n.language === 'it' && ( + + )} + + From a85698b009a031018afbb452e85168164dd0ce42 Mon Sep 17 00:00:00 2001 From: tapframe Date: Wed, 7 Jan 2026 12:44:42 +0530 Subject: [PATCH 5/5] added localization to themescreen. --- src/i18n/locales/ar.json | 30 +++ src/i18n/locales/en.json | 30 +++ src/i18n/locales/es.json | 30 +++ src/i18n/locales/fr.json | 30 +++ src/i18n/locales/it.json | 30 +++ src/i18n/locales/pt.json | 30 +++ src/screens/ThemeScreen.tsx | 415 +++++++++++++++++++----------------- 7 files changed, 395 insertions(+), 200 deletions(-) diff --git a/src/i18n/locales/ar.json b/src/i18n/locales/ar.json index 880bafd5..97fcf4aa 100644 --- a/src/i18n/locales/ar.json +++ b/src/i18n/locales/ar.json @@ -1193,5 +1193,35 @@ "repo_format_hint": "التنسيق: https://raw.githubusercontent.com/username/repo/refs/heads/branch", "cancel": "إلغاء", "add": "إضافة" + }, + "theme": { + "title": "سمات التطبيق", + "select_theme": "اختر السمة", + "create_custom": "إنشاء سمة مخصصة", + "options": "خيارات", + "use_dominant_color": "استخدام اللون المهيمن من العمل الفني", + "categories": { + "all": "كل السمات", + "dark": "سمات داكنة", + "colorful": "ملونة", + "custom": "سماتي" + }, + "editor": { + "theme_name_placeholder": "اسم السمة", + "save": "حفظ", + "primary": "أساسي", + "secondary": "ثانوي", + "background": "خلفية", + "invalid_name_title": "اسم غير صالح", + "invalid_name_msg": "يرجى إدخال اسم سمة صالح" + }, + "alerts": { + "delete_title": "حذف السمة", + "delete_msg": "هل أنت متأكد أنك تريد حذف \"{{name}}\"؟", + "ok": "حسناً", + "delete": "حذف", + "cancel": "إلغاء", + "back": "إعدادات" + } } } \ No newline at end of file diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 7493f969..8cb2a472 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -1193,5 +1193,35 @@ "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" + } } } \ No newline at end of file diff --git a/src/i18n/locales/es.json b/src/i18n/locales/es.json index 0f44c643..f0ce3bb8 100644 --- a/src/i18n/locales/es.json +++ b/src/i18n/locales/es.json @@ -1193,5 +1193,35 @@ "repo_format_hint": "Formato: https://raw.githubusercontent.com/usuario/repo/rama", "cancel": "Cancelar", "add": "Añadir" + }, + "theme": { + "title": "Temas de la App", + "select_theme": "SELECCIONAR TEMA", + "create_custom": "Crear Tema Personalizado", + "options": "OPCIONES", + "use_dominant_color": "Usar Color Dominante del Arte", + "categories": { + "all": "Todos los Temas", + "dark": "Temas Oscuros", + "colorful": "Coloridos", + "custom": "Mis Temas" + }, + "editor": { + "theme_name_placeholder": "Nombre del tema", + "save": "Guardar", + "primary": "Primario", + "secondary": "Secundario", + "background": "Fondo", + "invalid_name_title": "Nombre Inválido", + "invalid_name_msg": "Por favor ingresa un nombre válido" + }, + "alerts": { + "delete_title": "Eliminar Tema", + "delete_msg": "¿Estás seguro de que quieres eliminar \"{{name}}\"?", + "ok": "OK", + "delete": "Eliminar", + "cancel": "Cancelar", + "back": "Ajustes" + } } } \ No newline at end of file diff --git a/src/i18n/locales/fr.json b/src/i18n/locales/fr.json index 3af333bf..24e7827b 100644 --- a/src/i18n/locales/fr.json +++ b/src/i18n/locales/fr.json @@ -1193,5 +1193,35 @@ "repo_format_hint": "Format : https://raw.githubusercontent.com/utilisateur/repo/refs/heads/branche", "cancel": "Annuler", "add": "Ajouter" + }, + "theme": { + "title": "Thèmes de l'App", + "select_theme": "SÉLECTIONNER UN THÈME", + "create_custom": "Créer un Thème Personnalisé", + "options": "OPTIONS", + "use_dominant_color": "Utiliser la Couleur Dominante de l'Image", + "categories": { + "all": "Tous les Thèmes", + "dark": "Thèmes Sombres", + "colorful": "Colorés", + "custom": "Mes Thèmes" + }, + "editor": { + "theme_name_placeholder": "Nom du thème", + "save": "Enregistrer", + "primary": "Primaire", + "secondary": "Secondaire", + "background": "Arrière-plan", + "invalid_name_title": "Nom Invalide", + "invalid_name_msg": "Veuillez entrer un nom de thème valide" + }, + "alerts": { + "delete_title": "Supprimer le Thème", + "delete_msg": "Êtes-vous sûr de vouloir supprimer \"{{name}}\" ?", + "ok": "OK", + "delete": "Supprimer", + "cancel": "Annuler", + "back": "Paramètres" + } } } \ No newline at end of file diff --git a/src/i18n/locales/it.json b/src/i18n/locales/it.json index b3dc370d..40f47ea6 100644 --- a/src/i18n/locales/it.json +++ b/src/i18n/locales/it.json @@ -1193,5 +1193,35 @@ "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 dalla Copertina", + "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" + } } } \ No newline at end of file diff --git a/src/i18n/locales/pt.json b/src/i18n/locales/pt.json index 3d4a7575..7136dd8b 100644 --- a/src/i18n/locales/pt.json +++ b/src/i18n/locales/pt.json @@ -1159,5 +1159,35 @@ "repo_format_hint": "Formato: https://raw.githubusercontent.com/username/repo/refs/heads/branch", "cancel": "Cancelar", "add": "Adicionar" + }, + "theme": { + "title": "Temas do App", + "select_theme": "SELECIONAR TEMA", + "create_custom": "Criar Tema Personalizado", + "options": "OPÇÕES", + "use_dominant_color": "Usar Cor Dominante da Arte", + "categories": { + "all": "Todos os Temas", + "dark": "Temas Escuros", + "colorful": "Coloridos", + "custom": "Meus Temas" + }, + "editor": { + "theme_name_placeholder": "Nome do tema", + "save": "Salvar", + "primary": "Primário", + "secondary": "Secundário", + "background": "Fundo", + "invalid_name_title": "Nome Inválido", + "invalid_name_msg": "Por favor insira um nome válido" + }, + "alerts": { + "delete_title": "Excluir Tema", + "delete_msg": "Tem certeza que deseja excluir \"{{name}}\"?", + "ok": "OK", + "delete": "Excluir", + "cancel": "Cancelar", + "back": "Configurações" + } } } \ No newline at end of file diff --git a/src/screens/ThemeScreen.tsx b/src/screens/ThemeScreen.tsx index d9608d95..b68bb5c1 100644 --- a/src/screens/ThemeScreen.tsx +++ b/src/screens/ThemeScreen.tsx @@ -1,4 +1,5 @@ import React, { useState, useCallback, useEffect, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; import { View, Text, @@ -45,9 +46,9 @@ interface ThemeCardProps { onDelete?: () => void; } -const ThemeCard: React.FC = ({ - theme, - isSelected, +const ThemeCard: React.FC = ({ + theme, + isSelected, onSelect, onEdit, onDelete @@ -57,10 +58,10 @@ const ThemeCard: React.FC = ({ style={[ styles.themeCard, isSelected && styles.selectedThemeCard, - { + { borderColor: isSelected ? theme.colors.primary : 'transparent', - backgroundColor: Platform.OS === 'ios' - ? `${theme.colors.darkBackground}60` + backgroundColor: Platform.OS === 'ios' + ? `${theme.colors.darkBackground}60` : 'rgba(255, 255, 255, 0.07)' } ]} @@ -75,26 +76,26 @@ const ThemeCard: React.FC = ({ )} - + - + {theme.isEditable && ( {onEdit && ( - )} {onDelete && ( - @@ -114,11 +115,11 @@ interface FilterTabProps { primaryColor: string; } -const FilterTab: React.FC = ({ - category, - isActive, +const FilterTab: React.FC = ({ + category, + isActive, onPress, - primaryColor + primaryColor }) => ( = ({ ]} onPress={onPress} > - @@ -171,152 +172,157 @@ const ThemeColorEditor: React.FC { - const [themeName, setThemeName] = useState('Custom Theme'); - const [selectedColorKey, setSelectedColorKey] = useState('primary'); - const [themeColors, setThemeColors] = useState({ - primary: initialColors.primary, - secondary: initialColors.secondary, - darkBackground: initialColors.darkBackground, - }); - - const handleColorChange = useCallback((color: string) => { - setThemeColors(prev => ({ - ...prev, - [selectedColorKey]: color, - })); - }, [selectedColorKey]); - - const handleSave = () => { - if (!themeName.trim()) { - setAlertTitle('Invalid Name'); - setAlertMessage('Please enter a valid theme name'); - setAlertActions([{ label: 'OK', onPress: () => {} }]); - setAlertVisible(true); - return; - } - onSave({ - ...themeColors, - name: themeName + const { t } = useTranslation(); + const [themeName, setThemeName] = useState(t('theme.editor.theme_name_placeholder') || 'Custom Theme'); + const [selectedColorKey, setSelectedColorKey] = useState('primary'); + const [themeColors, setThemeColors] = useState({ + primary: initialColors.primary, + secondary: initialColors.secondary, + darkBackground: initialColors.darkBackground, }); - }; - // Compact preview component - const ThemePreview = () => ( - - - {/* App header */} - - - - - - - - - {/* Content area */} - - {/* Featured content poster */} - - - - - - + const handleColorChange = useCallback((color: string) => { + setThemeColors(prev => ({ + ...prev, + [selectedColorKey]: color, + })); + }, [selectedColorKey]); + + const handleSave = () => { + if (!themeName.trim()) { + if (!themeName.trim()) { + setAlertTitle(t('theme.editor.invalid_name_title')); + setAlertMessage(t('theme.editor.invalid_name_msg')); + setAlertActions([{ label: 'OK', onPress: () => { } }]); + setAlertVisible(true); + return; + } + setAlertVisible(true); + return; + } + onSave({ + ...themeColors, + name: themeName + }); + }; + + // Compact preview component + const ThemePreview = () => ( + + + {/* App header */} + + + + + - - {/* Content row */} - - - - - - - - - - - - ); - return ( - - - - - - - - Save - - - - - - - - - setSelectedColorKey('primary')} - > - Primary - - - setSelectedColorKey('secondary')} - > - Secondary - - - setSelectedColorKey('darkBackground')} - > - Background - + {/* Content area */} + + {/* Featured content poster */} + + + + + + + + + + {/* Content row */} + + + + + + + + - - - + ); + + return ( + + + + + + + + {t('theme.editor.save')} + + + + + + + + + setSelectedColorKey('primary')} + > + {t('theme.editor.primary')} + + + setSelectedColorKey('secondary')} + > + {t('theme.editor.secondary')} + + + setSelectedColorKey('darkBackground')} + > + {t('theme.editor.background')} + + + + + + + - - ); -}; + ); + }; const ThemeScreen: React.FC = () => { const { @@ -327,6 +333,7 @@ const ThemeScreen: React.FC = () => { updateCustomTheme, deleteCustomTheme } = useTheme(); + const { t } = useTranslation(); const navigation = useNavigation>(); const insets = useSafeAreaInsets(); const { settings, updateSetting } = useSettings(); @@ -335,7 +342,15 @@ const ThemeScreen: React.FC = () => { const headerTopPadding = Platform.OS === 'android' ? ANDROID_STATUSBAR_HEIGHT + 8 : 8; - + + // Theme categories for organization + const THEME_CATEGORIES = [ + { id: 'all', name: t('theme.categories.all') }, + { id: 'dark', name: t('theme.categories.dark') }, + { id: 'colorful', name: t('theme.categories.colorful') }, + { id: 'custom', name: t('theme.categories.custom') }, + ]; + const [isEditMode, setIsEditMode] = useState(false); const [editingTheme, setEditingTheme] = useState(null); const [activeFilter, setActiveFilter] = useState('all'); @@ -352,9 +367,9 @@ const ThemeScreen: React.FC = () => { StatusBar.setBackgroundColor('transparent'); } }; - + applyStatusBarConfig(); - + // Re-apply on focus const unsubscribe = navigation.addListener('focus', applyStatusBarConfig); return unsubscribe; @@ -365,19 +380,19 @@ const ThemeScreen: React.FC = () => { switch (activeFilter) { case 'dark': // Themes with darker colors - return availableThemes.filter(theme => - !theme.isEditable && - theme.id !== 'neon' && + return availableThemes.filter(theme => + !theme.isEditable && + theme.id !== 'neon' && theme.id !== 'retro' ); case 'colorful': // Themes with vibrant colors - return availableThemes.filter(theme => - !theme.isEditable && - (theme.id === 'neon' || - theme.id === 'retro' || - theme.id === 'sunset' || - theme.id === 'amber') + return availableThemes.filter(theme => + !theme.isEditable && + (theme.id === 'neon' || + theme.id === 'retro' || + theme.id === 'sunset' || + theme.id === 'amber') ); case 'custom': // User's custom themes @@ -398,18 +413,18 @@ const ThemeScreen: React.FC = () => { }, []); const handleDeleteTheme = useCallback((theme: Theme) => { - setAlertTitle('Delete Theme'); - setAlertMessage(`Are you sure you want to delete "${theme.name}"?`); + setAlertTitle(t('theme.alerts.delete_title')); + setAlertMessage(t('theme.alerts.delete_msg', { name: theme.name })); setAlertActions([ - { label: 'Cancel', style: { color: '#888' }, onPress: () => {} }, + { label: t('theme.alerts.cancel'), style: { color: '#888' }, onPress: () => { } }, { - label: 'Delete', + label: t('theme.alerts.delete'), style: { color: currentTheme.colors.error }, onPress: () => deleteCustomTheme(theme.id), }, ]); setAlertVisible(true); - }, [deleteCustomTheme, currentTheme.colors.error]); + }, [deleteCustomTheme, currentTheme.colors.error, t]); const handleCreateTheme = useCallback(() => { setEditingTheme(null); @@ -432,7 +447,7 @@ const ThemeScreen: React.FC = () => { } else { // Create new theme addCustomTheme({ - name: themeData.name || 'Custom Theme', + name: themeData.name || t('theme.create_custom'), colors: { ...currentTheme.colors, primary: themeData.primary, @@ -441,7 +456,7 @@ const ThemeScreen: React.FC = () => { } }); } - + setIsEditMode(false); setEditingTheme(null); }, [editingTheme, updateCustomTheme, addCustomTheme, currentTheme]); @@ -467,9 +482,9 @@ const ThemeScreen: React.FC = () => { const ThemeColorEditorWithAlert = (props: any) => { const handleSave = (themeName: string, themeColors: any, onSave: any) => { if (!themeName.trim()) { - setAlertTitle('Invalid Name'); - setAlertMessage('Please enter a valid theme name'); - setAlertActions([{ label: 'OK', onPress: () => {} }]); + setAlertTitle(t('theme.editor.invalid_name_title')); + setAlertMessage(t('theme.editor.invalid_name_msg')); + setAlertActions([{ label: 'OK', onPress: () => { } }]); setAlertVisible(true); return false; } @@ -503,7 +518,7 @@ const ThemeScreen: React.FC = () => { return ( @@ -529,31 +544,31 @@ const ThemeScreen: React.FC = () => { return ( - + - navigation.goBack()} > - Settings + {t('theme.alerts.back') || t('settings.app_settings_label').split(' ')[0] || 'Settings'} - + {/* Empty for now, but ready for future actions */} - + - App Themes + {t('theme.title')} - + {/* Category filter */} { contentContainerStyle={styles.filterList} /> - - - SELECT THEME + {t('theme.select_theme')} - + {filteredThemes.map(theme => ( { /> ))} - - - Create Custom Theme + {t('theme.create_custom')} - OPTIONS + {t('theme.options')} - Use Dominant Color from Artwork + {t('theme.use_dominant_color')} { actions={alertActions} onClose={() => setAlertVisible(false)} /> - + ); }; @@ -801,7 +816,7 @@ const styles = StyleSheet.create({ optionLabel: { fontSize: 14, }, - + // Editor styles editorContainer: { flex: 1, @@ -977,7 +992,7 @@ const styles = StyleSheet.create({ padding: 8, marginBottom: 10, }, - + // Legacy styles - keep for backward compatibility editorTitle: { fontSize: 18,