diff --git a/wallet/src/main/java/net/taler/wallet/HandleUriScreen.kt b/wallet/src/main/java/net/taler/wallet/HandleUriScreen.kt
index 03a3c56a..05e85ce8 100644
--- a/wallet/src/main/java/net/taler/wallet/HandleUriScreen.kt
+++ b/wallet/src/main/java/net/taler/wallet/HandleUriScreen.kt
@@ -14,6 +14,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
import androidx.core.net.toUri
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
@@ -46,6 +47,7 @@ fun HandleUriScreen(
val networkStatus by model.networkManager.networkStatus.observeAsState()
val payStatus by model.paymentManager.payStatus.observeAsState(PayStatus.None)
val scope = rememberCoroutineScope()
+ val invalidUriMessage = stringResource(R.string.uri_invalid)
fun processTalerUri() {
if (processing) return
@@ -146,7 +148,7 @@ fun HandleUriScreen(
onNavigate(WalletDestination.Contacts(addContactUri = u2), true)
}
else -> {
- errorInfo = TalerErrorInfo.makeCustomError("Unsupported URI: $u2")
+ errorInfo = TalerErrorInfo.makeCustomError("$invalidUriMessage: $u2")
}
}
} catch (_: CancellationException) {
diff --git a/wallet/src/main/java/net/taler/wallet/compose/ErrorComposable.kt b/wallet/src/main/java/net/taler/wallet/compose/ErrorComposable.kt
index 117a6d5a..5c1498d6 100644
--- a/wallet/src/main/java/net/taler/wallet/compose/ErrorComposable.kt
+++ b/wallet/src/main/java/net/taler/wallet/compose/ErrorComposable.kt
@@ -115,7 +115,29 @@ fun ErrorComposable(
)
}
} else {
- Text(
+ val known = remember(error) { error.errorMessage() }
+ if (known != null) {
+ Text(
+ text = stringResource(known.message),
+ style = MaterialTheme.typography.bodyLarge,
+ textAlign = TextAlign.Center,
+ )
+ known.guidance?.let {
+ Text(
+ text = stringResource(it),
+ modifier = Modifier.padding(top = 8.dp),
+ textAlign = TextAlign.Center,
+ )
+ }
+ // wallet-core's English diagnostic, for support
+ if (error.userFacingMsg.isNotEmpty()) Text(
+ text = error.userFacingMsg,
+ modifier = Modifier.padding(top = 16.dp),
+ style = MaterialTheme.typography.bodySmall,
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
+ textAlign = TextAlign.Center,
+ )
+ } else Text(
text = error.userFacingMsg.ifEmpty {
stringResource(R.string.error_code_only, error.rawCode)
},
diff --git a/wallet/src/main/java/net/taler/wallet/compose/ErrorMessages.kt b/wallet/src/main/java/net/taler/wallet/compose/ErrorMessages.kt
new file mode 100644
index 00000000..8905f033
--- /dev/null
+++ b/wallet/src/main/java/net/taler/wallet/compose/ErrorMessages.kt
@@ -0,0 +1,63 @@
+/*
+ * This file is part of GNU Taler
+ * (C) 2026 Taler Systems S.A.
+ *
+ * GNU Taler is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 3, or (at your option) any later version.
+ *
+ * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * GNU Taler; see the file COPYING. If not, see
+ */
+
+package net.taler.wallet.compose
+
+import androidx.annotation.StringRes
+import net.taler.wallet.R
+import net.taler.wallet.backend.TalerErrorCode.*
+import net.taler.wallet.backend.TalerErrorInfo
+
+/**
+ * A translated explanation of a wallet-core error, and what the user can do.
+ * wallet-core's own [TalerErrorInfo.hint] is English diagnostic text and stays
+ * available as technical detail.
+ */
+data class ErrorMessage(
+ @param:StringRes val message: Int,
+ @param:StringRes val guidance: Int? = null,
+)
+
+fun TalerErrorInfo.errorMessage(): ErrorMessage? = when (code) {
+ WALLET_NETWORK_ERROR,
+ WALLET_UNEXPECTED_REQUEST_ERROR,
+ WALLET_EXCHANGE_UNAVAILABLE -> ErrorMessage(
+ R.string.error_network,
+ R.string.error_network_guidance,
+ )
+ WALLET_HTTP_REQUEST_GENERIC_TIMEOUT,
+ GENERIC_TIMEOUT -> ErrorMessage(
+ R.string.error_timeout,
+ R.string.error_network_guidance,
+ )
+ WALLET_RECEIVED_MALFORMED_RESPONSE -> ErrorMessage(R.string.error_malformed_response)
+ WALLET_ORDER_ALREADY_CLAIMED -> ErrorMessage(
+ R.string.error_order_claimed,
+ R.string.error_order_new_guidance,
+ )
+ WALLET_ORDER_ALREADY_PAID -> ErrorMessage(R.string.error_order_paid)
+ WALLET_MERCHANT_ORDER_NOT_FOUND -> ErrorMessage(
+ R.string.error_order_not_found,
+ R.string.error_order_new_guidance,
+ )
+ WALLET_TALER_URI_MALFORMED,
+ WALLET_INVALID_TALER_PAY_URI -> ErrorMessage(R.string.uri_invalid)
+ WALLET_KYC_LIMIT_EXCEEDED -> ErrorMessage(R.string.error_kyc_limit)
+ WALLET_NO_SUITABLE_EXCHANGE -> ErrorMessage(R.string.error_no_suitable_exchange)
+ WALLET_PEER_PULL_DEBIT_PURSE_GONE,
+ WALLET_PEER_PUSH_CREDIT_PURSE_GONE -> ErrorMessage(R.string.error_peer_gone)
+ else -> null
+}
diff --git a/wallet/src/main/res/values/strings.xml b/wallet/src/main/res/values/strings.xml
index 89750709..22787d12 100644
--- a/wallet/src/main/res/values/strings.xml
+++ b/wallet/src/main/res/values/strings.xml
@@ -67,6 +67,17 @@ GNU Taler is immune to many types of fraud such as credit card data theft, phish
Error
Error code %1$d
Export error diagnostics
+ The wallet could not reach the service.
+ Check your connection and try again.
+ The service took too long to respond.
+ A service returned a response the wallet could not understand.
+ This order was already claimed by another wallet.
+ This order was already paid.
+ The merchant order no longer exists or has expired.
+ Return to the merchant and request a new order.
+ This amount exceeds the limit available without identity verification.
+ No compatible payment service is available for this operation.
+ This payment is no longer available.
Initialization error
An error occurred while starting the wallet. This can happen after an app update or if the wallet data is incompatible. Please retry, and if the problem persists, export the logs and report this issue to the developers.
Export database