etm-powersync-app/lib/l10n/app_localizations_fr.dart
Patrick Schurig cdffb9d804 feat(§10): l'avertissement de saisie — une consigne, pas une fatalité affichée
Lot configuration. La section « Obligation quotidienne » porte l'énergie due, son
échéance, et l'avertissement qui rend la déclaration honnête.

Pourquoi à la saisie : au résultat l'heure est passée, l'achat a eu lieu. Et sous
`unmeasurable` l'obligation ne peut JAMAIS être close — ni annoncée tenue (R6
interdit `ECO_FLOOR_MET`), ni chiffrée dans son manque (`missingWh` absent).
`ECO_FLOOR_MISSED` sort tous les jours par construction. Un mécanisme dont le
succès est inobservable et l'échec quotidien ne doit pas, en plus, dépenser en
silence.

Et c'est une consigne : les trois causes portent trois gestes — rattacher un
compteur (le plus fréquent, réparable), en mettre un qui totalise l'énergie, ou
constater que c'est le matériel. Une cause inconnue se dit sans inventer de
geste.

Le coût est CHIFFRÉ, pas estimé : le taux est constant depuis +etm55, donc le
total acheté vaut exactement la cible.

Trois silences délibérés, chacun testé : aucune obligation déclarée (personne ne
paiera), `progressMeasurable` absent (box antérieure à +etm49 — on ne conclut pas
d'une absence), et le nom du fuseau tant que l'app ne l'a pas lu. L'échéance dit
en revanche qu'elle est à l'heure de l'INSTALLATION, ce qui est vrai sans le
fuseau.

Revérifié à la sonde avant d'écrire, plutôt que cru : `progressMeasurable: true`
et `regime: measured` sortent bien sur la même charge — la contradiction du
2026-09-03 est fermée.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GKM8FDNHZWogAsjjfB8pSw
2026-09-05 08:40:35 +02:00

745 lines
24 KiB
Dart

// ignore: unused_import
import 'package:intl/intl.dart' as intl;
import 'app_localizations.dart';
// ignore_for_file: type=lint
/// The translations for French (`fr`).
class L10nFr extends L10n {
L10nFr([String locale = 'fr']) : super(locale);
@override
String get installerLoadsTitle => 'Rôles & appareils';
@override
String get installerLoadsSubtitle =>
'Ordre de service des charges pilotables';
@override
String get loadsEmpty =>
'Aucune charge pilotable déclarée sur cette installation.';
@override
String loadsLoadError(String error) {
return 'Lecture de la configuration impossible : $error';
}
@override
String get sectionUnclassified => 'Non classé';
@override
String get domainEv => 'Véhicule électrique';
@override
String get domainEcs => 'Eau chaude sanitaire';
@override
String get domainHvac => 'Climatisation';
@override
String get domainHeating => 'Chauffage';
@override
String domainUnknown(String code) {
return 'Domaine « $code »';
}
@override
String get adapterRelayRouter => 'Routeur de relais';
@override
String get adapterEtmVariableLoad => 'Charge modulable';
@override
String get adapterSgReady => 'SG-Ready';
@override
String get adapterEvCharger => 'Borne de recharge';
@override
String adapterUnknown(String code) {
return 'Mécanisme « $code »';
}
@override
String get loadEnabled => 'Active';
@override
String get loadDisabled => 'Exclue de l\'arbitrage';
@override
String loadPriority(int rank) {
return 'Rang $rank';
}
@override
String loadMinOn(int seconds) {
return 'Maintien ON minimal : $seconds s';
}
@override
String loadMinOff(int seconds) {
return 'Maintien OFF minimal : $seconds s';
}
@override
String loadRelayCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count relais',
one: '1 relais',
);
return '$_temp0';
}
@override
String get loadMeterSlot => 'Compteur dédié : non rattachable';
@override
String get orderNotGroupedTitle => 'Ordre non groupé';
@override
String get orderNotGroupedBody =>
'L\'ordre en place sur la box n\'est pas groupé par domaine : les charges d\'un même domaine ne se suivent pas. L\'ordre réel est affiché ci-dessous, tel que la box l\'applique.';
@override
String get orderNotGroupedAction => 'Normaliser l\'ordre';
@override
String get orderNotGroupedWarning =>
'Normaliser modifiera l\'ordre d\'arbitrage réel de l\'installation. Aucune écriture ne part tant que vous n\'avez pas confirmé.';
@override
String get orderNormalizeConfirm => 'Normaliser et enregistrer';
@override
String get cancel => 'Annuler';
@override
String get saveOrder => 'Enregistrer l\'ordre';
@override
String get savePending => 'Envoi en cours…';
@override
String get saveWaitingBox => 'En attente de confirmation de la box…';
@override
String get saveConfirmed => 'Configuration confirmée par la box.';
@override
String saveRejected(String reason) {
return 'La box a refusé : $reason';
}
@override
String get saveDiverged =>
'La box a enregistré un état différent de celui demandé. L\'affichage montre ce que la box applique réellement.';
@override
String validationDuplicateId(String id) {
return 'L\'identifiant de charge « $id » apparaît deux fois.';
}
@override
String validationThingClaimedTwice(
String thingId,
String first,
String second,
) {
return 'L\'appareil $thingId est commandé par deux charges actives, « $first » et « $second » : deux commandes contradictoires et une puissance comptée deux fois.';
}
@override
String get telemetryUnavailableTitle => 'Télémétrie indisponible';
@override
String get telemetryUnavailableBody =>
'Cette box n\'expose pas GetLoadTelemetry. Puissance allouée, verrous, disponibilité et motifs de décision ne sont pas lisibles depuis l\'app.';
@override
String telemetryStale(int seconds) {
return 'Données anciennes — dernier point il y a $seconds s';
}
@override
String get telemetryDegradedMode =>
'Mode dégradé L2 — compteur muet, planification suspendue';
@override
String get faultTitle => 'En défaut';
@override
String get faultClearAction => 'Lever le défaut';
@override
String get faultCleared => 'Défaut levé — confirmé par la télémétrie relue.';
@override
String get faultClearNoop => 'Aucun défaut à lever sur cette charge.';
@override
String faultUnknown(String code) {
return 'Défaut « $code »';
}
@override
String decisionUnknown(String code, String params) {
return 'Décision « $code »$params';
}
@override
String get telemetryTitle => 'Arbitrage en direct';
@override
String get telemetryNeverRan =>
'Aucun cycle d\'arbitrage depuis le démarrage du service.';
@override
String telemetryLastCycle(int seconds) {
return 'Dernier cycle il y a $seconds s';
}
@override
String get telemetryBudgetTitle => 'Budget de surplus du cycle';
@override
String get telemetryBudgetAbsent =>
'Aucun budget publié pour ce cycle : la planification est suspendue.';
@override
String get budgetSurplus => 'Surplus entrant';
@override
String get budgetEvReserved => 'Commandé, pas encore mesuré';
@override
String get budgetRecredited => 'Recrédité';
@override
String get budgetAllocated => 'Alloué';
@override
String get budgetRemaining => 'Reliquat';
@override
String watts(int watts) {
final intl.NumberFormat wattsNumberFormat =
intl.NumberFormat.decimalPattern(localeName);
final String wattsString = wattsNumberFormat.format(watts);
return '$wattsString W';
}
@override
String telemetryAllocated(int watts) {
final intl.NumberFormat wattsNumberFormat =
intl.NumberFormat.decimalPattern(localeName);
final String wattsString = wattsNumberFormat.format(watts);
return 'Alloué $wattsString W';
}
@override
String telemetryEvCharging(double current, int phases) {
final intl.NumberFormat currentNumberFormat =
intl.NumberFormat.decimalPattern(localeName);
final String currentString = currentNumberFormat.format(current);
return 'Borne : en charge, $currentString A sur $phases phase(s)';
}
@override
String get telemetryEvIdle => 'Borne : ne charge pas';
@override
String get telemetryEvUnplugged => 'Borne : aucun véhicule branché';
@override
String get telemetryEvStateUnknown => 'Borne : état non publié ce cycle';
@override
String get telemetryFundedByGrid =>
'Financée au réseau (échéance ou tarif dynamique) — hors budget de surplus';
@override
String get niveauEco => 'ÉCO';
@override
String get niveauConfort => 'CONFORT';
@override
String decisionEcoFloorGrid(int gridW) {
return 'Plancher servi — $gridW W achetés au réseau';
}
@override
String decisionEcoFloorMet(String livre, String cible) {
return 'Plancher atteint — $livre sur $cible livrés';
}
@override
String decisionEcoFloorMissed(String raison) {
return 'Obligation abandonnée à la bascule — $raison';
}
@override
String get ecoCauseDrawCap => 'le plafond de soutirage l\'a refusée';
@override
String get ecoCauseLoadCapacity =>
'la charge ne pouvait pas absorber davantage';
@override
String get ecoCauseUnmeasurable =>
'avancement non vérifiable — ni tenue, ni chiffrable';
@override
String get ecoCauseUnavailable => 'la charge était indisponible';
@override
String ecoManque(String wh) {
return ' · il manquait $wh';
}
@override
String ecoAvancementMesure(String livre, String cible) {
return '$livre sur $cible livrés';
}
@override
String ecoAvancementNonVerifiable(String wh) {
return 'reste $wh à livrer — avancement non vérifiable';
}
@override
String ecoMesureDepuis(String heure) {
return 'mesuré depuis $heure';
}
@override
String ecoMesureIntermittente(String heure) {
return 'mesuré par intermittence depuis $heure';
}
@override
String get ecoTauxConstant =>
'taux constant (cible ÷ jour) — sans mesure, rien ne le corrige';
@override
String get ecoAuMaximum => 'au maximum de la charge';
@override
String ecoAchete(String wh) {
return '$wh achetés pour cette obligation';
}
@override
String get obligationTitre => 'Obligation quotidienne';
@override
String get obligationSousTitre =>
'Le plancher que la box servira quoi qu\'il arrive, au réseau s\'il le faut.';
@override
String get obligationEnergie => 'Énergie due par jour';
@override
String get obligationEcheance => 'Avant';
@override
String obligationEcheanceAide(String fuseau) {
return 'Heure de l\'INSTALLATION$fuseau — pas celle de cet appareil. Une échéance est une habitude : elle suit la maison.';
}
@override
String get obligationNonMesurableTitre =>
'Cette obligation ne pourra jamais être close';
@override
String obligationNonMesurableCorps(String cout) {
return 'La box ne saura pas mesurer ce qu\'elle livre ici : l\'obligation ne pourra être annoncée ni tenue, ni manquée d\'une quantité connue. Elle sera achetée au réseau à taux constant — $cout par jour — sans que rien ne vienne le corriger.';
}
@override
String get obligationCauseNoMeter =>
'Aucun compteur n\'est rattaché à cette charge. Rattachez-en un ci-dessus : c\'est la cause la plus fréquente, et elle se répare en une manipulation.';
@override
String get obligationCauseNoEnergyMeter =>
'Le compteur rattaché publie une puissance, pas une énergie. Il faut un compteur qui totalise les kWh — la puissance seule ne suffit pas à mesurer ce qui a été livré.';
@override
String get obligationCauseNoSessionEnergy =>
'Cette borne ne publie pas l\'énergie de session. C\'est le matériel : aucun réglage ne le changera, et il vaut mieux le savoir maintenant qu\'après avoir cherché.';
@override
String obligationCauseInconnue(String cause) {
return 'La box ne dit pas pourquoi (cause « $cause » inconnue de cette version).';
}
@override
String get ecoIncoherenceTenue =>
'Incohérence de la box : plancher annoncé atteint alors que l\'avancement n\'est pas mesurable';
@override
String confortEnAttente(String motif) {
return 'En attente — $motif';
}
@override
String get telemetryNotArbitrated =>
'Non arbitrée — absente du dernier cycle publié';
@override
String lockMinOn(int seconds) {
return 'Maintien ON — $seconds s restantes';
}
@override
String lockMinOff(int seconds) {
return 'Maintien OFF — $seconds s restantes';
}
@override
String lockMinStateHold(int seconds) {
return 'Maintien d\'état — $seconds s restantes';
}
@override
String lockUnknown(String kind, int seconds) {
return 'Verrou « $kind » — $seconds s restantes';
}
@override
String get lockAsOfCycle => 'au dernier cycle';
@override
String mechanismRelayStage(int stage, int max) {
final intl.NumberFormat stageNumberFormat =
intl.NumberFormat.decimalPattern(localeName);
final String stageString = stageNumberFormat.format(stage);
final intl.NumberFormat maxNumberFormat = intl.NumberFormat.decimalPattern(
localeName,
);
final String maxString = maxNumberFormat.format(max);
return 'Palier $stageString W sur $maxString W';
}
@override
String mechanismVariableSetpoint(int setpoint, int percent) {
final intl.NumberFormat setpointNumberFormat =
intl.NumberFormat.decimalPattern(localeName);
final String setpointString = setpointNumberFormat.format(setpoint);
return 'Consigne $setpointString W ($percent %)';
}
@override
String mechanismSgReadyState(int state, int estimated) {
final intl.NumberFormat estimatedNumberFormat =
intl.NumberFormat.decimalPattern(localeName);
final String estimatedString = estimatedNumberFormat.format(estimated);
return 'État SG-Ready $state — estimation $estimatedString W';
}
@override
String mechanismEvCharger(double current, int phases) {
final intl.NumberFormat currentNumberFormat =
intl.NumberFormat.decimalPattern(localeName);
final String currentString = currentNumberFormat.format(current);
return '$currentString A sur $phases phase(s)';
}
@override
String mechanismUnknown(String kind) {
return 'Mécanisme « $kind »';
}
@override
String get decisionEvSurplus =>
'Véhicule rechargé sur le surplus photovoltaïque.';
@override
String get decisionEvSpotMarket =>
'Véhicule rechargé sur un créneau tarifaire favorable.';
@override
String get decisionEvDeadline =>
'Véhicule prioritaire : l\'échéance approche.';
@override
String get decisionEvEcoMin =>
'Pas de surplus : le courant minimum est maintenu.';
@override
String get decisionEvIdle => 'Pas de surplus : la recharge est suspendue.';
@override
String decisionLoadUnavailable(int frozen) {
final intl.NumberFormat frozenNumberFormat =
intl.NumberFormat.decimalPattern(localeName);
final String frozenString = frozenNumberFormat.format(frozen);
return 'Charge en défaut : aucune commande n\'est émise, et $frozenString W restent comptés au budget.';
}
@override
String decisionLockMinOn(int applied, int budget) {
final intl.NumberFormat appliedNumberFormat =
intl.NumberFormat.decimalPattern(localeName);
final String appliedString = appliedNumberFormat.format(applied);
final intl.NumberFormat budgetNumberFormat =
intl.NumberFormat.decimalPattern(localeName);
final String budgetString = budgetNumberFormat.format(budget);
return 'Le maintien ON a relevé la consigne à $appliedString W ; le budget en payait $budgetString W.';
}
@override
String decisionLockMinOff(int requested, int budget) {
final intl.NumberFormat requestedNumberFormat =
intl.NumberFormat.decimalPattern(localeName);
final String requestedString = requestedNumberFormat.format(requested);
final intl.NumberFormat budgetNumberFormat =
intl.NumberFormat.decimalPattern(localeName);
final String budgetString = budgetNumberFormat.format(budget);
return 'Le maintien OFF interdit l\'enclenchement ; le budget payait pourtant $requestedString W sur $budgetString W disponibles.';
}
@override
String decisionLockMinOffCapped(int applied, int requested, int budget) {
final intl.NumberFormat appliedNumberFormat =
intl.NumberFormat.decimalPattern(localeName);
final String appliedString = appliedNumberFormat.format(applied);
final intl.NumberFormat requestedNumberFormat =
intl.NumberFormat.decimalPattern(localeName);
final String requestedString = requestedNumberFormat.format(requested);
final intl.NumberFormat budgetNumberFormat =
intl.NumberFormat.decimalPattern(localeName);
final String budgetString = budgetNumberFormat.format(budget);
return 'Le maintien OFF plafonne la consigne à $appliedString W ; $requestedString W étaient demandés sur $budgetString W de budget.';
}
@override
String decisionSurplusSetpoint(int setpoint, int budget) {
final intl.NumberFormat setpointNumberFormat =
intl.NumberFormat.decimalPattern(localeName);
final String setpointString = setpointNumberFormat.format(setpoint);
final intl.NumberFormat budgetNumberFormat =
intl.NumberFormat.decimalPattern(localeName);
final String budgetString = budgetNumberFormat.format(budget);
return 'Consigne de $setpointString W servie par le surplus ($budgetString W de budget).';
}
@override
String decisionSurplusSetpointStepped(int setpoint, int budget) {
final intl.NumberFormat setpointNumberFormat =
intl.NumberFormat.decimalPattern(localeName);
final String setpointString = setpointNumberFormat.format(setpoint);
final intl.NumberFormat budgetNumberFormat =
intl.NumberFormat.decimalPattern(localeName);
final String budgetString = budgetNumberFormat.format(budget);
return 'Consigne de $setpointString W servie par le surplus, arrondie sur un palier déclaré ($budgetString W de budget).';
}
@override
String decisionSurplusInsufficient(int budget) {
final intl.NumberFormat budgetNumberFormat =
intl.NumberFormat.decimalPattern(localeName);
final String budgetString = budgetNumberFormat.format(budget);
return 'Aucun surplus à distribuer ($budgetString W) — rien n\'est commandé.';
}
@override
String decisionLockMinStateHold(int state) {
return 'État $state maintenu par protection anti court-cycle.';
}
@override
String decisionSgForced(int estimated, int budget) {
final intl.NumberFormat estimatedNumberFormat =
intl.NumberFormat.decimalPattern(localeName);
final String estimatedString = estimatedNumberFormat.format(estimated);
final intl.NumberFormat budgetNumberFormat =
intl.NumberFormat.decimalPattern(localeName);
final String budgetString = budgetNumberFormat.format(budget);
return 'Pompe à chaleur en état 4 (forcé) — estimation $estimatedString W sur $budgetString W de budget.';
}
@override
String decisionSgRecommended(int estimated, int budget) {
final intl.NumberFormat estimatedNumberFormat =
intl.NumberFormat.decimalPattern(localeName);
final String estimatedString = estimatedNumberFormat.format(estimated);
final intl.NumberFormat budgetNumberFormat =
intl.NumberFormat.decimalPattern(localeName);
final String budgetString = budgetNumberFormat.format(budget);
return 'Pompe à chaleur en état 3 (recommandation) — estimation $estimatedString W sur $budgetString W de budget.';
}
@override
String decisionSgNormal(int budget) {
final intl.NumberFormat budgetNumberFormat =
intl.NumberFormat.decimalPattern(localeName);
final String budgetString = budgetNumberFormat.format(budget);
return 'Aucun surplus ($budgetString W) — pompe à chaleur en état 2 (normal, sans consigne).';
}
@override
String decisionBelowMinPower(int budget, int min) {
final intl.NumberFormat budgetNumberFormat =
intl.NumberFormat.decimalPattern(localeName);
final String budgetString = budgetNumberFormat.format(budget);
final intl.NumberFormat minNumberFormat = intl.NumberFormat.decimalPattern(
localeName,
);
final String minString = minNumberFormat.format(min);
return 'Budget de $budgetString W sous le premier palier ($minString W) — rien n\'est commandé, le budget passe à la charge suivante.';
}
@override
String decisionBelowMinPowerState(int budget, int state, int min) {
final intl.NumberFormat budgetNumberFormat =
intl.NumberFormat.decimalPattern(localeName);
final String budgetString = budgetNumberFormat.format(budget);
final intl.NumberFormat minNumberFormat = intl.NumberFormat.decimalPattern(
localeName,
);
final String minString = minNumberFormat.format(min);
return 'Budget de $budgetString W sous l\'état $state ($minString W estimés) — maintien en état 2, le budget passe à la charge suivante.';
}
@override
String decisionEvGridStart(int grid, int budget, int floor) {
final intl.NumberFormat gridNumberFormat = intl.NumberFormat.decimalPattern(
localeName,
);
final String gridString = gridNumberFormat.format(grid);
final intl.NumberFormat budgetNumberFormat =
intl.NumberFormat.decimalPattern(localeName);
final String budgetString = budgetNumberFormat.format(budget);
final intl.NumberFormat floorNumberFormat =
intl.NumberFormat.decimalPattern(localeName);
final String floorString = floorNumberFormat.format(floor);
return 'Démarrage soutiré : $gridString W achetés au réseau pour compléter les $budgetString W de surplus et atteindre le plancher de $floorString W.';
}
@override
String decisionPhaseLimit(int limit, int required) {
final intl.NumberFormat limitNumberFormat =
intl.NumberFormat.decimalPattern(localeName);
final String limitString = limitNumberFormat.format(limit);
final intl.NumberFormat requiredNumberFormat =
intl.NumberFormat.decimalPattern(localeName);
final String requiredString = requiredNumberFormat.format(required);
return 'Limite de phase atteinte : $limitString W disponibles pour $requiredString W exigés — ce n\'est pas le surplus qui manque.';
}
@override
String decisionBatteryReserveWithheld(int withheld, int soc, int seuil) {
final intl.NumberFormat withheldNumberFormat =
intl.NumberFormat.decimalPattern(localeName);
final String withheldString = withheldNumberFormat.format(withheld);
return '$withheldString W de surplus retenus par la réserve batterie ($soc % sous le seuil de $seuil %) — il y a du surplus, mais une règle interdit de le distribuer.';
}
@override
String decisionBatteryReserve(int soc, int seuil) {
return 'Budget annulé par la réserve batterie ($soc % sous le seuil de $seuil %) — il y a du surplus, mais une règle interdit de le distribuer.';
}
@override
String get decisionBatteryReserveShort =>
'Budget annulé par la réserve batterie — il y a du surplus, mais une règle interdit de le distribuer.';
@override
String get decisionDegradedL2 =>
'Consigne de repli : le compteur est muet et le watchdog a suspendu la planification.';
@override
String get decisionSafeStateRelay =>
'État sûr avant retrait : tous les relais sont ouverts.';
@override
String get decisionSafeStateSetpoint =>
'État sûr avant retrait : consigne ramenée à 0 W.';
@override
String get decisionSafeStateSgReady =>
'État sûr avant retrait : état 2 (normal), jamais le blocage.';
@override
String get faultWriteFailed =>
'Échec d\'écriture : l\'échelle de commande est épuisée, plus aucune commande n\'est émise tant que le défaut n\'est pas levé.';
@override
String get faultThingMissing =>
'L\'appareil commandé n\'existe plus dans nymea — retiré, ou jamais réinstallé après un remplacement. La charge, elle, est toujours déclarée : c\'est pour ça qu\'elle apparaît ici.';
@override
String get faultThingMissingAction =>
'Rien à lever ici : le défaut ne partira pas tant qu\'aucun appareil ne répond. Réinstallez-le dans nymea, ou retirez cette charge de la configuration.';
@override
String get faultUnusableEncoding =>
'Encodage SG-Ready inexploitable : l\'état 2 ne peut pas être exprimé, cette pompe à chaleur n\'est pas pilotable.';
@override
String get faultClearPending =>
'Levée demandée — en attente de la télémétrie…';
@override
String get faultClearStillFaulty =>
'La charge est toujours en défaut après la levée.';
@override
String faultClearRejected(String reason) {
return 'La box a refusé la levée : $reason';
}
@override
String get faultClearHint =>
'La levée est une action délibérée : le défaut ne se lève jamais tout seul.';
@override
String get domainBattery => 'Batterie';
@override
String domainAssignTitle(String label) {
return 'Classer « $label »';
}
@override
String get domainAssignAction => 'Changer de domaine';
@override
String get domainAssignNone => 'Retirer du classement';
@override
String get domainAssignHint =>
'Le domaine est une intention déclarée par l\'installateur. L\'arbitre ne la lit pas : elle ne sert qu\'à grouper l\'ordre de service.';
@override
String localeRequested(String requested, String effective) {
return 'Locale demandée : $requested · retenue par la box : $effective';
}
}