From e87001d0db4e08341f80c01a091e5a130e1b8b5c Mon Sep 17 00:00:00 2001 From: German Khokhlov Date: Wed, 28 Oct 2026 17:55:20 +0400 Subject: [PATCH] i18n(fr): apply review round 2 Co-Authored-By: Claude Opus 5.5 --- docs/translations/fr.md | 124 ++++++++++++++++++---------------- src/i18n/locales/fr.json | 62 ++++++++--------- src/ui/radioBindings.js | 7 ++ src/ui/templates/context.html | 2 +- src/voice/speech.js | 5 +- src/voice/speech.test.mjs | 13 ++++ 6 files changed, 122 insertions(+), 91 deletions(-) diff --git a/docs/translations/fr.md b/docs/translations/fr.md index 695d1b48f..8a10804b0 100644 --- a/docs/translations/fr.md +++ b/docs/translations/fr.md @@ -28,64 +28,72 @@ A `tn()` entry needs three forms. `Intl.PluralRules('fr')` picks: | `many` | 1 000 000, 2 000 000… (exact millions) | 1 000 000 de caméras | | `other` | everything else: 2–999 999, 2,5… | 3 caméras | -The `many` form is the `other` text with « de » (« d' » before a vowel) after the number: « {count} de couches effacées », « {count} D'ENTRÉES INCONNUES ». When the words after the number cannot agree (an UPPERCASE readout, a list), put the number after a colon in every form: « CAPTÉS : {count} ». +The `many` form is the `other` text with « de » (« d' » before a vowel) after the number: « {count} de couches effacées », « {count} D'ENTRÉES INCONNUES ». When the words after the number cannot agree (an UPPERCASE readout, a list), put the number after a colon in every form: « CAPTÉS : {count} ». The same goes for a layer name whose gender and number vary: « Couche activée : {label} », not « {label} activé ». ## Glossary -| English | Français | -| --------------------------------------------------------- | ----------------------------------------------------------------------- | -| layer / data layer / DATA LAYERS (panel title) | couche / couche de données / COUCHES | -| feed / source / data source | flux / source / source de données | -| LIVE (status) / live data | DIRECT / données en direct | -| SIMULATED | SIMULÉ | -| stale / degraded / unavailable | obsolète / dégradé / indisponible | -| retry / retrying | nouvel essai / réessayez | -| rate-limited | limite de requêtes atteinte | -| key (API key) / POWER UP / POWERED UP | clé (clé API) / ALLUMER / ALLUMÉ | -| Provider Settings | paramètres des fournisseurs | -| enable / disable | activer / désactiver | -| globe / map / map source / basemap / map stack | globe / carte / source de carte / fond de carte / fond de carte | -| tile (map) | tuile | -| aircraft / flight / military flights | aéronef (avion in short copy) / vol / vols militaires | -| vessel, ship / tanker (AIS type) | navire / navire-citerne | -| satellite / shell (Starlink) / DENSE | satellite / couche de la constellation / DENSE | -| launch / launch site / space mission | lancement / site de lancement / mission spatiale | -| ascent / replay / payload / stage / downrange | ascension / rejeu / charge utile / étage / distance au sol (DIST. SOL) | -| contact / tracking / track (HUD) | contact / suivi / piste | -| installation / site | installation / site | -| mapped / observed | cartographié / observé | -| camera / CCTV camera / ALPR camera | caméra / caméra CCTV / caméra LAPI | -| earthquake | séisme | -| active fire (FIRMS detection) / wildfire | feu actif / incendie | -| contained / containment (wildfire) | circonscrit / part circonscrite (not « maîtrisé », which is controlled) | -| advisory (NHC/CPHC cyclone) | bulletin | -| forecast / lead hours / valid / issued | prévision / échéances / valide / émise | -| AQHI (Canada) | CQAS (cote air santé) | -| radio station / tune | station / régler | -| bikeshare / transit | vélos partagés / transports en commun | -| route (directions) / turn (maneuver) | itinéraire / manœuvre | -| scene / shot / scene recipe | scène / plan / recette de scène | -| bundle / data pack / anchor | archive / paquet de données / ancre | -| story beat | étape du récit | -| briefing / panel / dock | briefing / panneau / dock | -| cockpit | cockpit | -| preset / visual preset | préréglage / préréglage visuel | -| Scope (circular viewport mask) / keyhole | lunette / ouverture | -| detection overlay | surcouche de détection | -| Clean UI | vue épurée | -| Street Level (layer) / Street View (Google) | Niveau rue / Street View | -| Recent Imagery / box | imagerie récente / zone | -| pin (Draw tool) / pin (Recent Imagery) | épingle / épingler | -| Cyber (HUD layout, effect) | Cyber | -| heading / course (ground track) / altitude / ground speed | cap / route / altitude / vitesse sol | -| HDG / BRG / CRS / FOV / DEST / SPD / ALT | unchanged | -| RANGE / PITCH / HGT (CCTV calibration chips) | PORTÉE / INCL. / HAUT. | -| PREV / PREVIOUS / NEXT (chips) | PRÉC. / PRÉC. / SUIV. | -| Previous / Next (button text) | Précédent / Suivant | -| L / R (relative bearing) | G / D | -| SIG / NEWS / LOCAL (briefing tabs) | SIG / ACTUS / LOCAL | -| compass points (N, NNE, …, W, NW) | N, NNE, …, O, NO | -| NOT AN ALL-CLEAR | PAS UN FEU VERT | -| AI agent | agent IA | -| share (a view or link) | partager | -| click / click to … | cliquez / cliquer pour … | +| English | Français | +| ------------------------------------------------------------ | ----------------------------------------------------------------------- | +| layer / data layer / DATA LAYERS (panel title) | couche / couche de données / COUCHES | +| feed / source / data source | flux / source / source de données | +| LIVE (status) / live data | DIRECT / données en direct | +| SIMULATED | SIMULÉ | +| stale / degraded / unavailable | obsolète / dégradé / indisponible | +| retry / retrying (status: RETRYING X) | nouvel essai / réessayez (NOUVEL ESSAI : X) | +| rate-limited | limite de requêtes atteinte | +| key (API key) / POWER UP / POWERED UP | clé (clé API) / ALLUMER / ALLUMÉ | +| Provider Settings | paramètres des fournisseurs | +| enable / disable | activer / désactiver | +| globe / map / map source / basemap / map stack | globe / carte / source de carte / fond de carte / fond de carte | +| tile (map) | tuile | +| aircraft / flight / military flights | aéronef (avion in short copy) / vol / vols militaires | +| vessel, ship / tanker (AIS type) | navire / navire-citerne | +| satellite / shell (Starlink) / DENSE | satellite / couche de la constellation / DENSE | +| launch / launch site / space mission | lancement / site de lancement / mission spatiale | +| ascent / replay / payload / stage / downrange | ascension / rejeu / charge utile / étage / distance au sol (DIST. SOL) | +| contact / tracking / track (HUD) | contact / suivi / piste | +| installation / site | installation / site | +| mapped / observed | cartographié / observé | +| camera / CCTV camera / ALPR camera | caméra / caméra CCTV / caméra LAPI | +| earthquake | séisme | +| active fire (FIRMS detection) / wildfire | feu actif / incendie | +| contained / containment (wildfire) | circonscrit / part circonscrite (not « maîtrisé », which is controlled) | +| advisory (NHC/CPHC cyclone) | bulletin | +| forecast / lead hours / valid / issued | prévision / échéances / valide / émise | +| AQHI (Canada) | CQAS (cote air santé) | +| radio station / tune | station / régler | +| bikeshare / transit | vélos partagés / transports en commun | +| route (directions) / turn (maneuver) | itinéraire / manœuvre | +| scene / shot / scene recipe | scène / plan / recette de scène | +| bundle / data pack / anchor | archive / paquet de données / ancre | +| story beat | étape du récit | +| briefing / panel / dock | briefing / panneau / dock | +| cockpit | cockpit | +| preset / visual preset | préréglage / préréglage visuel | +| Scope (circular viewport mask) / keyhole | lunette / ouverture | +| detection overlay | surcouche de détection | +| Clean UI | vue épurée | +| Street Level (layer) / Street View (Google) | Niveau rue / Street View | +| Recent Imagery / box | imagerie récente / zone | +| pin (Draw tool) / pin (Recent Imagery) | épingle / épingler | +| Cyber (HUD layout, effect) | Cyber | +| heading / course (ground track) / altitude / ground speed | cap / route / altitude / vitesse sol | +| HDG / BRG / CRS / FOV / DEST / SPD / ALT | unchanged | +| RANGE / PITCH / HGT (CCTV calibration chips) | PORTÉE / INCL. / HAUT. | +| PREV / PREVIOUS / NEXT (chips) | PRÉC. / PRÉC. / SUIV. | +| Previous / Next (button text) | Précédent / Suivant | +| L / R (relative bearing) | G / D | +| SIG / NEWS / LOCAL (briefing tabs) | SIG / ACTUS / LOCAL | +| compass points (N, NNE, …, W, NW) | N, NNE, …, O, NO | +| NOT AN ALL-CLEAR | PAS UN FEU VERT | +| AI agent | agent IA | +| share (a view or link) | partager | +| click / click to … | cliquez / cliquer pour … | +| map heading (compass, north-up control) | orientation (de la carte) | +| subject (cockpit / shared contact) | cible | +| cloud cover / CLOUD (briefing readout) | nébulosité / NÉBULOSITÉ | +| LEVEL (cockpit horizon guide) | HORIZON | +| CALIBRATED / CURATED / RAW PRIOR (CCTV pose) | CALIBRÉE / VÉRIFIÉE / ESTIM. INITIALE | +| PORT TENDER (AIS type) | SERVITUDE PORT. (navire de servitude portuaire) | +| ChatGPT sign-in (action) / opening sign-in / sign-in expired | connexion / page de connexion / la session a expiré | +| authored shot (scene) | plan scénarisé | diff --git a/src/i18n/locales/fr.json b/src/i18n/locales/fr.json index 883621f94..f90c1d50e 100644 --- a/src/i18n/locales/fr.json +++ b/src/i18n/locales/fr.json @@ -169,7 +169,7 @@ "other": "{count} SOURCES" }, "OUTSIDE IMAGERY SWIPE": "HORS DE LA COMPARAISON D'IMAGES", - "{scene} / AUTHORED SHOT": "{scene} / PLAN CONÇU", + "{scene} / AUTHORED SHOT": "{scene} / PLAN SCÉNARISÉ", "NEPAL FLOOD INCIDENT": "CRUE AU NÉPAL", "Play {shot}": "Lire {shot}", "current scene shot": "le plan actuel de la scène", @@ -242,7 +242,7 @@ "Map tiles temporarily unavailable": "Tuiles de la carte temporairement indisponibles", "Mapped names temporarily unavailable": "Noms cartographiés temporairement indisponibles", "Overpass temporarily unavailable": "Overpass temporairement indisponible", - "Retrying mapped sites…": "Nouvel essai des sites cartographiés…", + "Retrying mapped sites…": "Nouvel essai : sites cartographiés…", "Fetching mapped sites…": "Récupération des sites cartographiés…", "{reason} — retrying in {seconds}s": "{reason} — nouvel essai dans {seconds} s", "{reason} — retry pending": "{reason} — nouvel essai en attente", @@ -286,7 +286,7 @@ "PILOT": "PILOTINE", "SAR": "SAR", "TUG": "REMORQUEUR", - "PORT TENDER": "SERVITUDE", + "PORT TENDER": "SERVITUDE PORT.", "ANTI-POLLUTION": "ANTIPOLLUTION", "LAW ENFORCE": "POLICE", "MEDICAL": "MÉDICAL", @@ -414,8 +414,8 @@ "Checking local ChatGPT OAuth sign-in…": "Vérification de la connexion OAuth ChatGPT locale…", "Could not check ChatGPT sign-in.": "Impossible de vérifier la connexion ChatGPT.", "ChatGPT OAuth selected for cloud voice. Your API key stays saved and available.": "OAuth ChatGPT sélectionné pour la voix cloud. Votre clé API reste enregistrée et disponible.", - "ChatGPT sign-in expired. Opening sign-in again…": "La connexion ChatGPT a expiré. Nouvelle ouverture de la connexion…", - "Opening ChatGPT sign-in in your browser…": "Ouverture de la connexion ChatGPT dans votre navigateur…", + "ChatGPT sign-in expired. Opening sign-in again…": "La session ChatGPT a expiré. Réouverture de la page de connexion…", + "Opening ChatGPT sign-in in your browser…": "Ouverture de la page de connexion ChatGPT dans votre navigateur…", "Could not start ChatGPT sign-in on this machine.": "Impossible de lancer la connexion ChatGPT sur cette machine.", "Finish ChatGPT sign-in in the browser. Waiting for it to complete…": "Terminez la connexion ChatGPT dans le navigateur. En attente de la fin…", "ChatGPT sign-in timed out. Click {button} to try again.": "Délai de connexion ChatGPT dépassé. Cliquez sur « {button} » pour réessayer.", @@ -473,7 +473,7 @@ "many": "{count} à proximité", "other": "{count} à proximité" }, - "retrying mapped ALPR cameras": "nouvel essai des caméras LAPI cartographiées", + "retrying mapped ALPR cameras": "nouvel essai : caméras LAPI cartographiées", "loading mapped ALPR cameras": "chargement des caméras LAPI cartographiées", "No ALPR data for this area — US and Canada only": "Aucune donnée LAPI pour cette zone — États-Unis et Canada seulement", "Zoom in to load mapped cameras": "Zoomez pour charger les caméras cartographiées", @@ -634,8 +634,8 @@ "At the end of the road, turn {direction}": "Au bout de la route, tournez {direction}", "At the end of the road, continue onto {road}": "Au bout de la route, continuez sur {road}", "At the end of the road, continue": "Au bout de la route, continuez", - "Keep {direction} at the fork onto {road}": "À l'embranchement, restez {direction} sur {road}", - "Keep {direction} at the fork": "À l'embranchement, restez {direction}", + "Keep {direction} at the fork onto {road}": "À l'embranchement, continuez {direction} sur {road}", + "Keep {direction} at the fork": "À l'embranchement, continuez {direction}", "Continue at the fork onto {road}": "À l'embranchement, continuez sur {road}", "Continue at the fork": "À l'embranchement, continuez", "Merge {direction} onto {road}": "Insérez-vous {direction} sur {road}", @@ -815,7 +815,7 @@ "ORBIT REPLAY · {mission}": "REJEU DE L'ORBITE · {mission}", "PAUSED · {title}": "PAUSE · {title}", "UNSPECIFIED": "NON PRÉCISÉ", - "+{count} additional payload records": "+{count} enregistrements de charge utile supplémentaires", + "+{count} additional payload records": "Enregistrements de charge utile supplémentaires : +{count}", "PAYLOAD DATA UNAVAILABLE": "DONNÉES DE CHARGE UTILE INDISPONIBLES", "{value} KM DOWNRANGE": "DIST. SOL : {value} KM", "POSITION UNAVAILABLE": "POSITION INDISPONIBLE", @@ -1005,7 +1005,7 @@ "Imagery catalog unavailable": "Catalogue d'images indisponible", "Catalog truncated · a newer clear day may exist": "Catalogue tronqué · un jour dégagé plus récent peut exister", "{label} catalog unavailable · {detail}": "Catalogue {label} indisponible · {detail}", - "Daily overview for today may still be empty until the pass": "La vue d'ensemble quotidienne peut rester vide jusqu'au passage du satellite", + "Daily overview for today may still be empty until the pass": "La vue quotidienne d'aujourd'hui peut rester vide jusqu'au passage du satellite", "Box and images cleared": "Zone et images effacées", "MORE DETAIL · 30 m": "PLUS DE DÉTAIL · 30 m", "Sentinel-2 and Landsat via HLS, 30 m, every 2–6 days": "Sentinel‑2 et Landsat via HLS, 30 m, tous les 2 à 6 jours", @@ -1238,7 +1238,7 @@ "Vivid": "Forte", "Image opacity; does not alter the observed values": "Opacité de l'image ; ne modifie pas les valeurs observées", "View US radar": "Voir le radar des États-Unis", - "View Americas & Pacific": "Voir Amériques et Pacifique", + "View Americas & Pacific": "Voir les Amériques et le Pacifique", "View coverage": "Voir la couverture", "{value} strikes/km²/min ×10³ (15-minute density)": "{value} impacts/km²/min ×10³ (densité sur 15 minutes)", "{value} dBZ radar reflectivity": "{value} dBZ de réflectivité radar", @@ -1246,7 +1246,7 @@ "Map center is outside source coverage": "Le centre de la carte est hors de la couverture de la source", "NOAA/NWS 15-minute lightning density derived from Vaisala NLDN/GLD360. Coverage 110°E across the Pacific/Americas to 0°, 25°S–80°N. Not a live strike count, global coverage or a safety warning.": "Densité de foudre sur 15 minutes de NOAA/NWS, dérivée de Vaisala NLDN/GLD360. Couverture de 110°E à 0° à travers le Pacifique et les Amériques, 25°S–80°N. Ce n'est ni un décompte d'impacts en direct, ni une couverture mondiale, ni une alerte de sécurité.", "NOAA MRMS radar echoes indicate precipitation patterns, not rain rate, a storm warning or a future forecast. Native source approximately 1 km; display is limited to level 6. Frames use exact advertised observation times.": "Les échos radar NOAA MRMS indiquent la structure des précipitations, pas l'intensité de la pluie, une alerte d'orage ou une prévision. Résolution native d'environ 1 km ; l'affichage est limité au niveau 6. Les images utilisent les heures d'observation exactes publiées.", - "GOES-19/18 longwave infrared Band 14 regional; NESDIS global longwave mosaic. Clouds only dims everything but bright, cold cloud tops; a brightness filter, not a cloud mask. Coverage and freshness differ by region.": "Infrarouge grandes longueurs d'onde GOES-19/18, bande 14, régional ; mosaïque mondiale grandes longueurs d'onde NESDIS. Nuages seuls assombrit tout sauf les sommets nuageux brillants et froids ; c'est un filtre de luminosité, pas un masque de nuages. La couverture et la fraîcheur varient selon la région.", + "GOES-19/18 longwave infrared Band 14 regional; NESDIS global longwave mosaic. Clouds only dims everything but bright, cold cloud tops; a brightness filter, not a cloud mask. Coverage and freshness differ by region.": "Infrarouge grandes longueurs d'onde GOES-19/18, bande 14, régional ; mosaïque mondiale grandes longueurs d'onde NESDIS. « Nuages seuls » assombrit tout sauf les sommets nuageux brillants et froids ; c'est un filtre de luminosité, pas un masque de nuages. La couverture et la fraîcheur varient selon la région.", "REGION": "RÉGION", "IMAGE": "IMAGE", "FILTER": "FILTRE", @@ -1254,7 +1254,7 @@ "OPACITY": "OPACITÉ", "Infrared decode failed": "Échec du décodage infrarouge", "Weather prefetch unavailable": "Préchargement météo indisponible", - "Weather tiles unavailable · previous frame retained": "Tuiles météo indisponibles · image précédente conservée", + "Weather tiles unavailable · previous frame retained": "Tuiles indisp. · image précédente conservée", "Some weather tiles unavailable": "Certaines tuiles météo indisponibles", "Malformed weather manifest": "Manifeste météo mal formé", "Weather request timed out": "Délai dépassé pour la requête météo", @@ -1552,7 +1552,7 @@ "Copy failed": "Échec de la copie", "Return map to straight-down view": "Remettre la carte en vue verticale", "Tilt map to oblique view": "Incliner la carte en vue oblique", - "Reset map to north up. Current heading {heading} degrees": "Réorienter la carte nord en haut. Cap actuel {heading} degrés", + "Reset map to north up. Current heading {heading} degrees": "Réorienter la carte nord en haut. Orientation actuelle : {heading} degrés", "Tilted view": "Vue inclinée", "Straight-down view": "Vue verticale", "North up": "Nord en haut", @@ -1596,7 +1596,7 @@ "FRAME · LOADING": "IMAGE · CHARGEMENT", "FRAME · UNAVAILABLE": "IMAGE · INDISPONIBLE", "CALIBRATED": "CALIBRÉE", - "CURATED": "SÉLECTIONNÉE", + "CURATED": "VÉRIFIÉE", "RAW PRIOR": "ESTIM. INITIALE", "CCTV ON": "CCTV ON", "CCTV OFF": "CCTV OFF", @@ -1651,17 +1651,17 @@ "NO RECENT LOCATION MATCHES": "AUCUNE ACTU RÉCENTE POUR CE LIEU", "{domain} · {age}": "{domain} · {age}", "{value} KM/H": "{value} KM/H", - "CLOUD {pct}%": "NUAGES {pct} %", - "CLOUD UNKNOWN": "NUAGES INCONNUS", + "CLOUD {pct}%": "NÉBULOSITÉ {pct} %", + "CLOUD UNKNOWN": "NÉBULOSITÉ INCONNUE", "{source} · LOCATION QUERY": "{source} · REQUÊTE DE LIEU", "REGIONAL NEWS": "ACTUS RÉGIONALES", "{label} · {radius} AIR/SEA WINDOW": "{label} · AIR/MER {radius}", "{scope} · INSTALLATIONS {coverage}": "{scope} · INSTALLATIONS : {coverage}", "CONTEXT STANDBY": "CONTEXTE EN VEILLE", "ENABLE GLOBAL CONTEXT FOR PROXIMITY PINGS": "ACTIVER LE CONTEXTE GLOBAL POUR LES PINGS DE PROXIMITÉ", - "CONTACT LOST · LAST KNOWN READOUT · NOT AN ALL-CLEAR": "CONTACT PERDU · DERNIÈRE POSITION CONNUE · PAS UN FEU VERT", + "CONTACT LOST · LAST KNOWN READOUT · NOT AN ALL-CLEAR": "CONTACT PERDU · DERNIER RELEVÉ CONNU · PAS UN FEU VERT", "CONTACT LOST · {subject}": "CONTACT PERDU · {subject}", - "SUBJECT LEFT ITS FEED · READOUT HOLDING LAST KNOWN": "SUJET SORTI DE SON FLUX · AFFICHAGE SUR LA DERNIÈRE POSITION CONNUE", + "SUBJECT LEFT ITS FEED · READOUT HOLDING LAST KNOWN": "CIBLE ABSENTE DU FLUX · DERNIER RELEVÉ CONSERVÉ", "{cohort} · {contact}": "{cohort} · {contact}", "NO AVAILABLE EXAMPLE": "AUCUN EXEMPLE DISPONIBLE", "{cohort}, Unavailable": "{cohort}, indisponible", @@ -1848,7 +1848,7 @@ "{state} · {source} · {detail}": "{state} · {source} · {detail}", "never": "jamais", "{source} · {loadingLabel}": "{source} · {loadingLabel}", - "{accepted} of {raw} records accepted": "{accepted} enregistrements acceptés sur {raw}", + "{accepted} of {raw} records accepted": "Enregistrements acceptés : {accepted} sur {raw}", "incomplete snapshot": "instantané incomplet", "{state} · {source} · {detail} · {ago}": "{state} · {source} · {detail} · {ago}", "{state} · {source} · {ago} · retrying in {seconds}s": "{state} · {source} · {ago} · nouvel essai dans {seconds} s", @@ -1980,7 +1980,7 @@ "PLAY": "LIRE", "selected": "sélectionnée", "nearest": "la plus proche", - "{action} {target} radio station": "{action} la station radio {target}", + "{action} {target} radio station": "{action} : station radio {target}", "RADIO STATE UNCERTAIN": "ÉTAT RADIO INCERTAIN", "SYNCING DIRECTORY": "SYNCHRO DE L'ANNUAIRE", "RADIO READY": "RADIO PRÊTE", @@ -2080,7 +2080,7 @@ "start here": "commencer ici", "No box": "Aucune zone", "Searching": "Recherche", - "Every day is empty here": "Tous les jours sont vides ici", + "Every day is empty here": "Aucune image trouvée ici", "Sources off": "Sources désactivées", "VS": "VS", "Not set": "Non défini", @@ -2102,7 +2102,7 @@ "Times, coverage, sources": "Heures, couverture, sources", "HIDE EMPTY DAYS": "MASQUER LES JOURS VIDES", "SHOW EMPTY DAYS · {count}": "AFFICHER LES JOURS VIDES · {count}", - "Days the probe found empty in this box": "Jours que la sonde a trouvés vides dans cette zone", + "Days the probe found empty in this box": "Jours sans image trouvée dans cette zone", "image": "image", "BASEMAP": "FOND DE CARTE", "The basemap": "Le fond de carte", @@ -2160,9 +2160,9 @@ "LOAD FAILED": "ÉCHEC DU CHARGEMENT", "LIVE DATA OFF": "DONNÉES EN DIRECT DÉSACTIVÉES", "MAPPED SITES LOADED": "SITES CARTOGRAPHIÉS CHARGÉS", - "RETRYING MAPPED SITES": "NOUVEL ESSAI SITES CARTOGRAPHIÉS", + "RETRYING MAPPED SITES": "NOUVEL ESSAI : SITES CARTOGRAPHIÉS", "FETCHING MAPPED SITES": "RÉCUPÉRATION DES SITES CARTOGRAPHIÉS", - "RETRYING ALPR CAMERAS": "NOUVEL ESSAI CAMÉRAS LAPI", + "RETRYING ALPR CAMERAS": "NOUVEL ESSAI : CAMÉRAS LAPI", "FETCHING ALPR CAMERAS": "RÉCUPÉRATION DES CAMÉRAS LAPI", "OVERPASS TEMPORARILY UNAVAILABLE": "OVERPASS TEMPORAIREMENT INDISPONIBLE", "OVERPASS RATE-LIMITED": "OVERPASS : LIMITE DE REQUÊTES ATTEINTE", @@ -2374,10 +2374,10 @@ "Gemini token response did not include a valid model": "La réponse de Gemini ne contenait pas de modèle valide", "Gemini token response did not include session settings": "La réponse de Gemini ne contenait pas les paramètres de session", "Gemini session settings do not match the input mode": "Les paramètres de session Gemini ne correspondent pas au mode de saisie", - "Requesting microphone": "Demande du microphone", + "Requesting microphone": "Demande d'accès au microphone", "Gemini could not submit this input. Start voice again.": "Gemini n'a pas pu envoyer cette saisie. Relancez la voix.", "Connecting to Gemini": "Connexion à Gemini", - "Ask or command": "Question ou ordre", + "Ask or command": "Question ou commande", "Gemini returned an oversized message": "Gemini a renvoyé un message trop volumineux", "Gemini Live connection failed. Check network access, then start voice again.": "Échec de la connexion à Gemini Live. Vérifiez l'accès au réseau, puis relancez la voix.", "Gemini Live connection ended. Start voice again for a new conversation.": "La connexion à Gemini Live est terminée. Relancez la voix pour une nouvelle conversation.", @@ -2394,7 +2394,7 @@ "Gemini action history is full. Start voice again for a fresh session.": "L'historique des actions de Gemini est plein. Relancez la voix pour une nouvelle session.", "Gemini requested too many simultaneous actions": "Gemini a demandé trop d'actions simultanées", "Running command": "Commande en cours", - "Voice text command is too long": "La commande vocale écrite est trop longue", + "Voice text command is too long": "La commande écrite est trop longue", "Gemini voice is not connected": "La voix Gemini n'est pas connectée", "Gemini interrupted input exceeded its buffer or wait limit. Start voice again.": "La saisie interrompue de Gemini a dépassé sa limite de tampon ou d'attente. Relancez la voix.", "Gemini Live connection is expiring. Start voice again for a new conversation.": "La connexion à Gemini Live expire. Relancez la voix pour une nouvelle conversation.", @@ -2523,7 +2523,7 @@ "the flights layer loads by viewport": "la couche des vols se charge selon la vue", "distance is ground distance, not overhead": "la distance est mesurée au sol jusqu'au point subsatellite, pas jusqu'au satellite", "the count is a lower bound because at least one layer reached its retained limit.": "le décompte est un minimum, car au moins une couche a atteint sa limite de rétention.", - "{label} off": "{label} : désactivé", + "{label} off": "Couche désactivée : {label}", "{label} loading": "{label} : chargement", "{label} error": "{label} : erreur", "counted the first {returned} loaded records": "{returned} premiers enregistrements chargés comptés", @@ -2576,7 +2576,7 @@ "{name} is no longer loaded": "{name} n'est plus chargé", "Unknown target layer: {target}": "Couche cible inconnue : {target}", "nominal": "nominal", - "{label} on": "{label} : activé", + "{label} on": "Couche activée : {label}", "Frame aircraft": "Cadrer les aéronefs", "Frame military aircraft": "Cadrer les aéronefs militaires", "Frame ships": "Cadrer les navires", @@ -2693,7 +2693,7 @@ "Recent speech": "Propos récents", "HERE": "ICI", "Aircraft cockpit view": "Vue cockpit de l'aéronef", - "LEVEL": "NIVEAU", + "LEVEL": "HORIZON", "Estimated destination direction": "Direction estimée de la destination", "DEST ---°": "DEST ---°", "OPTICAL PLANE · 01": "PLAN OPTIQUE · 01", diff --git a/src/ui/radioBindings.js b/src/ui/radioBindings.js index 88756d5bc..4e70764c6 100644 --- a/src/ui/radioBindings.js +++ b/src/ui/radioBindings.js @@ -9,6 +9,13 @@ import { /** Bind Radio input using a RadioControls receiver and supplied actions. */ export function bindRadioControls() { if (!this._radioPanel) return; + // The markup's placeholder option is the station-tag "All"; markup cannot + // carry the 'radio tag' context the loaded list uses, so set it here. + const allTagOption = this._radioFilter?.querySelector?.( + 'option[value="all"]', + ); + if (allTagOption) + allTagOption.textContent = t('All', { context: 'radio tag' }); const tunerListenerOptions = {}; const setRadioDisclosure = (expanded, { returnFocus = false } = {}) => { if (this.destroyed) return; diff --git a/src/ui/templates/context.html b/src/ui/templates/context.html index 9e88d20f4..e83d37aa1 100644 --- a/src/ui/templates/context.html +++ b/src/ui/templates/context.html @@ -122,7 +122,7 @@
diff --git a/src/voice/speech.js b/src/voice/speech.js index c53881595..cbfae621f 100644 --- a/src/voice/speech.js +++ b/src/voice/speech.js @@ -963,7 +963,10 @@ export function progressStepLabel(step, label) { : step ? spokenLabel(step, 24) : t('Working'); - return label ? `${base}: ${spokenLabel(label, 32)}` : base; + // The join is a catalog string so each language spaces its colon. + return label + ? t('{name}: {state}', { name: base, state: spokenLabel(label, 32) }) + : base; } /** diff --git a/src/voice/speech.test.mjs b/src/voice/speech.test.mjs index 36482290d..cc11fef6e 100644 --- a/src/voice/speech.test.mjs +++ b/src/voice/speech.test.mjs @@ -9,6 +9,7 @@ import { spokenLabel, } from './speech.js'; import { countWords, findHedges } from './speechLint.js'; +import { setLocale } from '../i18n/index.js'; function envelope(name, result, args) { return attachVoiceResult(name, result, args); @@ -459,3 +460,15 @@ test('labels are bounded and cleaned before they are spoken', () => { 'background steps stay on screen', ); }); + +test('progress step labels join through the catalog colon', () => { + setLocale('fr'); + try { + assert.equal( + progressStepLabel('resolve', 'Capitol'), + 'Recherche de lieux\u202f: Capitol', + ); + } finally { + setLocale('en'); + } +});