fix(api): watchdog con timeout su tutte le richieste — mai piu' attese infinite (v0.5.1)
Sintomo sul device: l'app restava sullo splash in caricamento per sempre. Causa: QNetworkAccessManager non applica timeout — una richiesta che non riceve risposta (rete del telefono, server occupato) lasciava la UI in attesa infinita, senza errori visibili. - ApiClient::armTimeout(): watchdog per reply (abort -> normale percorso d'errore del chiamante). Applicato a: checkAuth (12 s), login (20 s), refreshSessions e openSession (60 s), newSession/chat-start/switchProfile (20-30 s), fetchProfiles (20 s), transcribe (300 s), tts (120 s). Nessun watchdog sullo stream SSE (longevo per design). - checkAuth: imposta lastError anche in errore (messaggio tradotto "Server not reachable (timeout)") e logga indirizzo ed esito - refreshSessions: log della durata (misurato sul probe: il primo fetch dopo un cambio profilo puo' prendere 11-16 s lato server, anche su DB minuscolo — l'app ora lo gestisce senza sembrare bloccata) - SplashPage: rete di sicurezza (se authChecked non arriva entro 15 s si va comunque al login) + log di instradamento - i18n: nuova stringa tradotta it/fr/de
This commit is contained in:
@@ -76,6 +76,7 @@ speaks (validated live against a real instance).
|
|||||||
|
|
||||||
## Status
|
## Status
|
||||||
|
|
||||||
Version 0.5.0 — protocol and C++ core validated against a real `hermes-webui`
|
Version 0.5.1 — protocol and C++ core validated against a real `hermes-webui`
|
||||||
(login, sessions, streaming chat, TTS, STT, voice dialog, profile selection);
|
(login, sessions, streaming chat, TTS, STT, voice dialog, profile selection);
|
||||||
UI in English, Italian, French and German; on-device testing still ongoing.
|
UI in English, Italian, French and German; every request has a watchdog
|
||||||
|
timeout (no infinite waits); on-device testing still ongoing.
|
||||||
|
|||||||
+3
-3
@@ -76,12 +76,12 @@ richiede il probe WebUI su :8899). Esito 12/09/2026: unit 9/9, live completo OK.
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd ~/workspace
|
cd ~/workspace
|
||||||
tar czf harbour-hermes-0.5.0.tar.gz \
|
tar czf harbour-hermes-0.5.1.tar.gz \
|
||||||
--transform 's,^harbour-hermes,harbour-hermes-0.5.0,' \
|
--transform 's,^harbour-hermes,harbour-hermes-0.5.1,' \
|
||||||
--exclude='.git' harbour-hermes
|
--exclude='.git' harbour-hermes
|
||||||
```
|
```
|
||||||
|
|
||||||
Lo spec si aspetta la directory `harbour-hermes-0.5.0/` (pattern degli altri
|
Lo spec si aspetta la directory `harbour-hermes-0.5.1/` (pattern degli altri
|
||||||
progetti: cercato in modo robusto anche per i sorgenti live di sfdk).
|
progetti: cercato in modo robusto anche per i sorgenti live di sfdk).
|
||||||
|
|
||||||
## Traduzioni
|
## Traduzioni
|
||||||
|
|||||||
@@ -30,9 +30,18 @@ Page {
|
|||||||
onAuthChecked: page.route(loggedIn)
|
onAuthChecked: page.route(loggedIn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rete di sicurezza: se il controllo d'avvio non risponde entro 15 s
|
||||||
|
// (bug o rete muta), si va comunque al login invece di restare qui.
|
||||||
|
Timer {
|
||||||
|
interval: 15000
|
||||||
|
running: true
|
||||||
|
onTriggered: page.route(false)
|
||||||
|
}
|
||||||
|
|
||||||
Component.onCompleted: api.checkAuth()
|
Component.onCompleted: api.checkAuth()
|
||||||
|
|
||||||
function route(loggedIn) {
|
function route(loggedIn) {
|
||||||
|
console.log("[hermes] route: loggedIn=" + loggedIn)
|
||||||
if (loggedIn)
|
if (loggedIn)
|
||||||
pageStack.replace(Qt.resolvedUrl("SessionsPage.qml"))
|
pageStack.replace(Qt.resolvedUrl("SessionsPage.qml"))
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
Name: harbour-hermes
|
Name: harbour-hermes
|
||||||
Summary: Client for the Hermes Web UI with voice
|
Summary: Client for the Hermes Web UI with voice
|
||||||
Version: 0.5.0
|
Version: 0.5.1
|
||||||
Release: 1
|
Release: 1
|
||||||
Group: Qt/Qt
|
Group: Qt/Qt
|
||||||
License: MIT
|
License: MIT
|
||||||
@@ -25,6 +25,13 @@ Sessions list, chat with live streaming replies, voice dictation
|
|||||||
(server-side TTS), plus a hands-free conversation mode.
|
(server-side TTS), plus a hands-free conversation mode.
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Sun Sep 13 2026 Carlo Baratto <carlo@carlobaratto.it> - 0.5.1-1
|
||||||
|
- Fix blocco sullo splash all'avvio: watchdog con timeout su tutte le
|
||||||
|
richieste (QNAM non ne applica: una richiesta senza risposta lasciava
|
||||||
|
la UI in attesa infinita). checkAuth imposta un errore visibile
|
||||||
|
("Server non raggiungibile (timeout)") e logga esito e indirizzo;
|
||||||
|
refreshSessions logga la durata; splash con rete di sicurezza verso il
|
||||||
|
login dopo 15 s; nessun watchdog sullo stream SSE (longevo per design)
|
||||||
* Sat Sep 12 2026 Carlo Baratto <carlo@carlobaratto.it> - 0.5.0-1
|
* Sat Sep 12 2026 Carlo Baratto <carlo@carlobaratto.it> - 0.5.0-1
|
||||||
- Sezione Sessions nelle Impostazioni: selettore profilo (le sessioni
|
- Sezione Sessions nelle Impostazioni: selettore profilo (le sessioni
|
||||||
mostrate sono quelle del profilo scelto). Switch per-client via
|
mostrate sono quelle del profilo scelto). Switch per-client via
|
||||||
|
|||||||
+39
-1
@@ -10,6 +10,7 @@
|
|||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
|
#include <QTimer>
|
||||||
#include <QUrlQuery>
|
#include <QUrlQuery>
|
||||||
|
|
||||||
ApiClient::ApiClient(Settings *settings, QObject *parent)
|
ApiClient::ApiClient(Settings *settings, QObject *parent)
|
||||||
@@ -35,6 +36,25 @@ ApiClient::ApiClient(Settings *settings, QObject *parent)
|
|||||||
connect(m_settings, &Settings::profileChanged, this, &ApiClient::profileChanged);
|
connect(m_settings, &Settings::profileChanged, this, &ApiClient::profileChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// QNetworkAccessManager non applica timeout: senza watchdog una richiesta
|
||||||
|
// senza risposta (rete assente, server occupato) lascerebbe la UI in attesa
|
||||||
|
// per sempre. armTimeout aborta la reply dopo N secondi: poi si attiva il
|
||||||
|
// normale percorso d'errore del chiamante (messaggio visibile, stato resettato).
|
||||||
|
void ApiClient::armTimeout(QNetworkReply *reply, int seconds)
|
||||||
|
{
|
||||||
|
QTimer *timer = new QTimer(reply);
|
||||||
|
timer->setSingleShot(true);
|
||||||
|
timer->setInterval(seconds * 1000);
|
||||||
|
connect(timer, &QTimer::timeout, reply, [reply, seconds]() {
|
||||||
|
if (reply->isRunning()) {
|
||||||
|
qDebug() << "[api] timeout dopo" << seconds << "s su" << reply->url().path();
|
||||||
|
reply->abort();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
connect(reply, &QNetworkReply::finished, timer, &QTimer::stop);
|
||||||
|
timer->start();
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------- helpers --
|
// ---------------------------------------------------------------- helpers --
|
||||||
|
|
||||||
QUrl ApiClient::apiUrl(const QString &path) const
|
QUrl ApiClient::apiUrl(const QString &path) const
|
||||||
@@ -132,10 +152,16 @@ void ApiClient::checkAuth()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QNetworkReply *reply = m_nam.get(jsonRequest(QStringLiteral("/api/auth/status")));
|
QNetworkReply *reply = m_nam.get(jsonRequest(QStringLiteral("/api/auth/status")));
|
||||||
|
armTimeout(reply, 12); // il controllo d'avvio deve risolversi, sempre
|
||||||
|
qDebug() << "[api] checkAuth ->" << m_settings->baseUrl();
|
||||||
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
|
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
const QByteArray body = reply->readAll();
|
const QByteArray body = reply->readAll();
|
||||||
if (reply->error() != QNetworkReply::NoError) {
|
if (reply->error() != QNetworkReply::NoError) {
|
||||||
|
const bool wasTimeout = (reply->error() == QNetworkReply::OperationCanceledError);
|
||||||
|
setLastError(wasTimeout ? tr("Server not reachable (timeout)")
|
||||||
|
: errorFromReply(reply, body));
|
||||||
|
qDebug() << "[api] checkAuth FALLITO:" << m_lastError;
|
||||||
setLoggedIn(false);
|
setLoggedIn(false);
|
||||||
emit authChecked(false);
|
emit authChecked(false);
|
||||||
return;
|
return;
|
||||||
@@ -149,6 +175,7 @@ void ApiClient::checkAuth()
|
|||||||
} else {
|
} else {
|
||||||
setLoggedIn(false);
|
setLoggedIn(false);
|
||||||
}
|
}
|
||||||
|
qDebug() << "[api] checkAuth:" << (m_loggedIn ? "sessione valida" : "serve il login");
|
||||||
emit authChecked(m_loggedIn);
|
emit authChecked(m_loggedIn);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -165,6 +192,7 @@ void ApiClient::login(const QString &password)
|
|||||||
body.insert(QStringLiteral("password"), password);
|
body.insert(QStringLiteral("password"), password);
|
||||||
QNetworkReply *reply = m_nam.post(jsonRequest(QStringLiteral("/api/auth/login")),
|
QNetworkReply *reply = m_nam.post(jsonRequest(QStringLiteral("/api/auth/login")),
|
||||||
QJsonDocument(body).toJson(QJsonDocument::Compact));
|
QJsonDocument(body).toJson(QJsonDocument::Compact));
|
||||||
|
armTimeout(reply, 20);
|
||||||
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
|
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
setBusy(false);
|
setBusy(false);
|
||||||
@@ -210,9 +238,12 @@ void ApiClient::refreshSessions()
|
|||||||
q.addQueryItem(QStringLiteral("exclude_hidden"), QStringLiteral("1"));
|
q.addQueryItem(QStringLiteral("exclude_hidden"), QStringLiteral("1"));
|
||||||
url.setQuery(q);
|
url.setQuery(q);
|
||||||
QNetworkReply *reply = m_nam.get(QNetworkRequest(url));
|
QNetworkReply *reply = m_nam.get(QNetworkRequest(url));
|
||||||
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
|
armTimeout(reply, 60); // il primo fetch dopo un cambio profilo puo' essere lento
|
||||||
|
const qint64 t0 = QDateTime::currentMSecsSinceEpoch();
|
||||||
|
connect(reply, &QNetworkReply::finished, this, [this, reply, t0]() {
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
setBusy(false);
|
setBusy(false);
|
||||||
|
qDebug() << "[api] sessions in" << (QDateTime::currentMSecsSinceEpoch() - t0) << "ms";
|
||||||
const QByteArray body = reply->readAll();
|
const QByteArray body = reply->readAll();
|
||||||
if (reply->error() != QNetworkReply::NoError) {
|
if (reply->error() != QNetworkReply::NoError) {
|
||||||
setLastError(errorFromReply(reply, body));
|
setLastError(errorFromReply(reply, body));
|
||||||
@@ -238,6 +269,7 @@ QString ApiClient::profile() const
|
|||||||
void ApiClient::fetchProfiles()
|
void ApiClient::fetchProfiles()
|
||||||
{
|
{
|
||||||
QNetworkReply *reply = m_nam.get(jsonRequest(QStringLiteral("/api/profiles")));
|
QNetworkReply *reply = m_nam.get(jsonRequest(QStringLiteral("/api/profiles")));
|
||||||
|
armTimeout(reply, 20);
|
||||||
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
|
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
const QByteArray data = reply->readAll();
|
const QByteArray data = reply->readAll();
|
||||||
@@ -279,6 +311,7 @@ void ApiClient::switchProfile(const QString &name)
|
|||||||
body.insert(QStringLiteral("name"), target);
|
body.insert(QStringLiteral("name"), target);
|
||||||
QNetworkReply *reply = m_nam.post(jsonRequest(QStringLiteral("/api/profile/switch")),
|
QNetworkReply *reply = m_nam.post(jsonRequest(QStringLiteral("/api/profile/switch")),
|
||||||
QJsonDocument(body).toJson(QJsonDocument::Compact));
|
QJsonDocument(body).toJson(QJsonDocument::Compact));
|
||||||
|
armTimeout(reply, 20);
|
||||||
connect(reply, &QNetworkReply::finished, this, [this, reply, target]() {
|
connect(reply, &QNetworkReply::finished, this, [this, reply, target]() {
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
const QByteArray data = reply->readAll();
|
const QByteArray data = reply->readAll();
|
||||||
@@ -307,6 +340,7 @@ void ApiClient::openSession(const QString &id)
|
|||||||
q.addQueryItem(QStringLiteral("session_id"), id);
|
q.addQueryItem(QStringLiteral("session_id"), id);
|
||||||
url.setQuery(q);
|
url.setQuery(q);
|
||||||
QNetworkReply *reply = m_nam.get(QNetworkRequest(url));
|
QNetworkReply *reply = m_nam.get(QNetworkRequest(url));
|
||||||
|
armTimeout(reply, 60);
|
||||||
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
|
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
setBusy(false);
|
setBusy(false);
|
||||||
@@ -337,6 +371,7 @@ void ApiClient::newSession()
|
|||||||
body.insert(QStringLiteral("workspace"), ws);
|
body.insert(QStringLiteral("workspace"), ws);
|
||||||
QNetworkReply *reply = m_nam.post(jsonRequest(QStringLiteral("/api/session/new")),
|
QNetworkReply *reply = m_nam.post(jsonRequest(QStringLiteral("/api/session/new")),
|
||||||
QJsonDocument(body).toJson(QJsonDocument::Compact));
|
QJsonDocument(body).toJson(QJsonDocument::Compact));
|
||||||
|
armTimeout(reply, 30);
|
||||||
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
|
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
setBusy(false);
|
setBusy(false);
|
||||||
@@ -374,6 +409,7 @@ void ApiClient::startChat(const QString &sessionId, const QString &message)
|
|||||||
|
|
||||||
QNetworkReply *reply = m_nam.post(jsonRequest(QStringLiteral("/api/chat/start")),
|
QNetworkReply *reply = m_nam.post(jsonRequest(QStringLiteral("/api/chat/start")),
|
||||||
QJsonDocument(body).toJson(QJsonDocument::Compact));
|
QJsonDocument(body).toJson(QJsonDocument::Compact));
|
||||||
|
armTimeout(reply, 30);
|
||||||
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
|
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
|
||||||
handleStartReply(reply);
|
handleStartReply(reply);
|
||||||
});
|
});
|
||||||
@@ -540,6 +576,7 @@ void ApiClient::transcribeFile(const QString &path)
|
|||||||
QNetworkRequest req(apiUrl(QStringLiteral("/api/transcribe")));
|
QNetworkRequest req(apiUrl(QStringLiteral("/api/transcribe")));
|
||||||
QNetworkReply *reply = m_nam.post(req, multiPart);
|
QNetworkReply *reply = m_nam.post(req, multiPart);
|
||||||
multiPart->setParent(reply);
|
multiPart->setParent(reply);
|
||||||
|
armTimeout(reply, 300); // la trascrizione puo' richiedere tempo
|
||||||
|
|
||||||
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
|
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
@@ -584,6 +621,7 @@ void ApiClient::speak(const QString &text)
|
|||||||
body.insert(QStringLiteral("engine"), m_settings->ttsEngine());
|
body.insert(QStringLiteral("engine"), m_settings->ttsEngine());
|
||||||
QNetworkReply *reply = m_nam.post(jsonRequest(QStringLiteral("/api/tts")),
|
QNetworkReply *reply = m_nam.post(jsonRequest(QStringLiteral("/api/tts")),
|
||||||
QJsonDocument(body).toJson(QJsonDocument::Compact));
|
QJsonDocument(body).toJson(QJsonDocument::Compact));
|
||||||
|
armTimeout(reply, 120);
|
||||||
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
|
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
const QByteArray body = reply->readAll();
|
const QByteArray body = reply->readAll();
|
||||||
|
|||||||
@@ -112,6 +112,8 @@ private:
|
|||||||
void setTranscribing(bool on);
|
void setTranscribing(bool on);
|
||||||
void setLastError(const QString &error);
|
void setLastError(const QString &error);
|
||||||
void setLoggedIn(bool on);
|
void setLoggedIn(bool on);
|
||||||
|
// Watchdog: aborta la reply dopo N secondi (QNAM non applica timeout).
|
||||||
|
void armTimeout(QNetworkReply *reply, int seconds);
|
||||||
|
|
||||||
QUrl apiUrl(const QString &path) const;
|
QUrl apiUrl(const QString &path) const;
|
||||||
QNetworkRequest jsonRequest(const QString &path, bool post = false) const;
|
QNetworkRequest jsonRequest(const QString &path, bool post = false) const;
|
||||||
|
|||||||
+2
-2
@@ -21,9 +21,9 @@ Q_DECL_EXPORT int main(int argc, char *argv[])
|
|||||||
// AppConfigLocation = ~/.config/harbour/hermes (zona persistente della sandbox).
|
// AppConfigLocation = ~/.config/harbour/hermes (zona persistente della sandbox).
|
||||||
app->setOrganizationName(QStringLiteral("harbour"));
|
app->setOrganizationName(QStringLiteral("harbour"));
|
||||||
app->setApplicationName(QStringLiteral("hermes"));
|
app->setApplicationName(QStringLiteral("hermes"));
|
||||||
app->setApplicationVersion(QStringLiteral("0.5.0"));
|
app->setApplicationVersion(QStringLiteral("0.5.1"));
|
||||||
|
|
||||||
qDebug() << "harbour-hermes v0.5.0 build" << __DATE__ << __TIME__;
|
qDebug() << "harbour-hermes v0.5.1 build" << __DATE__ << __TIME__;
|
||||||
|
|
||||||
// Traduzioni: it/fr/de da /usr/share/harbour-hermes/translations, in base
|
// Traduzioni: it/fr/de da /usr/share/harbour-hermes/translations, in base
|
||||||
// alla lingua di sistema (l'inglese e' la lingua sorgente: nessun .qm).
|
// alla lingua di sistema (l'inglese e' la lingua sorgente: nessun .qm).
|
||||||
|
|||||||
@@ -4,17 +4,22 @@
|
|||||||
<context>
|
<context>
|
||||||
<name>ApiClient</name>
|
<name>ApiClient</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/apiclient.cpp" line="72" />
|
<location filename="../src/apiclient.cpp" line="92" />
|
||||||
<source>Authentication required</source>
|
<source>Authentication required</source>
|
||||||
<translation>Authentifizierung erforderlich</translation>
|
<translation>Authentifizierung erforderlich</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/apiclient.cpp" line="74" />
|
<location filename="../src/apiclient.cpp" line="94" />
|
||||||
<source>HTTP %1</source>
|
<source>HTTP %1</source>
|
||||||
<translation>HTTP %1</translation>
|
<translation>HTTP %1</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/apiclient.cpp" line="159" />
|
<location filename="../src/apiclient.cpp" line="162" />
|
||||||
|
<source>Server not reachable (timeout)</source>
|
||||||
|
<translation>Server nicht erreichbar (Zeitüberschreitung)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../src/apiclient.cpp" line="186" />
|
||||||
<source>Server address not configured</source>
|
<source>Server address not configured</source>
|
||||||
<translation>Serveradresse nicht konfiguriert</translation>
|
<translation>Serveradresse nicht konfiguriert</translation>
|
||||||
</message>
|
</message>
|
||||||
|
|||||||
@@ -4,17 +4,22 @@
|
|||||||
<context>
|
<context>
|
||||||
<name>ApiClient</name>
|
<name>ApiClient</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/apiclient.cpp" line="72" />
|
<location filename="../src/apiclient.cpp" line="92" />
|
||||||
<source>Authentication required</source>
|
<source>Authentication required</source>
|
||||||
<translation>Authentification requise</translation>
|
<translation>Authentification requise</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/apiclient.cpp" line="74" />
|
<location filename="../src/apiclient.cpp" line="94" />
|
||||||
<source>HTTP %1</source>
|
<source>HTTP %1</source>
|
||||||
<translation>HTTP %1</translation>
|
<translation>HTTP %1</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/apiclient.cpp" line="159" />
|
<location filename="../src/apiclient.cpp" line="162" />
|
||||||
|
<source>Server not reachable (timeout)</source>
|
||||||
|
<translation>Serveur injoignable (délai dépassé)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../src/apiclient.cpp" line="186" />
|
||||||
<source>Server address not configured</source>
|
<source>Server address not configured</source>
|
||||||
<translation>Adresse du serveur non configurée</translation>
|
<translation>Adresse du serveur non configurée</translation>
|
||||||
</message>
|
</message>
|
||||||
|
|||||||
@@ -4,17 +4,22 @@
|
|||||||
<context>
|
<context>
|
||||||
<name>ApiClient</name>
|
<name>ApiClient</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/apiclient.cpp" line="72" />
|
<location filename="../src/apiclient.cpp" line="92" />
|
||||||
<source>Authentication required</source>
|
<source>Authentication required</source>
|
||||||
<translation>Autenticazione richiesta</translation>
|
<translation>Autenticazione richiesta</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/apiclient.cpp" line="74" />
|
<location filename="../src/apiclient.cpp" line="94" />
|
||||||
<source>HTTP %1</source>
|
<source>HTTP %1</source>
|
||||||
<translation>HTTP %1</translation>
|
<translation>HTTP %1</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/apiclient.cpp" line="159" />
|
<location filename="../src/apiclient.cpp" line="162" />
|
||||||
|
<source>Server not reachable (timeout)</source>
|
||||||
|
<translation>Server non raggiungibile (timeout)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../src/apiclient.cpp" line="186" />
|
||||||
<source>Server address not configured</source>
|
<source>Server address not configured</source>
|
||||||
<translation>Indirizzo del server non configurato</translation>
|
<translation>Indirizzo del server non configurato</translation>
|
||||||
</message>
|
</message>
|
||||||
|
|||||||
Reference in New Issue
Block a user