etm-powersync-app/lib/l10n/app_localizations_fr.dart
Patrick Schurig dee20896c5 feat(télémétrie): lot B app — consommation de la télémétrie d'arbitrage
Le lot plugin B-bis (1.15.2+etm15) est sur .75 et GetLoadConfig/GetLoadTelemetry
concordent : les critères 4 à 8 du brief deviennent atteignables.

- models/load_telemetry.dart : vue en lecture. timestamp/budget/mechanism/lock/
  faultCode nullables — omis porte un sens que zéro ne porterait pas.
- services/telemetry_text.dart : rendu français des CODES (19 motifs, 3 défauts,
  3 verrous, 4 mécanismes). Repli lisible sur un code inconnu ET sur un code connu
  privé d'un paramètre : une phrase trouée a l'air d'une donnée, le code brut a
  l'air de ce qu'il est.
- providers/load_telemetry_provider.dart : instantané + LoadTelemetryChanged,
  fraîcheur à trois signaux (âge du timestamp, silence des trames, timestamp qui
  cesse d'avancer), machine à états de ClearLoadFault qui ne conclut JAMAIS sur
  l'acquittement du RPC.
- load_config_provider : assignDomain() en brouillon, énumération fermée.
- load_order_screen : budget, fraîcheur, mode dégradé, bloc par charge, encart
  défaut, menu de domaine.
- nymea_service : getLoadTelemetry(), clearLoadFault(), aiguillage de la
  notification. Une box sans la méthode s'annonce comme telle, pas comme en panne.

Seuil de fraîcheur à 180 s : le battement de cœur du plugin est de 60 s et le
timestamp avance d'un cycle par minute. Un seuil plus court crierait « données
anciennes » sur une installation vivante.

Fixtures v3 prises sur .75 après déploiement, dont un cycle porteur d'un verrou —
« lock » étant optionnel, un dump pris au mauvais moment ne l'éprouverait pas.

57 tests (30 avant), flutter analyze inchangé à 25 issues.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XPUo3RMr8SzK6qbFtfBm8H
2026-08-25 19:20:30 +02:00

522 lines
16 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 => 'Réservé au véhicule';
@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 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 'Le budget de $budgetString W ne payait aucun palier — aucun verrou n\'a mordu.';
}
@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 'Pompe à chaleur en état 2 (normal, sans consigne) — $budgetString W de budget.';
}
@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é est absent de la configuration nymea.';
@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';
}
}