Merge pull request #1322 from foXaCe/feat/extract-hardcoded-strings-pass6

i18n(fr): full FR parity + extract residual hardcoded strings (pass 6)
This commit is contained in:
Nayif 2026-06-15 00:02:56 +05:30 committed by GitHub
commit aaa3ab9f64
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 210 additions and 18 deletions

View file

@ -2,6 +2,7 @@ package com.nuvio.app.features.p2p
import android.content.Context
import android.util.Log
import com.nuvio.app.core.i18n.localizedP2pUnknownTorrentError
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@ -93,7 +94,7 @@ actual object P2pStreamingEngine {
throw e
} catch (e: Exception) {
if (isCurrentGeneration(generation)) {
_state.value = P2pStreamingState.Error(e.message ?: "Unknown torrent error")
_state.value = P2pStreamingState.Error(e.message ?: localizedP2pUnknownTorrentError())
}
throw e
}

View file

@ -320,8 +320,11 @@
<string name="compose_nav_search">Rechercher</string>
<string name="compose_player_audio_tracks">Pistes audio</string>
<string name="compose_player_audio">Audio</string>
<string name="compose_player_auto_sync">Synchro auto</string>
<string name="compose_player_bold">Gras</string>
<string name="compose_player_built_in">Intégré</string>
<string name="compose_player_bottom_offset">Décalage inférieur</string>
<string name="compose_player_capture_line">Capturer</string>
<string name="compose_player_close">Fermer le lecteur</string>
<string name="compose_player_color">Couleur</string>
<string name="compose_player_currently_playing">En cours de lecture</string>
@ -332,11 +335,15 @@
<string name="compose_player_font_size">Taille de police</string>
<string name="compose_player_font_size_value">%1$dsp</string>
<string name="compose_player_lock_controls">Verrouiller les contrôles du lecteur</string>
<string name="compose_player_loading_lines">Chargement des lignes de sous-titres…</string>
<string name="compose_player_no_subtitle_lines_found">Aucune ligne de sous-titres trouvée</string>
<string name="compose_player_subtitle_lines_load_error">Impossible de charger les lignes de sous-titres</string>
<string name="compose_player_no_audio_tracks_available">Aucune piste audio disponible</string>
<string name="compose_player_no_episodes_available">Aucun épisode disponible</string>
<string name="compose_player_no_streams_found">Aucun stream trouvé</string>
<string name="compose_player_none">Aucun</string>
<string name="compose_player_outline">Contour</string>
<string name="compose_player_outline_color">Couleur du contour</string>
<string name="compose_player_panel_episodes">Épisodes</string>
<string name="compose_player_panel_sources">Sources</string>
<string name="compose_player_panel_streams">Streams</string>
@ -344,6 +351,8 @@
<string name="compose_player_playing">Lecture en cours</string>
<string name="compose_player_fetch_subtitles">Appuie pour chercher des sous-titres</string>
<string name="compose_player_go_back">Retour</string>
<string name="compose_player_reload">Recharger</string>
<string name="compose_player_reset">Réinitialiser</string>
<string name="compose_player_reset_defaults">Rétablir les valeurs par défaut</string>
<string name="compose_player_resize_fill">Remplir</string>
<string name="compose_player_resize_fit">Ajuster</string>
@ -356,8 +365,11 @@
<string name="compose_player_seek_forward_10">Avancer de 10 secondes</string>
<string name="compose_player_sources">Sources</string>
<string name="compose_player_style">Style</string>
<string name="compose_player_select_addon_subtitle_first">Sélectionne dabord un sous-titre daddon</string>
<string name="compose_player_subs">Sous-titres</string>
<string name="compose_player_subtitle_delay">Délai des sous-titres</string>
<string name="compose_player_subtitles">Sous-titres</string>
<string name="compose_player_text_opacity">Opacité du texte</string>
<string name="compose_player_brightness_level">Luminosité %1$s</string>
<string name="compose_player_volume_level">Volume %1$s</string>
<string name="compose_player_muted">Muet</string>
@ -386,6 +398,7 @@
<string name="compose_settings_category_general">Général</string>
<string name="compose_settings_page_account">Compte</string>
<string name="compose_settings_page_addons">Addons</string>
<string name="compose_settings_page_advanced">Avancé</string>
<string name="compose_settings_page_appearance">Apparence</string>
<string name="compose_settings_page_content_discovery">Contenu et découverte</string>
<string name="compose_settings_page_continue_watching">Continuer à regarder</string>
@ -400,12 +413,15 @@
<string name="compose_settings_page_plugins">Plugins</string>
<string name="compose_settings_page_poster_customization">Personnalisation des affiches</string>
<string name="compose_settings_page_root">Paramètres</string>
<string name="compose_settings_page_streams">Streams</string>
<string name="compose_settings_page_supporters_contributors">Supporters et contributeurs</string>
<string name="compose_settings_page_tmdb_enrichment">Enrichissement TMDB</string>
<string name="compose_settings_page_trakt">Trakt</string>
<string name="compose_settings_root_about_section">À PROPOS</string>
<string name="compose_settings_root_account_description">Gère ton compte, déconnecte-toi ou supprime-le.</string>
<string name="compose_settings_root_account_section">COMPTE</string>
<string name="compose_settings_root_advanced_description">Comportement au démarrage et des profils.</string>
<string name="compose_settings_root_advanced_section">AVANCÉ</string>
<string name="compose_settings_root_appearance_description">Ajuste la présentation de laccueil et les préférences visuelles.</string>
<string name="compose_settings_root_check_updates_description">Rechercher de nouvelles versions de lapplication.</string>
<string name="compose_settings_root_check_updates_title">Vérifier les mises à jour</string>
@ -415,12 +431,20 @@
<string name="compose_settings_root_general_section">GÉNÉRAL</string>
<string name="compose_settings_root_integrations_description">Connecte les services TMDB et MDBList.</string>
<string name="compose_settings_root_notifications_description">Gère les alertes de sortie dépisodes et envoie une notification de test.</string>
<string name="compose_settings_root_streams_description">Affichage des résultats de streams et règles dURL de badges.</string>
<string name="compose_settings_root_switch_profile_description">Basculer vers un profil différent.</string>
<string name="compose_settings_root_switch_profile_title">Changer de profil</string>
<string name="compose_settings_root_trakt_description">Connecte Trakt, synchronise des listes et enregistre des titres directement dans Trakt.</string>
<string name="settings_search_empty">Aucun paramètre trouvé.</string>
<string name="settings_search_placeholder">Rechercher dans les paramètres…</string>
<string name="settings_search_results_section">RÉSULTATS</string>
<string name="settings_advanced_section_startup">DÉMARRAGE</string>
<string name="settings_advanced_section_cache">CACHE</string>
<string name="settings_advanced_remember_last_profile">Mémoriser le dernier profil</string>
<string name="settings_advanced_remember_last_profile_description">Mémorise le dernier profil sélectionné au démarrage</string>
<string name="settings_advanced_clear_cw_cache">Vider le cache de Continuer à regarder</string>
<string name="settings_advanced_clear_cw_cache_subtitle">Supprime les miniatures, titres et données denrichissement en cache pour Continuer à regarder</string>
<string name="settings_advanced_clear_cw_cache_done">Cache vidé</string>
<string name="settings_licenses_attributions_section_app">LICENCE DE LAPP</string>
<string name="settings_licenses_attributions_section_data">DONNÉES ET SERVICES</string>
<string name="settings_licenses_attributions_section_playback">LICENCE DE LECTURE</string>
@ -584,6 +608,8 @@
<string name="settings_continue_watching_section_visibility">VISIBILITÉ</string>
<string name="settings_continue_watching_show_description">Afficher le bandeau Continuer à regarder sur lécran daccueil.</string>
<string name="settings_continue_watching_show_title">Afficher Continuer à regarder</string>
<string name="settings_continue_watching_style_card">Carte</string>
<string name="settings_continue_watching_style_card_description">Carte paysage façon TV</string>
<string name="settings_continue_watching_style_poster">Affiche</string>
<string name="settings_continue_watching_style_poster_description">Carte daffiche centrée sur la couverture</string>
<string name="settings_continue_watching_style_wide">Large</string>
@ -646,6 +672,38 @@
<string name="settings_debrid_description_template_description">Contrôle les métadonnées affichées sous chaque résultat.</string>
<string name="settings_debrid_formatter_reset_title">Réinitialiser la mise en forme</string>
<string name="settings_debrid_formatter_reset_subtitle">Restaurer la mise en forme par défaut des résultats.</string>
<string name="settings_stream_badges_section">Style Fusion</string>
<string name="settings_stream_size_badges_title">Badges de taille</string>
<string name="settings_stream_size_badges_description">Affiche les badges de taille de fichier dans les résultats de flux et les panneaux de source du lecteur.</string>
<string name="settings_stream_addon_logo_title">Logo de laddon</string>
<string name="settings_stream_addon_logo_description">Affiche le logo et le nom de laddon à côté des sources de streams.</string>
<string name="settings_stream_display_section">Affichage</string>
<string name="settings_stream_badge_position_title">Position des badges</string>
<string name="settings_stream_badge_position_description">Choisis si les badges Fusion et de taille apparaissent au-dessus ou en dessous des cartes de flux.</string>
<string name="settings_stream_badge_position_dialog_title">Position des badges</string>
<string name="settings_stream_badge_position_dialog_description">Choisis où les badges de flux apparaissent sur les cartes de flux.</string>
<string name="settings_stream_badge_position_top">En haut</string>
<string name="settings_stream_badge_position_bottom">En bas</string>
<string name="settings_stream_badge_urls_title">URL de badges Fusion</string>
<string name="settings_stream_badge_urls_description">Importe jusquà %1$d URL JSON de badges de flux de style Fusion. Chaque URL peut être mise à jour ou supprimée séparément.</string>
<string name="settings_stream_badge_urls_search_description">Gère les URL JSON de badges de flux de style Fusion importées.</string>
<string name="settings_stream_badge_import_failed">Échec de limport du badge.</string>
<string name="settings_stream_badge_enter_url">Saisis une URL JSON de badge.</string>
<string name="settings_stream_badge_url_scheme_invalid">LURL du badge doit commencer par http:// ou https://.</string>
<string name="settings_stream_badge_import_limit">Tu peux importer jusquà %1$d URL de badges.</string>
<string name="settings_fusion_badges_summary">%1$d/%2$d URL, %3$d badges Fusion actifs</string>
<string name="settings_fusion_badges_empty">Aucune URL de badge Fusion importée.</string>
<string name="settings_fusion_badge_url_label">URL JSON du badge Fusion</string>
<string name="settings_fusion_badge_urls_imported">%1$d/%2$d URL Fusion importées</string>
<string name="settings_fusion_badge_url_active">Active</string>
<string name="settings_fusion_badge_url_inactive">Inactive</string>
<string name="settings_fusion_badge_url_status_summary">%1$s · %2$d badges activés · %3$d groupes</string>
<string name="settings_fusion_badge_preview_action">Aperçu</string>
<string name="settings_fusion_badge_preview_title">Aperçu du badge Fusion</string>
<string name="settings_fusion_badge_preview_count">%1$d badges de style Fusion depuis cette URL</string>
<string name="settings_fusion_badge_preview_empty">Aucune image de badge de style Fusion dans cette URL.</string>
<string name="settings_fusion_badge_group_title">Groupe %1$d</string>
<string name="settings_fusion_badge_other_group_title">Autres badges Fusion</string>
<string name="settings_debrid_key_valid">Clé API validée.</string>
<string name="settings_debrid_key_invalid">Impossible de valider cette clé API.</string>
<string name="settings_mdb_add_api_key_first">Ajoute ta clé API MDBList ci-dessous avant dactiver les notes.</string>
@ -669,6 +727,8 @@
<string name="settings_meta_comments_description">Section de commentaires Trakt.</string>
<string name="settings_meta_details">Détails</string>
<string name="settings_meta_details_description">Durée, statut, date de sortie, langue et informations associées.</string>
<string name="settings_meta_hero_trailer_playback">Lecture des bandes-annonces dans le Hero</string>
<string name="settings_meta_hero_trailer_playback_description">Lit un aperçu de la bande-annonce dans le Hero des métadonnées quand une bande-annonce est disponible.</string>
<string name="settings_meta_episode_cards">Cartes dépisodes</string>
<string name="settings_meta_episode_cards_description">Choisis comment les épisodes sont affichés sur lécran de métadonnées.</string>
<string name="settings_meta_episode_style_horizontal">Horizontal</string>
@ -768,10 +828,18 @@
<string name="settings_playback_external_player_app">App de lecteur externe</string>
<string name="settings_playback_external_player_description_android">Ouvre les nouvelles lectures avec lapp vidéo par défaut dAndroid ou le sélecteur système.</string>
<string name="settings_playback_external_player_description_ios">Ouvre les nouvelles lectures avec le lecteur installé sélectionné.</string>
<string name="settings_playback_external_player_forward_subtitles">Transmettre les sous-titres au lecteur externe</string>
<string name="settings_playback_external_player_forward_subtitles_description">Récupère les sous-titres daddons dans ta langue préférée et les transmet au lecteur externe.</string>
<string name="settings_playback_external_player_none_available">Aucun lecteur externe compatible nest installé</string>
<string name="settings_playback_player_preference">Lecteur</string>
<string name="settings_playback_player_preference_internal">Interne</string>
<string name="settings_playback_player_preference_external">Externe</string>
<string name="settings_playback_player_preference_description">Choisis quel lecteur gère les nouvelles lectures.</string>
<string name="settings_playback_hold_speed">Vitesse au maintien</string>
<string name="settings_playback_hold_to_speed">Maintenir pour accélérer</string>
<string name="settings_playback_hold_to_speed_description">Garde le doigt appuyé nimporte où sur le lecteur pour accélérer temporairement.</string>
<string name="settings_playback_touch_gestures">Gestes tactiles</string>
<string name="settings_playback_touch_gestures_description">Autorise les balayages et les doubles appuis sur le lecteur pour avancer ou reculer, ajuster la luminosité ou le volume.</string>
<string name="settings_playback_invalid_regex_pattern">Modèle regex invalide</string>
<string name="settings_playback_last_link_cache_duration">Durée du cache du dernier lien</string>
<string name="settings_playback_map_dv7_to_hevc">Mapper DV7 vers HEVC</string>
@ -785,6 +853,13 @@
<string name="settings_playback_option_device_language">Langue de lappareil</string>
<string name="settings_playback_option_forced">Forcé</string>
<string name="settings_playback_option_none">Aucun</string>
<string name="settings_playback_addon_subtitle_startup_all">Tous les sous-titres</string>
<string name="settings_playback_addon_subtitle_startup_all_description">Récupère et affiche tous les sous-titres daddons pour la vidéo.</string>
<string name="settings_playback_addon_subtitle_startup_fast">Démarrage rapide</string>
<string name="settings_playback_addon_subtitle_startup_fast_description">Ignore la récupération automatique des sous-titres daddons jusquà ce que tu la demandes dans le lecteur.</string>
<string name="settings_playback_addon_subtitle_startup_mode">Sous-titres daddons au démarrage</string>
<string name="settings_playback_addon_subtitle_startup_preferred">Préférés uniquement</string>
<string name="settings_playback_addon_subtitle_startup_preferred_description">Récupère les sous-titres daddons, mais naffiche que ceux dans tes langues préférées.</string>
<string name="settings_playback_prefer_binge_group">Préférer le groupe binge</string>
<string name="settings_playback_prefer_binge_group_description">Lors de la lecture automatique, préférer un stream du même groupe binge que le stream actuel.</string>
<string name="settings_playback_reuse_binge_group">Réutiliser le groupe de binge</string>
@ -829,6 +904,20 @@
<string name="settings_playback_selected_count">%1$d sélectionné(s)</string>
<string name="settings_playback_show_loading_overlay">Afficher la superposition de chargement</string>
<string name="settings_playback_show_loading_overlay_description">Afficher la superposition de chargement initiale pendant le démarrage dun stream.</string>
<string name="settings_playback_subtitle_background_color">Couleur darrière-plan</string>
<string name="settings_playback_subtitle_bold">Gras</string>
<string name="settings_playback_subtitle_bold_description">Utilise une graisse de police plus épaisse pour les sous-titres.</string>
<string name="settings_playback_subtitle_color_transparent">Transparent</string>
<string name="settings_playback_subtitle_outline">Contour</string>
<string name="settings_playback_subtitle_outline_color">Couleur du contour</string>
<string name="settings_playback_subtitle_outline_description">Dessine une bordure autour du texte des sous-titres.</string>
<string name="settings_playback_subtitle_show_preferred_only">Afficher uniquement les langues préférées</string>
<string name="settings_playback_subtitle_show_preferred_only_description">Naffiche que les sous-titres correspondant à tes langues de sous-titres préférées.</string>
<string name="settings_playback_subtitle_size">Taille des sous-titres</string>
<string name="settings_playback_subtitle_text_color">Couleur du texte</string>
<string name="settings_playback_subtitle_use_forced">Utiliser les sous-titres forcés</string>
<string name="settings_playback_subtitle_use_forced_description">Privilégie les sous-titres forcés selon tes préférences de langue de sous-titres.</string>
<string name="settings_playback_subtitle_vertical_offset">Décalage vertical</string>
<string name="settings_playback_skip_intro_outro_recap">Passer lintro/outro/récap</string>
<string name="settings_playback_skip_intro_outro_recap_description">Afficher un bouton de saut lors des segments dintro, doutro et de récapitulatif détectés.</string>
<string name="settings_playback_source_scope">Périmètre des sources</string>
@ -928,6 +1017,12 @@
<string name="trakt_watch_progress_source_nuvio">Nuvio Sync</string>
<string name="trakt_watch_progress_trakt_selected">Source de progression réglée sur Trakt</string>
<string name="trakt_watch_progress_nuvio_selected">Source de progression réglée sur Nuvio Sync</string>
<string name="trakt_more_like_this_source_title">Source de la section Similaires</string>
<string name="trakt_more_like_this_source_subtitle">Choisis la provenance des recommandations sur les pages de détails</string>
<string name="trakt_more_like_this_source_dialog_title">Source de la section Similaires</string>
<string name="trakt_more_like_this_source_dialog_subtitle">Sélectionne la source des recommandations affichées sur les pages de détails.</string>
<string name="trakt_more_like_this_source_trakt">Trakt</string>
<string name="trakt_more_like_this_source_tmdb">TMDB</string>
<string name="trakt_continue_watching_window">Fenêtre de Continuer à regarder</string>
<string name="trakt_continue_watching_subtitle">Historique Trakt pris en compte pour Continuer à regarder</string>
<string name="trakt_cw_window_title">Fenêtre de Continuer à regarder</string>
@ -1049,10 +1144,13 @@
<string name="app_exit_title">Quitter lapplication</string>
<string name="catalog_empty_message">Ce catalogue na renvoyé aucun élément.</string>
<string name="catalog_empty_title">Aucun titre trouvé</string>
<string name="details_actions_menu_label">Plus dactions</string>
<string name="details_check_connection">Vérifie ta connexion WiFi ou données mobiles et réessaie.</string>
<string name="details_director">Réalisateur</string>
<string name="details_failed_to_load">Échec du chargement</string>
<string name="details_more_like_this">À voir aussi</string>
<string name="detail_more_like_this_powered_by_tmdb">Données fournies par TMDB</string>
<string name="detail_more_like_this_powered_by_trakt">Données fournies par Trakt</string>
<string name="details_seasons">Saisons</string>
<string name="details_series_missing_numbers">Cet addon a renvoyé des vidéos pour la série, mais aucune nincluait de numéros de saison ou dépisode.</string>
<string name="details_series_no_metadata">Cet addon na fourni aucune métadonnée dépisode pour cette série.</string>
@ -1084,6 +1182,8 @@
<string name="episode_mark_watched">Marquer comme vu</string>
<string name="home_continue_watching_up_next">À suivre</string>
<string name="home_continue_watching_watched">%1$s vu</string>
<string name="home_continue_watching_hours_minutes_left">%1$d h %2$d min restantes</string>
<string name="home_continue_watching_minutes_left">%1$d min restantes</string>
<string name="home_empty_no_active_addons_message">Installe et valide au moins un addon avant de charger des lignes de catalogue à laccueil.</string>
<string name="home_empty_no_rows_message">Les addons installés nexposent actuellement aucun catalogue compatible avec le tableau sans extras requis.</string>
<string name="home_empty_no_rows_title">Aucune ligne daccueil disponible</string>
@ -1179,6 +1279,7 @@
<string name="streams_episode_title_with_name">S%1$dE%2$d - %3$s</string>
<string name="streams_fetching">Récupération…</string>
<string name="streams_finding_source">Recherche de la source…</string>
<string name="streams_loading_subtitles">Chargement des sous-titres depuis les addons…</string>
<string name="streams_finding_streams">Recherche des streams…</string>
<string name="streams_link_copied">Lien du stream copié</string>
<string name="streams_no_direct_link">Aucun lien direct du stream disponible</string>
@ -1283,6 +1384,16 @@
<string name="trakt_sign_in_complete_failed">Impossible de terminer la connexion Trakt</string>
<string name="trakt_user_fallback">Utilisateur Trakt</string>
<string name="trakt_watchlist">Liste de suivi</string>
<!-- Trakt library sync errors -->
<string name="trakt_error_authorization_expired">Autorisation Trakt expirée</string>
<string name="trakt_error_list_not_found">Liste Trakt introuvable</string>
<string name="trakt_error_list_limit_reached">Limite de listes Trakt atteinte</string>
<string name="trakt_error_rate_limit_reached">Limite de requêtes Trakt atteinte</string>
<string name="trakt_error_request_failed">Échec de la requête Trakt</string>
<string name="trakt_error_add_watchlist_failed">Échec de lajout à ta liste de suivi Trakt</string>
<string name="trakt_error_add_list_failed">Échec de lajout à la liste Trakt</string>
<string name="trakt_error_missing_ids">Identifiants Trakt compatibles manquants</string>
<string name="trakt_error_empty_response">Réponse vide du serveur</string>
<string name="tmdb_sources_api_key_required">Ajoute une clé API TMDB dans les paramètres pour utiliser les sources TMDB.</string>
<string name="tmdb_sources_collection_fallback_title">Collection TMDB %1$d</string>
<string name="tmdb_sources_collection_not_found">Collection TMDB introuvable</string>
@ -1483,6 +1594,11 @@
<string name="settings_playback_ios_display_color_hint_desc">Permettre à mpv de cibler par défaut lespace colorimétrique de lécran actif.</string>
<string name="settings_playback_ios_target_primaries">Primaires cibles</string>
<string name="settings_playback_ios_target_transfer">Transfert cible</string>
<string name="settings_playback_ios_audio_output_section">Sortie audio iOS</string>
<string name="settings_playback_ios_audio_output">Sortie audio</string>
<string name="settings_playback_ios_audio_output_auto_desc">Essaie dabord AVFoundation, puis bascule sur AudioUnit.</string>
<string name="settings_playback_ios_audio_output_avfoundation_desc">Prise en charge expérimentale de laudio spatial et de la sortie multicanal.</string>
<string name="settings_playback_ios_audio_output_audiounit_desc">Utilise lancienne sortie AudioUnit.</string>
<!-- Debrid Result Management section -->
<string name="settings_debrid_section_result_management">Gestion des résultats</string>
<string name="settings_debrid_max_results">Nombre max de résultats</string>
@ -1498,6 +1614,7 @@
<!-- Debrid misc -->
<string name="settings_debrid_learn_more">En savoir plus</string>
<string name="settings_debrid_template_default_format">Format par défaut</string>
<string name="settings_debrid_template_original_format">Format dorigine</string>
<string name="settings_debrid_release_groups_hint">Saisis un groupe par ligne.</string>
<!-- Debrid sort profiles -->
<string name="settings_debrid_sort_original">Ordre original</string>
@ -1582,6 +1699,7 @@
<string name="submit_intro_capture">Capturer</string>
<!-- iOS advanced playback -->
<string name="settings_playback_ios_hw_decoder_dialog">Décodeur matériel</string>
<string name="settings_playback_ios_audio_output_dialog">Sortie audio</string>
<string name="settings_playback_ios_target_primaries_dialog">Primaires cibles</string>
<string name="settings_playback_ios_target_transfer_dialog">Transfert cible</string>
<!-- Collection editor -->
@ -1760,4 +1878,21 @@
<!-- Pass 4 — platform stubs and dynamic title fallbacks -->
<string name="collections_editor_trakt_list_title_format">Liste Trakt %1$s</string>
<string name="external_player_android_system">Lecteur système Android</string>
<string name="p2p_consent_title">Streaming P2P</string>
<string name="p2p_consent_body">Ce flux utilise la technologie pair-à-pair (P2P). En activant le P2P, tu reconnais et acceptes que :\n\n• Ton adresse IP sera visible par les autres pairs du réseau\n• Tu es seul responsable du contenu auquel tu accèdes\n• Tu confirmes avoir le droit légal de visionner ce contenu dans ta juridiction\n• Nuvio nhéberge, ne distribue ni ne contrôle aucun contenu P2P\n• Nuvio ne peut être tenu responsable des conséquences légales liées à ton utilisation du streaming P2P\n\nTu utilises cette fonctionnalité entièrement à tes propres risques. Le P2P peut être désactivé à tout moment dans les paramètres.</string>
<string name="p2p_consent_enable">Activer le P2P</string>
<string name="p2p_consent_cancel">Annuler</string>
<string name="p2p_error_unknown">Erreur torrent inconnue</string>
<string name="settings_p2p_title">Streaming P2P</string>
<string name="settings_p2p_subtitle">Autoriser les flux pair-à-pair (torrent)</string>
<string name="settings_p2p_hide_stats_title">Masquer les statistiques torrent</string>
<string name="settings_p2p_hide_stats_subtitle">Masquer la mémoire tampon, les seeders, les pairs et la vitesse de téléchargement pendant le chargement et la lecture</string>
<string name="player_torrent_peer_info">%1$d sources · %2$d pairs</string>
<string name="player_torrent_buffered_status">%1$s en mémoire tampon · %2$s · %3$s</string>
<string name="player_torrent_status">%1$s · %2$s</string>
<string name="player_torrent_stats">%1$d pairs · %2$d sources · %3$d %%</string>
<string name="player_torrent_connecting_peers">Connexion aux pairs…</string>
<string name="player_torrent_starting_engine">Démarrage du moteur P2P…</string>
<string name="player_error_failed_start_torrent">Impossible de démarrer le torrent : %1$s</string>
<string name="player_error_torrent">Erreur de torrent : %1$s</string>
</resources>

View file

@ -344,6 +344,8 @@
<string name="compose_player_font_size_value">%1$dsp</string>
<string name="compose_player_lock_controls">Lock player controls</string>
<string name="compose_player_loading_lines">Loading subtitle lines...</string>
<string name="compose_player_no_subtitle_lines_found">No subtitle lines found</string>
<string name="compose_player_subtitle_lines_load_error">Unable to load subtitle lines</string>
<string name="compose_player_no_audio_tracks_available">No audio tracks available</string>
<string name="compose_player_no_episodes_available">No episodes available</string>
<string name="compose_player_no_streams_found">No streams found</string>
@ -693,6 +695,10 @@
<string name="settings_stream_badge_urls_title">Fusion badge URLs</string>
<string name="settings_stream_badge_urls_description">Import up to %1$d Fusion-style stream badge JSON URLs. Each URL can be updated or deleted separately.</string>
<string name="settings_stream_badge_urls_search_description">Manage imported Fusion-style stream badge JSON URLs.</string>
<string name="settings_stream_badge_import_failed">Badge import failed.</string>
<string name="settings_stream_badge_enter_url">Enter a badge JSON URL.</string>
<string name="settings_stream_badge_url_scheme_invalid">Badge URL must start with http:// or https://.</string>
<string name="settings_stream_badge_import_limit">You can import up to %1$d badge URLs.</string>
<string name="settings_fusion_badges_summary">%1$d/%2$d URLs, %3$d active Fusion badges</string>
<string name="settings_fusion_badges_empty">No Fusion badge URLs imported.</string>
<string name="settings_fusion_badge_url_label">Fusion badge JSON URL</string>
@ -1146,6 +1152,7 @@
<string name="app_exit_title">Exit app</string>
<string name="catalog_empty_message">This catalog did not return any items.</string>
<string name="catalog_empty_title">No titles found</string>
<string name="details_actions_menu_label">More actions</string>
<string name="details_check_connection">Check your Wi-Fi or mobile data connection and try again.</string>
<string name="details_director">Director</string>
<string name="details_failed_to_load">Failed to load</string>
@ -1389,6 +1396,16 @@
<string name="trakt_sign_in_complete_failed">Failed to complete Trakt sign in</string>
<string name="trakt_user_fallback">Trakt user</string>
<string name="trakt_watchlist">Watchlist</string>
<!-- Trakt library sync errors -->
<string name="trakt_error_authorization_expired">Trakt authorization expired</string>
<string name="trakt_error_list_not_found">Trakt list not found</string>
<string name="trakt_error_list_limit_reached">Trakt list limit reached</string>
<string name="trakt_error_rate_limit_reached">Trakt rate limit reached</string>
<string name="trakt_error_request_failed">Trakt request failed</string>
<string name="trakt_error_add_watchlist_failed">Failed to add to Trakt watchlist</string>
<string name="trakt_error_add_list_failed">Failed to add to Trakt list</string>
<string name="trakt_error_missing_ids">Missing compatible Trakt IDs</string>
<string name="trakt_error_empty_response">Empty response body</string>
<string name="tmdb_sources_api_key_required">Add a TMDB API key in Settings to use TMDB sources.</string>
<string name="tmdb_sources_collection_fallback_title">TMDB Collection %1$d</string>
<string name="tmdb_sources_collection_not_found">TMDB collection not found</string>
@ -1862,6 +1879,7 @@
<string name="p2p_consent_body">This stream uses peer-to-peer (P2P) technology. By enabling P2P, you acknowledge and agree that:\n\n• Your IP address will be visible to other peers in the network\n• You are solely responsible for the content you access\n• You confirm you have the legal right to stream this content in your jurisdiction\n• Nuvio does not host, distribute, or control any P2P content\n• Nuvio bears no liability for any legal consequences arising from your use of P2P streaming\n\nYou use this feature entirely at your own risk. P2P can be disabled anytime in Settings.</string>
<string name="p2p_consent_enable">Enable P2P</string>
<string name="p2p_consent_cancel">Cancel</string>
<string name="p2p_error_unknown">Unknown torrent error</string>
<string name="settings_p2p_title">P2P Streaming</string>
<string name="settings_p2p_subtitle">Allow peer-to-peer (torrent) streams</string>
<string name="settings_p2p_hide_stats_title">Hide torrent stats</string>

View file

@ -8,6 +8,8 @@ import nuvio.composeapp.generated.resources.action_resume
import nuvio.composeapp.generated.resources.action_resume_episode
import nuvio.composeapp.generated.resources.compose_player_episode_code_episode_only
import nuvio.composeapp.generated.resources.compose_player_episode_code_full
import nuvio.composeapp.generated.resources.compose_player_no_subtitle_lines_found
import nuvio.composeapp.generated.resources.compose_player_subtitle_lines_load_error
import nuvio.composeapp.generated.resources.continue_watching_up_next
import nuvio.composeapp.generated.resources.continue_watching_up_next_episode
import nuvio.composeapp.generated.resources.date_month_april
@ -40,6 +42,11 @@ import nuvio.composeapp.generated.resources.media_movie
import nuvio.composeapp.generated.resources.media_movies
import nuvio.composeapp.generated.resources.media_series
import nuvio.composeapp.generated.resources.media_tv
import nuvio.composeapp.generated.resources.p2p_error_unknown
import nuvio.composeapp.generated.resources.settings_stream_badge_enter_url
import nuvio.composeapp.generated.resources.settings_stream_badge_import_failed
import nuvio.composeapp.generated.resources.settings_stream_badge_import_limit
import nuvio.composeapp.generated.resources.settings_stream_badge_url_scheme_invalid
import nuvio.composeapp.generated.resources.unit_bytes_b
import nuvio.composeapp.generated.resources.unit_bytes_gb
import nuvio.composeapp.generated.resources.unit_bytes_kb
@ -134,6 +141,27 @@ fun localizedShortMonthName(month: Int): String =
else -> month.toString()
}
fun localizedNoSubtitleLinesFound(): String =
resourceString("No subtitle lines found") { getString(Res.string.compose_player_no_subtitle_lines_found) }
fun localizedSubtitleLinesLoadError(): String =
resourceString("Unable to load subtitle lines") { getString(Res.string.compose_player_subtitle_lines_load_error) }
fun localizedBadgeImportFailed(): String =
resourceString("Badge import failed.") { getString(Res.string.settings_stream_badge_import_failed) }
fun localizedBadgeEnterUrl(): String =
resourceString("Enter a badge JSON URL.") { getString(Res.string.settings_stream_badge_enter_url) }
fun localizedBadgeUrlSchemeInvalid(): String =
resourceString("Badge URL must start with http:// or https://.") { getString(Res.string.settings_stream_badge_url_scheme_invalid) }
fun localizedBadgeImportLimit(limit: Int): String =
resourceString("You can import up to $limit badge URLs.") { getString(Res.string.settings_stream_badge_import_limit, limit) }
fun localizedP2pUnknownTorrentError(): String =
resourceString("Unknown torrent error") { getString(Res.string.p2p_error_unknown) }
fun localizedByteUnit(unit: String): String =
when (unit) {
"GB" -> resourceString("GB") { getString(Res.string.unit_bytes_gb) }

View file

@ -58,7 +58,7 @@ fun DetailActionButtons(
modifier: Modifier = Modifier,
playLabel: String = stringResource(Res.string.action_play),
secondaryActions: List<DetailSecondaryAction> = emptyList(),
actionsMenuLabel: String = "More actions",
actionsMenuLabel: String = stringResource(Res.string.details_actions_menu_label),
isTablet: Boolean = false,
onPlayClick: () -> Unit = {},
onPlayLongClick: (() -> Unit)? = null,

View file

@ -1,5 +1,7 @@
package com.nuvio.app.features.player
import com.nuvio.app.core.i18n.localizedNoSubtitleLinesFound
import com.nuvio.app.core.i18n.localizedSubtitleLinesLoadError
import com.nuvio.app.features.addons.httpGetTextWithHeaders
import kotlinx.coroutines.launch
@ -33,13 +35,13 @@ internal fun PlayerScreenRuntime.loadSubtitleAutoSyncCues(force: Boolean = false
subtitleAutoSyncState = subtitleAutoSyncState.copy(
cues = cues,
isLoading = false,
errorMessage = if (cues.isEmpty()) "No subtitle lines found" else null,
errorMessage = if (cues.isEmpty()) localizedNoSubtitleLinesFound() else null,
)
},
onFailure = { error ->
subtitleAutoSyncState = subtitleAutoSyncState.copy(
isLoading = false,
errorMessage = error.message ?: "Unable to load subtitle lines",
errorMessage = error.message ?: localizedSubtitleLinesLoadError(),
)
},
)

View file

@ -1,5 +1,9 @@
package com.nuvio.app.features.streams
import com.nuvio.app.core.i18n.localizedBadgeEnterUrl
import com.nuvio.app.core.i18n.localizedBadgeImportFailed
import com.nuvio.app.core.i18n.localizedBadgeImportLimit
import com.nuvio.app.core.i18n.localizedBadgeUrlSchemeInvalid
import com.nuvio.app.features.addons.httpGetText
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.flow.MutableStateFlow
@ -77,12 +81,12 @@ object StreamBadgeSettingsRepository {
ensureLoaded()
val normalizedUrl = url.trim()
if (normalizedUrl.isBlank()) {
return StreamBadgeImportResult.Error("Enter a badge JSON URL.")
return StreamBadgeImportResult.Error(localizedBadgeEnterUrl())
}
if (!normalizedUrl.startsWith("https://", ignoreCase = true) &&
!normalizedUrl.startsWith("http://", ignoreCase = true)
) {
return StreamBadgeImportResult.Error("Badge URL must start with http:// or https://.")
return StreamBadgeImportResult.Error(localizedBadgeUrlSchemeInvalid())
}
return try {
@ -91,7 +95,7 @@ object StreamBadgeSettingsRepository {
import.sourceUrl.equals(normalizedUrl, ignoreCase = true)
}
if (!isExistingImport && currentRules.imports.size >= STREAM_BADGE_IMPORT_LIMIT) {
return StreamBadgeImportResult.Error("You can import up to $STREAM_BADGE_IMPORT_LIMIT badge URLs.")
return StreamBadgeImportResult.Error(localizedBadgeImportLimit(STREAM_BADGE_IMPORT_LIMIT))
}
val payload = httpGetText(normalizedUrl)
val parsedImport = StreamBadgeRulesParser.parse(
@ -104,7 +108,7 @@ object StreamBadgeSettingsRepository {
StreamBadgeImportResult.Success(streamBadgeRules)
} catch (error: Exception) {
if (error is CancellationException) throw error
StreamBadgeImportResult.Error(error.message ?: "Badge import failed.")
StreamBadgeImportResult.Error(error.message ?: localizedBadgeImportFailed())
}
}

View file

@ -14,10 +14,12 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withLock
import nuvio.composeapp.generated.resources.*
import org.jetbrains.compose.resources.StringResource
import org.jetbrains.compose.resources.getString
import kotlinx.coroutines.sync.withPermit
import kotlinx.coroutines.selects.select
@ -612,10 +614,10 @@ object TraktLibraryRepository {
followRedirects = true,
)
if (response.status !in 200..299) {
throw IllegalStateException(errorMessageForStatus(response.status, "Trakt request failed"))
throw IllegalStateException(errorMessageForStatus(response.status, localizedString(Res.string.trakt_error_request_failed)))
}
if (response.body.isBlank()) {
throw IllegalStateException("Empty response body")
throw IllegalStateException(localizedString(Res.string.trakt_error_empty_response))
}
return response
}
@ -636,7 +638,7 @@ object TraktLibraryRepository {
followRedirects = true,
)
if (response.status !in 200..299) {
throw IllegalStateException(errorMessageForStatus(response.status, "Trakt request failed"))
throw IllegalStateException(errorMessageForStatus(response.status, localizedString(Res.string.trakt_error_request_failed)))
}
return response
}
@ -661,7 +663,7 @@ object TraktLibraryRepository {
headers = headers,
)
if (!isSuccessfulAddResponse(response.body)) {
throw IllegalStateException(errorMessageForStatus(response.status, "Failed to add to Trakt watchlist"))
throw IllegalStateException(errorMessageForStatus(response.status, localizedString(Res.string.trakt_error_add_watchlist_failed)))
}
}
@ -682,7 +684,7 @@ object TraktLibraryRepository {
headers = headers,
)
if (!isSuccessfulAddResponse(response.body)) {
throw IllegalStateException(errorMessageForStatus(response.status, "Failed to add to Trakt list"))
throw IllegalStateException(errorMessageForStatus(response.status, localizedString(Res.string.trakt_error_add_list_failed)))
}
}
@ -699,7 +701,7 @@ object TraktLibraryRepository {
val type = normalizeType(item.type)
val ids = resolveIds(item)
if (!ids.hasAnyId()) {
throw IllegalStateException("Missing compatible Trakt IDs")
throw IllegalStateException(localizedString(Res.string.trakt_error_missing_ids))
}
val request = if (type == "movie") {
@ -819,12 +821,14 @@ object TraktLibraryRepository {
return addCount > 0 || existingCount > 0
}
private fun localizedString(resource: StringResource): String = runBlocking { getString(resource) }
private fun errorMessageForStatus(status: Int, defaultMessage: String): String =
when (status) {
401, 403 -> "Trakt authorization expired"
404 -> "Trakt list not found"
420 -> "Trakt list limit reached"
429 -> "Trakt rate limit reached"
401, 403 -> localizedString(Res.string.trakt_error_authorization_expired)
404 -> localizedString(Res.string.trakt_error_list_not_found)
420 -> localizedString(Res.string.trakt_error_list_limit_reached)
429 -> localizedString(Res.string.trakt_error_rate_limit_reached)
else -> "$defaultMessage ($status)"
}