0.4.0: interfaccia in italiano e inglese, scelta nelle impostazioni

L'inglese è la lingua sorgente delle stringhe; l'italiano sta nel catalogo
translations/harbour-aira-it.ts (99 stringhe), che lupdate aggiorna e lrelease
compila in build. Di serie la lingua del sistema, con ripiego sull'inglese.

- nuova classe Translator: risolve la lingua prima di createView e allinea
  QLocale::setDefault, così anche i numeri seguono la lingua (23,0 °C in
  italiano, 23.0 °C in inglese) — le temperature passano da formatValue, non
  più dal toFixed() di JavaScript che metteva sempre il punto
- Qt 5.6 non ha QQmlEngine::retranslate(): al cambio di lingua la radice QML
  viene ricaricata (setSource ricrea il componente anche a url invariata)
- le etichette di stato in airajson.cpp usano QCoreApplication::translate per
  esteso: lupdate non riconosce una funzione wrapper e le avrebbe perse
- selettore della lingua in Impostazioni (Sistema / English / Italiano)
- test: 41/41, carica il .qm vero come fa main.cpp e verifica entrambe le
  lingue, il ripiego e i formati numerici
- anteprime bilingui (docs/anteprima/ inglese, it/ italiano), README e PIANO
  aggiornati, spec RPM con i cataloghi in %files
This commit is contained in:
Carlo
2026-10-10 08:19:20 +02:00
parent e0dcf1f830
commit fddd21e16a
29 changed files with 1230 additions and 190 deletions
+39 -35
View File
@@ -38,12 +38,16 @@ Page {
return api.summary[key]
}
// I numeri si formattano nel C++ (ApiClient::formatValue): cosi' il
// separatore decimale segue la lingua dell'app, non il toFixed di JS.
function fmt(value) { return api.formatValue(value) }
function temp(key) {
return api.formatValue(page.raw(key))
}
function flag(key) {
return page.raw(key) ? qsTr("sì") : qsTr("no")
return page.raw(key) ? qsTr("yes") : qsTr("no")
}
function count(key) {
@@ -98,15 +102,15 @@ Page {
// fanno deref su null (TypeError + crash).
PullDownMenu {
MenuItem {
text: qsTr("Aggiorna")
text: qsTr("Refresh")
onClicked: api.refresh()
}
MenuItem {
text: qsTr("Impostazioni")
text: qsTr("Settings")
onClicked: page.openSettings()
}
MenuItem {
text: qsTr("Altri comandi")
text: qsTr("Other commands")
onClicked: page.openCommands()
}
}
@@ -116,7 +120,7 @@ Page {
width: parent.width
spacing: Theme.paddingSmall
PageHeader { title: qsTr("Aira") }
PageHeader { title: "Aira" }
// ---- avvisi ---------------------------------------------------- //
Label {
@@ -134,7 +138,7 @@ Page {
visible: !api.configured
x: Theme.horizontalPageMargin
width: parent.width - 2 * Theme.horizontalPageMargin
text: qsTr("Bridge non configurato. Apri le impostazioni e inserisci l'indirizzo del server e il token.")
text: qsTr("Bridge not configured. Open the settings and enter the server address and the token.")
color: Theme.secondaryColor
font.pixelSize: Theme.fontSizeSmall
wrapMode: Text.Wrap
@@ -143,7 +147,7 @@ Page {
Button {
visible: !api.configured
anchors.horizontalCenter: parent.horizontalCenter
text: qsTr("Impostazioni")
text: qsTr("Settings")
onClicked: page.openSettings()
}
@@ -151,7 +155,7 @@ Page {
Label {
visible: api.configured
anchors.horizontalCenter: parent.horizontalCenter
text: qsTr("Temperatura in casa")
text: qsTr("House temperature")
color: Theme.secondaryColor
font.pixelSize: Theme.fontSizeSmall
}
@@ -179,7 +183,7 @@ Page {
Label {
visible: api.configured
anchors.horizontalCenter: parent.horizontalCenter
text: qsTr("obiettivo %1 °C · %2").arg(page.temp("zone1_setpoint")).arg(api.modeLabel(page.raw("zone1_mode")))
text: qsTr("target %1 °C · %2").arg(page.temp("zone1_setpoint")).arg(api.modeLabel(page.raw("zone1_mode")))
color: Theme.secondaryColor
font.pixelSize: Theme.fontSizeExtraSmall
}
@@ -188,12 +192,12 @@ Page {
id: casaSlider
visible: api.configured
width: parent.width
label: qsTr("Nuovo obiettivo casa")
label: qsTr("New house target")
minimumValue: 5
maximumValue: 30
stepSize: 0.5
value: page.targetCasa
valueText: value.toFixed(1) + " °C"
valueText: page.fmt(value) + " °C"
onValueChanged: {
page.targetCasaTouched = true
page.targetCasa = Math.round(value * 2) / 2
@@ -209,14 +213,14 @@ Page {
leftMargin: Theme.horizontalPageMargin
rightMargin: Theme.horizontalPageMargin
}
text: qsTr("Imposta casa a %1 °C").arg(page.targetCasa.toFixed(1))
text: qsTr("Set house to %1 °C").arg(page.fmt(page.targetCasa))
onClicked: remorse.execute(setCasaButton,
qsTr("Temperatura della casa a %1 °C").arg(page.targetCasa.toFixed(1)),
qsTr("House temperature to %1 °C").arg(page.fmt(page.targetCasa)),
function() { api.sendCommand("set_heating_setpoint", page.targetCasa) })
}
// ---- acqua calda sanitaria ------------------------------------- //
SectionHeader { visible: api.configured; text: qsTr("Acqua calda sanitaria") }
SectionHeader { visible: api.configured; text: qsTr("Domestic hot water") }
Label {
visible: api.configured
@@ -240,19 +244,19 @@ Page {
InfoRow {
visible: api.configured
label: qsTr("Obiettivo acqua calda")
label: qsTr("DHW target")
value: page.temp("dhw_target") + " °C"
}
Slider {
visible: api.configured
width: parent.width
label: qsTr("Nuovo obiettivo")
label: qsTr("New target")
minimumValue: 45
maximumValue: 60
stepSize: 1
value: page.targetAcs
valueText: value + " °C"
valueText: page.fmt(value) + " °C"
onValueChanged: {
page.targetAcsTouched = true
page.targetAcs = Math.round(value)
@@ -268,9 +272,9 @@ Page {
leftMargin: Theme.horizontalPageMargin
rightMargin: Theme.horizontalPageMargin
}
text: qsTr("Imposta obiettivo %1 °C").arg(page.targetAcs)
text: qsTr("Set target %1 °C").arg(page.targetAcs)
onClicked: remorse.execute(setAcsButton,
qsTr("Obiettivo ACS a %1 °C").arg(page.targetAcs),
qsTr("DHW target to %1 °C").arg(page.targetAcs),
function() { api.sendCommand("set_hot_water_target", page.targetAcs) })
}
@@ -283,8 +287,8 @@ Page {
leftMargin: Theme.horizontalPageMargin
rightMargin: Theme.horizontalPageMargin
}
text: qsTr("Avvia boost ACS")
onClicked: remorse.execute(boostOnButton, qsTr("Avvio del boost ACS"),
text: qsTr("Start DHW boost")
onClicked: remorse.execute(boostOnButton, qsTr("Starting DHW boost"),
function() { api.sendCommand("hot_water_boost_on") })
}
@@ -297,24 +301,24 @@ Page {
leftMargin: Theme.horizontalPageMargin
rightMargin: Theme.horizontalPageMargin
}
text: qsTr("Ferma boost ACS")
onClicked: remorse.execute(boostOffButton, qsTr("Arresto del boost ACS"),
text: qsTr("Stop DHW boost")
onClicked: remorse.execute(boostOffButton, qsTr("Stopping DHW boost"),
function() { api.sendCommand("hot_water_boost_off") })
}
// ---- stato impianto ------------------------------------------- //
SectionHeader { visible: api.configured; text: qsTr("Impianto") }
SectionHeader { visible: api.configured; text: qsTr("Plant") }
InfoRow { visible: api.configured; label: qsTr("Temperatura esterna"); value: page.temp("outdoor_temperature") + " °C" }
InfoRow { visible: api.configured; label: qsTr("Temperatura interna (pompa)"); value: page.temp("indoor_temperature") + " °C" }
InfoRow { visible: api.configured; label: qsTr("Modalità"); value: api.modeLabel(page.raw("operating_status")) }
InfoRow { visible: api.configured; label: qsTr("Assenza"); value: page.flag("away_mode") }
InfoRow { visible: api.configured; label: qsTr("Outdoor temperature"); value: page.temp("outdoor_temperature") + " °C" }
InfoRow { visible: api.configured; label: qsTr("Indoor temperature (pump)"); value: page.temp("indoor_temperature") + " °C" }
InfoRow { visible: api.configured; label: qsTr("Mode"); value: api.modeLabel(page.raw("operating_status")) }
InfoRow { visible: api.configured; label: qsTr("Away mode"); value: page.flag("away_mode") }
InfoRow { visible: api.configured; label: qsTr("Night mode"); value: page.flag("night_mode") }
InfoRow { visible: api.configured; label: qsTr("Riscaldamento forzato"); value: page.flag("force_heating") }
InfoRow { visible: api.configured; label: qsTr("Resistenze integrative"); value: page.flag("inline_heater") }
InfoRow { visible: api.configured; label: qsTr("Forced heating"); value: page.flag("force_heating") }
InfoRow { visible: api.configured; label: qsTr("Inline heater"); value: page.flag("inline_heater") }
InfoRow { visible: api.configured; label: qsTr("Zone"); value: page.count("zones") }
InfoRow { visible: api.configured; label: qsTr("Errori"); value: page.count("errors_count") }
InfoRow { visible: api.configured; label: qsTr("Aggiornato alle"); value: page.shortTime(page.raw("pump_updated")) }
InfoRow { visible: api.configured; label: qsTr("Errors"); value: page.count("errors_count") }
InfoRow { visible: api.configured; label: qsTr("Last reading"); value: page.shortTime(page.raw("pump_updated")) }
Button {
visible: api.configured
@@ -324,7 +328,7 @@ Page {
leftMargin: Theme.horizontalPageMargin
rightMargin: Theme.horizontalPageMargin
}
text: qsTr("Altri comandi")
text: qsTr("Other commands")
onClicked: page.openCommands()
}
@@ -339,7 +343,7 @@ Page {
visible: api.configured
x: Theme.horizontalPageMargin
width: parent.width - 2 * Theme.horizontalPageMargin
text: qsTr("I comandi vengono inviati al bridge e da lì all'impianto: possono richiedere qualche secondo.")
text: qsTr("Commands are sent to the bridge and from there to the plant: they may take a few seconds.")
color: Theme.secondaryColor
font.pixelSize: Theme.fontSizeExtraSmall
wrapMode: Text.Wrap
@@ -351,4 +355,4 @@ Page {
if (api.configured)
api.refresh()
}
}
}