i18n(fr): extract remaining hardcoded strings to stringResource

- NetworkStatusRepository: titleForEmptyState/messageForEmptyState now @Composable, using existing keys (network_cannot_reach_servers, network_no_internet_connection, details_servers_unreachable, details_check_connection) + 2 new (network_connection_issue, network_please_check_connection)
- LibraryRepository: LOCAL_LIBRARY_LIST_TITLE moved to library_local_tab_title resource; toLibraryDisplayTitle() "Other" fallback uses library_other
- EpisodeReleaseNotificationsRepository: permission-disabled message uses settings_notifications_permission_disabled
- StreamsRepository.fallbackRepositoryLabel: uses streams_plugin_repository_fallback
- PluginRuntime: title fallback "Unknown" uses generic_unknown

4 new keys added (EN + FR with tutoiement, NBSP, apostrophes courbes).
Parity preserved: 1353 keys EN ↔ FR.
This commit is contained in:
Stéphane 2026-05-06 20:23:32 +02:00
parent 31d18869fc
commit af43edcf04
7 changed files with 45 additions and 14 deletions

View file

@ -1400,4 +1400,8 @@
<string name="settings_search_results_section">RÉSULTATS</string> <string name="settings_search_results_section">RÉSULTATS</string>
<string name="streams_open_external_player">Ouvrir dans un lecteur externe</string> <string name="streams_open_external_player">Ouvrir dans un lecteur externe</string>
<string name="streams_open_internal_player">Ouvrir dans le lecteur interne</string> <string name="streams_open_internal_player">Ouvrir dans le lecteur interne</string>
<string name="network_connection_issue">Problème de connexion</string>
<string name="network_please_check_connection">Vérifie ta connexion et réessaie.</string>
<string name="library_local_tab_title">Bibliothèque Nuvio</string>
<string name="streams_plugin_repository_fallback">Dépôt de plugin</string>
</resources> </resources>

View file

@ -1399,4 +1399,8 @@
<string name="submit_intro_button_submit">Submit</string> <string name="submit_intro_button_submit">Submit</string>
<string name="submit_intro_capture_button">Capture</string> <string name="submit_intro_capture_button">Capture</string>
<string name="settings_playback_introdb_invalid_key">Invalid API Key or connection failed</string> <string name="settings_playback_introdb_invalid_key">Invalid API Key or connection failed</string>
<string name="network_connection_issue">Connection issue</string>
<string name="network_please_check_connection">Please check your connection and try again.</string>
<string name="library_local_tab_title">Nuvio Library</string>
<string name="streams_plugin_repository_fallback">Plugin repository</string>
</resources> </resources>

View file

@ -1,5 +1,6 @@
package com.nuvio.app.core.network package com.nuvio.app.core.network
import androidx.compose.runtime.Composable
import com.nuvio.app.features.addons.httpRequestRaw import com.nuvio.app.features.addons.httpRequestRaw
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -9,6 +10,14 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withTimeoutOrNull import kotlinx.coroutines.withTimeoutOrNull
import nuvio.composeapp.generated.resources.Res
import nuvio.composeapp.generated.resources.details_check_connection
import nuvio.composeapp.generated.resources.details_servers_unreachable
import nuvio.composeapp.generated.resources.network_cannot_reach_servers
import nuvio.composeapp.generated.resources.network_connection_issue
import nuvio.composeapp.generated.resources.network_no_internet_connection
import nuvio.composeapp.generated.resources.network_please_check_connection
import org.jetbrains.compose.resources.stringResource
enum class NetworkCondition { enum class NetworkCondition {
Unknown, Unknown,
@ -28,18 +37,20 @@ data class NetworkStatusUiState(
get() = condition == NetworkCondition.NoInternet || condition == NetworkCondition.ServersUnreachable get() = condition == NetworkCondition.NoInternet || condition == NetworkCondition.ServersUnreachable
} }
@Composable
fun NetworkCondition.titleForEmptyState(): String = fun NetworkCondition.titleForEmptyState(): String =
when (this) { when (this) {
NetworkCondition.ServersUnreachable -> "Cannot reach servers" NetworkCondition.ServersUnreachable -> stringResource(Res.string.network_cannot_reach_servers)
NetworkCondition.NoInternet -> "No internet connection" NetworkCondition.NoInternet -> stringResource(Res.string.network_no_internet_connection)
else -> "Connection issue" else -> stringResource(Res.string.network_connection_issue)
} }
@Composable
fun NetworkCondition.messageForEmptyState(): String = fun NetworkCondition.messageForEmptyState(): String =
when (this) { when (this) {
NetworkCondition.ServersUnreachable -> "Your device is online, but Nuvio could not reach required servers." NetworkCondition.ServersUnreachable -> stringResource(Res.string.details_servers_unreachable)
NetworkCondition.NoInternet -> "Check your Wi-Fi or mobile data connection and try again." NetworkCondition.NoInternet -> stringResource(Res.string.details_check_connection)
else -> "Please check your connection and try again." else -> stringResource(Res.string.network_please_check_connection)
} }
object NetworkStatusRepository { object NetworkStatusRepository {

View file

@ -13,6 +13,11 @@ import com.nuvio.app.features.trakt.effectiveLibrarySourceMode as resolveEffecti
import com.nuvio.app.features.trakt.shouldUseTraktLibrary import com.nuvio.app.features.trakt.shouldUseTraktLibrary
import io.github.jan.supabase.postgrest.postgrest import io.github.jan.supabase.postgrest.postgrest
import io.github.jan.supabase.postgrest.rpc import io.github.jan.supabase.postgrest.rpc
import kotlinx.coroutines.runBlocking
import nuvio.composeapp.generated.resources.Res
import nuvio.composeapp.generated.resources.library_local_tab_title
import nuvio.composeapp.generated.resources.library_other
import org.jetbrains.compose.resources.getString
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
@ -405,12 +410,11 @@ object LibraryRepository {
} }
internal const val LOCAL_LIBRARY_LIST_KEY = "local" internal const val LOCAL_LIBRARY_LIST_KEY = "local"
internal const val LOCAL_LIBRARY_LIST_TITLE = "Nuvio Library"
internal fun localLibraryListTab(): TraktListTab = internal fun localLibraryListTab(): TraktListTab =
TraktListTab( TraktListTab(
key = LOCAL_LIBRARY_LIST_KEY, key = LOCAL_LIBRARY_LIST_KEY,
title = LOCAL_LIBRARY_LIST_TITLE, title = runBlocking { getString(Res.string.library_local_tab_title) },
type = TraktListType.WATCHLIST, type = TraktListType.WATCHLIST,
) )
@ -461,7 +465,7 @@ private fun LibraryItem.toSyncItem(): LibrarySyncItem = LibrarySyncItem(
internal fun String.toLibraryDisplayTitle(): String { internal fun String.toLibraryDisplayTitle(): String {
val normalized = trim() val normalized = trim()
if (normalized.isBlank()) return "Other" if (normalized.isBlank()) return runBlocking { getString(Res.string.library_other) }
return normalized return normalized
.split('-', '_', ' ') .split('-', '_', ' ')
@ -469,5 +473,5 @@ internal fun String.toLibraryDisplayTitle(): String {
.joinToString(" ") { token -> .joinToString(" ") { token ->
token.lowercase().replaceFirstChar { char -> char.uppercase() } token.lowercase().replaceFirstChar { char -> char.uppercase() }
} }
.ifBlank { "Other" } .ifBlank { runBlocking { getString(Res.string.library_other) } }
} }

View file

@ -21,6 +21,7 @@ import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.sync.Semaphore import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.sync.withPermit import kotlinx.coroutines.sync.withPermit
@ -294,7 +295,7 @@ object EpisodeReleaseNotificationsRepository {
permissionGranted = granted, permissionGranted = granted,
testTargetTitle = currentTestTarget()?.name, testTargetTitle = currentTestTarget()?.name,
errorMessage = when { errorMessage = when {
_uiState.value.isEnabled && !granted -> "System notifications are currently disabled for Nuvio." _uiState.value.isEnabled && !granted -> runBlocking { getString(Res.string.settings_notifications_permission_disabled) }
else -> _uiState.value.errorMessage else -> _uiState.value.errorMessage
}, },
) )
@ -362,7 +363,7 @@ object EpisodeReleaseNotificationsRepository {
scheduledCount = 0, scheduledCount = 0,
testTargetTitle = currentTestTarget()?.name, testTargetTitle = currentTestTarget()?.name,
errorMessage = if (_uiState.value.isEnabled && !permissionGranted) { errorMessage = if (_uiState.value.isEnabled && !permissionGranted) {
"System notifications are currently disabled for Nuvio." runBlocking { getString(Res.string.settings_notifications_permission_disabled) }
} else { } else {
null null
}, },

View file

@ -23,6 +23,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.runBlocking
import nuvio.composeapp.generated.resources.* import nuvio.composeapp.generated.resources.*
import org.jetbrains.compose.resources.getString import org.jetbrains.compose.resources.getString
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -596,6 +597,8 @@ private fun String.fallbackRepositoryLabel(): String {
val withoutManifest = withoutQuery.removeSuffix("/manifest.json") val withoutManifest = withoutQuery.removeSuffix("/manifest.json")
val host = withoutManifest.substringAfter("://", withoutManifest).substringBefore('/') val host = withoutManifest.substringAfter("://", withoutManifest).substringBefore('/')
return host.ifBlank { return host.ifBlank {
withoutManifest.substringAfterLast('/').ifBlank { "Plugin repository" } withoutManifest.substringAfterLast('/').ifBlank {
runBlocking { getString(Res.string.streams_plugin_repository_fallback) }
}
} }
} }

View file

@ -22,6 +22,10 @@ import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.contentOrNull import kotlinx.serialization.json.contentOrNull
import kotlinx.serialization.json.intOrNull import kotlinx.serialization.json.intOrNull
import kotlinx.serialization.json.jsonPrimitive import kotlinx.serialization.json.jsonPrimitive
import kotlinx.coroutines.runBlocking
import nuvio.composeapp.generated.resources.Res
import nuvio.composeapp.generated.resources.generic_unknown
import org.jetbrains.compose.resources.getString
import kotlin.random.Random import kotlin.random.Random
private const val PLUGIN_TIMEOUT_MS = 60_000L private const val PLUGIN_TIMEOUT_MS = 60_000L
@ -438,7 +442,7 @@ internal object PluginRuntime {
?.takeIf { it.isNotEmpty() } ?.takeIf { it.isNotEmpty() }
PluginRuntimeResult( PluginRuntimeResult(
title = item.stringOrNull("title") ?: item.stringOrNull("name") ?: "Unknown", title = item.stringOrNull("title") ?: item.stringOrNull("name") ?: runBlocking { getString(Res.string.generic_unknown) },
name = item.stringOrNull("name"), name = item.stringOrNull("name"),
url = url, url = url,
quality = item.stringOrNull("quality"), quality = item.stringOrNull("quality"),