From efe700fef8ddf095aa76da0ca5f7987d5f1c1bc8 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 18 Oct 2026 12:30:44 +0000 Subject: [PATCH 01/24] feat(i18n): add machine-assisted French language pack draft Spec 019 T096/T097 draft: fr/meta.json plus a French translation of all 2646 entries in fr/app.json, with sourceHash stamped from the English reference. Machine-assisted and awaiting native French review before it can be marked done. Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: Claude --- i18n/locales/fr/app.json | 13232 ++++++++++++++++++++++++++++++++++++ i18n/locales/fr/meta.json | 6 + 2 files changed, 13238 insertions(+) create mode 100644 i18n/locales/fr/app.json create mode 100644 i18n/locales/fr/meta.json diff --git a/i18n/locales/fr/app.json b/i18n/locales/fr/app.json new file mode 100644 index 000000000..fca382371 --- /dev/null +++ b/i18n/locales/fr/app.json @@ -0,0 +1,13232 @@ +{ + "audit.access.allowed": { + "defaultMessage": "Autorisé", + "description": "Audit log: access column for a 2xx or 3xx response", + "sourceHash": "1bb201d18835" + }, + "audit.access.denied": { + "defaultMessage": "Refusé", + "description": "Audit log: access column for a 403 response", + "sourceHash": "da404deb110f" + }, + "audit.access.error": { + "defaultMessage": "Erreur", + "description": "Audit log: access column for a 5xx response", + "sourceHash": "54a0e8c17ebb" + }, + "audit.access.failed": { + "defaultMessage": "Échec", + "description": "Audit log: access column for a non-403 4xx response", + "sourceHash": "031a8f0f659d" + }, + "audit.action.failed_sign_in": { + "defaultMessage": "Échec de connexion", + "description": "Audit log: action label for a sign-in attempt that was rejected", + "sourceHash": "e740dbb83365" + }, + "audit.action.fallback": { + "defaultMessage": "{action} {resource}", + "description": "Audit log: fallback action label for an unrecognised route; {action} is the verb, {resource} is the last path segment", + "sourceHash": "556bd310f3e3" + }, + "audit.action.object": { + "defaultMessage": "{action} {noun}{target}", + "description": "Audit log: action label such as 'Created server alpha'; {action} is the verb, {noun} is the object type, {target} is empty or a space followed by the object name", + "sourceHash": "ad99bf7d2fdd" + }, + "audit.action.restored_backup": { + "defaultMessage": "Backup restauré{target}", + "description": "Audit log: action label for a backup restore; {target} is empty or a space followed by the backup name", + "sourceHash": "a6ba7c1bbe7f" + }, + "audit.action.settings_updated": { + "defaultMessage": "Paramètres mis à jour", + "description": "Audit log: action label for a change to panel settings", + "sourceHash": "ef593bf59fb4" + }, + "audit.action.signed_in": { + "defaultMessage": "Connexion réussie", + "description": "Audit log: action label for a successful sign-in", + "sourceHash": "ca566c8968e7" + }, + "audit.noun.backup": { + "defaultMessage": "backup", + "description": "Audit log: object type in an action label, a backup", + "sourceHash": "54d00d867758" + }, + "audit.noun.backup_destination": { + "defaultMessage": "destination de backup", + "description": "Audit log: object type in an action label, a backup destination", + "sourceHash": "99cecfa57d89" + }, + "audit.noun.backup_schedule": { + "defaultMessage": "planification de backup", + "description": "Audit log: object type in an action label, a backup schedule", + "sourceHash": "edc68fe447db" + }, + "audit.noun.cluster": { + "defaultMessage": "cluster", + "description": "Audit log: object type in an action label, a cluster", + "sourceHash": "164cf1ca8391" + }, + "audit.noun.module": { + "defaultMessage": "module", + "description": "Audit log: object type in an action label, a module", + "sourceHash": "120970d81283" + }, + "audit.noun.module_source": { + "defaultMessage": "source de modules", + "description": "Audit log: object type in an action label, a module source", + "sourceHash": "9bd535ac63d7" + }, + "audit.noun.restore": { + "defaultMessage": "restore", + "description": "Audit log: object type in an action label, a restore", + "sourceHash": "f329e3a317ee" + }, + "audit.noun.role": { + "defaultMessage": "rôle", + "description": "Audit log: object type in an action label, a role", + "sourceHash": "4b168d88dc87" + }, + "audit.noun.server": { + "defaultMessage": "serveur", + "description": "Audit log: object type in an action label, a game server", + "sourceHash": "b3eacd33433b" + }, + "audit.noun.user": { + "defaultMessage": "utilisateur", + "description": "Audit log: object type in an action label, a user account", + "sourceHash": "04f8996da763" + }, + "audit.verb.cloned": { + "defaultMessage": "Cloné", + "description": "Audit log: action verb for cloning a game server", + "sourceHash": "05909025b56a" + }, + "audit.verb.created": { + "defaultMessage": "Créé", + "description": "Audit log: action verb for a POST request", + "sourceHash": "d70b9e24bca2" + }, + "audit.verb.deleted": { + "defaultMessage": "Supprimé", + "description": "Audit log: action verb for a DELETE request", + "sourceHash": "b48ff39c2e0f" + }, + "audit.verb.restarted": { + "defaultMessage": "Redémarré", + "description": "Audit log: action verb for restarting a game server", + "sourceHash": "170c587465b8" + }, + "audit.verb.started": { + "defaultMessage": "Démarré", + "description": "Audit log: action verb for starting a game server", + "sourceHash": "ecbc89cd37a0" + }, + "audit.verb.stopped": { + "defaultMessage": "Arrêté", + "description": "Audit log: action verb for stopping a game server", + "sourceHash": "1a4f630ac1b6" + }, + "audit.verb.transferred_ownership_of": { + "defaultMessage": "Transfert de propriété de", + "description": "Audit log: action verb for transferring a game server's ownership", + "sourceHash": "34914a98b573" + }, + "audit.verb.updated": { + "defaultMessage": "Modifié", + "description": "Audit log: action verb for a PUT or PATCH request", + "sourceHash": "3a5ecca188c0" + }, + "audit.verb.viewed": { + "defaultMessage": "Consulté", + "description": "Audit log: action verb for a GET request", + "sourceHash": "1b28d17855a7" + }, + "audit.verb.wiped_data_on": { + "defaultMessage": "Données effacées sur", + "description": "Audit log: action verb for wiping a game server's world data", + "sourceHash": "dcc4cdcc6b87" + }, + "condition.addressassignment.addressinuse": { + "defaultMessage": "L'adresse demandée ''{address}'' est déjà utilisée par le GameServer ''{server}''.", + "description": "AddressAssignment condition when another game server holds the requested address.", + "sourceHash": "ce9c0d5bfed5" + }, + "condition.addressassignment.allocationfailed": { + "defaultMessage": "Le gestionnaire d'adresses n'a pas pu allouer d'adresse : {detail}", + "description": "AddressAssignment condition when the address manager reports an allocation failure. detail is the address manager's own English message.", + "sourceHash": "3474e3b97a5b" + }, + "condition.addressassignment.assigned": { + "defaultMessage": "{assignedFrom, select, pool {Adresse {address} attribuée depuis le pool ''{pool}''.} other {Adresse {address} attribuée.}}", + "description": "AddressAssignment condition once an address is assigned. assignedFrom is pool or default.", + "sourceHash": "b6d424bfe291" + }, + "condition.addressassignment.assignmentpending": { + "defaultMessage": "Demande {requestKind, select, addressAndPool {adresse ''{address}'' du pool ''{pool}''} address {adresse ''{address}''} other {pool ''{pool}''}} : le gestionnaire d'adresses n'a pas encore attribué d'adresse.", + "description": "AddressAssignment condition while the address manager works.", + "sourceHash": "c980d6907507" + }, + "condition.addressassignment.ignoredforexposuremode": { + "defaultMessage": "La demande {requestKind, select, addressAndPool {adresse ''{address}'' du pool ''{pool}''} address {adresse ''{address}''} other {pool ''{pool}''}} est ignorée : elle ne s'applique qu'au mode d'exposition LoadBalancer, et ce serveur est exposé en mode ''{expose}''.", + "description": "AddressAssignment condition when an address or pool is requested for a server that is not exposed as a LoadBalancer. expose is the current expose mode.", + "sourceHash": "1a0b92d332d4" + }, + "condition.addressassignment.invalidaddress": { + "defaultMessage": "L'adresse demandée ''{address}'' n'est pas une adresse IP valide.", + "description": "AddressAssignment condition when the requested address does not parse.", + "sourceHash": "4c1bf7242a85" + }, + "condition.addressassignment.noaddressmanagerconfigured": { + "defaultMessage": "La demande {requestKind, select, addressAndPool {adresse ''{address}'' du pool ''{pool}''} address {adresse ''{address}''} other {pool ''{pool}''}} ne peut pas être honorée : ce cluster n'a aucun gestionnaire d'adresses LoadBalancer configuré, donc toute adresse provient de la politique d'attribution par défaut.", + "description": "AddressAssignment condition when no address manager (MetalLB or Cilium) is configured.", + "sourceHash": "377808e48153" + }, + "condition.addressassignment.poolnotfound": { + "defaultMessage": "Le pool d'adresses demandé ''{pool}'' est introuvable.", + "description": "AddressAssignment condition when the requested address pool does not exist.", + "sourceHash": "468ae2841bdf" + }, + "condition.addressassignment.servicenotready": { + "defaultMessage": "Demande {requestKind, select, addressAndPool {adresse ''{address}'' du pool ''{pool}''} address {adresse ''{address}''} other {pool ''{pool}''}} : en attente de la création du Service LoadBalancer qui porte la demande.", + "description": "AddressAssignment condition before the load-balancer Service exists.", + "sourceHash": "1e26e7413bfe" + }, + "condition.completed.failed": { + "defaultMessage": "Le backup ou le restore a échoué.", + "description": "Completed condition on a Backup or Restore that failed. The message holds the English detail.", + "sourceHash": "81278711d1d5" + }, + "condition.completed.jobfailed": { + "defaultMessage": "La tâche de backup a signalé un échec.", + "description": "Completed condition on a Backup whose job failed.", + "sourceHash": "4fd32faf1235" + }, + "condition.completed.pending": { + "defaultMessage": "Le backup attend de démarrer.", + "description": "Completed condition on a Backup that has not started (dynamic: reason is the phase).", + "sourceHash": "fcd4ee5f73b3" + }, + "condition.completed.running": { + "defaultMessage": "Le backup est en cours.", + "description": "Completed condition on a Backup in progress (dynamic: reason is the phase).", + "sourceHash": "6045c2e3f230" + }, + "condition.completed.succeeded": { + "defaultMessage": "Le backup ou le restore s'est terminé avec succès.", + "description": "Completed condition on a Backup or Restore that finished.", + "sourceHash": "ed54690bd16d" + }, + "condition.datawipe.jobfailed": { + "defaultMessage": "La tâche d'effacement des données ne s'est pas terminée. Le volume n'est peut-être pas entièrement vidé.", + "description": "DataWipe condition when the wipe job fails.", + "sourceHash": "f1c100d69216" + }, + "condition.failed.capture_already_in_progress": { + "defaultMessage": "Une autre capture est déjà en cours sur ce serveur.", + "description": "Failed condition when two captures target one server.", + "sourceHash": "8c6c3fb2a7bb" + }, + "condition.failed.failed": { + "defaultMessage": "La capture a échoué.", + "description": "Failed condition on a NetworkCapture; the message holds the English detail.", + "sourceHash": "a2f668070978" + }, + "condition.failed.podrestarted": { + "defaultMessage": "Le pod de jeu a redémarré pendant la capture.", + "description": "Failed condition when the pod was replaced during a capture.", + "sourceHash": "3db5afb8b7de" + }, + "condition.failed.sidecarcrashed": { + "defaultMessage": "Le sidecar de capture s'est arrêté de façon inattendue.", + "description": "Failed condition when the capture container died.", + "sourceHash": "9ae83a2d983a" + }, + "condition.filecleanupfailed.delete_failed": { + "defaultMessage": "La suppression du fichier de capture a échoué. Il peut rester au-delà de sa durée de rétention.", + "description": "Condition when the capture file could not be deleted.", + "sourceHash": "51005ec809bc" + }, + "condition.healthy.agentfresh": { + "defaultMessage": "L'agent envoie des heartbeats.", + "description": "Healthy condition while heartbeats arrive.", + "sourceHash": "a6fd7eaf64d5" + }, + "condition.healthy.agentstale": { + "defaultMessage": "L'agent n'a pas envoyé de heartbeat récemment.", + "description": "Healthy condition when heartbeats stopped.", + "sourceHash": "093593fa9e26" + }, + "condition.healthy.badkubeconfig": { + "defaultMessage": "Le kubeconfig n'a pas pu être utilisé.", + "description": "Healthy condition when the kubeconfig does not parse.", + "sourceHash": "13a8f957a226" + }, + "condition.healthy.discoveryclientfailed": { + "defaultMessage": "La connexion au cluster a échoué.", + "description": "Healthy condition when the discovery client cannot be built.", + "sourceHash": "75c4e9b394ee" + }, + "condition.healthy.failed": { + "defaultMessage": "Le serveur a échoué.", + "description": "Healthy condition when the server has failed.", + "sourceHash": "acf24503f4d7" + }, + "condition.healthy.healthcheckfailed": { + "defaultMessage": "La vérification de l'état du cluster a échoué.", + "description": "Healthy condition when the cluster does not answer.", + "sourceHash": "8b0a68910555" + }, + "condition.healthy.healthcheckpassed": { + "defaultMessage": "Le cluster est accessible et en bon état.", + "description": "Healthy condition on a registered cluster.", + "sourceHash": "0d012043f3fd" + }, + "condition.healthy.idleasleep": { + "defaultMessage": "Le serveur est en veille, car aucun joueur n'est connecté.", + "description": "Healthy condition for an idle-sleeping server.", + "sourceHash": "6f7cc70e71d3" + }, + "condition.healthy.invalidsecret": { + "defaultMessage": "Le secret kubeconfig n'a pas le label requis.", + "description": "Healthy condition when the secret lacks the cluster-kubeconfig label.", + "sourceHash": "2717464b2f7f" + }, + "condition.healthy.missingkey": { + "defaultMessage": "Le secret kubeconfig ne contient pas la clé attendue.", + "description": "Healthy condition when the kubeconfig key is absent.", + "sourceHash": "86219e1b8a84" + }, + "condition.healthy.namereserved": { + "defaultMessage": "Le nom de cluster \"local\" est réservé.", + "description": "Healthy condition when a cluster is registered with the reserved name.", + "sourceHash": "379906af89f0" + }, + "condition.healthy.pending": { + "defaultMessage": "Le serveur n'a pas encore démarré.", + "description": "Healthy condition before the first start.", + "sourceHash": "f15721dbf277" + }, + "condition.healthy.secretnotfound": { + "defaultMessage": "Le secret kubeconfig est introuvable.", + "description": "Healthy condition when the kubeconfig secret is missing.", + "sourceHash": "ca64433466f1" + }, + "condition.healthy.stopping": { + "defaultMessage": "Le serveur s'arrête.", + "description": "Healthy condition while stopping.", + "sourceHash": "c31c663342a6" + }, + "condition.healthy.suspended": { + "defaultMessage": "Le serveur est arrêté.", + "description": "Healthy condition for a stopped server.", + "sourceHash": "f9885c66186d" + }, + "condition.healthy.unknown": { + "defaultMessage": "L'état du serveur n'est pas encore connu.", + "description": "Healthy condition before the operator has observed the server.", + "sourceHash": "46c8dd841d10" + }, + "condition.idlescheduleinvalid.wakewindowunparseable": { + "defaultMessage": "Une fenêtre de réveil de la planification d'inactivité ne peut pas être analysée : {details}", + "description": "Condition when spec.idle has an invalid wake window. details is the English parse error.", + "sourceHash": "d9f4c4d6f354" + }, + "condition.localesvalid.invalidentries": { + "defaultMessage": "Certaines entrées de traduction du module sont invalides et ont été omises.", + "description": "LocalesValid condition on a GameTemplate when translation entries or files were dropped. The message holds the English per-language counts.", + "sourceHash": "aafc8a43b8b9" + }, + "condition.localesvalid.localestoolarge": { + "defaultMessage": "Certaines traductions du module ont été omises, car elles sont trop volumineuses.", + "description": "LocalesValid condition on a GameTemplate when a language file is over the size limit or the translations together exceed it. The message names the languages.", + "sourceHash": "e4e96e76ac69" + }, + "condition.localesvalid.valid": { + "defaultMessage": "Toutes les traductions du module sont valides.", + "description": "LocalesValid condition on a GameTemplate when every translation entry shipped with the module is valid.", + "sourceHash": "095c2c20c60f" + }, + "condition.localesvalid.writefailed": { + "defaultMessage": "Les traductions du module n'ont pas pu être enregistrées.", + "description": "LocalesValid condition on a GameTemplate when the translations ConfigMap could not be written. The message holds the English error.", + "sourceHash": "3a1641137307" + }, + "condition.progressing.containercreating": { + "defaultMessage": "Création du conteneur.", + "description": "Progressing detail while the kubelet creates the container.", + "sourceHash": "edb096f289fd" + }, + "condition.progressing.containerexited": { + "defaultMessage": "Le conteneur de jeu s'est arrêté avec le code {exitCode} pendant le démarrage. Consultez les journaux.", + "description": "Progressing condition when the game container exits during startup. exitCode is the process exit code.", + "sourceHash": "285469ad15a0" + }, + "condition.progressing.crashloopbackoff": { + "defaultMessage": "Le conteneur de jeu plante en boucle au démarrage ({restartCount} redémarrages jusqu'ici). Consultez les journaux.", + "description": "Progressing condition when the game container crash-loops. restartCount is the number of restarts.", + "sourceHash": "c3f03c820eb1" + }, + "condition.progressing.failed": { + "defaultMessage": "Le serveur a cessé d'essayer de démarrer.", + "description": "Progressing condition when startup has failed.", + "sourceHash": "6ce9634bd887" + }, + "condition.progressing.idleasleep": { + "defaultMessage": "Le serveur est en veille, car aucun joueur n'est connecté.", + "description": "Progressing condition for an idle-sleeping server.", + "sourceHash": "6f7cc70e71d3" + }, + "condition.progressing.imagepullfailed": { + "defaultMessage": "L'image de jeu ne peut pas être téléchargée. Vérifiez la référence de l'image.", + "description": "Progressing condition when the container image pull keeps failing.", + "sourceHash": "61e2c23bf32f" + }, + "condition.progressing.installingserverfiles": { + "defaultMessage": "Téléchargement des fichiers de jeu ou attente que le serveur soit prêt.", + "description": "Progressing detail during first-run installation.", + "sourceHash": "f74cde7a00e6" + }, + "condition.progressing.pending": { + "defaultMessage": "Le serveur attend d'être planifié.", + "description": "Progressing condition while the pod is waiting for a node.", + "sourceHash": "3c8d6159f198" + }, + "condition.progressing.pullingimage": { + "defaultMessage": "Téléchargement de l'image de jeu.", + "description": "Progressing detail while the container image downloads.", + "sourceHash": "6cf3e9d16729" + }, + "condition.progressing.pvcprovisioningfailed": { + "defaultMessage": "Le stockage ne peut pas être provisionné : le PVC ''{pvc}'' nécessite la StorageClass ''{storageClass}'', qui n'existe pas sur ce cluster.", + "description": "Progressing condition when the data volume cannot be created. pvc is the volume claim name, storageClass the missing class.", + "sourceHash": "14d2e2271d93" + }, + "condition.progressing.stable": { + "defaultMessage": "Le serveur est stable et rien ne change.", + "description": "Progressing condition once the server is running.", + "sourceHash": "0f39cdeb41a3" + }, + "condition.progressing.starting": { + "defaultMessage": "Le serveur démarre.", + "description": "Progressing condition while the pod starts.", + "sourceHash": "464274f8476d" + }, + "condition.progressing.stopping": { + "defaultMessage": "Le serveur s'arrête.", + "description": "Progressing condition while the pod stops.", + "sourceHash": "c31c663342a6" + }, + "condition.progressing.suspended": { + "defaultMessage": "Le serveur est arrêté et aucune opération n'est en cours.", + "description": "Progressing condition for a stopped server.", + "sourceHash": "d37d5eeddff3" + }, + "condition.progressing.unknown": { + "defaultMessage": "L'état du serveur n'est pas encore connu.", + "description": "Progressing condition before the operator has observed the server.", + "sourceHash": "46c8dd841d10" + }, + "condition.progressing.waitingforagent": { + "defaultMessage": "Le serveur est lancé. En attente du premier heartbeat de l'agent.", + "description": "Progressing detail once the game container runs but the agent has not reported yet.", + "sourceHash": "1933077e2851" + }, + "condition.pulling.applied": { + "defaultMessage": "Le téléchargement du module est terminé.", + "description": "Pulling condition once the template was applied.", + "sourceHash": "46f0e43f69bd" + }, + "condition.pulling.applytemplate": { + "defaultMessage": "L'application du template du module a échoué.", + "description": "Module condition when the GameTemplate cannot be written.", + "sourceHash": "3cbc868aaca6" + }, + "condition.pulling.digestmismatch": { + "defaultMessage": "Le contenu du module ne correspond pas à son digest épinglé.", + "description": "Module condition when the digest differs from the pin.", + "sourceHash": "8d6db66b3246" + }, + "condition.pulling.incompatibleoperator": { + "defaultMessage": "Le module nécessite un opérateur Gameplane plus récent.", + "description": "Module condition when gameplaneMinVersion is not met.", + "sourceHash": "9710ee351178" + }, + "condition.pulling.noversionavailable": { + "defaultMessage": "La source de modules n'a aucune version disponible.", + "description": "Module condition when no version can be chosen.", + "sourceHash": "9af8ef8603bd" + }, + "condition.pulling.pullfailed": { + "defaultMessage": "Le téléchargement du module a échoué.", + "description": "Module condition when the OCI pull fails.", + "sourceHash": "3cb170f7e82e" + }, + "condition.pulling.pulling": { + "defaultMessage": "Téléchargement du module.", + "description": "Pulling condition while a pull runs.", + "sourceHash": "b807e2e19f54" + }, + "condition.pulling.signatureinvalid": { + "defaultMessage": "La signature du module n'a pas pu être vérifiée.", + "description": "Module condition when cosign verification fails.", + "sourceHash": "e69d1daeabb9" + }, + "condition.pulling.sourceconfig": { + "defaultMessage": "La configuration de la source de modules est invalide.", + "description": "Module condition for a broken source setup.", + "sourceHash": "92e320a3c1fb" + }, + "condition.pulling.sourcenotfound": { + "defaultMessage": "La source de modules est introuvable.", + "description": "Module condition when the named ModuleSource does not exist.", + "sourceHash": "c671e879a084" + }, + "condition.pulling.verifyconfig": { + "defaultMessage": "Les paramètres de vérification de la signature sont invalides.", + "description": "Module condition for a bad verification setup.", + "sourceHash": "3d84bdf1c513" + }, + "condition.pulling.versionunavailable": { + "defaultMessage": "La version demandée n'est pas dans le catalogue.", + "description": "Module condition when the pinned version is missing.", + "sourceHash": "a4b327ee30da" + }, + "condition.pulling.waitingforcatalog": { + "defaultMessage": "En attente de l'indexation de ce module par la source de modules.", + "description": "Module condition while the source has no catalog entry yet.", + "sourceHash": "4618394ce7f4" + }, + "condition.ready.applied": { + "defaultMessage": "Le template du module a été appliqué.", + "description": "Ready condition on a Module after its GameTemplate was written.", + "sourceHash": "fbaef629aa65" + }, + "condition.ready.applytemplate": { + "defaultMessage": "L'application du template du module a échoué.", + "description": "Module condition when the GameTemplate cannot be written.", + "sourceHash": "3cbc868aaca6" + }, + "condition.ready.catalogpopulated": { + "defaultMessage": "Le catalogue des modules est rempli.", + "description": "Ready condition on a source with a catalog.", + "sourceHash": "7c2024da3ff7" + }, + "condition.ready.containerexited": { + "defaultMessage": "Le conteneur de jeu s'est arrêté avec le code {exitCode} pendant le démarrage. Consultez les journaux.", + "description": "Ready condition when the game container exits during startup. exitCode is the process exit code.", + "sourceHash": "285469ad15a0" + }, + "condition.ready.crashloopbackoff": { + "defaultMessage": "Le conteneur de jeu plante en boucle au démarrage ({restartCount} redémarrages jusqu'ici). Consultez les journaux.", + "description": "Ready condition when the game container crash-loops. restartCount is the number of restarts.", + "sourceHash": "c3f03c820eb1" + }, + "condition.ready.digestmismatch": { + "defaultMessage": "Le contenu du module ne correspond pas à son digest épinglé.", + "description": "Module condition when the digest differs from the pin.", + "sourceHash": "8d6db66b3246" + }, + "condition.ready.failed": { + "defaultMessage": "{detail}", + "description": "Ready condition when the server cannot start. detail is the English reason from the operator, for example a missing template or an invalid configuration.", + "sourceHash": "caf3c2c2d54b" + }, + "condition.ready.idleasleep": { + "defaultMessage": "Le serveur est en veille, car aucun joueur n'est connecté.", + "description": "Ready condition for a server put to sleep by idle auto-sleep.", + "sourceHash": "6f7cc70e71d3" + }, + "condition.ready.imagepullfailed": { + "defaultMessage": "L'image de jeu ne peut pas être téléchargée. Vérifiez la référence de l'image.", + "description": "Ready condition when the container image pull keeps failing.", + "sourceHash": "61e2c23bf32f" + }, + "condition.ready.incompatibleoperator": { + "defaultMessage": "Le module nécessite un opérateur Gameplane plus récent.", + "description": "Module condition when gameplaneMinVersion is not met.", + "sourceHash": "9710ee351178" + }, + "condition.ready.inuse": { + "defaultMessage": "Le module ne peut pas être supprimé tant que des serveurs de jeu utilisent son template.", + "description": "Ready condition when deletion is blocked.", + "sourceHash": "eff91a04c90c" + }, + "condition.ready.noversionavailable": { + "defaultMessage": "La source de modules n'a aucune version disponible.", + "description": "Module condition when no version can be chosen.", + "sourceHash": "9af8ef8603bd" + }, + "condition.ready.pending": { + "defaultMessage": "Le serveur attend d'être démarré.", + "description": "Ready condition while the game server is being created.", + "sourceHash": "5024651146af" + }, + "condition.ready.pullfailed": { + "defaultMessage": "Le téléchargement du module a échoué.", + "description": "Module condition when the OCI pull fails.", + "sourceHash": "3cb170f7e82e" + }, + "condition.ready.pvcprovisioningfailed": { + "defaultMessage": "Le stockage ne peut pas être provisionné : le PVC ''{pvc}'' nécessite la StorageClass ''{storageClass}'', qui n'existe pas sur ce cluster.", + "description": "Ready condition when the data volume cannot be created. pvc is the volume claim name, storageClass the missing class.", + "sourceHash": "14d2e2271d93" + }, + "condition.ready.running": { + "defaultMessage": "Le serveur est en cours d'exécution et l'agent envoie des heartbeats.", + "description": "Ready condition while the game server is up.", + "sourceHash": "bc04c0dc0fd1" + }, + "condition.ready.servingstalecatalog": { + "defaultMessage": "Le catalogue en cache est utilisé. La dernière tentative d'indexation a échoué.", + "description": "Ready condition while an older catalog stays available.", + "sourceHash": "b7cdaf3e61d9" + }, + "condition.ready.signatureinvalid": { + "defaultMessage": "La signature du module n'a pas pu être vérifiée.", + "description": "Module condition when cosign verification fails.", + "sourceHash": "e69d1daeabb9" + }, + "condition.ready.sourceconfig": { + "defaultMessage": "La configuration de la source de modules est invalide.", + "description": "Module condition for a broken source setup.", + "sourceHash": "92e320a3c1fb" + }, + "condition.ready.sourcenotfound": { + "defaultMessage": "La source de modules est introuvable.", + "description": "Module condition when the named ModuleSource does not exist.", + "sourceHash": "c671e879a084" + }, + "condition.ready.sourceunreachable": { + "defaultMessage": "La source de modules n'a jamais été indexée avec succès.", + "description": "Ready condition on a source that cannot be reached.", + "sourceHash": "a365503b3359" + }, + "condition.ready.starting": { + "defaultMessage": "Le serveur démarre.", + "description": "Ready condition while the pod is starting up.", + "sourceHash": "464274f8476d" + }, + "condition.ready.stopping": { + "defaultMessage": "Le serveur s'arrête.", + "description": "Ready condition while the pod is shutting down.", + "sourceHash": "c31c663342a6" + }, + "condition.ready.suspended": { + "defaultMessage": "Le serveur est arrêté.", + "description": "Ready condition for a server that was stopped.", + "sourceHash": "f9885c66186d" + }, + "condition.ready.unknown": { + "defaultMessage": "L'état du serveur n'est pas encore connu.", + "description": "Ready condition before the operator has observed the server.", + "sourceHash": "46c8dd841d10" + }, + "condition.ready.verifyconfig": { + "defaultMessage": "Les paramètres de vérification de la signature sont invalides.", + "description": "Module condition for a bad verification setup.", + "sourceHash": "3d84bdf1c513" + }, + "condition.ready.versionunavailable": { + "defaultMessage": "La version demandée n'est pas dans le catalogue.", + "description": "Module condition when the pinned version is missing.", + "sourceHash": "a4b327ee30da" + }, + "condition.ready.waitingforcatalog": { + "defaultMessage": "En attente de l'indexation de ce module par la source de modules.", + "description": "Module condition while the source has no catalog entry yet.", + "sourceHash": "4618394ce7f4" + }, + "condition.retentionstopfailed.sidecar_stop_failed": { + "defaultMessage": "L'arrêt du sidecar de capture à la fin de la rétention a échoué.", + "description": "Condition when the retention-expiry stop failed.", + "sourceHash": "1bf2ca9bc80f" + }, + "condition.retentiontrimmed.trimfailed": { + "defaultMessage": "L'application de la politique de rétention a échoué.", + "description": "RetentionTrimmed condition when old backups could not be removed.", + "sourceHash": "2fa1a4345931" + }, + "condition.retentiontrimmed.trimsucceeded": { + "defaultMessage": "La politique de rétention a été appliquée.", + "description": "RetentionTrimmed condition after a successful trim.", + "sourceHash": "c389ba94d579" + }, + "condition.schedulevalid.cronparseerror": { + "defaultMessage": "La planification n'est pas une expression cron valide.", + "description": "ScheduleValid condition for an unparseable schedule.", + "sourceHash": "29456803cb5a" + }, + "condition.schedulevalid.cronvalid": { + "defaultMessage": "La planification est une expression cron valide.", + "description": "ScheduleValid condition for a good schedule.", + "sourceHash": "249f72efe2f7" + }, + "condition.sidecarstopfailed.sidecar_stop_failed": { + "defaultMessage": "L'arrêt du sidecar de capture a échoué.", + "description": "Condition when the stop request to the sidecar failed.", + "sourceHash": "7f6aa735661a" + }, + "condition.sidecarstopped.never_started": { + "defaultMessage": "La capture a été arrêtée avant son démarrage.", + "description": "SidecarStopped condition when a pending capture was cancelled.", + "sourceHash": "090e5f7f7c79" + }, + "condition.sidecarstopped.stopped": { + "defaultMessage": "Le sidecar de capture a reçu l'ordre de s'arrêter.", + "description": "SidecarStopped condition after a stop request.", + "sourceHash": "5aa96d5643e4" + }, + "condition.snapshotforgotten.forgetabandoned": { + "defaultMessage": "Le snapshot n'a pas été supprimé du dépôt de backup lorsque le backup a été supprimé.", + "description": "SnapshotForgotten condition when the forget step was abandoned.", + "sourceHash": "04703d88c7b9" + }, + "condition.snapshotforgotten.forgotten": { + "defaultMessage": "Le snapshot a été supprimé du dépôt de backup.", + "description": "SnapshotForgotten condition after a deleted Backup's snapshot was forgotten.", + "sourceHash": "fea1a2b54ea8" + }, + "condition.synced.indexed": { + "defaultMessage": "La source de modules a été indexée.", + "description": "Synced condition after a successful index.", + "sourceHash": "ab32284d4cac" + }, + "condition.synced.indexfailed": { + "defaultMessage": "L'indexation de la source de modules a échoué.", + "description": "Synced condition after a failed index.", + "sourceHash": "22e0f09ed361" + }, + "condition.tunneladdressinvalid.validationfailed": { + "defaultMessage": "Certains points de terminaison du tunnel ont été rejetés : {details}", + "description": "Condition when endpoints reported by the playit tunnel pod fail validation. details is the English validation text.", + "sourceHash": "6470f0544ab3" + }, + "condition.tunnelhostnameignored.settingignored": { + "defaultMessage": "Ces paramètres s'appliquent au Service sous-jacent, et non au trafic du tunnel : {settings}.", + "description": "Informational condition when hostname, source ranges or node ports are set on a server with a tunnel. settings is a comma-separated list of field names.", + "sourceHash": "da6f52aef121" + }, + "condition.tunnelready.deploymentnotready": { + "defaultMessage": "En attente de la disponibilité du Deployment du tunnel.", + "description": "TunnelReady condition while the tunnel pod is not ready.", + "sourceHash": "f78325e29ccc" + }, + "condition.tunnelready.invalidconfig": { + "defaultMessage": "{provider, select, none {Le tunnel est activé, mais le serveur n'a aucune configuration de tunnel.} other {Le fournisseur de tunnel ''{provider}'' est sélectionné, mais sa configuration est manquante.}}", + "description": "TunnelReady condition for an incomplete tunnel setup. provider is the selected provider, or none.", + "sourceHash": "b5507bfe2784" + }, + "condition.tunnelready.nocredentials": { + "defaultMessage": "Aucun secret d'identifiants n'a été fourni, donc le tunnel ''{provider}'' ne peut pas s'authentifier.", + "description": "TunnelReady condition when a tunnel provider needs credentials that are missing.", + "sourceHash": "8a1d3aa3cb65" + }, + "condition.tunnelready.portnotmapped": { + "defaultMessage": "Aucun port distant frp n'est configuré pour : {ports}.", + "description": "TunnelReady condition for frp when advertised ports have no remote port. ports is a comma-separated list of port names.", + "sourceHash": "cf3421f74b54" + }, + "condition.tunnelready.ready": { + "defaultMessage": "{provider, select, playit {Le Deployment du tunnel est prêt. En attente de l'attribution du point de terminaison playit.} tailscale {Le Deployment du tunnel est prêt et authentifié.} other {Le Deployment du tunnel est prêt et tous les ports sont mappés.}}", + "description": "TunnelReady condition when the tunnel works.", + "sourceHash": "6168f444396b" + }, + "condition.tunnelready.tunnelcredentialrefused": { + "defaultMessage": "Le secret d'identifiants du tunnel ''{secret}'' n'appartient pas à ce GameServer. {remedy, select, defaultName {Ajoutez un ownerReference de ce GameServer au Secret, ou supprimez le Secret et enregistrez à nouveau l'identifiant depuis le tableau de bord.} other {Enregistrez l'identifiant depuis le tableau de bord, ou ajoutez un ownerReference de ce GameServer au Secret.}}", + "description": "TunnelReady condition when the operator refuses to mount a credentials secret. secret is the secret name, remedy is defaultName or other.", + "sourceHash": "e6eaf872d117" + }, + "condition.tunnelready.unknownprovider": { + "defaultMessage": "Fournisseur de tunnel inconnu ''{provider}''.", + "description": "TunnelReady condition when the provider name is not supported.", + "sourceHash": "3ce0f9c66837" + }, + "condition.unquiesced.failed": { + "defaultMessage": "Le serveur n'a pas pu reprendre après le backup. La sauvegarde automatique de son monde est peut-être restée désactivée.", + "description": "Unquiesced condition when the unfreeze failed.", + "sourceHash": "e86bb8b5faec" + }, + "condition.unquiesced.unquiesced": { + "defaultMessage": "Le serveur a repris après le backup.", + "description": "Unquiesced condition when the world was unfrozen.", + "sourceHash": "f578002a1303" + }, + "notify.detail.reason_message": { + "defaultMessage": "{reason} — {message}", + "description": "Body line that joins the Kubernetes reason and message of an event. The reason and message are quoted from Kubernetes in English and stay untranslated. The dash is an em dash.", + "sourceHash": "c91b66f037ce" + }, + "notify.detail.test": { + "defaultMessage": "Ceci est une notification de test de Gameplane. Votre destination est correctement configurée.", + "description": "Body of a test notification sent from Admin Settings.", + "sourceHash": "52623c7cda52" + }, + "notify.event.backup.failed": { + "defaultMessage": "échec du backup", + "description": "Event name used inside a notification headline when a backup fails.", + "sourceHash": "ae8fb1bebc80" + }, + "notify.event.backup.succeeded": { + "defaultMessage": "backup réussi", + "description": "Event name used inside a notification headline when a backup succeeds.", + "sourceHash": "13f24c7665e5" + }, + "notify.event.restore.failed": { + "defaultMessage": "échec du restore", + "description": "Event name used inside a notification headline when a restore fails.", + "sourceHash": "87466483f633" + }, + "notify.event.restore.succeeded": { + "defaultMessage": "restore réussi", + "description": "Event name used inside a notification headline when a restore succeeds.", + "sourceHash": "f616fa8a4a87" + }, + "notify.event.server.recovered": { + "defaultMessage": "serveur rétabli", + "description": "Event name used inside a notification headline when a game server recovers.", + "sourceHash": "f7d97576372b" + }, + "notify.event.server.unhealthy": { + "defaultMessage": "serveur en mauvais état", + "description": "Event name used inside a notification headline when a game server becomes unhealthy.", + "sourceHash": "9dae2f44e86f" + }, + "notify.event.test": { + "defaultMessage": "notification de test", + "description": "Event name used inside the headline of a test notification.", + "sourceHash": "2ecb59da1e4f" + }, + "notify.title.cluster": { + "defaultMessage": "[{instance}] {event} : {cluster}/{namespace}/{name}", + "description": "Headline of a notification about a named resource on a remote cluster. instance is the Gameplane instance name, event is the event name (for example \"backup failed\"), cluster is the cluster name, namespace and name identify the resource.", + "sourceHash": "a5a954837853" + }, + "notify.title.named": { + "defaultMessage": "[{instance}] {event} : {namespace}/{name}", + "description": "Headline of a notification about a named resource. instance is the Gameplane instance name, event is the event name (for example \"backup failed\"), namespace and name identify the resource.", + "sourceHash": "79c79d758dc8" + }, + "notify.title.unnamed": { + "defaultMessage": "[{instance}] {event}", + "description": "Headline of a notification without a resource, such as a test send. instance is the Gameplane instance name, event is the event name.", + "sourceHash": "129c13bcf821" + }, + "server.already_exists": { + "defaultMessage": "existe déjà", + "description": "API error when creating a resource whose name is already taken.", + "sourceHash": "3214256d6e1a" + }, + "server.auth.csrf_mismatch": { + "defaultMessage": "incohérence CSRF", + "description": "API error from the session layer (sessions.go:Authenticate).", + "sourceHash": "3cb4d1166311" + }, + "server.auth.invalid_credentials": { + "defaultMessage": "identifiants invalides", + "description": "Login error. It is generic on purpose, so it never reveals whether an account exists.", + "sourceHash": "8310ebc38b28" + }, + "server.auth_providers.client_secret_required": { + "defaultMessage": "clientSecret est requis", + "description": "API error from the auth provider credentials API (auth_provider_secret.go:put).", + "sourceHash": "962b894a21a2" + }, + "server.bad_request": { + "defaultMessage": "requête incorrecte", + "description": "API error for a malformed request that does not fit a more specific message.", + "sourceHash": "c7ccdfa9e932" + }, + "server.cluster_forbidden": { + "defaultMessage": "cluster non autorisé", + "description": "API error when the ?cluster= query value names a cluster the caller may not use or that is unknown.", + "sourceHash": "ba9c18dc6c95" + }, + "server.common.invalid_json": { + "defaultMessage": "JSON invalide", + "description": "API error when a request body is not valid JSON.", + "sourceHash": "9e1ff4275ee1" + }, + "server.common.namespace_not_permitted": { + "defaultMessage": "namespace non autorisé", + "description": "API error when the caller's role does not cover the requested namespace (400 form, returned by the permission check and the role-binding API).", + "sourceHash": "880214660c29" + }, + "server.common.too_many_requests": { + "defaultMessage": "trop de requêtes", + "description": "API error when a signed-in user sends requests faster than the per-user rate limit allows; retry later.", + "sourceHash": "2c419cbe186f" + }, + "server.common.unauthenticated": { + "defaultMessage": "non authentifié", + "description": "API error when a route that needs a signed-in user is called without a valid session.", + "sourceHash": "b6545eef9260" + }, + "server.common.user_not_found": { + "defaultMessage": "utilisateur introuvable", + "description": "API error when the user named in the request does not exist.", + "sourceHash": "036932891ffe" + }, + "server.config.read_body": { + "defaultMessage": "lecture du corps", + "description": "API error from the admin config API (config.go:put).", + "sourceHash": "d406c2891de5" + }, + "server.config.unknown_section": { + "defaultMessage": "section inconnue", + "description": "API error from the admin config API (config.go:put).", + "sourceHash": "45489b716774" + }, + "server.conflict_retry": { + "defaultMessage": "conflit, réessayez", + "description": "API error when a concurrent write changed the resource; the caller should re-read and retry.", + "sourceHash": "546461e01863" + }, + "server.empty_body": { + "defaultMessage": "corps vide", + "description": "API error when a route that expects a JSON body receives none.", + "sourceHash": "f40dad1f5ab4" + }, + "server.forbidden": { + "defaultMessage": "interdit", + "description": "API error when the caller, or the API's service identity in Kubernetes, is not allowed to perform the operation.", + "sourceHash": "43ed5c457b79" + }, + "server.generic.400": { + "defaultMessage": "La requête n'a pas été acceptée.", + "description": "Generic sentence shown before the detail text for an HTTP 400 response.", + "sourceHash": "5cf289c144dd" + }, + "server.generic.401": { + "defaultMessage": "Vous n'êtes pas connecté.", + "description": "Generic sentence shown before the detail text for an HTTP 401 response.", + "sourceHash": "f98ef69eefc2" + }, + "server.generic.403": { + "defaultMessage": "Vous n'avez pas la permission de faire cela.", + "description": "Generic sentence shown before the detail text for an HTTP 403 response.", + "sourceHash": "f2ed3ec2290f" + }, + "server.generic.404": { + "defaultMessage": "L'élément demandé est introuvable.", + "description": "Generic sentence shown before the detail text for an HTTP 404 response.", + "sourceHash": "3cbc1838812b" + }, + "server.generic.409": { + "defaultMessage": "La requête est en conflit avec l'état actuel.", + "description": "Generic sentence shown before the detail text for an HTTP 409 response.", + "sourceHash": "7dbbfe6e5be8" + }, + "server.generic.413": { + "defaultMessage": "La requête est trop volumineuse.", + "description": "Generic sentence shown before the detail text for an HTTP 413 response.", + "sourceHash": "83c5a651418b" + }, + "server.generic.415": { + "defaultMessage": "Le type de requête n'est pas pris en charge.", + "description": "Generic sentence shown before the detail text for an HTTP 415 response.", + "sourceHash": "f7e1c1085485" + }, + "server.generic.422": { + "defaultMessage": "La requête contient des valeurs non valides.", + "description": "Generic sentence shown before the detail text for an HTTP 422 response.", + "sourceHash": "eb0e16ce3004" + }, + "server.generic.429": { + "defaultMessage": "Trop de requêtes. Veuillez patienter, puis réessayer.", + "description": "Generic sentence shown before the detail text for an HTTP 429 response.", + "sourceHash": "02f43f163768" + }, + "server.generic.500": { + "defaultMessage": "Le serveur a rencontré une erreur interne.", + "description": "Generic sentence shown before the detail text for an HTTP 500 response.", + "sourceHash": "e475ee4c2338" + }, + "server.generic.501": { + "defaultMessage": "Ceci n'est pas pris en charge ici.", + "description": "Generic sentence shown before the detail text for an HTTP 501 response.", + "sourceHash": "f4f25fde31e8" + }, + "server.generic.502": { + "defaultMessage": "Un service en amont a renvoyé une erreur.", + "description": "Generic sentence shown before the detail text for an HTTP 502 response.", + "sourceHash": "14dec3b80eb6" + }, + "server.generic.503": { + "defaultMessage": "Le service est temporairement indisponible.", + "description": "Generic sentence shown before the detail text for an HTTP 503 response.", + "sourceHash": "b904d1e3b1ee" + }, + "server.i18n.unknown_language": { + "defaultMessage": "langue inconnue : {language}", + "description": "Shown when a user picks a language this release does not offer. language = the language tag that was sent.", + "sourceHash": "6d2ea6bac3c9" + }, + "server.i18n.unlisted_language": { + "defaultMessage": "langue non disponible : {language}", + "description": "Shown when an admin picks an instance default language that is not offered (missing or below the completeness threshold). language = the tag.", + "sourceHash": "22b4091c82a4" + }, + "server.internal": { + "defaultMessage": "erreur interne", + "description": "API error for an unexpected server failure. The real error is logged on the server only.", + "sourceHash": "5ca568fafc62" + }, + "server.invalid": { + "defaultMessage": "{detail}", + "description": "API validation error. The argument detail is the validation message from Kubernetes, in English, and describes the caller's input.", + "sourceHash": "caf3c2c2d54b" + }, + "server.lifecycle.confirmation_mismatch": { + "defaultMessage": "la confirmation ne correspond pas au nom du serveur", + "description": "API error from the server lifecycle API (lifecycle.go:wipeDataHandler).", + "sourceHash": "606a5a570ce8" + }, + "server.lifecycle.new_name_required": { + "defaultMessage": "newName requis", + "description": "API error from the server lifecycle API (lifecycle.go:cloneHandler).", + "sourceHash": "05842a7a8cbc" + }, + "server.namespace_forbidden": { + "defaultMessage": "namespace non autorisé", + "description": "API error when the user's role does not cover the requested namespace.", + "sourceHash": "880214660c29" + }, + "server.not_found": { + "defaultMessage": "introuvable", + "description": "API error when the requested resource does not exist.", + "sourceHash": "907ba78b4545" + }, + "server.notifications.sink_name_not_dns_label": { + "defaultMessage": "le nom de la destination doit être un label DNS (minuscules, chiffres, tirets)", + "description": "API error from the notification sinks API (notifications.go:deleteSecret, notifications.go:putSecret).", + "sourceHash": "2ed704024480" + }, + "server.ownership.invalid_request": { + "defaultMessage": "requête invalide", + "description": "API error from the server ownership API (ownership.go:setCollaborators).", + "sourceHash": "8daf3a877523" + }, + "server.ownership.user_id_required": { + "defaultMessage": "userId requis", + "description": "API error from the server ownership API (ownership.go:transfer).", + "sourceHash": "987c37b84822" + }, + "server.ownership.user_not_found_400": { + "defaultMessage": "utilisateur introuvable", + "description": "API error from the server ownership API (ownership.go:setCollaborators).", + "sourceHash": "036932891ffe" + }, + "server.registry.api_key_required": { + "defaultMessage": "apiKey est requis", + "description": "API error from the mod-registry credentials API (registry_secret.go:put).", + "sourceHash": "5c5c7589e94d" + }, + "server.remote_cluster_not_implemented": { + "defaultMessage": "non implémenté : aucun agent inter-cluster pour le moment ; cette route ne dessert que le cluster d'origine", + "description": "API response for a home-cluster-only route called with a ?cluster= value that names another cluster. No cross-cluster agent exists yet.", + "sourceHash": "dbcb3db64f43" + }, + "server.roles.admin_not_editable": { + "defaultMessage": "le rôle admin ne peut pas être modifié", + "description": "API error from the roles API (roles.go:update).", + "sourceHash": "0701cabef591" + }, + "server.roles.builtin_not_deletable": { + "defaultMessage": "les rôles intégrés ne peuvent pas être supprimés", + "description": "API error from the roles API (roles.go:del).", + "sourceHash": "c4f152bbb0cd" + }, + "server.roles.invalid_role_name": { + "defaultMessage": "nom de rôle invalide", + "description": "API error from the roles API (roles.go:create).", + "sourceHash": "24f8e57b185a" + }, + "server.roles.remote_bindings_block_control_plane": { + "defaultMessage": "supprimez les liaisons distantes à l'échelle du cluster avant d'ajouter des permissions du plan de contrôle", + "description": "API error from the roles API (roles.go:update).", + "sourceHash": "4f81f9797d26" + }, + "server.roles.role_already_exists": { + "defaultMessage": "le rôle existe déjà", + "description": "API error from the roles API (roles.go:create).", + "sourceHash": "20daf78b866e" + }, + "server.roles.role_in_use": { + "defaultMessage": "le rôle est attribué à un ou plusieurs utilisateurs", + "description": "API error from the roles API (roles.go:del).", + "sourceHash": "6ac6292832eb" + }, + "server.roles.role_not_found": { + "defaultMessage": "rôle introuvable", + "description": "API error from the roles API (roles.go:del, roles.go:update).", + "sourceHash": "07ba515a3db3" + }, + "server.shares.expires_at_in_past": { + "defaultMessage": "expiresAt doit être dans le futur", + "description": "API error from the share-links API (shares.go:createShareHandler).", + "sourceHash": "b5c56c00f879" + }, + "server.shares.expiry_mutually_exclusive": { + "defaultMessage": "expiresAt et neverExpires s'excluent mutuellement", + "description": "API error from the share-links API (shares.go:createShareHandler).", + "sourceHash": "d794466b9d03" + }, + "server.shares.expiry_required": { + "defaultMessage": "vous devez indiquer exactement un de expiresAt ou neverExpires (ou le champ obsolète expiresIn)", + "description": "API error from the share-links API (shares.go:createShareHandler).", + "sourceHash": "4b41fc4e2660" + }, + "server.shares.invalid_expires_at": { + "defaultMessage": "expiresAt invalide : doit être au format RFC3339", + "description": "API error from the share-links API (shares.go:createShareHandler).", + "sourceHash": "8ccb019cf11a" + }, + "server.shares.invalid_expires_in": { + "defaultMessage": "expiresIn invalide", + "description": "API error from the share-links API (shares.go:createShareHandler).", + "sourceHash": "5dacb87beef0" + }, + "server.telemetry.invalid_notice_action": { + "defaultMessage": "action doit être l'une de seen|keep|extended-off|all-off", + "description": "API error from the telemetry API (telemetry.go:postNotice). Quoted words and tokens in the text are API keywords; keep them as written.", + "sourceHash": "1f34c45cf5d9" + }, + "server.telemetry.notice_not_pending": { + "defaultMessage": "la notice de télémétrie n'est pas en attente", + "description": "API error from the telemetry API (telemetry.go:postNotice).", + "sourceHash": "5b603426eafc" + }, + "server.tunnel.credentials_in_use": { + "defaultMessage": "impossible de supprimer les identifiants du tunnel tant que le tunnel est activé ; désactivez d'abord le tunnel, ou utilisez PUT pour renouveler les identifiants", + "description": "API error from the tunnel credentials API (tunnelcreds.go:delete).", + "sourceHash": "f08b70bd3563" + }, + "server.tunnel.invalid_request_body": { + "defaultMessage": "corps de requête invalide", + "description": "API error from the tunnel credentials API (tunnelcreds.go:put).", + "sourceHash": "6f148e463f2c" + }, + "server.tunnel.secret_not_owned_conflict": { + "defaultMessage": "conflit : le secret existant n'appartient pas à ce serveur", + "description": "API error from the tunnel credentials API (tunnelcreds.go:put).", + "sourceHash": "a35340e9faae" + }, + "server.tunnel.secret_not_owned_forbidden": { + "defaultMessage": "interdit : impossible de supprimer un secret qui n'appartient pas à ce serveur", + "description": "API error from the tunnel credentials API (tunnelcreds.go:delete).", + "sourceHash": "1e7a78d9c096" + }, + "server.unauthorized": { + "defaultMessage": "non autorisé", + "description": "API error when the request carries no valid credentials, or Kubernetes rejects the credentials used for the operation.", + "sourceHash": "e9d83f01c9af" + }, + "server.users.appearance_mode_invalid": { + "defaultMessage": "appearanceMode doit être \"light\", \"dark\" ou \"system\"", + "description": "API error from the users API (users.go:putPreferences, users.go:resetPreferences). Quoted words and tokens in the text are API keywords; keep them as written.", + "sourceHash": "b8bbdfb59876" + }, + "server.users.appearance_mode_required": { + "defaultMessage": "appearanceMode est requis", + "description": "API error from the users API (users.go:putPreferences).", + "sourceHash": "25bcdc4a8dd3" + }, + "server.users.bad_id": { + "defaultMessage": "identifiant incorrect", + "description": "API error from the users API (users.go:addBinding, users.go:del, users.go:deleteBinding, users.go:listBindings, users.go:resetPassword, users.go:update).", + "sourceHash": "4b2d2ff66c98" + }, + "server.users.binding_already_exists": { + "defaultMessage": "la liaison existe déjà", + "description": "API error from the users API (users.go:addBinding).", + "sourceHash": "def9b9bcd9fd" + }, + "server.users.binding_not_found": { + "defaultMessage": "liaison introuvable", + "description": "API error from the users API (users.go:deleteBinding).", + "sourceHash": "6f57171b1bb8" + }, + "server.users.cannot_delete_last_manager": { + "defaultMessage": "impossible de supprimer le dernier utilisateur pouvant gérer les utilisateurs", + "description": "API error from the users API (users.go:del).", + "sourceHash": "5394435bcce6" + }, + "server.users.cannot_delete_self": { + "defaultMessage": "impossible de vous supprimer vous-même", + "description": "API error from the users API (users.go:del).", + "sourceHash": "fb94931ca370" + }, + "server.users.cannot_demote_last_manager": { + "defaultMessage": "impossible de rétrograder le dernier utilisateur pouvant gérer les utilisateurs", + "description": "API error from the users API (users.go:update).", + "sourceHash": "2261e6b4b22a" + }, + "server.users.cannot_remove_own_management": { + "defaultMessage": "vous ne pouvez pas retirer votre propre accès à la gestion des utilisateurs", + "description": "API error from the users API (users.go:update).", + "sourceHash": "dc935c2ab278" + }, + "server.users.cluster_role_is_primary": { + "defaultMessage": "le rôle à l'échelle du cluster est géré via le rôle principal", + "description": "API error from the users API (users.go:deleteBinding).", + "sourceHash": "d7e4103cf873" + }, + "server.users.custom_accent_invalid": { + "defaultMessage": "customColors.accent doit être une couleur hexadécimale #RRGGBB", + "description": "API error from the users API (users.go:putPreferences). Quoted words and tokens in the text are API keywords; keep them as written.", + "sourceHash": "bd1de9c6e3ff" + }, + "server.users.custom_css_enabled_required": { + "defaultMessage": "customCssEnabled est requis", + "description": "API error from the users API (users.go:putPreferences).", + "sourceHash": "497c29efba20" + }, + "server.users.custom_surface_invalid": { + "defaultMessage": "customColors.surface doit être une couleur hexadécimale #RRGGBB", + "description": "API error from the users API (users.go:putPreferences). Quoted words and tokens in the text are API keywords; keep them as written.", + "sourceHash": "58959f5039e7" + }, + "server.users.invalid_email": { + "defaultMessage": "adresse e-mail invalide", + "description": "API error from the users API (users.go:create, users.go:update).", + "sourceHash": "4cc4d0ec3f22" + }, + "server.users.invalid_role": { + "defaultMessage": "rôle invalide", + "description": "API error from the users API (users.go:addBinding, users.go:create, users.go:update).", + "sourceHash": "395e6be13d81" + }, + "server.users.invalid_username": { + "defaultMessage": "nom d'utilisateur invalide", + "description": "API error from the users API (users.go:create).", + "sourceHash": "c28ef65bfd2a" + }, + "server.users.password_too_short": { + "defaultMessage": "mot de passe trop court", + "description": "API error from the users API (users.go:create, users.go:resetPassword).", + "sourceHash": "dcd0b27fe9fa" + }, + "server.users.preset_id_invalid": { + "defaultMessage": "presetId doit être \"pink\" ou \"legacy\"", + "description": "API error from the users API (users.go:putPreferences, users.go:resetPreferences). Quoted words and tokens in the text are API keywords; keep them as written.", + "sourceHash": "456c77f0eda0" + }, + "server.users.preset_id_required": { + "defaultMessage": "presetId est requis", + "description": "API error from the users API (users.go:putPreferences).", + "sourceHash": "3696c017077b" + }, + "server.users.remote_binding_not_removable": { + "defaultMessage": "les liaisons de plan de contrôle distant ne peuvent pas être supprimées via les autorisations supplémentaires", + "description": "API error from the users API (users.go:deleteBinding).", + "sourceHash": "16222a1f56e7" + }, + "server.users.remote_role_scope_limited": { + "defaultMessage": "les rôles distants à l'échelle du cluster ne peuvent accorder que cluster:read et des permissions par namespace", + "description": "API error from the users API (users.go:addBinding).", + "sourceHash": "a5aaa60faedf" + }, + "server.users.theme_type_invalid": { + "defaultMessage": "themeType doit être \"preset\" ou \"custom_colors\"", + "description": "API error from the users API (users.go:putPreferences). Quoted words and tokens in the text are API keywords; keep them as written.", + "sourceHash": "a39d95b6b1db" + }, + "server.users.theme_type_required": { + "defaultMessage": "themeType est requis", + "description": "API error from the users API (users.go:putPreferences).", + "sourceHash": "d8336bfeb1f4" + }, + "server.ws.invalid_server_name": { + "defaultMessage": "nom de serveur invalide", + "description": "API error from the console and log WebSocket routes (attach.go:handle, podlogs.go:handle).", + "sourceHash": "7862564a67b1" + }, + "web.admin_settings.language.default_hint": { + "defaultMessage": "Utilisé lorsqu'un utilisateur n'a pas choisi de langue et qu'aucune des langues de son navigateur n'est disponible. Les destinations de notification réglées sur Valeur par défaut de l'instance l'utilisent aussi.", + "description": "Hint under the instance default language select on the admin Language card", + "sourceHash": "3367129c5fe9" + }, + "web.admin_settings.language.default_label": { + "defaultMessage": "Langue par défaut", + "description": "Label of the instance default language select on the admin Language card", + "sourceHash": "730f861754ff" + }, + "web.admin_settings.language.instance_default": { + "defaultMessage": "Valeur par défaut de l'instance", + "description": "Option that makes a notification sink use the instance default language", + "sourceHash": "6a7175b9427d" + }, + "web.admin_settings.language.save": { + "defaultMessage": "Enregistrer les modifications", + "description": "Save button of the admin Language card (instance default language)", + "sourceHash": "dd0ae7a5cbcf" + }, + "web.admin_settings.language.subtitle": { + "defaultMessage": "Langue par défaut de cette instance Gameplane.", + "description": "Subtitle of the admin Language card that sets the instance default language", + "sourceHash": "80ff679888ff" + }, + "web.admin_settings.language.title": { + "defaultMessage": "Langue", + "description": "Title of the admin Language card that sets the instance default language", + "sourceHash": "a4fe65264ef7" + }, + "web.admin_settings.sink.language_for": { + "defaultMessage": "Langue pour {name}", + "description": "Accessible name of a notification sink's language select on the admin Notifications card; {name} is the sink name", + "sourceHash": "be6e3264a913" + }, + "web.admin_settings.sink.language_hint": { + "defaultMessage": "Les notifications envoyées à cette destination sont rédigées dans cette langue.", + "description": "Helper text under the language select in the Add sink form on the admin Notifications card", + "sourceHash": "6d7e86c73fb4" + }, + "web.admin_settings.sink.language_label": { + "defaultMessage": "Langue", + "description": "Label of the language select in the Add sink form on the admin Notifications card", + "sourceHash": "a4fe65264ef7" + }, + "web.admin_settings.sink.language_select": { + "defaultMessage": "Langue de la destination", + "description": "Accessible name of the language select in the Add sink form on the admin Notifications card", + "sourceHash": "b2fb5dcaa181" + }, + "web.admin_settings.sink.language_text": { + "defaultMessage": "Langue", + "description": "Label beside the language select of each notification sink row on the admin Notifications card", + "sourceHash": "a4fe65264ef7" + }, + "web.common.close_glyph": { + "defaultMessage": "×", + "description": "Multiplication sign used as the close icon on dismiss buttons", + "sourceHash": "8db71ed28b0f" + }, + "web.common.empty_value": { + "defaultMessage": "—", + "description": "Shown in place of a value that is not set", + "sourceHash": "bda050585a00" + }, + "web.common.required_marker": { + "defaultMessage": "*", + "description": "Asterisk shown after the label of a required field", + "sourceHash": "684888c0ebb1" + }, + "web.common.separator": { + "defaultMessage": "·", + "description": "Separator character between items in a list, such as wizard steps", + "sourceHash": "a137f17a19a0" + }, + "web.common.separator_bar": { + "defaultMessage": "|", + "description": "Vertical bar that separates inline action links", + "sourceHash": "cbe5cfdf7c21" + }, + "web.common.separator_dot": { + "defaultMessage": "·", + "description": "Visual separator between two items", + "sourceHash": "a137f17a19a0" + }, + "web.components.app_layout.admin": { + "defaultMessage": "Administration", + "description": "Sidebar: heading of the admin navigation group", + "sourceHash": "c1c224b03cd9" + }, + "web.components.app_layout.audit_log": { + "defaultMessage": "Journal d'audit", + "description": "Sidebar: navigation link to the audit log", + "sourceHash": "e4d36f9a4e22" + }, + "web.components.app_layout.backups": { + "defaultMessage": "Backups", + "description": "Sidebar: navigation link to backups", + "sourceHash": "3334fee2c77f" + }, + "web.components.app_layout.central_management": { + "defaultMessage": "Gestion centralisée", + "description": "Sidebar: cluster name shown on central-management pages", + "sourceHash": "803edfc3f5f3" + }, + "web.components.app_layout.central_management_lead": { + "defaultMessage": "Gestion centralisée.", + "description": "Bold lead-in of the central-management note shown above page content", + "sourceHash": "54cd326e3ef2" + }, + "web.components.app_layout.cluster": { + "defaultMessage": "Cluster", + "description": "Sidebar: navigation link to the cluster page", + "sourceHash": "c137a7f43e23" + }, + "web.components.app_layout.clusters": { + "defaultMessage": "Clusters", + "description": "Sidebar: navigation link to the clusters page", + "sourceHash": "92d5e4c5cd37" + }, + "web.components.app_layout.dashboard": { + "defaultMessage": "Tableau de bord", + "description": "Sidebar: navigation link to the dashboard", + "sourceHash": "67b696468610" + }, + "web.components.app_layout.general": { + "defaultMessage": "Général", + "description": "Sidebar: heading of the general navigation group", + "sourceHash": "c910d474dcd7" + }, + "web.components.app_layout.modules": { + "defaultMessage": "Modules", + "description": "Sidebar: navigation link to modules", + "sourceHash": "76c86c4c3243" + }, + "web.components.app_layout.page_not_found": { + "defaultMessage": "Page introuvable", + "description": "Breadcrumb for the 404 page", + "sourceHash": "a469ab4ca4e5" + }, + "web.components.app_layout.servers": { + "defaultMessage": "Serveurs", + "description": "Sidebar: navigation link to the servers list", + "sourceHash": "68d7beb6df59" + }, + "web.components.app_layout.settings": { + "defaultMessage": "Paramètres", + "description": "Sidebar: navigation link to admin settings", + "sourceHash": "74a883a037bc" + }, + "web.components.app_layout.system_logs": { + "defaultMessage": "Journaux système", + "description": "Sidebar: navigation link to system logs", + "sourceHash": "a6851520b7d9" + }, + "web.components.app_layout.this_catalog_account_or_installation": { + "defaultMessage": "Ce catalogue, ce compte ou ce paramètre d’installation appartient à l’installation Gameplane centrale. Les actions sur les serveurs utilisent l’emplacement de chaque serveur.", + "description": "App layout: note shown when central management applies; catalog, account or installation settings belong to the central installation, while server actions use each server's location", + "sourceHash": "cbe6610ee1f8" + }, + "web.components.app_layout.users_rbac": { + "defaultMessage": "Utilisateurs et RBAC", + "description": "Sidebar: navigation link to users and roles", + "sourceHash": "0c6ed78342e6" + }, + "web.components.backups.backup_detail_drawer.backup_details": { + "defaultMessage": "Détails du backup", + "description": "Backup details drawer: heading at the top of the side panel for one backup; the backup name is shown below it", + "sourceHash": "85ece27eaabf" + }, + "web.components.backups.backup_detail_drawer.completed": { + "defaultMessage": "Terminé", + "description": "Backup details drawer: field label above the time the backup completed, shown in relative time", + "sourceHash": "22a970d2e5b1" + }, + "web.components.backups.backup_detail_drawer.delete": { + "defaultMessage": "Supprimer", + "description": "Backup detail drawer: delete button label", + "sourceHash": "e2d0a54968ea" + }, + "web.components.backups.backup_detail_drawer.deleting": { + "defaultMessage": "Suppression…", + "description": "Backup detail drawer: delete button label while the delete request is pending", + "sourceHash": "43b5894c3e4e" + }, + "web.components.backups.backup_detail_drawer.phase": { + "defaultMessage": "Phase", + "description": "Backup details drawer: field label above the backup's current phase, shown as a status chip", + "sourceHash": "46342ec1eec9" + }, + "web.components.backups.backup_detail_drawer.restore": { + "defaultMessage": "Restaurer", + "description": "Backup details drawer: visible text of the restore button in the drawer header and of the primary restore button in the footer; keep it short", + "sourceHash": "a76e13b98392" + }, + "web.components.backups.backup_detail_drawer.restore_backup": { + "defaultMessage": "Restaurer le backup", + "description": "Backup details drawer: screen-reader name of the restore button in the drawer header, read instead of its visible word Restore; not visible on screen", + "sourceHash": "6da1c554212c" + }, + "web.components.backups.backup_detail_drawer.server": { + "defaultMessage": "Serveur", + "description": "Backup details drawer: field label above the name of the server the backup belongs to", + "sourceHash": "aef7de28d529" + }, + "web.components.backups.backup_detail_drawer.size": { + "defaultMessage": "Taille", + "description": "Backup details drawer: field label above the backup's size; a dash is shown when the size is unknown", + "sourceHash": "1af851907331" + }, + "web.components.backups.backup_detail_drawer.snapshot_id": { + "defaultMessage": "ID du snapshot", + "description": "Backup details drawer: field label above the snapshot identifier of the backup; a dash is shown when there is none", + "sourceHash": "69e057b03709" + }, + "web.components.backups.backup_detail_drawer.started": { + "defaultMessage": "Démarré", + "description": "Backup details drawer: field label above the time the backup started, shown in relative time", + "sourceHash": "ecbc89cd37a0" + }, + "web.components.backups.backup_filters.all_phases": { + "defaultMessage": "Toutes les phases", + "description": "Backup filters: first phase dropdown option that clears the phase filter so backups in every phase show; keep it short", + "sourceHash": "aa556a303839" + }, + "web.components.backups.backup_filters.all_phases_text": { + "defaultMessage": "Toutes les phases", + "description": "Backup filters: typeahead text of the all-phases option", + "sourceHash": "aa556a303839" + }, + "web.components.backups.backup_filters.all_servers": { + "defaultMessage": "Tous les serveurs", + "description": "Backup filters: first server dropdown option that clears the server filter so backups from all servers show; keep it short", + "sourceHash": "3205261a6537" + }, + "web.components.backups.backup_filters.all_servers_text": { + "defaultMessage": "Tous les serveurs", + "description": "Backup filters: typeahead text of the all-servers option", + "sourceHash": "3205261a6537" + }, + "web.components.backups.backup_filters.filter": { + "defaultMessage": "Filtrer", + "description": "Backup filters: label of the Filter button that opens the filter panel, with a count chip showing how many filters are set; keep it short", + "sourceHash": "638e249f4a15" + }, + "web.components.backups.backup_filters.filter_by_phase": { + "defaultMessage": "Filtrer par phase", + "description": "Backup filters: screen-reader label for the phase dropdown in the filter panel; not visible on screen", + "sourceHash": "c73212a6314e" + }, + "web.components.backups.backup_filters.filter_by_server": { + "defaultMessage": "Filtrer par serveur", + "description": "Backup filters: screen-reader label for the server dropdown in the filter panel; not visible on screen", + "sourceHash": "b6947fc72c99" + }, + "web.components.backups.backup_filters.location": { + "defaultMessage": "Emplacement", + "description": "Backup filters: label above the location dropdown in the filter panel", + "sourceHash": "15b61974b270" + }, + "web.components.backups.backup_filters.phase": { + "defaultMessage": "Phase", + "description": "Backup filters: label above the phase dropdown in the filter panel; the dropdown appears only when phases exist", + "sourceHash": "46342ec1eec9" + }, + "web.components.backups.backup_filters.phase_failed": { + "defaultMessage": "Échec", + "description": "Backup filters: option label for the Failed backup or restore phase", + "sourceHash": "031a8f0f659d" + }, + "web.components.backups.backup_filters.phase_options": { + "defaultMessage": "Options de phase", + "description": "Backup filters: screen-reader name of the list of phase choices in the phase dropdown; not visible on screen", + "sourceHash": "563b5ae1c097" + }, + "web.components.backups.backup_filters.phase_pending": { + "defaultMessage": "En attente", + "description": "Backup filters: option label for the Pending backup or restore phase", + "sourceHash": "331551b0de41" + }, + "web.components.backups.backup_filters.phase_resuming": { + "defaultMessage": "Reprise", + "description": "Backup filters: option label for the Resuming restore phase", + "sourceHash": "e71708dd9311" + }, + "web.components.backups.backup_filters.phase_running": { + "defaultMessage": "En cours", + "description": "Backup filters: option label for the Running backup or restore phase", + "sourceHash": "f4ccae29e1bb" + }, + "web.components.backups.backup_filters.phase_succeeded": { + "defaultMessage": "Réussi", + "description": "Backup filters: option label for the Succeeded backup or restore phase", + "sourceHash": "6d9a6f97a5fd" + }, + "web.components.backups.backup_filters.phase_suspending": { + "defaultMessage": "Suspension", + "description": "Backup filters: option label for the Suspending restore phase", + "sourceHash": "5e62da3022f8" + }, + "web.components.backups.backup_filters.search_backups_schedules_or_restores": { + "defaultMessage": "Rechercher des backups, des planifications ou des restores", + "description": "Backup filters: screen-reader label for the search box that finds backups, schedules or restores; not visible on screen", + "sourceHash": "a8954b42ad22" + }, + "web.components.backups.backup_filters.search_by_name_or_server": { + "defaultMessage": "Rechercher par nom ou serveur…", + "description": "Backup filters: placeholder text inside the search box giving an example of what to type, a backup name or server name", + "sourceHash": "30dfa91d0bd2" + }, + "web.components.backups.backup_filters.server": { + "defaultMessage": "Serveur", + "description": "Backup filters: label above the server dropdown in the filter panel", + "sourceHash": "aef7de28d529" + }, + "web.components.backups.backup_filters.server_option_label": { + "defaultMessage": "{name} · {cluster} / {namespace}", + "description": "Backup filters: server option label with its location; {name} is the server name, {cluster} the cluster, {namespace} the namespace", + "sourceHash": "997bbb9e64a7" + }, + "web.components.backups.backup_filters.server_options": { + "defaultMessage": "Options de serveur", + "description": "Backup filters: screen-reader name of the list of server choices in the server dropdown; not visible on screen", + "sourceHash": "c20ffd9997a8" + }, + "web.components.backups.backup_row.cluster_namespace": { + "defaultMessage": "{cluster} / {namespace}", + "description": "Backup row: cluster and namespace of the backup's target server; {cluster} is the cluster name, {namespace} is the namespace", + "sourceHash": "d2efd80cd0eb" + }, + "web.components.backups.backup_row.restore": { + "defaultMessage": "Restaurer", + "description": "Backup row: button in each backup table row that starts restoring that backup; keep it short", + "sourceHash": "a76e13b98392" + }, + "web.components.backups.restore_dialog.a_server_named_target_already_exists": { + "defaultMessage": "Un serveur nommé \"{target}\" existe déjà : choisissez un nouveau nom.", + "description": "Restore dialog: sentence with target in p", + "sourceHash": "ead6c06c241d" + }, + "web.components.backups.restore_dialog.cancel": { + "defaultMessage": "Annuler", + "description": "Restore dialog: button that closes the restore dialog without restoring; keep it short", + "sourceHash": "19766ed6ccb2" + }, + "web.components.backups.restore_dialog.in_place_note": { + "defaultMessage": "Le serveur cible sera suspendu, le volume restauré depuis le snapshot, puis le serveur repris.", + "description": "Restore dialog: explains an in-place restore", + "sourceHash": "a3da57cc0dba" + }, + "web.components.backups.restore_dialog.my_restored_server": { + "defaultMessage": "mon-serveur-restaure", + "description": "Restore dialog: example value shown inside the empty new server name field; a sample name, not a default value", + "sourceHash": "f31c99315c5d" + }, + "web.components.backups.restore_dialog.new_server_name": { + "defaultMessage": "Nom du nouveau serveur", + "description": "Restore dialog: field label for the name of the new server to create from the backup; shown in the volume snapshot restore mode", + "sourceHash": "8adac8bcfcc9" + }, + "web.components.backups.restore_dialog.new_server_note": { + "defaultMessage": "Un nouveau serveur sera provisionné à partir de ce snapshot. Le serveur d'origine n'est pas modifié.", + "description": "Restore dialog: explains that restoring creates a new server", + "sourceHash": "ea8c32861a5b" + }, + "web.components.backups.restore_dialog.restore": { + "defaultMessage": "Restaurer", + "description": "Restore dialog: submit button label for an in-place restore", + "sourceHash": "a76e13b98392" + }, + "web.components.backups.restore_dialog.restore_backup": { + "defaultMessage": "Restaurer le backup", + "description": "Restore dialog: title of the dialog for restoring a backup onto a server", + "sourceHash": "6da1c554212c" + }, + "web.components.backups.restore_dialog.restore_failed": { + "defaultMessage": "Échec de la restauration", + "description": "Restore dialog: fallback error text when the restore request fails without a server message", + "sourceHash": "b8476ceedfdd" + }, + "web.components.backups.restore_dialog.restore_to_new_server": { + "defaultMessage": "Restaurer sur un nouveau serveur", + "description": "Restore dialog: submit button label when restoring to a new server", + "sourceHash": "b478a24862b4" + }, + "web.components.backups.restore_dialog.select_server": { + "defaultMessage": "Sélectionnez un serveur…", + "description": "Restore dialog: placeholder of the target server select when none is chosen", + "sourceHash": "0da661c11f32" + }, + "web.components.backups.restore_dialog.select_target_server": { + "defaultMessage": "Sélectionner le serveur cible", + "description": "Restore dialog: screen-reader name of the list of existing servers shown when picking the restore target; not visible on screen", + "sourceHash": "d7614eeecbfb" + }, + "web.components.backups.restore_dialog.source_backup": { + "defaultMessage": "Backup source", + "description": "Restore dialog: field label above the read-only name of the backup being restored", + "sourceHash": "3d94e2cd53ee" + }, + "web.components.backups.restore_dialog.starting": { + "defaultMessage": "Démarrage…", + "description": "Restore dialog: submit button label while the restore starts", + "sourceHash": "bbe5fc3b9ef3" + }, + "web.components.backups.restore_dialog.target_game_server": { + "defaultMessage": "Serveur de jeu cible", + "description": "Restore dialog: field label for choosing the existing server to restore the backup onto", + "sourceHash": "bdebd01e25c3" + }, + "web.components.backups.restore_dialog.the_new_server_copies_name_s": { + "defaultMessage": "Le nouveau serveur copie la configuration de {name} et démarre avec les données du snapshot. Rien ne change sur le serveur d'origine.", + "description": "Restore dialog: sentence with name in div", + "sourceHash": "1dbb5a73d26b" + }, + "web.components.backups.restore_dialog.this_will_overwrite_all_data_on_the": { + "defaultMessage": "Cette action écrasera toutes les données du serveur cible. Les joueurs seront déconnectés pendant la restauration.", + "description": "Restore dialog: red warning under the restore mode choice saying the target server's data will be overwritten and players will be disconnected during the restore", + "sourceHash": "af3b804deb62" + }, + "web.components.backups.retention_fields.daily": { + "defaultMessage": "Quotidien", + "description": "Retention field: label of the daily snapshot count input", + "sourceHash": "b36c2611dcdf" + }, + "web.components.backups.retention_fields.hourly": { + "defaultMessage": "Horaire", + "description": "Retention field: label of the hourly snapshot count input", + "sourceHash": "eab0cd8fdf9b" + }, + "web.components.backups.retention_fields.keep_last": { + "defaultMessage": "Garder les derniers", + "description": "Retention field: label of the keep-last-N snapshots input", + "sourceHash": "8771c56bf6d8" + }, + "web.components.backups.retention_fields.monthly": { + "defaultMessage": "Mensuel", + "description": "Retention field: label of the monthly snapshot count input", + "sourceHash": "9b11f6b707d2" + }, + "web.components.backups.retention_fields.placeholder_zero": { + "defaultMessage": "0", + "description": "Retention field: placeholder example value of zero", + "sourceHash": "5feceb66ffc8" + }, + "web.components.backups.retention_fields.weekly": { + "defaultMessage": "Hebdomadaire", + "description": "Retention field: label of the weekly snapshot count input", + "sourceHash": "2975132481a7" + }, + "web.components.backups.retention_fields.yearly": { + "defaultMessage": "Annuel", + "description": "Retention field: label of the yearly snapshot count input", + "sourceHash": "6e69b59ed34d" + }, + "web.components.backups.schedule_form.backup_type": { + "defaultMessage": "Type de sauvegarde", + "description": "Schedule form: label above the backup type dropdown that chooses between a repository snapshot and a volume snapshot; also the screen-reader name of its option list", + "sourceHash": "d69324ca4de9" + }, + "web.components.backups.schedule_form.cancel": { + "defaultMessage": "Annuler", + "description": "Schedule form: button that closes the form without creating a schedule; keep it short", + "sourceHash": "19766ed6ccb2" + }, + "web.components.backups.schedule_form.create_schedule": { + "defaultMessage": "Créer la planification", + "description": "Schedule form: submit button label", + "sourceHash": "5b08f3c71912" + }, + "web.components.backups.schedule_form.creating": { + "defaultMessage": "Création…", + "description": "Schedule form: submit button label while the schedule is created", + "sourceHash": "c79ed9492e3c" + }, + "web.components.backups.schedule_form.cron_placeholder": { + "defaultMessage": "0 */6 * * *", + "description": "Schedule form: placeholder example of a cron expression", + "sourceHash": "16ddb6345476" + }, + "web.components.backups.schedule_form.csi_hint": { + "defaultMessage": "Snapshot de volume CSI récurrent selon une expression cron. Aucun dépôt restic requis.", + "description": "Schedule form: hint for the CSI snapshot schedule type", + "sourceHash": "ddaad17d883f" + }, + "web.components.backups.schedule_form.destination": { + "defaultMessage": "Destination", + "description": "Schedule form: label above the dropdown that picks the backup destination", + "sourceHash": "293d404a500f" + }, + "web.components.backups.schedule_form.new_backup_schedule": { + "defaultMessage": "Nouvelle planification de sauvegarde", + "description": "Schedule form: heading of the card for creating a new backup schedule", + "sourceHash": "e517db70069e" + }, + "web.components.backups.schedule_form.no_destinations_configured": { + "defaultMessage": "Aucune destination configurée", + "description": "Schedule form: disabled option in the destination dropdown shown when no backup destinations have been set up", + "sourceHash": "b9532fce3cfc" + }, + "web.components.backups.schedule_form.repo_secret_key": { + "defaultMessage": "Secret du dépôt · clé", + "description": "Schedule form: label above the field that names the key inside the repository secret; not shown for volume snapshot schedules", + "sourceHash": "7511011b9f6b" + }, + "web.components.backups.schedule_form.restic_hint": { + "defaultMessage": "Snapshot récurrent selon une expression cron, stocké dans un dépôt restic configuré.", + "description": "Schedule form: hint for the restic schedule type", + "sourceHash": "83850ec01073" + }, + "web.components.backups.schedule_form.restic_snapshot_to_a_repository": { + "defaultMessage": "Snapshot restic (vers un dépôt)", + "description": "Schedule form: option in the backup type dropdown for backups stored in a configured restic repository; restic is a technical term, usually left untranslated; keep it short", + "sourceHash": "e2a8a254d3b0" + }, + "web.components.backups.schedule_form.retention_policy": { + "defaultMessage": "Politique de rétention", + "description": "Schedule form: label above the retention settings that decide how many old backups are kept", + "sourceHash": "49bd31dc520d" + }, + "web.components.backups.schedule_form.schedule_cron": { + "defaultMessage": "Planification (cron)", + "description": "Schedule form: label above the cron schedule text field that sets when backups run; cron is a technical term, usually left untranslated", + "sourceHash": "58f2d2a2337f" + }, + "web.components.backups.schedule_form.volume_snapshot_csi": { + "defaultMessage": "Snapshot de volume (CSI)", + "description": "Schedule form: option in the backup type dropdown for a cluster volume snapshot that needs no repository; CSI is a technical term, usually left untranslated; keep it short", + "sourceHash": "ce385b5d4cd2" + }, + "web.components.capture_widget.actions": { + "defaultMessage": "Actions", + "description": "Capture widget: column heading for the per-capture row actions in the captures table; keep it short", + "sourceHash": "ff8059dc6752" + }, + "web.components.capture_widget.auto_delete_after": { + "defaultMessage": "Les captures seront supprimées automatiquement après {hours, plural, one {# heure} other {# heures}}", + "description": "Capture widget: retention window before captures auto-delete; {hours} is the retention in hours", + "sourceHash": "c5df87e242f3" + }, + "web.components.capture_widget.cancel": { + "defaultMessage": "Annuler", + "description": "Capture widget: button that closes the delete confirmation or the Start Capture dialog without making changes; keep it short", + "sourceHash": "19766ed6ccb2" + }, + "web.components.capture_widget.capture_access_unavailable": { + "defaultMessage": "L'accès aux captures est temporairement indisponible à cet emplacement.", + "description": "Capture widget: status line when capture access is unavailable at this location", + "sourceHash": "2015021b1085" + }, + "web.components.capture_widget.capture_is_not_enabled_on_this_server": { + "defaultMessage": "La capture n'est pas activée sur ce serveur.", + "description": "Capture widget: heading in the empty-state card shown when packet capture is turned off for this server", + "sourceHash": "3c746d28ccac" + }, + "web.components.capture_widget.capture_started_ago": { + "defaultMessage": "Capture démarrée il y a {duration}", + "description": "Capture widget: how long ago the active capture started; {duration} is a formatted duration", + "sourceHash": "6e69c979fad1" + }, + "web.components.capture_widget.capture_stops_when_reached": { + "defaultMessage": ". La capture s'arrête une fois cette limite atteinte.", + "description": "Capture widget: second part of the maximum size hint, after the size limit; says the capture stops once that size is reached", + "sourceHash": "b0db2b4d2b4a" + }, + "web.components.capture_widget.captures": { + "defaultMessage": "Captures", + "description": "Capture widget: section heading above the table of completed and failed captures for this server", + "sourceHash": "502d75c486cf" + }, + "web.components.capture_widget.captures_disabled_at_location": { + "defaultMessage": "Les nouvelles captures sont désactivées à cet emplacement. Les fichiers de capture existants restent disponibles.", + "description": "Capture widget: status line when new captures are disabled at this location", + "sourceHash": "49cf03413704" + }, + "web.components.capture_widget.capturing": { + "defaultMessage": "Capture en cours…", + "description": "Capture widget: status while a capture is running", + "sourceHash": "806e32d3e4fa" + }, + "web.components.capture_widget.checking_access": { + "defaultMessage": "Vérification de l'accès…", + "description": "Capture widget: loading text shown in the capture panel while the user's packet capture access is being checked", + "sourceHash": "22e56ba5f355" + }, + "web.components.capture_widget.checking_capture_support": { + "defaultMessage": "Vérification de la prise en charge de la capture…", + "description": "Capture widget: status line while capture support loads", + "sourceHash": "17fcc5a46f8a" + }, + "web.components.capture_widget.completed_and_failed_packet_captures": { + "defaultMessage": "Captures de paquets terminées et échouées pour ce serveur", + "description": "Capture widget: accessible table name read by screen readers for the list of this server's completed and failed packet captures; not visible on screen", + "sourceHash": "b66861b2769c" + }, + "web.components.capture_widget.completed_at": { + "defaultMessage": "Terminée le", + "description": "Capture widget: column heading for when each capture finished in the captures table; keep it short", + "sourceHash": "996a9fb4ae66" + }, + "web.components.capture_widget.created": { + "defaultMessage": "Créée", + "description": "Capture widget: label in the expanded details of a capture row, followed by when the capture was created", + "sourceHash": "d70b9e24bca2" + }, + "web.components.capture_widget.days": { + "defaultMessage": "jours", + "description": "Capture widget: retention unit dropdown option meaning days; keep it short", + "sourceHash": "ab51004e9d71" + }, + "web.components.capture_widget.default_filter": { + "defaultMessage": "par défaut", + "description": "Capture widget and table: shown when a capture has no packet filter, lowercase", + "sourceHash": "37a8eec1ce19" + }, + "web.components.capture_widget.delete_capture": { + "defaultMessage": "Supprimer la capture ?", + "description": "Capture widget: title of the confirmation dialog that asks whether the selected capture should be deleted", + "sourceHash": "f0fa38181652" + }, + "web.components.capture_widget.delete_capture_aria": { + "defaultMessage": "Supprimer la capture {id}", + "description": "Capture table: aria-label of the delete button; {id} is the capture id", + "sourceHash": "3a1bfd62ac67" + }, + "web.components.capture_widget.delete_capture_button": { + "defaultMessage": "Supprimer la capture", + "description": "Capture widget: delete button label in the delete confirmation", + "sourceHash": "0b2c96558a2e" + }, + "web.components.capture_widget.delete_warning": { + "defaultMessage": "Cette action supprime définitivement la capture {captureId} et ses paquets enregistrés. Elle est irréversible.", + "description": "Capture widget: warning in the delete confirmation dialog that the capture and its recorded packets are permanently removed and the action cannot be undone; {captureId} is the capture ID, shown in monospace inside the tag, which must be kept around it", + "sourceHash": "bd61b4b8af20" + }, + "web.components.capture_widget.disable_capture": { + "defaultMessage": "Désactiver la capture", + "description": "Capture widget: button that turns capture off for this server; keep it short", + "sourceHash": "c9c81a52981c" + }, + "web.components.capture_widget.download_capture_aria": { + "defaultMessage": "Télécharger la capture {id}", + "description": "Capture table: aria-label of the download button; {id} is the capture id", + "sourceHash": "a3cb329f7455" + }, + "web.components.capture_widget.duration": { + "defaultMessage": "Durée", + "description": "Capture widget: column heading for how long each capture ran in the captures table; keep it short", + "sourceHash": "4fc52a3c4c55" + }, + "web.components.capture_widget.enable_capture": { + "defaultMessage": "Activer la capture", + "description": "Capture widget: enable capture button label", + "sourceHash": "cc6cea7d7e2a" + }, + "web.components.capture_widget.enabling": { + "defaultMessage": "Activation…", + "description": "Capture widget: enable button label while the request is pending", + "sourceHash": "e22c24238eb0" + }, + "web.components.capture_widget.expires_in": { + "defaultMessage": "Expire dans", + "description": "Capture widget: column heading for the time left before each capture is auto-deleted; keep it short", + "sourceHash": "de9967524f05" + }, + "web.components.capture_widget.filter": { + "defaultMessage": "Filtre :", + "description": "Capture widget: label before the packet filter text of the active capture card, or the word 'default' when no filter is set", + "sourceHash": "e1146f006ab3" + }, + "web.components.capture_widget.filter_2": { + "defaultMessage": "Filtre", + "description": "Capture widget: column heading for the packet filter used by each capture in the captures table; keep it short", + "sourceHash": "638e249f4a15" + }, + "web.components.capture_widget.gateway_update_needed": { + "defaultMessage": "Cet emplacement nécessite une mise à jour de la passerelle avant que les captures puissent y être gérées.", + "description": "Capture widget: status line when the location's gateway is too old for captures", + "sourceHash": "fb4570b401f4" + }, + "web.components.capture_widget.gb": { + "defaultMessage": "GB", + "description": "Capture widget: option in the size unit dropdown meaning gigabytes; keep it short", + "sourceHash": "b4043b0b8297" + }, + "web.components.capture_widget.hours": { + "defaultMessage": "heures", + "description": "Capture widget: retention unit dropdown option meaning hours; keep it short", + "sourceHash": "404314b1f4bd" + }, + "web.components.capture_widget.how_long_the_capture_is_retained_before": { + "defaultMessage": "Durée de conservation de la capture avant suppression automatique. Maximum : {duration}.", + "description": "Capture widget: retention help text; {duration} is the maximum retention", + "sourceHash": "757087486007" + }, + "web.components.capture_widget.how_long_the_capture_runs_before_auto": { + "defaultMessage": "Durée d'exécution de la capture avant son arrêt automatique. Plage : 1 à 3600 secondes.", + "description": "Capture widget: hint under the run-time field saying how long the capture runs before it stops automatically; the allowed range is 1 to 3600 seconds", + "sourceHash": "abdb6621c285" + }, + "web.components.capture_widget.id": { + "defaultMessage": "ID", + "description": "Capture widget: column heading for the capture ID in the captures table; keep it short", + "sourceHash": "3843971dcfde" + }, + "web.components.capture_widget.max_duration": { + "defaultMessage": "Durée max.", + "description": "Capture widget: label above the time-remaining value on the active capture card, showing the run-time limit", + "sourceHash": "d2168941ba13" + }, + "web.components.capture_widget.max_duration_2": { + "defaultMessage": "Durée max. *", + "description": "Capture widget: label of the run-time field in the Start Capture dialog; the asterisk marks the field as required", + "sourceHash": "27214a79811f" + }, + "web.components.capture_widget.max_duration_unit": { + "defaultMessage": "Unité de durée max.", + "description": "Capture widget: screen-reader label for the unit dropdown (seconds or minutes) next to the run-time number; not visible on screen", + "sourceHash": "e5d54d6a5fd9" + }, + "web.components.capture_widget.max_duration_value": { + "defaultMessage": "Valeur de durée max.", + "description": "Capture widget: screen-reader label for the number box that sets how long the capture runs; not visible on screen", + "sourceHash": "9b2f18b9daab" + }, + "web.components.capture_widget.max_size": { + "defaultMessage": "Taille max.", + "description": "Capture widget: label above the space-remaining value on the active capture card, showing the file size limit", + "sourceHash": "59eed040eaeb" + }, + "web.components.capture_widget.max_size_2": { + "defaultMessage": "Taille max. *", + "description": "Capture widget: label of the maximum file size field in the Start Capture dialog; the asterisk marks the field as required", + "sourceHash": "0dbc57794fa1" + }, + "web.components.capture_widget.max_size_unit": { + "defaultMessage": "Unité de taille max.", + "description": "Capture widget: screen-reader label for the size unit dropdown (MB or GB) next to the maximum size number; not visible on screen", + "sourceHash": "53f0378cfce0" + }, + "web.components.capture_widget.max_size_value": { + "defaultMessage": "Valeur de taille max.", + "description": "Capture widget: screen-reader label for the number box that sets the maximum capture file size; not visible on screen", + "sourceHash": "9cc976694d22" + }, + "web.components.capture_widget.maximum_file_size": { + "defaultMessage": "Taille de fichier maximale :", + "description": "Capture widget: first part of the hint under the maximum size field, followed by the server's size limit formatted with a unit", + "sourceHash": "55c5319012ae" + }, + "web.components.capture_widget.mb": { + "defaultMessage": "MB", + "description": "Capture widget: option in the size unit dropdown meaning megabytes; keep it short", + "sourceHash": "1d09f6fa2323" + }, + "web.components.capture_widget.minutes": { + "defaultMessage": "minutes", + "description": "Capture widget: option in the run-time unit dropdown meaning minutes; keep it short", + "sourceHash": "90e63d85fa1a" + }, + "web.components.capture_widget.network_packet_capture_is_an_optional": { + "defaultMessage": "La capture de paquets réseau est une fonctionnalité facultative qui enregistre le trafic brut du protocole de jeu. Activez-la pour capturer les paquets des joueurs qui se connectent, afin d'en analyser le protocole.", + "description": "Capture widget: explanation under the not-enabled heading of what packet capture does, recording raw game protocol traffic for protocol analysis once enabled", + "sourceHash": "4ba2bec44c22" + }, + "web.components.capture_widget.no_captures_yet": { + "defaultMessage": "Aucune capture pour l'instant.", + "description": "Capture widget: empty-state text in the captures list shown when this server has no captures yet", + "sourceHash": "d35d96d5002c" + }, + "web.components.capture_widget.optional_use_pcap_filter_syntax_e_g_tcp": { + "defaultMessage": "Facultatif. Utilisez la syntaxe pcap-filter (ex. \"tcp port 8080\", \"host 192.168.1.5\"). Laissez vide pour ne capturer que les ports de jeu de ce serveur.", + "description": "Capture widget: hint under the packet filter field saying the filter is optional, uses pcap-filter syntax with example expressions, and that blank captures only the server's game ports", + "sourceHash": "aee25e6cbea7" + }, + "web.components.capture_widget.packet_filter": { + "defaultMessage": "Filtre de paquets", + "description": "Capture widget: label for the optional packet filter text field in the Start Capture dialog", + "sourceHash": "0c10a9cf30d5" + }, + "web.components.capture_widget.packets": { + "defaultMessage": "Paquets", + "description": "Capture widget: column heading for the number of packets recorded by each capture in the captures table; keep it short", + "sourceHash": "0d34e73475ec" + }, + "web.components.capture_widget.packets_captured": { + "defaultMessage": "paquets capturés", + "description": "Capture widget: unit word shown right after the packet count of the active capture; the number before it is the packets written so far", + "sourceHash": "a78a0e41953d" + }, + "web.components.capture_widget.phase_completed": { + "defaultMessage": "Terminée", + "description": "Capture table: status chip for a capture that finished", + "sourceHash": "22a970d2e5b1" + }, + "web.components.capture_widget.phase_expired": { + "defaultMessage": "Expirée", + "description": "Capture table: status chip for a capture whose retention window ended", + "sourceHash": "424a2551d356" + }, + "web.components.capture_widget.phase_failed": { + "defaultMessage": "Échouée", + "description": "Capture table: status chip for a capture that failed", + "sourceHash": "031a8f0f659d" + }, + "web.components.capture_widget.phase_pending": { + "defaultMessage": "En attente", + "description": "Capture table: status chip for a capture that is waiting to start", + "sourceHash": "331551b0de41" + }, + "web.components.capture_widget.phase_running": { + "defaultMessage": "En cours", + "description": "Capture table: status chip for a capture that is recording", + "sourceHash": "f4ccae29e1bb" + }, + "web.components.capture_widget.ready": { + "defaultMessage": "Prête", + "description": "Capture widget: status when no capture is running", + "sourceHash": "5fa7aac5375c" + }, + "web.components.capture_widget.records_raw_network_traffic_on_this": { + "defaultMessage": "Enregistre le trafic réseau brut sur les ports annoncés de ce serveur (ou selon un filtre personnalisé) pour un téléchargement ultérieur.", + "description": "Capture widget: sentence under the Start Capture dialog heading saying the capture records raw network traffic on the server's ports or a custom filter for later download", + "sourceHash": "5bbab20e0fac" + }, + "web.components.capture_widget.retention": { + "defaultMessage": "Rétention", + "description": "Capture widget: label of the retention field in the Start Capture dialog, which sets how long the capture is kept before auto-delete", + "sourceHash": "3b1f58d94eac" + }, + "web.components.capture_widget.retention_unit": { + "defaultMessage": "Unité de rétention", + "description": "Capture widget: screen-reader label for the retention unit dropdown (hours or days) next to the retention number; not visible on screen", + "sourceHash": "3fb25b94445e" + }, + "web.components.capture_widget.retention_value": { + "defaultMessage": "Valeur de rétention", + "description": "Capture widget: screen-reader label for the number box that sets how long the capture is kept; not visible on screen", + "sourceHash": "cdad679ffcb5" + }, + "web.components.capture_widget.seconds": { + "defaultMessage": "secondes", + "description": "Capture widget: option in the run-time unit dropdown meaning seconds; keep it short", + "sourceHash": "59f006d63bd0" + }, + "web.components.capture_widget.server": { + "defaultMessage": "Serveur", + "description": "Capture widget: label in the expanded details of a capture row, followed by the name of the server the capture belongs to", + "sourceHash": "aef7de28d529" + }, + "web.components.capture_widget.showing_length_of": { + "defaultMessage": "Affichage de {length} sur {total} {total, plural, one {capture} other {captures}}", + "description": "Capture widget: count line under the capture table; {length} is the rows shown, {total} is the total capture count", + "sourceHash": "e6d35c0c4742" + }, + "web.components.capture_widget.size": { + "defaultMessage": "Taille", + "description": "Capture widget: column heading for the file size of each capture in the captures table; keep it short", + "sourceHash": "1af851907331" + }, + "web.components.capture_widget.space_remaining": { + "defaultMessage": "Espace restant : ~{size}", + "description": "Capture widget: space left before a capture reaches its size cap; {size} is a formatted size", + "sourceHash": "35aa9d46bace" + }, + "web.components.capture_widget.start_capture": { + "defaultMessage": "Démarrer la capture", + "description": "Capture widget: label of the button that starts a capture", + "sourceHash": "df02298c4c4b" + }, + "web.components.capture_widget.started": { + "defaultMessage": "Démarrée", + "description": "Capture widget: label in the expanded details of a capture row, followed by when recording started or a dash if it has not started", + "sourceHash": "ecbc89cd37a0" + }, + "web.components.capture_widget.starting": { + "defaultMessage": "Démarrage…", + "description": "Capture widget: start button label while the request is pending", + "sourceHash": "bbe5fc3b9ef3" + }, + "web.components.capture_widget.status": { + "defaultMessage": "Statut", + "description": "Capture widget: column heading for the state of each capture in the captures table; keep it short", + "sourceHash": "920e413c7d41" + }, + "web.components.capture_widget.stop_capture": { + "defaultMessage": "Arrêter la capture", + "description": "Capture widget: button that stops the running capture; keep it short", + "sourceHash": "3527eb804662" + }, + "web.components.capture_widget.tcp_port_25565": { + "defaultMessage": "tcp port 25565", + "description": "Capture widget: example text shown inside the empty packet filter field in the Start Capture dialog; it is a sample filter, not a default value", + "sourceHash": "d903dc789651" + }, + "web.components.capture_widget.time_remaining": { + "defaultMessage": "Temps restant : ~{duration}", + "description": "Capture widget: time left before the capture reaches its max duration; {duration} is a formatted duration", + "sourceHash": "153ba1fb13ea" + }, + "web.components.capture_widget.view_capture_aria": { + "defaultMessage": "Afficher la capture {id}", + "description": "Capture table: aria-label of the view button; {id} is the capture id", + "sourceHash": "a46f3395e283" + }, + "web.components.capture_widget.viewing_starting_and_downloading": { + "defaultMessage": "Afficher, démarrer et télécharger des captures nécessite un accès aux captures pour ce serveur. Demandez à un administrateur si vous en avez besoin.", + "description": "Capture widget: hint under the no-access heading saying capture access is needed to view, start or download captures; users should ask an administrator", + "sourceHash": "d49abd5f71cb" + }, + "web.components.capture_widget.working": { + "defaultMessage": "En cours…", + "description": "Capture widget: delete confirmation button label while the request is pending", + "sourceHash": "5474eef8d0f1" + }, + "web.components.capture_widget.you_don_t_have_access_to_packet_capture": { + "defaultMessage": "Vous n'avez pas accès à la capture de paquets sur ce serveur.", + "description": "Capture widget: heading in the empty-state card shown when the user has no packet capture access on this server", + "sourceHash": "bec3aac2d299" + }, + "web.components.cluster_selector.actions": { + "defaultMessage": "Actions", + "description": "Cluster selector: screen-reader name of the dropdown section that holds the 'View all clusters' action; not visible on screen", + "sourceHash": "ff8059dc6752" + }, + "web.components.cluster_selector.cluster": { + "defaultMessage": "Cluster :", + "description": "Cluster selector: label before the current cluster's name on the switcher button; hidden on small screens", + "sourceHash": "470f25a41df4" + }, + "web.components.cluster_selector.cluster_options": { + "defaultMessage": "Options du cluster", + "description": "Cluster selector: screen-reader name of the dropdown menu that lists clusters and actions; not visible on screen", + "sourceHash": "6babbb4e4d21" + }, + "web.components.cluster_selector.clusters": { + "defaultMessage": "Clusters", + "description": "Cluster selector: screen-reader name of the dropdown section that holds the cluster choices; not visible on screen", + "sourceHash": "92d5e4c5cd37" + }, + "web.components.cluster_selector.error_loading": { + "defaultMessage": "Erreur de chargement des clusters", + "description": "Cluster selector: text shown when the cluster list fails to load", + "sourceHash": "aa1fd2837cf3" + }, + "web.components.cluster_selector.loading": { + "defaultMessage": "Chargement…", + "description": "Cluster selector: text while the cluster list loads", + "sourceHash": "ba3bbbe10d8b" + }, + "web.components.cluster_selector.manage_clusters": { + "defaultMessage": "Gérer les clusters", + "description": "Cluster selector: typeahead text of the manage-clusters item", + "sourceHash": "1399f611d95b" + }, + "web.components.cluster_selector.no_clusters_available_manage_clusters": { + "defaultMessage": "Aucun cluster disponible · Gérer les clusters", + "description": "Cluster selector: dropdown message shown when no clusters exist, followed by the words 'Manage clusters' in the same text", + "sourceHash": "b53d5cab46e3" + }, + "web.components.cluster_selector.select_cluster": { + "defaultMessage": "Sélectionner un cluster", + "description": "Cluster selector: screen-reader label for the cluster switcher button; not visible on screen", + "sourceHash": "dec2cf62b573" + }, + "web.components.cluster_selector.select_cluster_label": { + "defaultMessage": "Sélectionner un cluster", + "description": "Cluster selector: label shown when no cluster is selected", + "sourceHash": "dec2cf62b573" + }, + "web.components.cluster_selector.view_all_clusters": { + "defaultMessage": "Voir tous les clusters", + "description": "Cluster selector: dropdown menu item that navigates to the clusters view; keep it short", + "sourceHash": "72e31a05fb9b" + }, + "web.components.cluster_selector.view_all_clusters_text": { + "defaultMessage": "Voir tous les clusters", + "description": "Cluster selector: typeahead text of the view-all-clusters item", + "sourceHash": "72e31a05fb9b" + }, + "web.components.fleet_scope.all_locations": { + "defaultMessage": "Tous les emplacements", + "description": "Fleet scope: first option in the location dropdown that clears the location filter and shows every location; keep it short", + "sourceHash": "0e23b0cfebfc" + }, + "web.components.fleet_scope.all_locations_text": { + "defaultMessage": "Tous les emplacements", + "description": "Fleet scope: typeahead text of the all-locations option", + "sourceHash": "0e23b0cfebfc" + }, + "web.components.fleet_scope.counts_cover_only_returned_resources": { + "defaultMessage": "Les totaux ne portent que sur les ressources renvoyées.", + "description": "Fleet scope: second part of the partial-results warning, after the heading, saying counts include only the resources that were returned", + "sourceHash": "c000b077f521" + }, + "web.components.fleet_scope.filter_by_location": { + "defaultMessage": "Filtrer par emplacement", + "description": "Fleet scope: screen-reader label for the location dropdown that narrows the view to one location; not visible on screen", + "sourceHash": "428a2c79b765" + }, + "web.components.fleet_scope.label_are_partial": { + "defaultMessage": "{label} sont partiels.", + "description": "Fleet scope: sentence with label in strong", + "sourceHash": "f721ce1b485f" + }, + "web.components.fleet_scope.label_are_unavailable_try_again": { + "defaultMessage": "{label} sont indisponibles. Réessayez.", + "description": "Fleet scope: sentence with label in div", + "sourceHash": "f54e7804e6d1" + }, + "web.components.fleet_scope.limit_reached": { + "defaultMessage": "Limite de résultats atteinte.", + "description": "Fleet scope: warning shown when results are capped", + "sourceHash": "3ca572d55a38" + }, + "web.components.fleet_scope.location_options": { + "defaultMessage": "Options d'emplacement", + "description": "Fleet scope: screen-reader name of the list of location choices inside the location dropdown; not visible on screen", + "sourceHash": "174550da18f7" + }, + "web.components.fleet_scope.results": { + "defaultMessage": "Résultats", + "description": "Fleet scope: default noun for the returned resources in the coverage notice, used when no label is passed", + "sourceHash": "219c4a6c86a7" + }, + "web.components.fleet_scope.scope_message": { + "defaultMessage": "{scope} : {message}", + "description": "Fleet scope: warning line; {scope} is the cluster and namespace or the selected scope label, {message} is the warning text", + "sourceHash": "2794472650ae" + }, + "web.components.fleet_scope.selected_scope": { + "defaultMessage": "Périmètre sélectionné", + "description": "Fleet scope: label of the selected scope when no cluster or namespace is set", + "sourceHash": "48da53908d62" + }, + "web.components.fleet_scope.unavailable": { + "defaultMessage": "Indisponible.", + "description": "Fleet scope: warning shown when a cluster cannot be queried", + "sourceHash": "70c9a967bcdb" + }, + "web.components.module_target.couldn_t_load_installation_capabilities": { + "defaultMessage": "Impossible de charger les capacités de l'installation.", + "description": "Module target: error card message shown when the installation's capabilities cannot be loaded; the card has a retry button", + "sourceHash": "76468b1e3822" + }, + "web.components.module_target.couldn_t_load_registered_clusters": { + "defaultMessage": "Impossible de charger les clusters enregistrés.", + "description": "Module target: error card message shown when the list of registered clusters cannot be loaded; the card has a retry button", + "sourceHash": "241f789c2cc1" + }, + "web.components.module_target.loading_clusters": { + "defaultMessage": "Chargement des clusters…", + "description": "Module target: loading text shown while the list of registered clusters is being fetched", + "sourceHash": "fc84c68d8cd9" + }, + "web.components.module_target.loading_installation": { + "defaultMessage": "Chargement de l'installation…", + "description": "Module target: loading message while the installation settings load, used when no loadingMessage is passed", + "sourceHash": "9b3317b01b4b" + }, + "web.components.module_target.loading_permissions": { + "defaultMessage": "Chargement des autorisations…", + "description": "Module target: loading message while permissions load", + "sourceHash": "946f1570503e" + }, + "web.components.module_target.manage_clusters": { + "defaultMessage": "Gérer les clusters", + "description": "Module target: link in the select-a-cluster empty state that opens the clusters page; keep it short", + "sourceHash": "1399f611d95b" + }, + "web.components.module_target.modules_and_their_sources_are_managed": { + "defaultMessage": "Les modules et leurs sources sont gérés par l'opérateur sur un cluster de charge enregistré.", + "description": "Module target: explanation under the select-a-cluster heading saying modules and their sources are managed by the operator on a registered workload cluster", + "sourceHash": "c957e0a81a5e" + }, + "web.components.module_target.no_permission": { + "defaultMessage": "Vous n'avez pas l'autorisation de voir les modules sur ce cluster.", + "description": "Module target: message shown when the user cannot view modules on this cluster", + "sourceHash": "35b3167bb4a0" + }, + "web.components.module_target.permissions_error": { + "defaultMessage": "Impossible de charger vos autorisations.", + "description": "Module target: error message when permissions fail to load", + "sourceHash": "0ab6e5d0ed89" + }, + "web.components.module_target.select_a_workload_cluster": { + "defaultMessage": "Sélectionner un cluster de charge", + "description": "Module target: heading of the empty state asking the user to pick a workload cluster before its modules can be shown", + "sourceHash": "135026530ce2" + }, + "web.components.modules.build_module_dialog.add_custom_tag": { + "defaultMessage": "Ajouter une étiquette personnalisée...", + "description": "Build module dialog: placeholder inside the custom tag text field asking the user to type a tag; the trailing dots are part of the text", + "sourceHash": "a6f9b91eef5a" + }, + "web.components.modules.build_module_dialog.add_port": { + "defaultMessage": "Ajouter un port", + "description": "Build module dialog: button that adds another row to the port mappings list; keep it short", + "sourceHash": "20192c6ef6cf" + }, + "web.components.modules.build_module_dialog.add_tag": { + "defaultMessage": "Ajouter l'étiquette", + "description": "Build module dialog: button that adds the typed custom tag to the categories; keep it short", + "sourceHash": "3e10b08cc874" + }, + "web.components.modules.build_module_dialog.advertise": { + "defaultMessage": "Annoncer", + "description": "Build module dialog: checkbox label on a port row that sets whether the port is advertised", + "sourceHash": "7c5ebf3ea684" + }, + "web.components.modules.build_module_dialog.all_schema_constraints_dns_names_and": { + "defaultMessage": "Toutes les contraintes de schéma, les noms DNS et les règles d'image sont respectés.", + "description": "Build module dialog: explanation under the validation result saying the module passed the schema, DNS name and image checks; shown only when there are no errors", + "sourceHash": "e4a2d0fdff1d" + }, + "web.components.modules.build_module_dialog.archetype_or_image_required": { + "defaultMessage": "Veuillez sélectionner un archétype ou fournir une image de conteneur.", + "description": "Build module dialog: validation error when neither an archetype nor a container image is chosen", + "sourceHash": "da91344b1441" + }, + "web.components.modules.build_module_dialog.archetype_preset": { + "defaultMessage": "Préréglage d'archétype", + "description": "Build module dialog: label above the archetype preset choice on step 1 that fills the form from a starting preset", + "sourceHash": "04479563fb23" + }, + "web.components.modules.build_module_dialog.back": { + "defaultMessage": "Retour", + "description": "Build module dialog: button that returns to the previous step; keep it short", + "sourceHash": "76900f1bfd16" + }, + "web.components.modules.build_module_dialog.cancel": { + "defaultMessage": "Annuler", + "description": "Build module dialog: button that closes the wizard without creating the module; keep it short", + "sourceHash": "19766ed6ccb2" + }, + "web.components.modules.build_module_dialog.categories": { + "defaultMessage": "Catégories", + "description": "Build module dialog: label above the category tags, which are chosen from suggestions or typed in as custom tags", + "sourceHash": "b8b1d894c683" + }, + "web.components.modules.build_module_dialog.category_adventure": { + "defaultMessage": "Aventure", + "description": "Build module dialog: label of the Adventure category chip (the stored category value stays English)", + "sourceHash": "9a3c16424750" + }, + "web.components.modules.build_module_dialog.category_building": { + "defaultMessage": "Construction", + "description": "Build module dialog: label of the Building category chip (the stored category value stays English)", + "sourceHash": "87c5912f2cef" + }, + "web.components.modules.build_module_dialog.category_co_op": { + "defaultMessage": "Coopératif", + "description": "Build module dialog: label of the Co-op category chip (the stored category value stays English)", + "sourceHash": "71fcd7b022f2" + }, + "web.components.modules.build_module_dialog.category_creative": { + "defaultMessage": "Créatif", + "description": "Build module dialog: label of the Creative category chip (the stored category value stays English)", + "sourceHash": "2c7e63313045" + }, + "web.components.modules.build_module_dialog.category_horror": { + "defaultMessage": "Horreur", + "description": "Build module dialog: label of the Horror category chip (the stored category value stays English)", + "sourceHash": "271a22f75d73" + }, + "web.components.modules.build_module_dialog.category_mark": { + "defaultMessage": "×", + "description": "Build module dialog: marker shown on a category chip", + "sourceHash": "8db71ed28b0f" + }, + "web.components.modules.build_module_dialog.category_modded": { + "defaultMessage": "Avec mods", + "description": "Build module dialog: label of the Modded category chip (the stored category value stays English)", + "sourceHash": "588ddd638736" + }, + "web.components.modules.build_module_dialog.category_pvp": { + "defaultMessage": "PvP", + "description": "Build module dialog: label of the PvP category chip (the stored category value stays English)", + "sourceHash": "16a872c45a80" + }, + "web.components.modules.build_module_dialog.category_sandbox": { + "defaultMessage": "Bac à sable", + "description": "Build module dialog: label of the Sandbox category chip (the stored category value stays English)", + "sourceHash": "67fc62497e85" + }, + "web.components.modules.build_module_dialog.category_shooter": { + "defaultMessage": "Jeu de tir", + "description": "Build module dialog: label of the Shooter category chip (the stored category value stays English)", + "sourceHash": "78cbec508481" + }, + "web.components.modules.build_module_dialog.category_simulation": { + "defaultMessage": "Simulation", + "description": "Build module dialog: label of the Simulation category chip (the stored category value stays English)", + "sourceHash": "45314e97b0c2" + }, + "web.components.modules.build_module_dialog.category_survival": { + "defaultMessage": "Survie", + "description": "Build module dialog: label of the Survival category chip (the stored category value stays English)", + "sourceHash": "8302496de0ac" + }, + "web.components.modules.build_module_dialog.cluster_destination_source": { + "defaultMessage": "Source de destination du cluster", + "description": "Build module dialog: label above the dropdown that picks where the module is uploaded in the cluster; shown only when sources exist", + "sourceHash": "550271eba7c4" + }, + "web.components.modules.build_module_dialog.config_key_label": { + "defaultMessage": "{key} :", + "description": "Build module dialog: label of a computed config row; {key} is the config key", + "sourceHash": "4629a9ca24c4" + }, + "web.components.modules.build_module_dialog.container_image": { + "defaultMessage": "Image de conteneur", + "description": "Build module dialog: label above the container image field on step 2; the image reference should be pinned by digest", + "sourceHash": "76f1ee299b20" + }, + "web.components.modules.build_module_dialog.container_ports": { + "defaultMessage": "Conteneur et ports", + "description": "Build module dialog: name of step 2 in the wizard's progress bar, for the container image and ports; keep it short", + "sourceHash": "0147cbb4a073" + }, + "web.components.modules.build_module_dialog.continue_to_container_ports": { + "defaultMessage": "Continuer vers Conteneur et ports", + "description": "Build module dialog: button on step 1 that moves on to the container and ports step; keep it short", + "sourceHash": "9659044d517b" + }, + "web.components.modules.build_module_dialog.continue_to_review_export": { + "defaultMessage": "Continuer vers Vérification et export", + "description": "Build module dialog: button on step 2 that moves on to the review and export step; keep it short", + "sourceHash": "d4733a31dada" + }, + "web.components.modules.build_module_dialog.create_game_module": { + "defaultMessage": "Créer un module de jeu", + "description": "Build module dialog: title of the three-step wizard for creating a new game module", + "sourceHash": "893156ae4d8b" + }, + "web.components.modules.build_module_dialog.custom_tag_input": { + "defaultMessage": "Saisie d'étiquette personnalisée", + "description": "Build module dialog: screen-reader label for the custom tag text field; not visible on screen", + "sourceHash": "d385b9cb0932" + }, + "web.components.modules.build_module_dialog.display_name": { + "defaultMessage": "Nom d'affichage", + "description": "Build module dialog: label above the display name field, the friendly name shown for the module", + "sourceHash": "18d67c992b71" + }, + "web.components.modules.build_module_dialog.dns_invalid": { + "defaultMessage": "Doit être en minuscules alphanumériques avec des tirets", + "description": "Build module dialog: hint shown when the module name is not a valid DNS-1123 label", + "sourceHash": "20bdddf86881" + }, + "web.components.modules.build_module_dialog.dns_valid": { + "defaultMessage": "✓ Étiquette DNS-1123 valide", + "description": "Build module dialog: hint shown when the module name is a valid DNS-1123 label", + "sourceHash": "a0023ea43933" + }, + "web.components.modules.build_module_dialog.download_tar_gz": { + "defaultMessage": "Télécharger .tar.gz", + "description": "Build module dialog: button on step 3 that downloads the built module as an archive; keep the file extension as written; keep it short", + "sourceHash": "57c8332ef229" + }, + "web.components.modules.build_module_dialog.e_g_10gi": { + "defaultMessage": "ex. 10Gi", + "description": "Build module dialog: example text shown inside the empty volume size field; a sample size value, not a default", + "sourceHash": "680ba2b87d2b" + }, + "web.components.modules.build_module_dialog.e_g_counter_strike_2": { + "defaultMessage": "ex. Counter-Strike 2", + "description": "Build module dialog: example text shown inside the empty display name field; a sample name, not a default value", + "sourceHash": "1b3c709e4b60" + }, + "web.components.modules.build_module_dialog.e_g_cs2_match": { + "defaultMessage": "ex. cs2-match", + "description": "Build module dialog: example text shown inside the empty module name field; a sample name, not a default value", + "sourceHash": "2f4ef9f961c5" + }, + "web.components.modules.build_module_dialog.e_g_data": { + "defaultMessage": "ex. /data", + "description": "Build module dialog: example text shown inside the empty mount path field; a sample folder path, not a default", + "sourceHash": "7e4546a70887" + }, + "web.components.modules.build_module_dialog.e_g_ghcr_io_valgul_game_v1_0_sha256": { + "defaultMessage": "ex. ghcr.io/valgul/game:v1.0@sha256:...", + "description": "Build module dialog: example image reference shown inside the empty container image field; keep the reference format as written", + "sourceHash": "5b79c4fd14b5" + }, + "web.components.modules.build_module_dialog.env_name_label": { + "defaultMessage": "{name} :", + "description": "Build module dialog: label of an environment row; {name} is the variable name", + "sourceHash": "8b736e0d031b" + }, + "web.components.modules.build_module_dialog.errors": { + "defaultMessage": "erreurs,", + "description": "Build module dialog: word after the error count in the validation summary; the number shown before it is the count of errors", + "sourceHash": "8a3c6f41b660" + }, + "web.components.modules.build_module_dialog.failed_to_load_archetypes": { + "defaultMessage": "Échec du chargement des archétypes", + "description": "Build module dialog: error message shown in place of the archetype preset choices when they cannot be loaded", + "sourceHash": "58a6a4cbc043" + }, + "web.components.modules.build_module_dialog.install_to_cluster": { + "defaultMessage": "Installer dans le cluster", + "description": "Build module dialog: button on step 3 that installs the built module into the chosen cluster source; keep it short", + "sourceHash": "1a0ddebd83fe" + }, + "web.components.modules.build_module_dialog.loading_archetypes": { + "defaultMessage": "Chargement des archétypes...", + "description": "Build module dialog: loading text shown in place of the archetype preset choices while they are fetched; the dots are part of the text", + "sourceHash": "030968c29c4e" + }, + "web.components.modules.build_module_dialog.memory_simulation": { + "defaultMessage": "Simulation de mémoire", + "description": "Build module dialog: label of the memory estimate card on step 3; the value beside it is the simulated memory", + "sourceHash": "6bb55b07c30b" + }, + "web.components.modules.build_module_dialog.module_definition_source": { + "defaultMessage": "Source de définition du module", + "description": "Build module dialog: screen-reader name of the large text box holding the module definition source; not visible on screen", + "sourceHash": "43445f23788d" + }, + "web.components.modules.build_module_dialog.module_name": { + "defaultMessage": "Nom du module", + "description": "Build module dialog: label above the module name field; the name must be a DNS-1123 label of lowercase letters, digits and hyphens; DNS-1123 is a technical term, usually left untranslated", + "sourceHash": "7e71328f1bee" + }, + "web.components.modules.build_module_dialog.module_name_invalid": { + "defaultMessage": "Le nom du module doit être une étiquette DNS-1123 valide en minuscules.", + "description": "Build module dialog: validation error when the module name is not a valid DNS-1123 label", + "sourceHash": "3709c8632492" + }, + "web.components.modules.build_module_dialog.module_validated_cleanly": { + "defaultMessage": "Module validé sans erreur", + "description": "Build module dialog: success heading in the validation panel, shown when the module passes validation with no errors or warnings", + "sourceHash": "ef11d5a0b309" + }, + "web.components.modules.build_module_dialog.mount_path": { + "defaultMessage": "Chemin de montage", + "description": "Build module dialog: label above the mount path field, the folder inside the server where the volume is attached", + "sourceHash": "ca72ba9bab3f" + }, + "web.components.modules.build_module_dialog.name": { + "defaultMessage": "nom", + "description": "Build module dialog: example text shown inside the port name field of a port row; a short placeholder word, not a default value", + "sourceHash": "82a3537ff0db" + }, + "web.components.modules.build_module_dialog.persistent_storage": { + "defaultMessage": "Stockage persistant", + "description": "Build module dialog: label above the persistent storage settings, the volume size and mount path, on step 2", + "sourceHash": "2492fbe6db06" + }, + "web.components.modules.build_module_dialog.pinned_digest": { + "defaultMessage": "✓ Épinglée par digest (@sha256:...)", + "description": "Build module dialog: hint shown when the image is pinned by digest", + "sourceHash": "2ab56f00bfd6" + }, + "web.components.modules.build_module_dialog.port": { + "defaultMessage": "port", + "description": "Build module dialog: example text shown inside the container port number field of a port row; a placeholder word, not a default value", + "sourceHash": "f8d397a33fcb" + }, + "web.components.modules.build_module_dialog.port_mappings": { + "defaultMessage": "Mappages de ports", + "description": "Build module dialog: label above the list of port mappings on step 2", + "sourceHash": "6f163c079dee" + }, + "web.components.modules.build_module_dialog.preset_metadata": { + "defaultMessage": "Préréglage et métadonnées", + "description": "Build module dialog: name of step 1 in the wizard's progress bar, where the preset and module details are set; keep it short", + "sourceHash": "aee19c3876c3" + }, + "web.components.modules.build_module_dialog.protocol": { + "defaultMessage": "Protocole", + "description": "Build module dialog: screen-reader label for the protocol dropdown of a port row; not visible on screen", + "sourceHash": "cf0883343f4a" + }, + "web.components.modules.build_module_dialog.protocol_options": { + "defaultMessage": "Options de protocole", + "description": "Build module dialog: screen-reader name of the list of protocol choices in a port row's dropdown; not visible on screen", + "sourceHash": "180b06bfb48d" + }, + "web.components.modules.build_module_dialog.recommended": { + "defaultMessage": "Recommandé", + "description": "Build module dialog: small note beside the Categories label marking the suggested tags", + "sourceHash": "d70604e84304" + }, + "web.components.modules.build_module_dialog.remove_port": { + "defaultMessage": "Supprimer le port", + "description": "Build module dialog: button that removes this port row; keep it short", + "sourceHash": "ca7884ccf017" + }, + "web.components.modules.build_module_dialog.review_export": { + "defaultMessage": "Vérification et export", + "description": "Build module dialog: name of step 3 in the wizard's progress bar, for checking the module and exporting it; keep it short", + "sourceHash": "2a97895b93a9" + }, + "web.components.modules.build_module_dialog.selected_tags": { + "defaultMessage": "Étiquettes sélectionnées", + "description": "Build module dialog: heading above the list of custom tags already added; shown only when at least one custom tag exists", + "sourceHash": "d164fdad3844" + }, + "web.components.modules.build_module_dialog.short_description_of_the_game_server": { + "defaultMessage": "Brève description du serveur de jeu", + "description": "Build module dialog: example text shown inside the empty summary field, hinting what to write there", + "sourceHash": "f10df808c021" + }, + "web.components.modules.build_module_dialog.source_options": { + "defaultMessage": "Options de source", + "description": "Build module dialog: screen-reader name of the list of upload source choices in the cluster destination dropdown; not visible on screen", + "sourceHash": "386ce6e5d9a7" + }, + "web.components.modules.build_module_dialog.step_1_of_3": { + "defaultMessage": "Étape 1 sur 3", + "description": "Build module dialog: footer text on step 1 showing that this is the first of the three wizard steps", + "sourceHash": "a56fdaed4612" + }, + "web.components.modules.build_module_dialog.step_container_ports": { + "defaultMessage": "Conteneur et ports", + "description": "Build module dialog: name of step 2 in the step header", + "sourceHash": "0147cbb4a073" + }, + "web.components.modules.build_module_dialog.step_done": { + "defaultMessage": "✓", + "description": "Build module dialog: mark shown on a completed step", + "sourceHash": "1dabba21cdad" + }, + "web.components.modules.build_module_dialog.step_number_1": { + "defaultMessage": "1", + "description": "Build module dialog: number of step 1 in the step indicator", + "sourceHash": "6b86b273ff34" + }, + "web.components.modules.build_module_dialog.step_number_2": { + "defaultMessage": "2", + "description": "Build module dialog: number of step 2 in the step indicator", + "sourceHash": "d4735e3a265e" + }, + "web.components.modules.build_module_dialog.step_number_3": { + "defaultMessage": "3", + "description": "Build module dialog: number of step 3 in the step indicator", + "sourceHash": "4e07408562be" + }, + "web.components.modules.build_module_dialog.step_preset_metadata": { + "defaultMessage": "Préréglage et métadonnées", + "description": "Build module dialog: name of step 1 in the step header", + "sourceHash": "aee19c3876c3" + }, + "web.components.modules.build_module_dialog.step_review_export": { + "defaultMessage": "Vérification, aperçu et export", + "description": "Build module dialog: name of step 3 in the step header", + "sourceHash": "c5811602bfe9" + }, + "web.components.modules.build_module_dialog.step_step_of_3": { + "defaultMessage": "Étape {step} sur 3 ·", + "description": "Build module dialog: sentence with step in Description", + "sourceHash": "e53071dbb65d" + }, + "web.components.modules.build_module_dialog.summary": { + "defaultMessage": "Résumé", + "description": "Build module dialog: label above the summary field, a short description of the module", + "sourceHash": "8e76a94ac832" + }, + "web.components.modules.build_module_dialog.tcp": { + "defaultMessage": "TCP", + "description": "Build module dialog: protocol option TCP in a port row's dropdown; technical term, usually left untranslated; keep it short", + "sourceHash": "2e9430507b92" + }, + "web.components.modules.build_module_dialog.udp": { + "defaultMessage": "UDP", + "description": "Build module dialog: protocol option UDP in a port row's dropdown; technical term, usually left untranslated; keep it short", + "sourceHash": "dc4030f9688d" + }, + "web.components.modules.build_module_dialog.unpinned_image": { + "defaultMessage": "⚠ Image non épinglée (recommandé : @sha256:...)", + "description": "Build module dialog: warning shown when the image is not pinned by digest", + "sourceHash": "497b741ce82d" + }, + "web.components.modules.build_module_dialog.upload_destination_required": { + "defaultMessage": "Veuillez sélectionner une destination de source d'envoi.", + "description": "Build module dialog: validation error when no upload source destination is chosen", + "sourceHash": "7a10f31f9028" + }, + "web.components.modules.build_module_dialog.validation_failed": { + "defaultMessage": "La validation a échoué", + "description": "Build module dialog: fallback finding message when validation fails without a message", + "sourceHash": "23a86f5a2dae" + }, + "web.components.modules.build_module_dialog.volume_size": { + "defaultMessage": "Taille du volume", + "description": "Build module dialog: label above the volume size field, the storage size requested for the module's data", + "sourceHash": "6186b78d543c" + }, + "web.components.modules.build_module_dialog.warnings": { + "defaultMessage": "avertissements", + "description": "Build module dialog: word after the warning count at the end of the validation summary; the number shown before it is the count of warnings", + "sourceHash": "227690d7c303" + }, + "web.components.modules.install_dialog.cancel": { + "defaultMessage": "Annuler", + "description": "Install dialog: button that closes the install dialog without installing; keep it short", + "sourceHash": "19766ed6ccb2" + }, + "web.components.modules.install_dialog.fields_required": { + "defaultMessage": "la source, la version et le nom sont tous obligatoires", + "description": "Install dialog: validation error when the source, version or name is missing", + "sourceHash": "d8789225b4f3" + }, + "web.components.modules.install_dialog.install": { + "defaultMessage": "Installer", + "description": "Install dialog: the word Install used as the dialog title, followed by the module name, and as the confirm button; keep it short", + "sourceHash": "569ca49f4aaf" + }, + "web.components.modules.install_dialog.install_as": { + "defaultMessage": "Installer sous le nom", + "description": "Install dialog: label above the name field that sets the name the installed module will get", + "sourceHash": "7b52d2e01de9" + }, + "web.components.modules.install_dialog.pulls_the_module_bundle_and_creates_a": { + "defaultMessage": "Récupère le bundle du module et crée une ressource Module. L'opérateur du cluster matérialise le GameTemplate en arrière-plan.", + "description": "Install dialog: explanation under the title saying the install pulls the module bundle and creates a Module resource; Module and GameTemplate are Kubernetes resource names, usually left untranslated", + "sourceHash": "9c7245cead36" + }, + "web.components.modules.install_dialog.source": { + "defaultMessage": "Source", + "description": "Install dialog: label above the source field, which is a dropdown or a fixed read-only box depending on the available sources", + "sourceHash": "0e570ca6fabe" + }, + "web.components.modules.install_dialog.source_label": { + "defaultMessage": "{name} ({type})", + "description": "Install dialog: module source name and type; {name} is the source name, {type} is the source type", + "sourceHash": "bd0aa116c5c6" + }, + "web.components.modules.install_dialog.the_module_gametemplate_name_must_be_a": { + "defaultMessage": "Le nom Module + GameTemplate. Doit être une étiquette DNS.", + "description": "Install dialog: hint under the install name field saying the name is shared by the Module and GameTemplate and must be a DNS label; these resource names are usually left untranslated", + "sourceHash": "28565a63ff11" + }, + "web.components.modules.install_dialog.title": { + "defaultMessage": "Installer {name}", + "description": "Install dialog: dialog title; {name} is the module display name", + "sourceHash": "15d27e180bc6" + }, + "web.components.modules.install_dialog.version": { + "defaultMessage": "Version", + "description": "Install dialog: label above the version field, a dropdown of module versions or a fixed read-only box depending on what is available", + "sourceHash": "dd167905de0d" + }, + "web.components.modules.module_card.deploy_locally": { + "defaultMessage": "Déployer localement", + "description": "Module card: button label to deploy the module to the local cluster", + "sourceHash": "77cbab48d570" + }, + "web.components.modules.module_card.deploy_to": { + "defaultMessage": "Déployer sur {cluster}", + "description": "Module card: button label to deploy the module to a remote cluster; {cluster} is the cluster name", + "sourceHash": "600c373abbca" + }, + "web.components.modules.module_card.install": { + "defaultMessage": "Installer", + "description": "Module card: button that installs a module that is not installed yet; keep it short", + "sourceHash": "569ca49f4aaf" + }, + "web.components.modules.module_card.installed_version": { + "defaultMessage": "v{version} installée", + "description": "Module card: installed version text; {version} is the installed module version", + "sourceHash": "0a43327ef057" + }, + "web.components.modules.module_card.keyless_policy": { + "defaultMessage": "politique de signature sans clé (Fulcio) déclarée", + "description": "Module card: tooltip when a keyless (Fulcio) signature policy is declared", + "sourceHash": "a3df65aced62" + }, + "web.components.modules.module_card.keyless_verified": { + "defaultMessage": "signature sans clé (Fulcio) vérifiée", + "description": "Module card: tooltip when a keyless (Fulcio) signature is verified", + "sourceHash": "fafd6eab273f" + }, + "web.components.modules.module_card.latest_version": { + "defaultMessage": "v{version}", + "description": "Module card: latest published version; {version} is the version number", + "sourceHash": "da95c4621978" + }, + "web.components.modules.module_card.no_published_versions": { + "defaultMessage": "aucune version publiée", + "description": "Module card: shown when a module has no published version", + "sourceHash": "23038b970737" + }, + "web.components.modules.module_card.no_summary": { + "defaultMessage": "Aucune description.", + "description": "Module card: text shown when a module has no summary", + "sourceHash": "cc652bed88c5" + }, + "web.components.modules.module_card.pinned_to_vpinnedversion_no_longer_in": { + "defaultMessage": "Épinglée sur v{pinnedVersion} — n'est plus dans le catalogue. Mettez à jour vers v{latestVersion}.", + "description": "Module card: sentence with pinnedVersion, latestVersion in div", + "sourceHash": "734820ca97d1" + }, + "web.components.modules.module_card.policy": { + "defaultMessage": "politique", + "description": "Module card: small badge meaning a signature policy is declared for the module; its tooltip says whether keyless (Fulcio) signing is used; keep it short", + "sourceHash": "823412d1eacb" + }, + "web.components.modules.module_card.remove_upload": { + "defaultMessage": "Supprimer le fichier envoyé", + "description": "Module card: button that removes an uploaded module bundle; keep it short", + "sourceHash": "b57e3abc42c8" + }, + "web.components.modules.module_card.rollback_target_vpreviousversion": { + "defaultMessage": "cible de restauration · v{previousVersion}", + "description": "Module card: sentence with previousVersion in div", + "sourceHash": "80fba8a63010" + }, + "web.components.modules.module_card.signature_policy": { + "defaultMessage": "politique de signature déclarée", + "description": "Module card: tooltip when a signature policy is declared", + "sourceHash": "527301315010" + }, + "web.components.modules.module_card.signature_verified": { + "defaultMessage": "signature vérifiée", + "description": "Module card: tooltip when a signature is verified", + "sourceHash": "382d189939f6" + }, + "web.components.modules.module_card.source_label": { + "defaultMessage": "{name} ({type})", + "description": "Module card: module source name and type; {name} is the source name, {type} is the source type", + "sourceHash": "bd0aa116c5c6" + }, + "web.components.modules.module_card.sources_count": { + "defaultMessage": "{count, plural, one {# source} other {# sources}}", + "description": "Module card: number of module sources; {count} is the count", + "sourceHash": "cc2236559fe9" + }, + "web.components.modules.module_card.status_available": { + "defaultMessage": "disponible", + "description": "Module card: status chip for a module that is not installed", + "sourceHash": "ddd9818abacf" + }, + "web.components.modules.module_card.status_failed": { + "defaultMessage": "échec", + "description": "Module card: status chip for an installed module whose last operation failed", + "sourceHash": "5d28a90f4498" + }, + "web.components.modules.module_card.status_installed": { + "defaultMessage": "installé", + "description": "Module card: status chip for an installed module", + "sourceHash": "a51a6c19a1ff" + }, + "web.components.modules.module_card.status_pending": { + "defaultMessage": "en attente", + "description": "Module card: status chip for an installed module with no phase yet", + "sourceHash": "62a2fed3d6e0" + }, + "web.components.modules.module_card.status_update": { + "defaultMessage": "mise à jour", + "description": "Module card: status chip for an installed module with a newer version available", + "sourceHash": "2937013f2181" + }, + "web.components.modules.module_card.uninstall": { + "defaultMessage": "Désinstaller", + "description": "Module card: button that removes an installed module; keep it short", + "sourceHash": "fe199528850a" + }, + "web.components.modules.module_card.update_available_suffix": { + "defaultMessage": "· v{version} disponible", + "description": "Module card: suffix naming the newer version that is available; {version} is that version", + "sourceHash": "903275ac2c64" + }, + "web.components.modules.module_card.update_to_vlatestversion": { + "defaultMessage": "Mettre à jour vers v{latestVersion}", + "description": "Module card: sentence with latestVersion in Button", + "sourceHash": "7247656d75cd" + }, + "web.components.modules.module_card.upgrade": { + "defaultMessage": "Mettre à niveau", + "description": "Module card: button that upgrades an installed module to its latest version; keep it short", + "sourceHash": "7ec0261b8e8a" + }, + "web.components.modules.module_card.verified": { + "defaultMessage": "vérifié", + "description": "Module card: small badge meaning the module's signature was verified; its tooltip says whether keyless (Fulcio) verification passed; keep it short", + "sourceHash": "1c34f88707b5" + }, + "web.components.modules.module_sources_panel.add_source": { + "defaultMessage": "Ajouter une source", + "description": "Module sources panel: button that opens the dialog to add a new module source; keep it short", + "sourceHash": "6ea6c9ec2a76" + }, + "web.components.modules.module_sources_panel.delete_source_aria": { + "defaultMessage": "Supprimer {name}", + "description": "Module sources panel: aria-label of the delete button; {name} is the source name", + "sourceHash": "3cf8146e529f" + }, + "web.components.modules.module_sources_panel.edit_source_aria": { + "defaultMessage": "Modifier {name}", + "description": "Module sources panel: aria-label of the edit button; {name} is the source name", + "sourceHash": "2313fe21ea84" + }, + "web.components.modules.module_sources_panel.empty": { + "defaultMessage": "Aucune ressource ModuleSource configurée.", + "description": "Module sources panel: empty-state sentence shown when no module sources are configured; the tag wraps the resource name ModuleSource, a technical term usually left untranslated, and must be kept around it", + "sourceHash": "54c24fc83a53" + }, + "web.components.modules.module_sources_panel.failed_to_load_module_sources": { + "defaultMessage": "Échec du chargement des sources de modules.", + "description": "Module sources panel: error card message shown when the module sources cannot be loaded; the card has a retry button", + "sourceHash": "a0edcc84eaac" + }, + "web.components.modules.module_sources_panel.insecure": { + "defaultMessage": "non sécurisée", + "description": "Module sources panel: small warning badge on a source row marking a source that allows plain HTTP; keep it short", + "sourceHash": "1d92dae504a7" + }, + "web.components.modules.module_sources_panel.intro": { + "defaultMessage": "Registres, dépôts git, archives et envois dont l'opérateur récupère les bundles de modules. Équivalent à l'application directe de ressources ModuleSource.", + "description": "Module sources panel: explanation under the heading saying sources are registries, git repositories, archives or uploads, the same as applying ModuleSource resources directly; the tag wraps the resource name ModuleSource, a technical term usually left untranslated, and must be kept around it", + "sourceHash": "41b1502e47fe" + }, + "web.components.modules.module_sources_panel.loading_module_sources": { + "defaultMessage": "Chargement des sources de modules…", + "description": "Module sources panel: loading text shown while the list of module sources is being fetched", + "sourceHash": "ffc433afb31f" + }, + "web.components.modules.module_sources_panel.loading_sources": { + "defaultMessage": "Chargement des sources de modules…", + "description": "Module sources panel: loading message while module sources load", + "sourceHash": "ffc433afb31f" + }, + "web.components.modules.module_sources_panel.local_location": { + "defaultMessage": "local", + "description": "Module sources panel: location shown for a local source that has no path", + "sourceHash": "25bf8e1a2393" + }, + "web.components.modules.module_sources_panel.module_sources": { + "defaultMessage": "Sources de modules", + "description": "Module sources panel: heading of the panel that lists the sources module bundles are pulled from", + "sourceHash": "b8ba2797838f" + }, + "web.components.modules.module_sources_panel.modulecount_module": { + "defaultMessage": "{moduleCount, plural, one {# module} other {# modules}}", + "description": "Module sources panel: module count in a source card; {moduleCount} is the number of modules", + "sourceHash": "d5007bccc112" + }, + "web.components.modules.module_sources_panel.serving_stale_catalog": { + "defaultMessage": "catalogue obsolète", + "description": "Module sources panel: warning text on a source row shown when the source is still serving an old catalog of modules", + "sourceHash": "7e98edecde75" + }, + "web.components.modules.module_sources_panel.synced": { + "defaultMessage": "· synchronisée", + "description": "Module sources panel: text after the module count on a source row, followed by the time of the last sync in relative time; the leading dot is part of the text", + "sourceHash": "62c5e248b6f9" + }, + "web.components.modules.module_sources_panel.type_oci": { + "defaultMessage": "oci", + "description": "Module sources panel: fallback source type when the source sets none", + "sourceHash": "7e887edb52b7" + }, + "web.components.modules.module_sources_panel.uploaded_bundles": { + "defaultMessage": "bundles envoyés", + "description": "Module sources panel: location shown for a source whose modules are uploaded bundles", + "sourceHash": "e37cf1d46f10" + }, + "web.components.modules.source_dialog.1h": { + "defaultMessage": "1h", + "description": "Source dialog: example value shown inside the empty refresh interval field; a sample duration, keep it as written", + "sourceHash": "894816281e23" + }, + "web.components.modules.source_dialog.a_tar_gz_or_zip_of_module_directories": { + "defaultMessage": "Un .tar.gz ou .zip de répertoires de modules.", + "description": "Source dialog: hint under the archive URL field saying the archive is a .tar.gz or .zip of module directories; file extensions are kept as written", + "sourceHash": "8d558517b1e0" + }, + "web.components.modules.source_dialog.add_source": { + "defaultMessage": "Ajouter la source", + "description": "Source dialog: submit button label when adding a source", + "sourceHash": "6ea6c9ec2a76" + }, + "web.components.modules.source_dialog.add_source_title": { + "defaultMessage": "Ajouter une source de modules", + "description": "Source dialog: title when adding a source", + "sourceHash": "d200d08dd1be" + }, + "web.components.modules.source_dialog.allow_list": { + "defaultMessage": "Liste d'autorisation", + "description": "Source dialog: label above the allow list field, an optional filter of module names for the source", + "sourceHash": "f1eb6f7f3511" + }, + "web.components.modules.source_dialog.allow_plain_http_local_registries_only": { + "defaultMessage": "Autoriser HTTP non chiffré (registres locaux uniquement)", + "description": "Source dialog: checkbox label that allows plain HTTP for a source, meant only for local registries; the same label appears in the edit view", + "sourceHash": "7178ae5116f0" + }, + "web.components.modules.source_dialog.archive_creds": { + "defaultMessage": "archive-creds", + "description": "Source dialog: example value shown inside the empty credentials secret field for archive sources; a sample secret name, not a default value", + "sourceHash": "4742c2197b93" + }, + "web.components.modules.source_dialog.archive_url": { + "defaultMessage": "URL de l'archive", + "description": "Source dialog: label above the archive URL field, shown for archive sources, where the archive is downloaded from", + "sourceHash": "727fdec005f4" + }, + "web.components.modules.source_dialog.branch_or_tag_defaults_to_main": { + "defaultMessage": "Branche ou tag. Par défaut : main.", + "description": "Source dialog: hint under the git ref field saying the value is a branch or tag and that it defaults to main", + "sourceHash": "61b3ed0e90f6" + }, + "web.components.modules.source_dialog.bundles": { + "defaultMessage": "bundles", + "description": "Source dialog: example value shown inside the empty path field for local sources; a sample folder name, not a default value", + "sourceHash": "114b245d0458" + }, + "web.components.modules.source_dialog.cancel": { + "defaultMessage": "Annuler", + "description": "Source dialog: button that closes the source dialog without saving; keep it short", + "sourceHash": "19766ed6ccb2" + }, + "web.components.modules.source_dialog.certificate_identity": { + "defaultMessage": "Identité du certificat", + "description": "Source dialog: label above the certificate identity field, shown for keyless verification; it names the identity that signed the bundle", + "sourceHash": "97b88d6d3feb" + }, + "web.components.modules.source_dialog.clone_url": { + "defaultMessage": "URL de clonage", + "description": "Source dialog: label above the clone URL field, shown for git sources, where the repository is fetched from", + "sourceHash": "0232581854df" + }, + "web.components.modules.source_dialog.comma_separated_module_names_registries": { + "defaultMessage": "Noms de modules séparés par des virgules (les registres ne peuvent pas être énumérés).", + "description": "Source dialog: hint under the modules field saying the names are comma-separated and that registries cannot be listed in full", + "sourceHash": "eccd6a85d355" + }, + "web.components.modules.source_dialog.community": { + "defaultMessage": "community", + "description": "Source dialog: example value shown inside the empty source name field; a sample name, not a default value", + "sourceHash": "f354ee99e2bc" + }, + "web.components.modules.source_dialog.cosign_pub": { + "defaultMessage": "cosign-pub", + "description": "Source dialog: example value shown inside the empty public key secret field; a sample secret name, not a default value", + "sourceHash": "8be48734aa9f" + }, + "web.components.modules.source_dialog.credentials_secret": { + "defaultMessage": "Secret d'identifiants", + "description": "Source dialog: label above the credentials secret name field, shown for git and archive sources; the secret holds access credentials", + "sourceHash": "9fb7bdd04728" + }, + "web.components.modules.source_dialog.dns_label_identifying_this_source": { + "defaultMessage": "Étiquette DNS identifiant cette source.", + "description": "Source dialog: hint under the source name field saying it is a DNS label that identifies the source; DNS label is a technical term, usually left untranslated", + "sourceHash": "f8dcc7868cce" + }, + "web.components.modules.source_dialog.dockerconfigjson_secret_in_the_operator": { + "defaultMessage": "Secret dockerconfigjson dans l'espace de noms de l'opérateur (facultatif).", + "description": "Source dialog: hint under the pull secret field saying it is an optional dockerconfigjson Secret in the operator namespace; dockerconfigjson is a technical term, usually left untranslated", + "sourceHash": "2f7174102470" + }, + "web.components.modules.source_dialog.edit_source_title": { + "defaultMessage": "Modifier la source {name}", + "description": "Source dialog: title when editing; {name} is the source name", + "sourceHash": "a7e83d2a4951" + }, + "web.components.modules.source_dialog.gh_creds": { + "defaultMessage": "gh-creds", + "description": "Source dialog: example value shown inside the empty credentials secret field; a sample secret name, not a default value", + "sourceHash": "fb30c64ff310" + }, + "web.components.modules.source_dialog.ghcr_io_gameplanepanel_gameplane_modules": { + "defaultMessage": "ghcr.io/gameplanepanel/gameplane-modules", + "description": "Source dialog: example value shown inside the empty registry URL field; a sample registry address, keep it as written", + "sourceHash": "1f2529f45806" + }, + "web.components.modules.source_dialog.github_com_org_repo_github_workflows": { + "defaultMessage": "github.com/org/repo/.github/workflows/release.yml@refs/heads/main", + "description": "Source dialog: example value shown inside the empty certificate identity field; a sample identity path, keep it as written", + "sourceHash": "fdaf6b0996aa" + }, + "web.components.modules.source_dialog.how_often_the_catalog_re_indexes": { + "defaultMessage": "Fréquence de réindexation du catalogue. Par défaut : 1h.", + "description": "Source dialog: hint under the refresh interval field saying how often the catalog is re-indexed and that the default is 1h", + "sourceHash": "7f227877173b" + }, + "web.components.modules.source_dialog.https_example_com_modules_tar_gz": { + "defaultMessage": "https://example.com/modules.tar.gz", + "description": "Source dialog: example value shown inside the empty archive URL field; a sample address, keep it as written", + "sourceHash": "9dd5ec353d60" + }, + "web.components.modules.source_dialog.https_github_com_example_gameplane": { + "defaultMessage": "https://github.com/example/gameplane-modules", + "description": "Source dialog: example value shown inside the empty clone URL field; a sample repository address, keep it as written", + "sourceHash": "d2bc1ab80801" + }, + "web.components.modules.source_dialog.https_token_actions_githubusercontent": { + "defaultMessage": "https://token.actions.githubusercontent.com", + "description": "Source dialog: example value shown inside the empty OIDC issuer field; a sample issuer address, keep it as written", + "sourceHash": "dd9c65dd99f4" + }, + "web.components.modules.source_dialog.indexes_bundles_uploaded_through_the": { + "defaultMessage": "Indexe les bundles envoyés depuis le tableau de bord (stockés sous forme de ConfigMaps dans l'espace de noms de l'opérateur). Aucune configuration supplémentaire.", + "description": "Source dialog: note for upload sources saying uploaded bundles are indexed and kept as ConfigMaps in the operator namespace; ConfigMaps is a technical term, usually left untranslated", + "sourceHash": "245adadcd655" + }, + "web.components.modules.source_dialog.issuer_embedded_in_the_signing": { + "defaultMessage": "Émetteur intégré au certificat de signature.", + "description": "Source dialog: hint under the OIDC issuer field saying it is the issuer embedded in the signing certificate", + "sourceHash": "ff5635224008" + }, + "web.components.modules.source_dialog.keyed_secret_required": { + "defaultMessage": "La vérification par clé nécessite un nom de secret de clé publique", + "description": "Source dialog: validation error when keyed verification has no public key secret name", + "sourceHash": "3dcb14249213" + }, + "web.components.modules.source_dialog.keyless_identity_required": { + "defaultMessage": "La vérification sans clé nécessite un émetteur et une identité", + "description": "Source dialog: validation error when keyless verification has no issuer or identity", + "sourceHash": "57e645de112d" + }, + "web.components.modules.source_dialog.main": { + "defaultMessage": "main", + "description": "Source dialog: example value shown inside the empty git ref field; the branch name main as a sample", + "sourceHash": "0d6e4079e367" + }, + "web.components.modules.source_dialog.minecraft": { + "defaultMessage": "minecraft-*", + "description": "Source dialog: example value shown inside the empty allow list field; a sample name pattern with a wildcard, keep it as written", + "sourceHash": "2db7e34d3675" + }, + "web.components.modules.source_dialog.minecraft_java_valheim": { + "defaultMessage": "minecraft-java, valheim", + "description": "Source dialog: example value shown inside the empty modules field; a sample list of module names separated by commas", + "sourceHash": "49201c8fa9f9" + }, + "web.components.modules.source_dialog.modules": { + "defaultMessage": "Modules", + "description": "Source dialog: label above the modules field for registry sources, which lists the module names to pull", + "sourceHash": "76c86c4c3243" + }, + "web.components.modules.source_dialog.modules_2": { + "defaultMessage": "modules", + "description": "Source dialog: example value shown inside the empty subdirectory field; a sample folder name, not a default value", + "sourceHash": "fbc6c1d4c3b6" + }, + "web.components.modules.source_dialog.name": { + "defaultMessage": "Nom", + "description": "Source dialog: label above the source name field; the field is shown only when adding a new source, not when editing", + "sourceHash": "dcd1d5223f73" + }, + "web.components.modules.source_dialog.name_required": { + "defaultMessage": "le nom est obligatoire", + "description": "Source dialog: validation error when the source name is empty", + "sourceHash": "c0fda8c2408a" + }, + "web.components.modules.source_dialog.oci_modules_required": { + "defaultMessage": "Les sources OCI nécessitent au moins un nom de module", + "description": "Source dialog: validation error when an OCI source lists no module names", + "sourceHash": "cc5b25a006f2" + }, + "web.components.modules.source_dialog.oidc_issuer": { + "defaultMessage": "Émetteur OIDC", + "description": "Source dialog: label above the OIDC issuer field, shown for keyless verification; OIDC is a technical term, usually left untranslated", + "sourceHash": "fe4ff13c4702" + }, + "web.components.modules.source_dialog.optional_module_name_filter_exact_names": { + "defaultMessage": "Filtre facultatif de noms de modules : noms exacts ou globs, séparés par des virgules.", + "description": "Source dialog: hint under the allow list field saying the optional filter takes exact module names or globs, separated by commas", + "sourceHash": "ee757af23599" + }, + "web.components.modules.source_dialog.path": { + "defaultMessage": "Chemin", + "description": "Source dialog: label above the path field for local sources, the folder path inside the operator's module mount", + "sourceHash": "62fa5a5b0d3c" + }, + "web.components.modules.source_dialog.public_key_secret": { + "defaultMessage": "Secret de clé publique", + "description": "Source dialog: label above the public key secret field, shown only for key-based verification", + "sourceHash": "df8efba0897f" + }, + "web.components.modules.source_dialog.pull_secret": { + "defaultMessage": "Secret de récupération", + "description": "Source dialog: label above the pull secret name field for registry sources; the secret holds registry credentials", + "sourceHash": "bd5938dadd3d" + }, + "web.components.modules.source_dialog.ref": { + "defaultMessage": "Réf.", + "description": "Source dialog: label above the git ref field, the branch or tag to use from the repository", + "sourceHash": "9b6e88e8943a" + }, + "web.components.modules.source_dialog.refresh_interval": { + "defaultMessage": "Intervalle de rafraîchissement", + "description": "Source dialog: label above the refresh interval field, how often the module catalog is re-indexed", + "sourceHash": "39f9aeef2311" + }, + "web.components.modules.source_dialog.registry_creds": { + "defaultMessage": "registry-creds", + "description": "Source dialog: example value shown inside the empty pull secret field; a sample secret name, not a default value", + "sourceHash": "c2a2e1130fab" + }, + "web.components.modules.source_dialog.registry_url": { + "defaultMessage": "URL du registre", + "description": "Source dialog: label above the registry URL field, shown for OCI registry sources; OCI is a technical term, usually left untranslated", + "sourceHash": "6800d04b1630" + }, + "web.components.modules.source_dialog.relative_to_the_operator_s_module_mount": { + "defaultMessage": "Relatif au point de montage des modules de l'opérateur (Helm : operator.localModules). Vide analyse la racine du point de montage.", + "description": "Source dialog: hint under the local path field; the path is relative to the operator's module mount (Helm value operator.localModules) and empty scans the mount root", + "sourceHash": "5a32fcb5559b" + }, + "web.components.modules.source_dialog.require_a_valid_cosign_signature_on": { + "defaultMessage": "Exiger une signature cosign valide sur chaque bundle récupéré depuis cette source.", + "description": "Source dialog: hint under the signature verification dropdown saying every bundle from the source must have a valid cosign signature; cosign is a technical term, usually left untranslated", + "sourceHash": "30ceba5c1fe3" + }, + "web.components.modules.source_dialog.san_identity_that_must_have_produced": { + "defaultMessage": "Identité SAN qui doit avoir produit la signature.", + "description": "Source dialog: hint under the certificate identity field saying it is the SAN identity that must have produced the signature; SAN is a technical term, usually left untranslated", + "sourceHash": "6ef8728f5081" + }, + "web.components.modules.source_dialog.save": { + "defaultMessage": "Enregistrer", + "description": "Source dialog: submit button label when editing an existing source", + "sourceHash": "1509f561f241" + }, + "web.components.modules.source_dialog.scan_only_this_path_inside_the_repo": { + "defaultMessage": "Analyser uniquement ce chemin dans le dépôt (facultatif).", + "description": "Source dialog: hint under the subdirectory field saying only this path inside the repository is scanned and that the field is optional", + "sourceHash": "df884ce94df7" + }, + "web.components.modules.source_dialog.secret_holding_the_cosign_public_key": { + "defaultMessage": "Secret contenant la clé publique cosign sous la clé de données cosign.pub.", + "description": "Source dialog: hint under the public key secret field saying the secret holds the cosign public key under the cosign.pub data key; key names are kept as written", + "sourceHash": "886a40a13cf3" + }, + "web.components.modules.source_dialog.secret_with_token_bearer_or_username": { + "defaultMessage": "Secret avec jeton (Bearer) ou identifiant+mot de passe. Facultatif.", + "description": "Source dialog: hint under the archive credentials secret field saying the secret holds a Bearer token or a username and password; the field is optional", + "sourceHash": "f64d0f79dd2a" + }, + "web.components.modules.source_dialog.secret_with_token_username_password": { + "defaultMessage": "Secret avec jeton / identifiant+mot de passe (https) ou ssh-privatekey + known_hosts (ssh). Facultatif.", + "description": "Source dialog: hint under the credentials secret field saying the secret holds a token or username and password for https, or an SSH key and known hosts for ssh; the field is optional", + "sourceHash": "a3a97ae1d3ef" + }, + "web.components.modules.source_dialog.signature_verification": { + "defaultMessage": "Vérification de la signature", + "description": "Source dialog: label above the signature verification dropdown that sets how bundles are checked; also the screen-reader name of that dropdown", + "sourceHash": "337c04b8fed8" + }, + "web.components.modules.source_dialog.signature_verification_options": { + "defaultMessage": "Options de vérification de la signature", + "description": "Source dialog: screen-reader name of the list of signature verification choices; not visible on screen", + "sourceHash": "bab4df1c50f2" + }, + "web.components.modules.source_dialog.subdirectory": { + "defaultMessage": "Sous-répertoire", + "description": "Source dialog: label above the subdirectory field for git sources, the folder inside the repository to scan", + "sourceHash": "7edfe57f0b8f" + }, + "web.components.modules.source_dialog.type": { + "defaultMessage": "Type", + "description": "Source dialog: label above the source type dropdown that picks the kind of source; also the screen-reader name of that dropdown", + "sourceHash": "baaddf70fb5d" + }, + "web.components.modules.source_dialog.type_git": { + "defaultMessage": "Dépôt Git", + "description": "Source dialog: source type option for a git repository", + "sourceHash": "fe8dcd07a9ed" + }, + "web.components.modules.source_dialog.type_http": { + "defaultMessage": "Archive HTTP (.tar.gz / .zip)", + "description": "Source dialog: source type option for an HTTP archive", + "sourceHash": "13db8e6f2b90" + }, + "web.components.modules.source_dialog.type_local": { + "defaultMessage": "Répertoire local (monté dans l’opérateur)", + "description": "Source dialog: source type option for a directory mounted into the operator", + "sourceHash": "eca01c99f2d9" + }, + "web.components.modules.source_dialog.type_oci": { + "defaultMessage": "Registre OCI", + "description": "Source dialog: source type option for an OCI registry", + "sourceHash": "5fab4f23af01" + }, + "web.components.modules.source_dialog.type_options": { + "defaultMessage": "Types de source", + "description": "Source dialog: screen-reader name of the list of source type choices; not visible on screen", + "sourceHash": "4a088ec3c4ce" + }, + "web.components.modules.source_dialog.type_upload": { + "defaultMessage": "Bundles importés", + "description": "Source dialog: source type option for bundles uploaded to the dashboard", + "sourceHash": "75a5d524ae6a" + }, + "web.components.modules.source_dialog.url_required": { + "defaultMessage": "url est requise", + "description": "Source dialog: validation error when the source URL is empty", + "sourceHash": "982bf6090294" + }, + "web.components.modules.source_dialog.verify_keyed": { + "defaultMessage": "Avec clé (clé publique cosign)", + "description": "Source dialog: signature verification option that checks a cosign public key", + "sourceHash": "de4826d3636e" + }, + "web.components.modules.source_dialog.verify_keyless": { + "defaultMessage": "Sans clé (Fulcio)", + "description": "Source dialog: signature verification option that checks a keyless Fulcio signature", + "sourceHash": "42a397ffc7b6" + }, + "web.components.modules.source_dialog.verify_none": { + "defaultMessage": "Aucune", + "description": "Source dialog: signature verification option that turns verification off", + "sourceHash": "dc937b598926" + }, + "web.components.modules.source_dialog.where_the_operator_discovers_and_pulls": { + "defaultMessage": "Emplacement où l’opérateur détecte et récupère les bundles de modules.", + "description": "Source dialog: explanation under the Add or Edit source heading saying where the operator discovers and pulls module bundles from", + "sourceHash": "7928268e2a26" + }, + "web.components.modules.upload_module_dialog.a_tar_gz_or_zip_holding_one_module": { + "defaultMessage": "Un .tar.gz ou .zip contenant un seul répertoire de module (module.yaml + template.yaml). Il est stocké dans le cluster et indexé immédiatement par le catalogue.", + "description": "Upload module dialog: explanation under the title; the file is a .tar.gz or .zip holding one module directory, stored and indexed right away", + "sourceHash": "2ee2bda7dac0" + }, + "web.components.modules.upload_module_dialog.cancel": { + "defaultMessage": "Annuler", + "description": "Upload module dialog: button that closes the dialog without uploading; keep it short", + "sourceHash": "19766ed6ccb2" + }, + "web.components.modules.upload_module_dialog.choose_archive": { + "defaultMessage": "Choisir une archive de bundle…", + "description": "Upload module dialog: file picker label shown before a file is chosen", + "sourceHash": "5d7223884ba7" + }, + "web.components.modules.upload_module_dialog.game": { + "defaultMessage": "jeu :", + "description": "Upload module dialog: label before the game identifier in the preview card of the chosen bundle; the value after it is the game name", + "sourceHash": "fa09651f6658" + }, + "web.components.modules.upload_module_dialog.pick_bundle_first": { + "defaultMessage": "sélectionnez d’abord un fichier de bundle", + "description": "Upload module dialog: validation error when no bundle file has been chosen", + "sourceHash": "ae57cbc5399a" + }, + "web.components.modules.upload_module_dialog.summary_line": { + "defaultMessage": "— {summary}", + "description": "Upload module dialog: module summary after an em dash; {summary} is the module summary", + "sourceHash": "184021179cd4" + }, + "web.components.modules.upload_module_dialog.upload": { + "defaultMessage": "Importer", + "description": "Upload module dialog: button that uploads the chosen bundle; keep it short", + "sourceHash": "865e89de78d9" + }, + "web.components.modules.upload_module_dialog.upload_module": { + "defaultMessage": "Importer un module", + "description": "Upload module dialog: title of the dialog for uploading a module bundle", + "sourceHash": "803f9a75009b" + }, + "web.components.modules.upload_module_dialog.upload_to": { + "defaultMessage": "Importer vers", + "description": "Upload module dialog: label above the dropdown that picks the cluster source the module is uploaded to", + "sourceHash": "e2077215883b" + }, + "web.components.modules.upload_module_dialog.vversion": { + "defaultMessage": "v{version}", + "description": "Upload module dialog: sentence with version in span", + "sourceHash": "da95c4621978" + }, + "web.components.register_cluster_dialog.cancel": { + "defaultMessage": "Annuler", + "description": "Register cluster dialog: button that closes the dialog without registering the cluster; keep it short", + "sourceHash": "19766ed6ccb2" + }, + "web.components.register_cluster_dialog.connect_an_existing_workload_cluster": { + "defaultMessage": "Connectez un cluster de charge de travail existant sur lequel l’opérateur Gameplane et les CRD sont installés. L’hôte du panneau ne devient pas un nœud de charge de travail.", + "description": "Register cluster dialog: intro under the title saying the cluster needs the Gameplane operator and CRDs installed and that the panel host does not become a workload node", + "sourceHash": "684fb841039e" + }, + "web.components.register_cluster_dialog.display_name": { + "defaultMessage": "Nom d’affichage", + "description": "Register cluster dialog: label for the cluster display name field", + "sourceHash": "2b7f6a84de91" + }, + "web.components.register_cluster_dialog.kubeconfig": { + "defaultMessage": "Kubeconfig", + "description": "Register cluster dialog: label for the large text box where the cluster's kubeconfig is pasted; technical term, usually left untranslated", + "sourceHash": "afe324cd9c0c" + }, + "web.components.register_cluster_dialog.name": { + "defaultMessage": "Nom", + "description": "Register cluster dialog: label for the cluster name field, the short identifier of the cluster", + "sourceHash": "dcd1d5223f73" + }, + "web.components.register_cluster_dialog.production": { + "defaultMessage": "production", + "description": "Register cluster dialog: example value shown inside the empty cluster name field; a sample name, not a default value", + "sourceHash": "ab8e18ef4ebe" + }, + "web.components.register_cluster_dialog.register": { + "defaultMessage": "Enregistrer", + "description": "Register cluster dialog: button that submits the form and registers the cluster; keep it short", + "sourceHash": "bb7234ec1245" + }, + "web.components.register_cluster_dialog.register_cluster": { + "defaultMessage": "Enregistrer le cluster", + "description": "Register cluster dialog: title of the dialog for adding an existing workload cluster to the panel", + "sourceHash": "bed8e2c07c18" + }, + "web.components.register_cluster_dialog.use_a_self_contained_kubeconfig_with": { + "defaultMessage": "Utilisez un kubeconfig autonome avec des identifiants et une adresse d’API accessible depuis ce panneau. La console, les fichiers et les journaux de l’agent nécessitent aussi une configuration de passerelle distante.", + "description": "Register cluster dialog: hint under the kubeconfig box saying the file must be self-contained with an API address this panel can reach, and that console, files and logs also need gateway setup", + "sourceHash": "949dc1520d0c" + }, + "web.components.registry_browser.all_categories": { + "defaultMessage": "Toutes", + "description": "Registry browser: category chip that clears the category filter", + "sourceHash": "a52ace420f21" + }, + "web.components.registry_browser.category_unavailable": { + "defaultMessage": "Le filtrage par catégorie n’est pas disponible pour {provider}.", + "description": "Registry browser: tooltip when a provider has no category filter; {provider} is the provider label", + "sourceHash": "e0eb6c207d39" + }, + "web.components.registry_browser.in_app_browse_isn_t_available_for_this": { + "defaultMessage": "La navigation dans l’application n’est pas disponible pour ce serveur.", + "description": "Registry browser: message shown instead of the results when no usable registry provider exists for this server, so in-app browsing is unavailable", + "sourceHash": "5407546a7b31" + }, + "web.components.registry_browser.load_more": { + "defaultMessage": "Charger plus", + "description": "Registry browser: button label to load the next page of results", + "sourceHash": "ac8991ef0101" + }, + "web.components.registry_browser.load_more_loading": { + "defaultMessage": "Chargement…", + "description": "Registry browser: load-more button label while the next page loads", + "sourceHash": "ba3bbbe10d8b" + }, + "web.components.registry_browser.loading": { + "defaultMessage": "Chargement…", + "description": "Registry browser: loading text shown while registry results are being fetched", + "sourceHash": "ba3bbbe10d8b" + }, + "web.components.registry_browser.mod_registry_aria": { + "defaultMessage": "Registre de mods", + "description": "Registry browser: aria-label for the mod registry", + "sourceHash": "87d2b43f3619" + }, + "web.components.registry_browser.modpack_registry_aria": { + "defaultMessage": "Registre de modpacks", + "description": "Registry browser: aria-label for the modpack registry", + "sourceHash": "60a78ddc8e8c" + }, + "web.components.registry_browser.no_results": { + "defaultMessage": "Aucun résultat.", + "description": "Registry browser: empty-state text shown when the registry search returns nothing", + "sourceHash": "c9a4ff4843db" + }, + "web.components.registry_browser.request_failed": { + "defaultMessage": "échec de la requête", + "description": "Registry browser: fallback error text when a registry request fails without a server message", + "sourceHash": "fbbc1b5ba864" + }, + "web.components.registry_browser.search_modpacks": { + "defaultMessage": "Rechercher des modpacks…", + "description": "Registry browser: search field placeholder for modpacks", + "sourceHash": "3c52a362cd65" + }, + "web.components.registry_browser.search_mods": { + "defaultMessage": "Rechercher des mods…", + "description": "Registry browser: search field placeholder for mods", + "sourceHash": "3c09536fa46b" + }, + "web.components.registry_browser.sort_aria": { + "defaultMessage": "Trier", + "description": "Registry browser: aria-label of the sort control", + "sourceHash": "bec69036aa27" + }, + "web.components.registry_browser.sort_disabled_aria": { + "defaultMessage": "Trier (désactivé, trié par pertinence pendant la recherche)", + "description": "Registry browser: aria-label of the sort control while it is disabled during search", + "sourceHash": "abd838b6adf3" + }, + "web.components.registry_browser.sort_downloads": { + "defaultMessage": "Téléchargements", + "description": "Registry browser: sort option that orders results by download count", + "sourceHash": "d5fdc1af021e" + }, + "web.components.registry_browser.sort_label": { + "defaultMessage": "Tri : {label}", + "description": "Registry browser: sort option text, {label} is the sort option name", + "sourceHash": "e211791b2b8a" + }, + "web.components.registry_browser.sort_newest": { + "defaultMessage": "Plus récents", + "description": "Registry browser: sort option that orders results by release date", + "sourceHash": "d15efa178cf6" + }, + "web.components.registry_browser.sort_updated": { + "defaultMessage": "Récemment mis à jour", + "description": "Registry browser: sort option that orders results by last update", + "sourceHash": "474b2a869ac1" + }, + "web.components.require_role.access_denied": { + "defaultMessage": "Accès refusé", + "description": "Require role: heading of the card shown when the user's role does not allow the page they opened", + "sourceHash": "cc11d415d932" + }, + "web.components.require_role.back_to_dashboard": { + "defaultMessage": "Retour au tableau de bord", + "description": "Require role: link on the access denied card that returns to the home dashboard; keep it short", + "sourceHash": "b59e0c1cc1e1" + }, + "web.components.require_role.can_t_reach_the_control_plane": { + "defaultMessage": "Impossible de joindre le plan de contrôle", + "description": "Require role: heading of the error card shown when the user's identity cannot be confirmed because the API cannot be reached", + "sourceHash": "2b623fdff8c9" + }, + "web.components.require_role.try_again": { + "defaultMessage": "Réessayer", + "description": "Require role: button on the unreachable error card that retries the identity check; keep it short", + "sourceHash": "d8b8392e2c54" + }, + "web.components.require_role.unreachable_explanation": { + "defaultMessage": "Nous n’avons pas pu confirmer votre identité{hasDetail, select, true { ({detail})} other {}}. Le problème est généralement temporaire : l’API est peut-être en cours de redémarrage.", + "description": "Require role: explanation under the unreachable heading saying the user's identity could not be confirmed and the problem is usually temporary; {hasDetail} is true or false and decides whether the error detail in parentheses is shown; {detail} is the error detail, e.g. HTTP 502", + "sourceHash": "f23466e6202f" + }, + "web.components.require_role.your_role_doesn_t_permit_access_to_this": { + "defaultMessage": "Votre rôle ne permet pas d’accéder à cette page. Demandez à un administrateur si vous pensez qu’il s’agit d’une erreur.", + "description": "Require role: explanation under the access denied heading saying the user's role does not allow this page and to ask an administrator if this is a mistake", + "sourceHash": "dd5510a1ff0b" + }, + "web.components.server.clone_server_dialog.cancel": { + "defaultMessage": "Annuler", + "description": "Clone server dialog: button that closes the dialog without cloning; keep it short", + "sourceHash": "19766ed6ccb2" + }, + "web.components.server.clone_server_dialog.clone_failed_status": { + "defaultMessage": "Échec du clonage ({status}).", + "description": "Clone server dialog: fallback error with the HTTP status; {status} is the status code", + "sourceHash": "50849e02f752" + }, + "web.components.server.clone_server_dialog.clone_server": { + "defaultMessage": "Cloner le serveur", + "description": "Clone server dialog: heading and submit button label", + "sourceHash": "abd76434587d" + }, + "web.components.server.clone_server_dialog.cloning": { + "defaultMessage": "Clonage…", + "description": "Clone server dialog: submit button label while the clone is created", + "sourceHash": "2c3cf7cbe2e1" + }, + "web.components.server.clone_server_dialog.creates_a_new_server_with_the_same": { + "defaultMessage": "Crée un nouveau serveur avec la même configuration. Les données du monde ne sont pas copiées.", + "description": "Clone server dialog: explanation under the Clone server title saying the copy gets the same configuration and that world data is not copied", + "sourceHash": "6abdb079b9f2" + }, + "web.components.server.clone_server_dialog.forbidden": { + "defaultMessage": "Votre rôle ne permet pas de cloner des serveurs.", + "description": "Clone server dialog: error when the user's role may not clone servers", + "sourceHash": "38be546fbc4b" + }, + "web.components.server.clone_server_dialog.mc_survival_copy": { + "defaultMessage": "mc-survival-copy", + "description": "Clone server dialog: example value shown inside the empty new name field; a sample name, not a default value", + "sourceHash": "66a6e0982a17" + }, + "web.components.server.clone_server_dialog.name_must_be_lowercase_letters_digits": { + "defaultMessage": "Le nom doit contenir uniquement des lettres minuscules, des chiffres et des tirets (63 caractères max)", + "description": "Clone server dialog: validation error under the new name field shown when the name has invalid characters or is longer than the limit", + "sourceHash": "e7deb9a0e1d4" + }, + "web.components.server.clone_server_dialog.name_taken": { + "defaultMessage": "Un serveur nommé {name} existe déjà.", + "description": "Clone server dialog: error when a server with the new name already exists; {name} is the requested name", + "sourceHash": "fd721aef46eb" + }, + "web.components.server.clone_server_dialog.new_name": { + "defaultMessage": "Nouveau nom", + "description": "Clone server dialog: label above the new name field for the copied server", + "sourceHash": "04eea2a4ae80" + }, + "web.components.server.clone_server_dialog.unknown_error": { + "defaultMessage": "Erreur inconnue", + "description": "Clone server dialog: fallback error when the cause is not known", + "sourceHash": "27c2ccd962c2" + }, + "web.components.server.config_fields.auto_of_memory_limit": { + "defaultMessage": "Auto : {percent} % de la limite de mémoire", + "description": "Config fields: placeholder of a field derived from the memory limit; {percent} is the percentage of the limit", + "sourceHash": "e928cb3f40e6" + }, + "web.components.server.config_fields.false": { + "defaultMessage": "faux", + "description": "Config fields: dropdown option meaning the setting is off, the value false; keep it short", + "sourceHash": "fcbcf165908d" + }, + "web.components.server.config_fields.option_select": { + "defaultMessage": "Sélectionner…", + "description": "Config fields: option label of an empty select with no value and no default, shown until a value is picked", + "sourceHash": "731fe041ce28" + }, + "web.components.server.config_fields.option_unchanged": { + "defaultMessage": "Inchangé", + "description": "Config fields: option label of an empty select whose stored value is kept as unchanged", + "sourceHash": "88b6f7429ce7" + }, + "web.components.server.config_fields.password_will_be_removed": { + "defaultMessage": "Le mot de passe sera supprimé", + "description": "Config fields: placeholder of a stored password field that is marked for removal", + "sourceHash": "2524de1799c2" + }, + "web.components.server.config_fields.remove_password": { + "defaultMessage": "Supprimer le mot de passe", + "description": "Config fields: button under a stored password field that marks the password for removal; keep it short", + "sourceHash": "ddaf79e10a50" + }, + "web.components.server.config_fields.removing_password_note": { + "defaultMessage": "Supprimé à l’enregistrement. Annulez pour conserver le mot de passe enregistré.", + "description": "Config fields: note under a stored password field marked for removal; explains that the stored value is removed on save and Undo keeps it", + "sourceHash": "8a787b3816df" + }, + "web.components.server.config_fields.true": { + "defaultMessage": "vrai", + "description": "Config fields: dropdown option meaning the setting is on, the value true; keep it short", + "sourceHash": "b5bea41b6c62" + }, + "web.components.server.config_fields.unchanged_type_to_replace": { + "defaultMessage": "Inchangé — saisissez pour remplacer", + "description": "Config fields: placeholder of a stored password field; typing replaces the stored value", + "sourceHash": "a88ee8206482" + }, + "web.components.server.config_fields.undo": { + "defaultMessage": "Annuler", + "description": "Config fields: button under a password field that cancels the pending removal of the stored password; keep it short", + "sourceHash": "a8283ade3185" + }, + "web.components.server.config_fields.write_only": { + "defaultMessage": "Écriture seule", + "description": "Config fields: small note under a password field saying the stored value is write-only and cannot be shown again", + "sourceHash": "c4cba2f1ebe0" + }, + "web.components.server.config_fields.written_to_file": { + "defaultMessage": "Écrit dans un fichier", + "description": "Config fields: small note under a setting saying its value is written to a file rather than set directly", + "sourceHash": "7abe6e10a42d" + }, + "web.components.server.delete_server_dialog.delete_failed": { + "defaultMessage": "échec de la suppression", + "description": "Delete server dialog: fallback error text when the delete request fails without a message", + "sourceHash": "160b94ad07cc" + }, + "web.components.server.delete_server_dialog.delete_server": { + "defaultMessage": "Supprimer le serveur", + "description": "Delete server dialog: confirm button that deletes the server once the user has typed the server name; keep it short", + "sourceHash": "7e9464a23e43" + }, + "web.components.server.delete_server_dialog.this_will_permanently_remove_the": { + "defaultMessage": "Cette action supprime définitivement la ressource GameServer et détache son volume persistant. Les données seront récupérées par le cluster selon la politique de reclaim de la StorageClass.", + "description": "Delete server dialog: warning that the GameServer resource is removed permanently and its volume detached, with data reclaimed per the StorageClass policy; resource names are kept in English", + "sourceHash": "ba9aa7af854f" + }, + "web.components.server.delete_server_dialog.title": { + "defaultMessage": "Supprimer {name} ?", + "description": "Delete server dialog: dialog title; {name} is the server name", + "sourceHash": "a4b982a9e0bc" + }, + "web.components.server.event_list.no_events_yet": { + "defaultMessage": "Aucun événement pour le moment.", + "description": "Event list: text shown when there are no events, used when no emptyMessage is passed", + "sourceHash": "80c652c4eeec" + }, + "web.components.server.event_list.system_source": { + "defaultMessage": "système", + "description": "Event list: source shown when an event has no source", + "sourceHash": "bbc5e661e106" + }, + "web.components.server.port_overrides_editor.add_override": { + "defaultMessage": "Ajouter une exception", + "description": "Port overrides editor: button that adds a new port override row; keep it short", + "sourceHash": "fafc22c398f0" + }, + "web.components.server.port_overrides_editor.game": { + "defaultMessage": "game", + "description": "Port overrides editor: example value shown inside the empty port name field of a row; a sample port name, not a default value", + "sourceHash": "6ca5cab77e70" + }, + "web.components.server.port_overrides_editor.nodeport": { + "defaultMessage": "NodePort", + "description": "Port overrides editor: column heading for the NodePort number in the list of port overrides; NodePort is a technical term, usually left untranslated; keep it short", + "sourceHash": "2a4730517ac2" + }, + "web.components.server.port_overrides_editor.port_name": { + "defaultMessage": "Nom du port", + "description": "Port overrides editor: column heading for the port name in the list of port overrides; keep it short", + "sourceHash": "239a365f3b9b" + }, + "web.components.server.port_overrides_editor.port_range_placeholder": { + "defaultMessage": "30000-32767", + "description": "Port overrides editor: placeholder example of a NodePort range", + "sourceHash": "9a3517bfd3a2" + }, + "web.components.server.port_overrides_editor.remove": { + "defaultMessage": "Supprimer", + "description": "Port overrides editor: icon-only button that removes this override row; its screen-reader name and tooltip read Remove; keep it short", + "sourceHash": "c3812fc4acb8" + }, + "web.components.server.port_overrides_editor.service_port": { + "defaultMessage": "Port du service", + "description": "Port overrides editor: column heading for the service port number in the list of port overrides; keep it short", + "sourceHash": "861fbb9c5f22" + }, + "web.components.server.server_actions_card.action_failed": { + "defaultMessage": "échec de l’action", + "description": "Server actions card: fallback error when a server action fails without a message", + "sourceHash": "af266d85e290" + }, + "web.components.server.server_actions_card.action_ran": { + "defaultMessage": "{name} exécutée", + "description": "Server actions card: status line after an action returns no output; {name} is the action name", + "sourceHash": "3097358abc3d" + }, + "web.components.server.server_actions_card.action_sent": { + "defaultMessage": "{name} envoyée", + "description": "Server actions card: status line after a fire-and-forget action is sent; {name} is the action name", + "sourceHash": "4df9f435654a" + }, + "web.components.server.server_actions_card.actions": { + "defaultMessage": "Actions", + "description": "Server actions card: small heading above the actions that do not belong to a group", + "sourceHash": "ff8059dc6752" + }, + "web.components.server.server_actions_card.cancel": { + "defaultMessage": "Annuler", + "description": "Server actions card: button that closes the action confirmation dialog without running the action; keep it short", + "sourceHash": "19766ed6ccb2" + }, + "web.components.server.server_actions_card.dismiss": { + "defaultMessage": "ignorer", + "description": "Server actions card: small button that clears the status message shown after an action; keep it short", + "sourceHash": "5880072b27d4" + }, + "web.components.server.server_actions_card.forbidden_run": { + "defaultMessage": "Votre rôle ne permet pas d’exécuter des actions.", + "description": "Server actions card: error when the user's role may not run actions", + "sourceHash": "b4e39a6b4427" + }, + "web.components.server.server_actions_card.must_be_whole_number": { + "defaultMessage": "Doit être un nombre entier", + "description": "Server actions card: validation error for an integer parameter that is not a whole number", + "sourceHash": "f5bf7f32f033" + }, + "web.components.server.server_actions_card.no_console": { + "defaultMessage": "Les actions nécessitent une console active ; ce jeu n’en a pas.", + "description": "Server actions card: note when this game has no live console", + "sourceHash": "35f5e41dc8e0" + }, + "web.components.server.server_actions_card.no_control_characters": { + "defaultMessage": "Aucun caractère de contrôle", + "description": "Server actions card: validation error for a parameter containing control characters", + "sourceHash": "b0c3fd525a00" + }, + "web.components.server.server_actions_card.no_rcon": { + "defaultMessage": "Certaines actions nécessitent RCON ; ce jeu n’en a pas.", + "description": "Server actions card: note when this game has no RCON console", + "sourceHash": "83ceb505fbbe" + }, + "web.components.server.server_actions_card.open_console": { + "defaultMessage": "Ouvrir la console", + "description": "Server actions card: button that opens the server's console; keep it short", + "sourceHash": "f4063d1b2a07" + }, + "web.components.server.server_actions_card.param_required": { + "defaultMessage": "{name} est obligatoire", + "description": "Server actions card: validation error for a required action parameter; {name} is the parameter's display name", + "sourceHash": "04af6947c302" + }, + "web.components.server.server_actions_card.pick_valid_option": { + "defaultMessage": "Choisissez une option valide", + "description": "Server actions card: validation error for an enum parameter with a value that is not offered", + "sourceHash": "3e4ff0da0b60" + }, + "web.components.server.server_actions_card.quick_actions": { + "defaultMessage": "Actions rapides", + "description": "Server actions card: heading of the card that holds the server's quick action buttons", + "sourceHash": "1810407f5ab7" + }, + "web.components.server.server_actions_card.request_failed_status": { + "defaultMessage": "échec de la requête ({status})", + "description": "Server actions card: fallback error with the HTTP status; {status} is the status code", + "sourceHash": "f6c6e4104a40" + }, + "web.components.server.server_actions_card.requires_operator_role": { + "defaultMessage": "Rôle opérateur requis", + "description": "Server actions card: tooltip when the user lacks the operator role", + "sourceHash": "e454d6eabdee" + }, + "web.components.server.server_actions_card.restart": { + "defaultMessage": "Redémarrer", + "description": "Server actions card: button that restarts the server; keep it short", + "sourceHash": "6b983a81e5e8" + }, + "web.components.server.server_actions_card.run": { + "defaultMessage": "Exécuter", + "description": "Server actions card: button label to run an action", + "sourceHash": "00d60e31a4e6" + }, + "web.components.server.server_actions_card.run_this_action_now": { + "defaultMessage": "Exécuter cette action maintenant ?", + "description": "Server actions card: question in the confirmation dialog asking whether to run the action; shown when the action has no parameters to fill in", + "sourceHash": "ab8b7d842301" + }, + "web.components.server.server_actions_card.running": { + "defaultMessage": "Exécution…", + "description": "Server actions card: run button label while an action runs", + "sourceHash": "46c541363b02" + }, + "web.components.server.server_actions_card.start": { + "defaultMessage": "Démarrer", + "description": "Server actions card: button that starts a stopped server; keep it short", + "sourceHash": "e4bb9f1ece9a" + }, + "web.components.server.server_actions_card.stop": { + "defaultMessage": "Arrêter", + "description": "Server actions card: button that stops the running server; keep it short", + "sourceHash": "cae7d57bc067" + }, + "web.components.server.server_actions_card.wake": { + "defaultMessage": "Réveiller", + "description": "Server actions card: button that wakes a sleeping server; keep it short", + "sourceHash": "33a7e908344d" + }, + "web.components.server.server_actions_menu.clone_server": { + "defaultMessage": "Cloner le serveur", + "description": "Server actions menu: menu item that opens the Clone server dialog; keep it short", + "sourceHash": "abd76434587d" + }, + "web.components.server.server_actions_menu.delete_server": { + "defaultMessage": "Supprimer le serveur", + "description": "Server actions menu: destructive menu item that opens the delete server dialog; keep it short", + "sourceHash": "7e9464a23e43" + }, + "web.components.server.server_actions_menu.requires_operator_role": { + "defaultMessage": "Rôle opérateur requis", + "description": "Server actions menu: hint shown on a disabled item when the user lacks the operator role", + "sourceHash": "e454d6eabdee" + }, + "web.components.server.server_actions_menu.requires_owner_or_operator_role": { + "defaultMessage": "Rôle propriétaire ou opérateur requis", + "description": "Server actions menu: hint shown on a disabled item when the user lacks the owner or operator role", + "sourceHash": "ae5d287ff5c5" + }, + "web.components.server.server_actions_menu.server_actions": { + "defaultMessage": "Actions du serveur", + "description": "Server actions menu: screen-reader label for the icon-only menu button that opens the server actions; not visible on screen", + "sourceHash": "0ed6308b6431" + }, + "web.components.server.server_actions_menu.transfer_ownership": { + "defaultMessage": "Transférer la propriété", + "description": "Server actions menu: menu item that opens the transfer ownership dialog; disabled with a hint for users without the operator role; keep it short", + "sourceHash": "3667f939d52f" + }, + "web.components.server.server_actions_menu.wipe_world_data": { + "defaultMessage": "Effacer les données du monde", + "description": "Server actions menu: menu item that opens the wipe world data dialog; disabled with a hint for users without owner or operator rights; keep it short", + "sourceHash": "2cca819060c6" + }, + "web.components.server.server_sleep_card.afterminutes_minute": { + "defaultMessage": "{afterMinutes, plural, one {{minutes} minute} other {{minutes} minutes}}", + "description": "Server sleep card: minutes until the server sleeps; {afterMinutes} is the number of minutes (selects the plural form) and {minutes} is the same number shown as digits without grouping", + "sourceHash": "ecc2c0eb5ca7" + }, + "web.components.server.server_sleep_card.asleep": { + "defaultMessage": "En veille", + "description": "Server sleep card: value shown for the sleep state when the server is asleep", + "sourceHash": "60135e8f5cbb" + }, + "web.components.server.server_sleep_card.asleep_since": { + "defaultMessage": "En veille depuis", + "description": "Server sleep card: label in the sleep details for when the server fell asleep, shown in relative time", + "sourceHash": "d08ad39842a7" + }, + "web.components.server.server_sleep_card.configured": { + "defaultMessage": "Configuré", + "description": "Server sleep card: small heading above the configured sleep settings", + "sourceHash": "84aebc69a1bf" + }, + "web.components.server.server_sleep_card.counting_down": { + "defaultMessage": "Compte à rebours en cours", + "description": "Server sleep card: status text while the sleep countdown runs", + "sourceHash": "d2650d3cdd8f" + }, + "web.components.server.server_sleep_card.empty_since": { + "defaultMessage": "Vide depuis", + "description": "Server sleep card: label in the sleep details for when the server became empty of players, shown in relative time", + "sourceHash": "6b3d28a40adf" + }, + "web.components.server.server_sleep_card.idle_auto_sleep_needs_a_live_player": { + "defaultMessage": "La mise en veille automatique à l’inactivité nécessite un nombre de joueurs en direct. Ce jeu n’en fournit aucun via RCON ni via un protocole de requête, donc le compte à rebours ne démarre jamais.", + "description": "Server sleep card: note under the never-sleep warning; idle auto-sleep needs a live player count this game cannot give, so the countdown never starts", + "sourceHash": "e1f575c154b0" + }, + "web.components.server.server_sleep_card.last_woken": { + "defaultMessage": "Dernier réveil", + "description": "Server sleep card: label in the sleep details for when the server was last woken, shown in relative time", + "sourceHash": "e4ea6b7618f2" + }, + "web.components.server.server_sleep_card.not_configured": { + "defaultMessage": "Non configuré", + "description": "Server sleep card: value shown in the scheduled sleep line when no cron schedule is set", + "sourceHash": "dd1841d29502" + }, + "web.components.server.server_sleep_card.reason_sentence": { + "defaultMessage": "{reason}.", + "description": "Server sleep card: sleep reason as a sentence; {reason} is the capitalized reason", + "sourceHash": "04c74dc81035" + }, + "web.components.server.server_sleep_card.sleep": { + "defaultMessage": "Veille", + "description": "Server sleep card: heading of the card that shows the server's sleep state", + "sourceHash": "d466bcf52eb6" + }, + "web.components.server.server_sleep_card.sleep_after": { + "defaultMessage": "Mise en veille après", + "description": "Server sleep card: label for the idle time before the server sleeps; the value beside it is the configured number of minutes", + "sourceHash": "828986727f1b" + }, + "web.components.server.server_sleep_card.state": { + "defaultMessage": "État", + "description": "Server sleep card: label in the sleep details for the current sleep state, such as Asleep or Counting down", + "sourceHash": "a3b50c476732" + }, + "web.components.server.server_sleep_card.waiting_for_the_operator": { + "defaultMessage": "En attente de l’opérateur", + "description": "Server sleep card: status text shown after sleep is enabled, until the operator has applied the change", + "sourceHash": "c6e6e58c38b0" + }, + "web.components.server.server_sleep_card.waking_takes_normal_boot_time_before": { + "defaultMessage": "Le réveil prend le temps de démarrage normal avant que les joueurs puissent se connecter.", + "description": "Server sleep card: note in the countdown state saying that waking takes normal boot time before players can connect", + "sourceHash": "cb6443a256dc" + }, + "web.components.server.server_sleep_card.will_never_sleep": { + "defaultMessage": "Ne se mettra jamais en veille", + "description": "Server sleep card: heading of the warning shown when the server will never go to sleep; the reason is shown in the text below", + "sourceHash": "23a76702d327" + }, + "web.components.server.server_status_card.game_status": { + "defaultMessage": "État du jeu", + "description": "Server status card: heading of the card that shows the game's status figures", + "sourceHash": "f9a56c85fda5" + }, + "web.components.server.server_status_card.offline": { + "defaultMessage": "hors ligne", + "description": "Server status card: state shown when the server is not running", + "sourceHash": "8e2c7ac50813" + }, + "web.components.server.server_status_card.phase": { + "defaultMessage": "Phase", + "description": "Server status card: label of the row showing the server's current phase; a dash is shown when it is unknown", + "sourceHash": "46342ec1eec9" + }, + "web.components.server.server_status_card.players": { + "defaultMessage": "Joueurs", + "description": "Server status card: label of the row showing how many players are online", + "sourceHash": "84e12ac655dd" + }, + "web.components.server.server_status_card.uptime": { + "defaultMessage": "Temps de fonctionnement", + "description": "Server status card: label of the row showing how long the server has been running", + "sourceHash": "d63ab4711473" + }, + "web.components.server.server_status_card.version": { + "defaultMessage": "Version", + "description": "Server status card: label of the row showing the installed game version; a dash is shown when it is unknown", + "sourceHash": "dd167905de0d" + }, + "web.components.server.transfer_server_dialog.cancel": { + "defaultMessage": "Annuler", + "description": "Transfer server dialog: button that closes the dialog without transferring the server; keep it short", + "sourceHash": "19766ed6ccb2" + }, + "web.components.server.transfer_server_dialog.current_owner": { + "defaultMessage": "Propriétaire actuel : {owner}.", + "description": "Transfer server dialog: current owner line; {owner} is the owner username or the unassigned label", + "sourceHash": "4ca0b0f81c54" + }, + "web.components.server.transfer_server_dialog.select_user": { + "defaultMessage": "Sélectionner un utilisateur…", + "description": "Transfer server dialog: placeholder of the user select when none is chosen", + "sourceHash": "b8b6a4095269" + }, + "web.components.server.transfer_server_dialog.transfer": { + "defaultMessage": "Transférer", + "description": "Transfer server dialog: submit button label", + "sourceHash": "dde8bef78cbb" + }, + "web.components.server.transfer_server_dialog.transfer_failed": { + "defaultMessage": "échec du transfert", + "description": "Transfer server dialog: fallback error text when the transfer request fails without a message", + "sourceHash": "4f8f152824c4" + }, + "web.components.server.transfer_server_dialog.transfer_name": { + "defaultMessage": "Transférer {name}", + "description": "Transfer server dialog: sentence with name in ModalHeading", + "sourceHash": "0d3b4844d925" + }, + "web.components.server.transfer_server_dialog.transfer_to": { + "defaultMessage": "Transférer à", + "description": "Transfer server dialog: screen-reader name of the list of users to pick as the new owner; not visible on screen", + "sourceHash": "bf19b91edbc9" + }, + "web.components.server.transfer_server_dialog.transferring": { + "defaultMessage": "Transfert…", + "description": "Transfer server dialog: submit button label while the transfer runs", + "sourceHash": "930c3f4bfd66" + }, + "web.components.server.transfer_server_dialog.unassigned": { + "defaultMessage": "non attribué", + "description": "Transfer server dialog: shown when the server has no owner", + "sourceHash": "2f23e841e448" + }, + "web.components.server.transfer_server_dialog.you_need_permission_to_list_users_to": { + "defaultMessage": "Vous avez besoin d’une autorisation pour lister les utilisateurs et choisir un destinataire.", + "description": "Transfer server dialog: error text shown when the user lacks permission to list users, so no new owner can be picked", + "sourceHash": "13ea0f82cd71" + }, + "web.components.server.wipe_server_dialog.cancel": { + "defaultMessage": "Annuler", + "description": "Wipe server dialog: button that closes the dialog without wiping the world; keep it short", + "sourceHash": "19766ed6ccb2" + }, + "web.components.server.wipe_server_dialog.i_understand_this_will_permanently": { + "defaultMessage": "Je comprends que cette action supprimera définitivement les données du monde", + "description": "Wipe server dialog: checkbox label where the user confirms that the world data will be permanently deleted", + "sourceHash": "dbcd5fdb9bf0" + }, + "web.components.server.wipe_server_dialog.wipe_failed": { + "defaultMessage": "échec de l’effacement", + "description": "Wipe server dialog: fallback error text when the wipe request fails without a message", + "sourceHash": "6b7684431514" + }, + "web.components.server.wipe_server_dialog.wipe_warning": { + "defaultMessage": "Cette action supprime définitivement les données du monde de {serverName}. Les sauvegardes ne sont pas supprimées. Cette action est irréversible.", + "description": "Wipe server dialog: warning that the world data is permanently deleted, backups are kept and the action cannot be undone; {serverName} is the server name, shown in monospace inside the tag, which must be kept around it", + "sourceHash": "7788a430901b" + }, + "web.components.server.wipe_server_dialog.wipe_world": { + "defaultMessage": "Effacer le monde ?", + "description": "Wipe server dialog: title of the danger-styled confirmation dialog asking whether to wipe the world", + "sourceHash": "4db1bab27629" + }, + "web.components.server.wipe_server_dialog.wipe_world_button": { + "defaultMessage": "Effacer le monde", + "description": "Wipe server dialog: submit button label", + "sourceHash": "7ed8d20f0636" + }, + "web.components.server.wipe_server_dialog.working": { + "defaultMessage": "En cours…", + "description": "Wipe server dialog: submit button label while the wipe runs", + "sourceHash": "5474eef8d0f1" + }, + "web.components.ui.admin.edit_user_dialog.cancel": { + "defaultMessage": "Annuler", + "description": "Edit user dialog: button that closes the dialog without saving; keep it short", + "sourceHash": "19766ed6ccb2" + }, + "web.components.ui.admin.edit_user_dialog.display_name": { + "defaultMessage": "Nom d’affichage", + "description": "Edit user dialog: label above the display name field; the same words are the field's placeholder", + "sourceHash": "2b7f6a84de91" + }, + "web.components.ui.admin.edit_user_dialog.display_name_is_required": { + "defaultMessage": "Le nom d’affichage est obligatoire", + "description": "Edit user dialog: validation error when the display name is empty", + "sourceHash": "c6892c89e29a" + }, + "web.components.ui.admin.edit_user_dialog.edit_user": { + "defaultMessage": "Modifier l’utilisateur", + "description": "Edit user dialog: title of the dialog for editing a user; the username appears under the title", + "sourceHash": "05b88d18fc0d" + }, + "web.components.ui.admin.edit_user_dialog.email": { + "defaultMessage": "E-mail", + "description": "Edit user dialog: label above the email address field", + "sourceHash": "969ccbd3cf63" + }, + "web.components.ui.admin.edit_user_dialog.email_example_com": { + "defaultMessage": "email@example.com", + "description": "Edit user dialog: example value shown inside the empty email field; a sample address, keep it as written", + "sourceHash": "2a539d652026" + }, + "web.components.ui.admin.edit_user_dialog.email_is_required": { + "defaultMessage": "L’e-mail est obligatoire", + "description": "Edit user dialog: validation error when the email is empty", + "sourceHash": "aae929110124" + }, + "web.components.ui.admin.edit_user_dialog.primary_role": { + "defaultMessage": "Rôle principal", + "description": "Edit user dialog: screen-reader name of the list of roles in the primary role dropdown; not visible on screen", + "sourceHash": "e8baefef21d1" + }, + "web.components.ui.admin.edit_user_dialog.primary_role_cluster_wide": { + "defaultMessage": "Rôle principal (à l’échelle du cluster)", + "description": "Edit user dialog: label above the primary role dropdown; the role applies across the whole cluster", + "sourceHash": "4d9f3899120a" + }, + "web.components.ui.admin.edit_user_dialog.role_is_required": { + "defaultMessage": "Le rôle est obligatoire", + "description": "Edit user dialog: validation error when no role is selected", + "sourceHash": "4bd52bf1428b" + }, + "web.components.ui.admin.edit_user_dialog.save_changes": { + "defaultMessage": "Enregistrer les modifications", + "description": "Edit user dialog: save button label", + "sourceHash": "dd0ae7a5cbcf" + }, + "web.components.ui.admin.edit_user_dialog.saving": { + "defaultMessage": "Enregistrement…", + "description": "Edit user dialog: save button label while the update is in progress", + "sourceHash": "23e39291d613" + }, + "web.components.ui.admin.edit_user_dialog.you_can_t_remove_your_own_ability_to": { + "defaultMessage": "Vous ne pouvez pas retirer votre propre droit de gérer les utilisateurs.", + "description": "Edit user dialog: warning shown under the role dropdown when the change would remove the user's own ability to manage users", + "sourceHash": "beb9cb605278" + }, + "web.components.ui.admin.invite_user_dialog.alice": { + "defaultMessage": "alice", + "description": "Invite user dialog: example value shown inside the empty username field; a sample name, not a default value", + "sourceHash": "2bd806c97f0e" + }, + "web.components.ui.admin.invite_user_dialog.alice_example_com": { + "defaultMessage": "alice@example.com", + "description": "Invite user dialog: example value shown inside the empty email field; a sample address, keep it as written", + "sourceHash": "ff8d9819fc0e" + }, + "web.components.ui.admin.invite_user_dialog.alice_operator": { + "defaultMessage": "Alice Opérateur", + "description": "Invite user dialog: example value shown inside the empty display name field; a sample name, not a default value", + "sourceHash": "2d0e5803f6c4" + }, + "web.components.ui.admin.invite_user_dialog.at_least_characters": { + "defaultMessage": "Au moins {min} caractères.", + "description": "Invite user dialog: live validation under the password field while it is shorter than the minimum; {min} is the minimum password length", + "sourceHash": "f4c77786ccf7" + }, + "web.components.ui.admin.invite_user_dialog.cancel": { + "defaultMessage": "Annuler", + "description": "Invite user dialog: button that closes the dialog without inviting the user; keep it short", + "sourceHash": "19766ed6ccb2" + }, + "web.components.ui.admin.invite_user_dialog.create_a_local_account_leave_password": { + "defaultMessage": "Créez un compte local. Laissez le mot de passe vide pour envoyer une invitation OIDC plus tard.", + "description": "Invite user dialog: explanation under the Invite user title saying a local account is created, and that a blank password sends an OIDC invite later; OIDC is a technical term, usually left untranslated", + "sourceHash": "564e897b58d6" + }, + "web.components.ui.admin.invite_user_dialog.display_name": { + "defaultMessage": "Nom d’affichage", + "description": "Invite user dialog: label above the display name field", + "sourceHash": "2b7f6a84de91" + }, + "web.components.ui.admin.invite_user_dialog.display_name_is_required": { + "defaultMessage": "Le nom d’affichage est obligatoire", + "description": "Invite user dialog: validation error when the display name is empty", + "sourceHash": "c6892c89e29a" + }, + "web.components.ui.admin.invite_user_dialog.email": { + "defaultMessage": "E-mail", + "description": "Invite user dialog: label above the email field", + "sourceHash": "969ccbd3cf63" + }, + "web.components.ui.admin.invite_user_dialog.email_is_required": { + "defaultMessage": "L’e-mail est obligatoire", + "description": "Invite user dialog: validation error when the email is empty", + "sourceHash": "aae929110124" + }, + "web.components.ui.admin.invite_user_dialog.initial_password": { + "defaultMessage": "Mot de passe initial", + "description": "Invite user dialog: label above the initial password field, which can be left blank", + "sourceHash": "d4992a3313fc" + }, + "web.components.ui.admin.invite_user_dialog.invite_user": { + "defaultMessage": "Inviter un utilisateur", + "description": "Invite user dialog: heading and default submit button label", + "sourceHash": "6ac487a0242c" + }, + "web.components.ui.admin.invite_user_dialog.inviting": { + "defaultMessage": "Invitation…", + "description": "Invite user dialog: submit button label while the invite is in progress", + "sourceHash": "a4c2059a1177" + }, + "web.components.ui.admin.invite_user_dialog.password_hint": { + "defaultMessage": "Au moins {min} caractères", + "description": "Invite user dialog: password field placeholder; {min} is the minimum password length", + "sourceHash": "48ff8fe4eeda" + }, + "web.components.ui.admin.invite_user_dialog.password_is_required": { + "defaultMessage": "Le mot de passe initial est obligatoire", + "description": "Invite user dialog: validation error when the initial password is empty", + "sourceHash": "7aeae1412484" + }, + "web.components.ui.admin.invite_user_dialog.password_must_be_at_least_characters": { + "defaultMessage": "Le mot de passe doit contenir au moins {min} caractères", + "description": "Invite user dialog: validation error when the initial password is shorter than the minimum; {min} is the minimum password length", + "sourceHash": "01ac886f00e5" + }, + "web.components.ui.admin.invite_user_dialog.role": { + "defaultMessage": "Rôle", + "description": "Invite user dialog: label above the role dropdown, shown only when roles are available", + "sourceHash": "14736a2eb9f4" + }, + "web.components.ui.admin.invite_user_dialog.username": { + "defaultMessage": "Nom d’utilisateur", + "description": "Invite user dialog: label above the username field", + "sourceHash": "e3b89e9d33f8" + }, + "web.components.ui.admin.invite_user_dialog.username_is_required": { + "defaultMessage": "Le nom d’utilisateur est obligatoire", + "description": "Invite user dialog: validation error when the username is empty", + "sourceHash": "d18de1563a61" + }, + "web.components.ui.admin.reset_password_dialog.at_least_characters": { + "defaultMessage": "Au moins {min} caractères.", + "description": "Reset password dialog: live validation under the password field while it is shorter than the minimum; {min} is the minimum password length", + "sourceHash": "f4c77786ccf7" + }, + "web.components.ui.admin.reset_password_dialog.cancel": { + "defaultMessage": "Annuler", + "description": "Reset password dialog: button that closes the dialog without resetting the password; keep it short", + "sourceHash": "19766ed6ccb2" + }, + "web.components.ui.admin.reset_password_dialog.must_be_at_least_characters": { + "defaultMessage": "Doit contenir au moins {min} caractères.", + "description": "Reset password dialog: validation error when the password is shorter than the minimum; {min} is the minimum password length", + "sourceHash": "9b2c45e97d4b" + }, + "web.components.ui.admin.reset_password_dialog.new_password": { + "defaultMessage": "Nouveau mot de passe", + "description": "Reset password dialog: label above the new password field", + "sourceHash": "3dd9df4441fb" + }, + "web.components.ui.admin.reset_password_dialog.password_hint": { + "defaultMessage": "Au moins {min} caractères", + "description": "Reset password dialog: password field placeholder; {min} is the minimum password length", + "sourceHash": "48ff8fe4eeda" + }, + "web.components.ui.admin.reset_password_dialog.password_is_required": { + "defaultMessage": "Le mot de passe est obligatoire", + "description": "Reset password dialog: validation error when the password is empty", + "sourceHash": "7aeae1412484" + }, + "web.components.ui.admin.reset_password_dialog.reset_password": { + "defaultMessage": "Réinitialiser le mot de passe", + "description": "Reset password dialog: default submit button label", + "sourceHash": "e0edfeb3a71a" + }, + "web.components.ui.admin.reset_password_dialog.reset_password_for_username": { + "defaultMessage": "Réinitialiser le mot de passe de {username}", + "description": "Reset password dialog: heading; {username} is the account whose password is being reset", + "sourceHash": "4175f57e117f" + }, + "web.components.ui.admin.reset_password_dialog.resetting": { + "defaultMessage": "Réinitialisation…", + "description": "Reset password dialog: submit button label while the reset is in progress", + "sourceHash": "1505f40de62c" + }, + "web.components.ui.admin.reset_password_dialog.they_will_need_to_sign_in_again_with": { + "defaultMessage": "L’utilisateur devra se reconnecter avec le nouveau mot de passe.", + "description": "Reset password dialog: explanation under the Reset password title saying the user must sign in again with the new password", + "sourceHash": "cf38e32a6433" + }, + "web.components.ui.app_loading_skeleton.admin": { + "defaultMessage": "Administration", + "description": "App loading skeleton: placeholder heading above the admin group of navigation items while the page loads", + "sourceHash": "c1c224b03cd9" + }, + "web.components.ui.app_loading_skeleton.gameplane": { + "defaultMessage": "gameplane", + "description": "App loading skeleton: product name shown in the placeholder header while the app loads; keep the name as written", + "sourceHash": "c48499141ee6" + }, + "web.components.ui.app_loading_skeleton.general": { + "defaultMessage": "Général", + "description": "App loading skeleton: placeholder heading above the first group of navigation items while the page loads", + "sourceHash": "c910d474dcd7" + }, + "web.components.ui.app_loading_skeleton.loading": { + "defaultMessage": "Chargement", + "description": "App loading skeleton: screen-reader label announced while the app is loading; not visible on screen", + "sourceHash": "dc380888c4e2" + }, + "web.components.ui.appearance_toggle.appearance": { + "defaultMessage": "Apparence", + "description": "Appearance toggle: screen-reader name of the group of theme buttons for dark, light or system appearance; not visible on screen", + "sourceHash": "3907fa7f8072" + }, + "web.components.ui.appearance_toggle.dark": { + "defaultMessage": "Sombre", + "description": "Appearance toggle: label of the dark mode button", + "sourceHash": "60acc53f13a5" + }, + "web.components.ui.appearance_toggle.light": { + "defaultMessage": "Clair", + "description": "Appearance toggle: label of the light mode button", + "sourceHash": "dbcd5e7bb7a0" + }, + "web.components.ui.appearance_toggle.set_by_your_surface_color": { + "defaultMessage": "Défini par la couleur de votre surface", + "description": "Appearance toggle: tooltip and label shown while a custom theme sets the mode", + "sourceHash": "197b3ca75d18" + }, + "web.components.ui.appearance_toggle.system": { + "defaultMessage": "Système", + "description": "Appearance toggle: label of the follow-system mode button", + "sourceHash": "6725e7bbcd28" + }, + "web.components.ui.audit_integrity_banner.dismiss_audit_integrity_alert": { + "defaultMessage": "Fermer l’alerte d’intégrité de l’audit", + "description": "Audit integrity banner: screen-reader name of the icon-only close button on the alert; not visible on screen", + "sourceHash": "e9d1a27c35db" + }, + "web.components.ui.audit_integrity_banner.re_check": { + "defaultMessage": "Revérifier", + "description": "Audit integrity banner: button that runs the audit integrity check again; keep it short", + "sourceHash": "24fcca00ea50" + }, + "web.components.ui.breadcrumbs.audit_log": { + "defaultMessage": "Journal d’audit", + "description": "Breadcrumb: label for the audit log crumb", + "sourceHash": "e4d36f9a4e22" + }, + "web.components.ui.breadcrumbs.backups": { + "defaultMessage": "Sauvegardes", + "description": "Breadcrumb: label for the backups crumb", + "sourceHash": "3334fee2c77f" + }, + "web.components.ui.breadcrumbs.breadcrumb": { + "defaultMessage": "Fil d’Ariane", + "description": "Breadcrumbs: screen-reader name of the navigation area holding the breadcrumb trail; not visible on screen", + "sourceHash": "2bd873d6c734" + }, + "web.components.ui.breadcrumbs.cluster": { + "defaultMessage": "Cluster", + "description": "Breadcrumb: label for the cluster crumb", + "sourceHash": "c137a7f43e23" + }, + "web.components.ui.breadcrumbs.clusters": { + "defaultMessage": "Clusters", + "description": "Breadcrumb: label for the clusters crumb", + "sourceHash": "92d5e4c5cd37" + }, + "web.components.ui.breadcrumbs.dashboard": { + "defaultMessage": "Tableau de bord", + "description": "Breadcrumb: label of the leaf crumb on the root (dashboard) page", + "sourceHash": "67b696468610" + }, + "web.components.ui.breadcrumbs.modules": { + "defaultMessage": "Modules", + "description": "Breadcrumb: label for the modules crumb", + "sourceHash": "76c86c4c3243" + }, + "web.components.ui.breadcrumbs.new": { + "defaultMessage": "Nouveau", + "description": "Breadcrumb: label for the create-new crumb", + "sourceHash": "18fdd549b2ed" + }, + "web.components.ui.breadcrumbs.servers": { + "defaultMessage": "Serveurs", + "description": "Breadcrumb: label for the servers list crumb", + "sourceHash": "68d7beb6df59" + }, + "web.components.ui.breadcrumbs.settings": { + "defaultMessage": "Paramètres", + "description": "Breadcrumb: label for the settings crumb (also used for the theme settings page)", + "sourceHash": "74a883a037bc" + }, + "web.components.ui.breadcrumbs.system_logs": { + "defaultMessage": "Journaux système", + "description": "Breadcrumb: label for the system logs crumb", + "sourceHash": "a6851520b7d9" + }, + "web.components.ui.breadcrumbs.users_rbac": { + "defaultMessage": "Utilisateurs et RBAC", + "description": "Breadcrumb: label for the users and roles crumb", + "sourceHash": "0c6ed78342e6" + }, + "web.components.ui.capture_warning_banner.captures_are_not_redacted_or_sanitized": { + "defaultMessage": "Les captures ne sont ni expurgées ni assainies. L’accès est réservé aux administrateurs. Les captures sont supprimées automatiquement après la durée de conservation configurée (actuellement {retentionHours} {retentionHours, plural, one {heure} other {heures}}).", + "description": "Capture warning banner: paragraph in Alert.Description; {retentionHours} is the capture retention window in hours", + "sourceHash": "338e91c84462" + }, + "web.components.ui.capture_warning_banner.caution_network_packet_captures_contain": { + "defaultMessage": "Attention : les captures réseau contiennent des données réelles de joueurs", + "description": "Capture warning banner: title of the warning shown with captures, saying they contain real player data", + "sourceHash": "effc421079d4" + }, + "web.components.ui.capture_warning_banner.dismiss": { + "defaultMessage": "Ignorer", + "description": "Capture warning banner: link-style button that hides the warning; keep it short", + "sourceHash": "48845bff334a" + }, + "web.components.ui.capture_warning_banner.for_some_games_in_band_credentials": { + "defaultMessage": "• Pour certains jeux, des identifiants transmis dans les échanges (mots de passe, jetons, clés de session)", + "description": "Capture warning banner: third bullet saying some games send passwords, tokens or session keys in captures; the bullet mark is part of the text", + "sourceHash": "c641570be951" + }, + "web.components.ui.capture_warning_banner.network_timing_and_game_protocol": { + "defaultMessage": "• Temporisations réseau et messages du protocole de jeu", + "description": "Capture warning banner: second bullet in the list of what captures can contain, naming network timing and game protocol messages; the bullet mark is part of the text", + "sourceHash": "2ba69777e260" + }, + "web.components.ui.capture_warning_banner.player_ip_addresses_and_port_numbers": { + "defaultMessage": "• Adresses IP et numéros de port des joueurs", + "description": "Capture warning banner: first bullet in the list of what captures can contain, naming player IP addresses and port numbers; the bullet mark is part of the text", + "sourceHash": "d1c82617782d" + }, + "web.components.ui.confirm_admin_mapping_dialog.confirm_admin_role_mapping": { + "defaultMessage": "Confirmer l’association au rôle administrateur ?", + "description": "Confirm admin mapping dialog: title of the dialog asking the user to confirm the admin role mapping", + "sourceHash": "ad1f1cfb2746" + }, + "web.components.ui.confirm_admin_mapping_dialog.full_admin_access": { + "defaultMessage": "Accès administrateur complet", + "description": "Confirm admin mapping dialog: bold warning heading shown above the explanation of mapping users to the admin role", + "sourceHash": "5c1752207d69" + }, + "web.components.ui.confirm_admin_mapping_dialog.group_s_being_mapped_to_admin": { + "defaultMessage": "Groupe(s) associé(s) au rôle administrateur :", + "description": "Confirm admin mapping dialog: label above the list of groups that are being mapped to the admin role", + "sourceHash": "5148828a7961" + }, + "web.components.ui.confirm_admin_mapping_dialog.map_to_admin_role": { + "defaultMessage": "Associer au rôle administrateur", + "description": "Confirm admin mapping dialog: confirm button that maps the groups to the admin role; keep it short", + "sourceHash": "9cf2c934d069" + }, + "web.components.ui.confirm_admin_mapping_dialog.mapping_users_to_the_admin_role_grants": { + "defaultMessage": "Associer des utilisateurs au rôle administrateur accorde un contrôle total du cluster. Assurez-vous que le groupe associé ne contient que du personnel autorisé. Toute personne de ces groupes obtient un accès administrateur complet dès sa prochaine connexion.", + "description": "Confirm admin mapping dialog: warning that the admin role grants full cluster control, so only trusted people belong in the mapped groups; applies at next sign-in", + "sourceHash": "75e54429205c" + }, + "web.components.ui.confirm_dialog.cancel": { + "defaultMessage": "Annuler", + "description": "Confirm dialog: button that closes the confirmation dialog without confirming; keep it short", + "sourceHash": "19766ed6ccb2" + }, + "web.components.ui.confirm_dialog.confirm": { + "defaultMessage": "Confirmer", + "description": "Confirm dialog: default confirm button label when the caller gives none", + "sourceHash": "eebdd24a77d9" + }, + "web.components.ui.confirm_dialog.type_phrase_to_confirm": { + "defaultMessage": "Saisissez {phrase} pour confirmer", + "description": "Confirm dialog: label above the text input asking the user to type {phrase}, the exact phrase shown in monospace inside , to enable the confirm button", + "sourceHash": "a690154e4cfc" + }, + "web.components.ui.confirm_dialog.type_to_confirm": { + "defaultMessage": "Saisissez pour confirmer", + "description": "Confirm dialog: placeholder inside the field where the user retypes the phrase shown above to confirm", + "sourceHash": "7dd9d5009077" + }, + "web.components.ui.confirm_dialog.working": { + "defaultMessage": "En cours…", + "description": "Confirm dialog: confirm button label while the action is running", + "sourceHash": "5474eef8d0f1" + }, + "web.components.ui.dropdown_menu.checked_mark": { + "defaultMessage": "✓", + "description": "Check mark shown beside a checked menu item", + "sourceHash": "1dabba21cdad" + }, + "web.components.ui.dropdown_menu.label_with_hint": { + "defaultMessage": "{label} : {hint}", + "description": "Dropdown menu item: accessible name combining the item label and its hint; {label} is the item text and {hint} the hint text", + "sourceHash": "455605cc918b" + }, + "web.components.ui.error_banner.dismiss_error": { + "defaultMessage": "Fermer l’erreur", + "description": "Error banner: screen-reader name of the icon-only close button on an error message; not visible on screen", + "sourceHash": "2db046678b9c" + }, + "web.components.ui.error_card.dismiss_error": { + "defaultMessage": "Fermer l’erreur", + "description": "Error card: screen-reader name of the icon-only button that hides the error card; not visible on screen", + "sourceHash": "2db046678b9c" + }, + "web.components.ui.error_card.retry": { + "defaultMessage": "Réessayer", + "description": "Error card: button that retries the failed request; keep it short", + "sourceHash": "942087cc2d41" + }, + "web.components.ui.filter_popover.apply": { + "defaultMessage": "Appliquer", + "description": "Filter popover: button that applies the chosen filters; keep it short", + "sourceHash": "31e392d1c037" + }, + "web.components.ui.filter_popover.clear": { + "defaultMessage": "Effacer", + "description": "Filter popover: button that clears all chosen filters; keep it short", + "sourceHash": "83b12c2216ef" + }, + "web.components.ui.filter_popover.game": { + "defaultMessage": "Jeu", + "description": "Filter popover: small heading above the game choices in the filter menu", + "sourceHash": "5c412a262086" + }, + "web.components.ui.filter_popover.namespace": { + "defaultMessage": "Namespace", + "description": "Filter popover: small heading above the namespace choices in the filter menu", + "sourceHash": "c4e4e7abda20" + }, + "web.components.ui.global_search.no_matches_in_available_results": { + "defaultMessage": "Aucune correspondance dans les résultats disponibles.", + "description": "Global search: empty-state text when partial results have no match", + "sourceHash": "16c16e080403" + }, + "web.components.ui.global_search.no_servers_match": { + "defaultMessage": "Aucun serveur ne correspond.", + "description": "Global search: empty-state text when no server matches the query", + "sourceHash": "4ee3dc20fad4" + }, + "web.components.ui.global_search.search_is_unavailable": { + "defaultMessage": "La recherche est indisponible.", + "description": "Global search: empty-state text when the search request failed", + "sourceHash": "a2630b22116e" + }, + "web.components.ui.global_search.search_results_are_partial": { + "defaultMessage": "Les résultats de recherche sont partiels.", + "description": "Global search: status note at the top of the results list saying some results may be missing", + "sourceHash": "252c92f5082f" + }, + "web.components.ui.global_search.search_servers": { + "defaultMessage": "Rechercher des serveurs", + "description": "Global search: screen-reader name of the server search box, which is hidden on narrow screens; not visible on screen", + "sourceHash": "3d7d709f953e" + }, + "web.components.ui.global_search.search_servers_2": { + "defaultMessage": "Rechercher des serveurs…", + "description": "Global search: example hint inside the server search box telling the user what to type; the ellipsis is part of the text", + "sourceHash": "b11d96510005" + }, + "web.components.ui.loading_card.loading": { + "defaultMessage": "Chargement…", + "description": "Loading card: default message shown below the spinner when the caller gives none", + "sourceHash": "ba3bbbe10d8b" + }, + "web.components.ui.meter.percent": { + "defaultMessage": "{value} %", + "description": "Meter: usage percentage shown beside the bar; {value} is the whole-number percentage", + "sourceHash": "522346dd585b" + }, + "web.components.ui.notifications_panel.event_line": { + "defaultMessage": "{verb, select, added {ajouté} modified {modifié} deleted {supprimé} other {{verb}}} {kind} {name}", + "description": "Notifications panel: one line in the events feed. {verb} is the lower-cased event type (added, modified or deleted, otherwise the raw type), {kind} is the singular object kind such as GameServer, {name} is the object name and may be empty", + "sourceHash": "40f8fbf55264" + }, + "web.components.ui.notifications_panel.no_recent_activity": { + "defaultMessage": "Aucune activité récente.", + "description": "Notifications panel: empty-state text shown when there are no recent notifications", + "sourceHash": "44b67bb3678a" + }, + "web.components.ui.notifications_panel.notifications": { + "defaultMessage": "Notifications", + "description": "Notifications panel: screen-reader name of the trigger that opens the notifications list; not visible on screen", + "sourceHash": "788011833a5a" + }, + "web.components.ui.notifications_panel.recent_activity": { + "defaultMessage": "Activité récente", + "description": "Notifications panel: heading at the top of the notifications list showing recent activity", + "sourceHash": "6cb44b56336a" + }, + "web.components.ui.notifications_panel.unread_overflow": { + "defaultMessage": "9+", + "description": "Notifications panel: unread badge text when there are more than nine unread items", + "sourceHash": "db15098d6cfe" + }, + "web.components.ui.phase_chip.asleep": { + "defaultMessage": "En veille", + "description": "Phase badge: label for a suspended server that is asleep (derived display state)", + "sourceHash": "60135e8f5cbb" + }, + "web.components.ui.phase_chip.failed": { + "defaultMessage": "Échec", + "description": "Phase badge: label for a resource whose last operation failed", + "sourceHash": "031a8f0f659d" + }, + "web.components.ui.phase_chip.pending": { + "defaultMessage": "En attente", + "description": "Phase badge: label for a resource that is waiting to start", + "sourceHash": "331551b0de41" + }, + "web.components.ui.phase_chip.resuming": { + "defaultMessage": "Reprise", + "description": "Phase badge: label for a suspended server that is resuming", + "sourceHash": "e71708dd9311" + }, + "web.components.ui.phase_chip.running": { + "defaultMessage": "En cours d’exécution", + "description": "Phase badge: label for a server that is running", + "sourceHash": "f4ccae29e1bb" + }, + "web.components.ui.phase_chip.starting": { + "defaultMessage": "Démarrage", + "description": "Phase badge: label for a server that is starting", + "sourceHash": "aeed4d26bb5f" + }, + "web.components.ui.phase_chip.stopped": { + "defaultMessage": "Arrêté", + "description": "Phase badge: label for a server that is stopped", + "sourceHash": "1a4f630ac1b6" + }, + "web.components.ui.phase_chip.stopping": { + "defaultMessage": "Arrêt en cours", + "description": "Phase badge: label for a server that is stopping", + "sourceHash": "a71ee1d4ac5c" + }, + "web.components.ui.phase_chip.succeeded": { + "defaultMessage": "Réussi", + "description": "Phase badge: label for a backup or restore that finished successfully", + "sourceHash": "6d9a6f97a5fd" + }, + "web.components.ui.phase_chip.suspended": { + "defaultMessage": "Suspendu", + "description": "Phase badge: label for a server that is suspended", + "sourceHash": "e392a3891c07" + }, + "web.components.ui.phase_chip.suspending": { + "defaultMessage": "Suspension en cours", + "description": "Phase badge: label for a server that is being suspended", + "sourceHash": "5e62da3022f8" + }, + "web.components.ui.provenance_badge.from_helm_values": { + "defaultMessage": "Depuis les valeurs Helm", + "description": "Provenance badge: label when the value comes from Helm chart values", + "sourceHash": "a31054beaa10" + }, + "web.components.ui.provenance_badge.not_configured": { + "defaultMessage": "Non configuré", + "description": "Provenance badge: label when the value is not set anywhere", + "sourceHash": "dd1841d29502" + }, + "web.components.ui.provenance_badge.overridden_in_dashboard": { + "defaultMessage": "Remplacé dans le tableau de bord", + "description": "Provenance badge: label when the value was edited in the dashboard", + "sourceHash": "c231c34eed4e" + }, + "web.components.ui.removable_group_chip.remove": { + "defaultMessage": "Retirer {label}", + "description": "Removable chip: accessible name of the remove button; {label} is the chip text, e.g. a group or role name", + "sourceHash": "57ce3c2ced60" + }, + "web.components.ui.resource_input.cpu_cores": { + "defaultMessage": "Cœurs CPU", + "description": "Resource input: accessible name of the CPU slider and its fields", + "sourceHash": "5dbed0bb8927" + }, + "web.components.ui.resource_input.memory_gib": { + "defaultMessage": "Mémoire (GiB)", + "description": "Resource input: accessible name of the memory slider and its fields", + "sourceHash": "e360625ba4be" + }, + "web.components.ui.resource_input.unit_cores": { + "defaultMessage": "cœurs", + "description": "Resource input: CPU unit option in the unit select, meaning whole CPU cores (not millicores)", + "sourceHash": "f664a2d346d8" + }, + "web.components.ui.resource_input.unit_label": { + "defaultMessage": "Unité de {name}", + "description": "Resource input: accessible name of the unit select; {name} is the resource name, e.g. CPU cores", + "sourceHash": "3daba76d7a8a" + }, + "web.components.ui.resource_input.value_label": { + "defaultMessage": "Valeur de {name}", + "description": "Resource input: accessible name of the number field; {name} is the resource name, e.g. CPU cores", + "sourceHash": "36d7c2c2179f" + }, + "web.components.ui.role_editor_modal.cancel": { + "defaultMessage": "Annuler", + "description": "Role editor dialog: button that closes the role editor without saving; keep it short", + "sourceHash": "19766ed6ccb2" + }, + "web.components.ui.role_editor_modal.create_role": { + "defaultMessage": "Créer le rôle", + "description": "Role editor modal: save button label when creating a role", + "sourceHash": "221163d6e73f" + }, + "web.components.ui.role_editor_modal.description": { + "defaultMessage": "Description", + "description": "Role editor dialog: label above the optional description field for the role", + "sourceHash": "526e0087cc3f" + }, + "web.components.ui.role_editor_modal.edit_role": { + "defaultMessage": "Modifier le rôle : {name}", + "description": "Role editor modal: heading when editing an existing role; {name} is the role name", + "sourceHash": "8b3d475f6dd7" + }, + "web.components.ui.role_editor_modal.grant_a_curated_set_of_permissions": { + "defaultMessage": "Accorde un ensemble de permissions sélectionnées.", + "description": "Role editor dialog: explanation under the title saying the role grants a curated set of permissions", + "sourceHash": "a1e15fdc70fd" + }, + "web.components.ui.role_editor_modal.manage_game_servers_backups_templates": { + "defaultMessage": "Gérer les serveurs de jeu, les sauvegardes, les modèles et les planifications.", + "description": "Role editor dialog: example value shown inside the empty role description field, hinting what a role can cover", + "sourceHash": "f69e230e3bb7" + }, + "web.components.ui.role_editor_modal.name": { + "defaultMessage": "Nom", + "description": "Role editor dialog: label above the role name field; shown only when creating a new role, not when editing", + "sourceHash": "dcd1d5223f73" + }, + "web.components.ui.role_editor_modal.new_role": { + "defaultMessage": "Nouveau rôle", + "description": "Role editor modal: heading when creating a role", + "sourceHash": "500f2f9d3956" + }, + "web.components.ui.role_editor_modal.ns": { + "defaultMessage": "ns", + "description": "Role editor dialog: small tag shown next to a permission name to mark it as namespaced; ns is short for namespace; keep it short", + "sourceHash": "7bfba6e0021f" + }, + "web.components.ui.role_editor_modal.save_role": { + "defaultMessage": "Enregistrer le rôle", + "description": "Role editor modal: save button label when editing a role", + "sourceHash": "c7523b152e8b" + }, + "web.components.ui.role_editor_modal.saving": { + "defaultMessage": "Enregistrement…", + "description": "Role editor modal: save button label while the role is being saved", + "sourceHash": "23e39291d613" + }, + "web.components.ui.role_editor_modal.support": { + "defaultMessage": "support", + "description": "Role editor dialog: example value shown inside the empty role name field; a sample role name, not a default value", + "sourceHash": "a18603086e5b" + }, + "web.components.ui.safe_mode_banner.dismiss": { + "defaultMessage": "Ignorer", + "description": "Safe mode banner: link-style button that hides the Safe Mode banner; keep it short", + "sourceHash": "48845bff334a" + }, + "web.components.ui.safe_mode_banner.open_appearance_settings": { + "defaultMessage": "Ouvrir les paramètres d’apparence", + "description": "Safe mode banner: link-style button that opens the Appearance settings; keep it short", + "sourceHash": "149a18154423" + }, + "web.components.ui.safe_mode_banner.safe_mode_active_custom_css_is_suspended": { + "defaultMessage": "Mode sans échec activé : le CSS personnalisé est suspendu.", + "description": "Safe mode banner: warning saying Safe Mode is active and custom CSS is suspended; CSS is a technical term, usually left untranslated", + "sourceHash": "8442bc99fce6" + }, + "web.components.ui.settings_nav.about": { + "defaultMessage": "À propos", + "description": "Settings nav: label of the About settings section", + "sourceHash": "4efca0d10c5f" + }, + "web.components.ui.settings_nav.authentication": { + "defaultMessage": "Authentification", + "description": "Settings nav: label of the Authentication settings section", + "sourceHash": "66880d2d8216" + }, + "web.components.ui.settings_nav.backup_destinations": { + "defaultMessage": "Destinations de sauvegarde", + "description": "Settings nav: label of the Backup destinations settings section", + "sourceHash": "6facc55bbf94" + }, + "web.components.ui.settings_nav.general": { + "defaultMessage": "Général", + "description": "Settings nav: label of the General settings section", + "sourceHash": "c910d474dcd7" + }, + "web.components.ui.settings_nav.mod_registries": { + "defaultMessage": "Registres de mods", + "description": "Settings nav: label of the Mod registries settings section", + "sourceHash": "189e782e24ab" + }, + "web.components.ui.settings_nav.module_sources": { + "defaultMessage": "Sources de modules", + "description": "Settings nav: label of the Module sources settings section", + "sourceHash": "b8ba2797838f" + }, + "web.components.ui.settings_nav.notifications": { + "defaultMessage": "Notifications", + "description": "Settings nav: label of the Notifications settings section", + "sourceHash": "788011833a5a" + }, + "web.components.ui.settings_nav.settings_sections": { + "defaultMessage": "Sections des paramètres", + "description": "Settings navigation: screen-reader name of the list of settings sections; not visible on screen", + "sourceHash": "e26d51d36781" + }, + "web.components.ui.settings_nav.telemetry": { + "defaultMessage": "Télémétrie", + "description": "Settings nav: label of the Telemetry settings section", + "sourceHash": "e87c57f59542" + }, + "web.components.ui.settings_nav.theme": { + "defaultMessage": "Thème", + "description": "Settings nav: label of the Theme settings page", + "sourceHash": "efb52e7172b7" + }, + "web.components.ui.settings_nav.updates": { + "defaultMessage": "Mises à jour", + "description": "Settings nav: label of the Updates settings section", + "sourceHash": "22e2bada8f1c" + }, + "web.components.ui.sidebar.close_navigation": { + "defaultMessage": "Fermer la navigation", + "description": "Sidebar: icon-only button that closes the navigation drawer on small screens; screen-reader name, not visible on screen", + "sourceHash": "99904db30de4" + }, + "web.components.ui.sidebar.customize_theme": { + "defaultMessage": "Personnaliser le thème", + "description": "Sidebar: icon-only button for changing the theme; its tooltip and screen-reader name read Customize theme; keep it short", + "sourceHash": "f362ed5a2366" + }, + "web.components.ui.sidebar.gameplane": { + "defaultMessage": "gameplane", + "description": "Sidebar: product name shown at the top of the side navigation, with the cluster name beneath it; keep the name as written", + "sourceHash": "c48499141ee6" + }, + "web.components.ui.sidebar.guest": { + "defaultMessage": "invité", + "description": "Sidebar: name shown for a user with no display name or username", + "sourceHash": "84983c60f7da" + }, + "web.components.ui.sidebar.mobile_navigation": { + "defaultMessage": "Navigation mobile", + "description": "Sidebar: accessible name of the navigation landmark in the mobile drawer", + "sourceHash": "806f228ffbd4" + }, + "web.components.ui.sidebar.primary": { + "defaultMessage": "Principale", + "description": "Sidebar: accessible name of the main navigation landmark", + "sourceHash": "efe10c80ec8a" + }, + "web.components.ui.sidebar.sidebar": { + "defaultMessage": "Barre latérale", + "description": "Sidebar: screen-reader name of the side navigation panel; not visible on screen", + "sourceHash": "f7efa7bc1fc5" + }, + "web.components.ui.sidebar.sidebar_footer": { + "defaultMessage": "Pied de la barre latérale", + "description": "Sidebar: screen-reader name of the footer area at the bottom of the side navigation; not visible on screen", + "sourceHash": "309f5e339fbb" + }, + "web.components.ui.sidebar.sign_out": { + "defaultMessage": "Se déconnecter", + "description": "Sidebar: icon-only button that signs the user out; screen-reader name, not visible on screen; keep it short", + "sourceHash": "48f0d3d397d4" + }, + "web.components.ui.telemetry_notice.anonymous_usage_metrics_are_on_for_this": { + "defaultMessage": "Les métriques d’usage anonymes sont activées pour cette installation.", + "description": "Telemetry notice: bold heading saying anonymous usage metrics are on for this installation", + "sourceHash": "b79e7d3d4591" + }, + "web.components.ui.telemetry_notice.basic_metrics": { + "defaultMessage": "Métriques de base", + "description": "Telemetry notice: heading of the first group of shared data, the basic metrics", + "sourceHash": "17077b491db9" + }, + "web.components.ui.telemetry_notice.could_not_save_your_choice_try_again": { + "defaultMessage": "Impossible d’enregistrer votre choix. Réessayez.", + "description": "Telemetry notice: error message shown when saving the telemetry choice fails; it asks the user to try again", + "sourceHash": "1628699e90e1" + }, + "web.components.ui.telemetry_notice.data_handling_statement": { + "defaultMessage": "Déclaration de traitement des données", + "description": "Telemetry notice: link to the data-handling statement, which opens in a new tab; keep it short", + "sourceHash": "0f26aef79d22" + }, + "web.components.ui.telemetry_notice.extended_metrics": { + "defaultMessage": "Métriques étendues", + "description": "Telemetry notice: heading of the second group of shared data, the extended metrics", + "sourceHash": "a44645f0cca2" + }, + "web.components.ui.telemetry_notice.gameplane_version_number_of_servers": { + "defaultMessage": "Version de Gameplane, nombre de serveurs, nombre de modèles.", + "description": "Telemetry notice: description of the basic metrics, listing the Gameplane version, number of servers and number of templates", + "sourceHash": "b8081402afe1" + }, + "web.components.ui.telemetry_notice.keep_sharing": { + "defaultMessage": "Continuer le partage", + "description": "Telemetry notice: button that keeps the current telemetry sharing on; keep it short", + "sourceHash": "ec042dc6cb65" + }, + "web.components.ui.telemetry_notice.kubernetes_version_distribution_cpu": { + "defaultMessage": "Version de Kubernetes, distribution, architecture CPU et tranche de nombre de nœuds ; serveurs par module de jeu officiel (les autres comptés comme personnalisés) ; fonctionnalités optionnelles utilisées ; et un identifiant d’installation aléatoire.", + "description": "Telemetry notice: description of the extended metrics, listing the Kubernetes version, distribution, CPU architecture, node-count band, servers per game module, optional features and an install ID", + "sourceHash": "766deef7939b" + }, + "web.components.ui.telemetry_notice.open_telemetry_settings": { + "defaultMessage": "Ouvrir les paramètres de télémétrie", + "description": "Telemetry notice: link-style button that opens the Telemetry settings; keep it short", + "sourceHash": "f7a03ec3c413" + }, + "web.components.ui.telemetry_notice.reports_are_sent_to_bundled": { + "defaultMessage": "Les rapports sont envoyés à {host}, le récepteur fourni avec cette installation. Les rapports restent dans votre cluster.", + "description": "Telemetry notice: sentence naming the receiver bundled with the install; {host} is its host name, shown in bold inside ", + "sourceHash": "7338f5d22f2b" + }, + "web.components.ui.telemetry_notice.reports_are_sent_to_custom": { + "defaultMessage": "Les rapports sont envoyés à {host}, la destination configurée par votre opérateur.", + "description": "Telemetry notice: sentence naming a telemetry destination the operator configured; {host} is its host name, shown in bold inside ", + "sourceHash": "b1b9c083ed1a" + }, + "web.components.ui.telemetry_notice.reports_are_sent_to_default": { + "defaultMessage": "Les rapports sont envoyés à {host}, le service de télémétrie du projet Gameplane.", + "description": "Telemetry notice: sentence naming the default telemetry receiver; {host} is its host name, shown in bold inside ", + "sourceHash": "a8523aef6e7e" + }, + "web.components.ui.telemetry_notice.telemetry_notice": { + "defaultMessage": "Avis de télémétrie", + "description": "Telemetry notice: screen-reader name of the notice about anonymous usage metrics; not visible on screen", + "sourceHash": "6bddd75ed3ac" + }, + "web.components.ui.telemetry_notice.turn_off_all": { + "defaultMessage": "Tout désactiver", + "description": "Telemetry notice: button that turns off all telemetry sharing; keep it short", + "sourceHash": "d4d6f9f0d48c" + }, + "web.components.ui.telemetry_notice.turn_off_extended": { + "defaultMessage": "Désactiver l'option étendue", + "description": "Telemetry notice: button that turns off the extended metrics only; keep it short", + "sourceHash": "85c4ad145fe3" + }, + "web.components.ui.top_bar.guest": { + "defaultMessage": "invité", + "description": "Top bar: name shown for a user with no display name or username", + "sourceHash": "84983c60f7da" + }, + "web.components.ui.top_bar.open_navigation": { + "defaultMessage": "Ouvrir la navigation", + "description": "Top bar: icon-only button that opens the navigation on small screens; screen-reader name, not visible on screen", + "sourceHash": "0ed77fd2619b" + }, + "web.components.ui.top_bar.sign_out": { + "defaultMessage": "Se déconnecter", + "description": "Top bar: user menu item that signs the user out; keep it short", + "sourceHash": "48f0d3d397d4" + }, + "web.components.ui.top_bar.theme_appearance": { + "defaultMessage": "Thème et apparence", + "description": "Top bar: user menu item that opens the Theme and Appearance settings; keep it short", + "sourceHash": "33947e018bc1" + }, + "web.components.ui.top_bar.user_menu": { + "defaultMessage": "Menu utilisateur", + "description": "Top bar: screen-reader name of the user avatar dropdown menu; not visible on screen", + "sourceHash": "6fa25be1c50b" + }, + "web.lib.errors.request_failed": { + "defaultMessage": "échec de la requête", + "description": "Fallback error text when a failed request has no usable message", + "sourceHash": "fbbc1b5ba864" + }, + "web.lib.errors.request_failed_status": { + "defaultMessage": "échec de la requête ({status})", + "description": "Fallback error text for an HTTP error with an empty body; {status} is the HTTP status code", + "sourceHash": "f6c6e4104a40" + }, + "web.lib.fleet.results_unavailable": { + "defaultMessage": "Résultats du serveur indisponibles.", + "description": "Fleet view issue shown for a cluster or namespace whose server list failed to load", + "sourceHash": "599c4e957b91" + }, + "web.lib.games.category.other": { + "defaultMessage": "Autre", + "description": "Module category name shown as a filter chip and badge: modules that fit no other category", + "sourceHash": "f97e9da0e3b8" + }, + "web.lib.games.category.sandbox": { + "defaultMessage": "Bac à sable", + "description": "Module category name shown as a filter chip and badge: sandbox games", + "sourceHash": "67fc62497e85" + }, + "web.lib.games.category.shooter": { + "defaultMessage": "Tir", + "description": "Module category name shown as a filter chip and badge: shooter games", + "sourceHash": "78cbec508481" + }, + "web.lib.games.category.survival": { + "defaultMessage": "Survie", + "description": "Module category name shown as a filter chip and badge: survival games", + "sourceHash": "8302496de0ac" + }, + "web.lib.settings_merge.conflict": { + "defaultMessage": "Le serveur a changé depuis l'ouverture de cette page.", + "description": "Error shown when saving server settings after the server was changed elsewhere", + "sourceHash": "5526f30b91d5" + }, + "web.lib.theme_export.appearance_mode_values": { + "defaultMessage": "export de thème invalide : preferences.appearanceMode doit être \"light\", \"dark\" ou \"system\"", + "description": "Theme import error: appearanceMode has an unknown value; light, dark and system are the value names, keep them as written", + "sourceHash": "14c6a28648ce" + }, + "web.lib.theme_export.bad_format": { + "defaultMessage": "export de thème invalide : format doit être \"{format}\"", + "description": "Theme import error: the document's format field is wrong; {format} is the expected format name, gameplane-theme", + "sourceHash": "66a0a69424e1" + }, + "web.lib.theme_export.custom_color_hex": { + "defaultMessage": "export de thème invalide : preferences.customColors.{key} doit être une couleur hexadécimale #RRGGBB", + "description": "Theme import error: a custom color is not a #RRGGBB hex value; {key} is accent or surface", + "sourceHash": "34c1680a611c" + }, + "web.lib.theme_export.custom_colors_object": { + "defaultMessage": "export de thème invalide : preferences.customColors doit être un objet ou null", + "description": "Theme import error: customColors is neither an object nor null", + "sourceHash": "1be55ffbd0b5" + }, + "web.lib.theme_export.custom_css_enabled_boolean": { + "defaultMessage": "export de thème invalide : preferences.customCssEnabled doit être un booléen", + "description": "Theme import error: customCssEnabled is not true or false", + "sourceHash": "18560d9bf93d" + }, + "web.lib.theme_export.custom_css_string": { + "defaultMessage": "export de thème invalide : preferences.customCss doit être une chaîne ou null", + "description": "Theme import error: customCss is neither a string nor null", + "sourceHash": "9b0eab197e1e" + }, + "web.lib.theme_export.custom_css_too_long": { + "defaultMessage": "export de thème invalide : preferences.customCss dépasse la longueur maximale de {max} caractères", + "description": "Theme import error: the imported custom CSS is longer than the limit; {max} is the maximum number of characters", + "sourceHash": "9ded4e55d3bb" + }, + "web.lib.theme_export.missing_field": { + "defaultMessage": "export de thème invalide : champ obligatoire \"{field}\" manquant", + "description": "Theme import error: a required field is missing; {field} is the field path, such as preferences.presetId", + "sourceHash": "75a399f37583" + }, + "web.lib.theme_export.not_json": { + "defaultMessage": "export de thème invalide : JSON non valide", + "description": "Theme import error: the selected text is not valid JSON", + "sourceHash": "37fda165bc8e" + }, + "web.lib.theme_export.not_object": { + "defaultMessage": "export de thème invalide : un objet JSON est attendu", + "description": "Theme import error: the document is valid JSON but not an object", + "sourceHash": "3aa582589f40" + }, + "web.lib.theme_export.preferences_not_object": { + "defaultMessage": "export de thème invalide : preferences doit être un objet", + "description": "Theme import error: the preferences field is not an object", + "sourceHash": "0fdaebf36f2a" + }, + "web.lib.theme_export.preset_id_values": { + "defaultMessage": "export de thème invalide : preferences.presetId doit être \"pink\" ou \"legacy\"", + "description": "Theme import error: presetId has an unknown value; pink and legacy are the value names, keep them as written", + "sourceHash": "52dd75980ca3" + }, + "web.lib.theme_export.theme_type_values": { + "defaultMessage": "export de thème invalide : preferences.themeType doit être \"preset\" ou \"custom_colors\"", + "description": "Theme import error: themeType has an unknown value; preset and custom_colors are the value names, keep them as written", + "sourceHash": "2167270b6c94" + }, + "web.lib.theme_export.version_must_be": { + "defaultMessage": "export de thème invalide : version doit être {version}", + "description": "Theme import error: the document's version is not supported; {version} is the supported version number", + "sourceHash": "429549d0c4a7" + }, + "web.lib.theme_export.version_newer": { + "defaultMessage": "export de thème invalide : version {version} non prise en charge — ce thème a été exporté par une version plus récente de Gameplane ; mettez à jour cette instance avant l'importation", + "description": "Theme import error: the document was exported by a newer Gameplane version; {version} is the document's version number", + "sourceHash": "fe74d93bb9e1" + }, + "web.lib.theme_export.version_not_number": { + "defaultMessage": "export de thème invalide : version doit être un nombre", + "description": "Theme import error: the document's version field is not a number", + "sourceHash": "28e07457cd50" + }, + "web.lib.use_server_capabilities.capture_unavailable": { + "defaultMessage": "Les limites de capture ne sont pas disponibles pour cet emplacement.", + "description": "Error shown when the server reports packet capture limits that are not usable", + "sourceHash": "67268c3a5852" + }, + "web.lib.use_server_capabilities.identity_changed": { + "defaultMessage": "L'identité du serveur a changé. Rechargez ce serveur avant de continuer.", + "description": "Error shown when a server's capabilities response no longer matches the server being viewed", + "sourceHash": "7e5780e849ce" + }, + "web.lib.use_theme_preferences.request_failed": { + "defaultMessage": "Échec de la requête ({status})", + "description": "Error shown when saving theme preferences fails with an HTTP error that has no message; {status} is the HTTP status code", + "sourceHash": "d5f9abf9557a" + }, + "web.lib.use_theme_preferences.update_failed": { + "defaultMessage": "Échec de la mise à jour du thème", + "description": "Error shown when saving theme preferences fails for an unknown reason", + "sourceHash": "024b10ffc035" + }, + "web.lib.utils.days_ago": { + "defaultMessage": "il y a {count} j", + "description": "Relative time in days, e.g. 3d ago; {count} is a number of days", + "sourceHash": "a41234397f3c" + }, + "web.lib.utils.days_until": { + "defaultMessage": "dans {count} j", + "description": "Relative time until a future event in days; {count} is a number of days", + "sourceHash": "b9df999c63ce" + }, + "web.lib.utils.due": { + "defaultMessage": "échu", + "description": "Schedule timing: the next scheduled run time has already passed", + "sourceHash": "458a4770df2b" + }, + "web.lib.utils.hours_ago": { + "defaultMessage": "il y a {count} h", + "description": "Relative time in hours, e.g. 2h ago; {count} is a number of hours", + "sourceHash": "520c737ea624" + }, + "web.lib.utils.hours_until": { + "defaultMessage": "dans {count} h", + "description": "Relative time until a future event in hours; {count} is a number of hours", + "sourceHash": "721207cdcfee" + }, + "web.lib.utils.minutes_ago": { + "defaultMessage": "il y a {count} min", + "description": "Relative time in minutes, e.g. 5m ago; {count} is a number of minutes", + "sourceHash": "0a03167c208c" + }, + "web.lib.utils.minutes_until": { + "defaultMessage": "dans {count} min", + "description": "Relative time until a future event in minutes; {count} is a number of minutes", + "sourceHash": "77ae24fd8467" + }, + "web.lib.utils.seconds_ago": { + "defaultMessage": "il y a {count} s", + "description": "Relative time: a moment ago in seconds, e.g. 45s ago; {count} is a number of seconds", + "sourceHash": "244073ecb2be" + }, + "web.lib.utils.seconds_until": { + "defaultMessage": "dans {count} s", + "description": "Relative time until a future event in seconds; {count} is a number of seconds", + "sourceHash": "fd20ebe73c03" + }, + "web.lib.utils.storage_of_physical": { + "defaultMessage": "sur {total} physiques", + "description": "Storage capacity line: provisioned storage out of node physical disk; {total} is a formatted size such as 40 GB", + "sourceHash": "fc252920f594" + }, + "web.lib.utils.storage_overcommitted": { + "defaultMessage": "sur {total} physiques — surallocation", + "description": "Storage capacity line when provisioned storage exceeds physical disk; {total} is a formatted size such as 40 GB", + "sourceHash": "da09f580e7b4" + }, + "web.lib.utils.uptime_days_hours": { + "defaultMessage": "{days} j {hours} h", + "description": "Server uptime in days and hours, e.g. 2d 5h; {days} and {hours} are numbers", + "sourceHash": "be43ecf3668b" + }, + "web.lib.utils.uptime_hours_minutes": { + "defaultMessage": "{hours} h {minutes} min", + "description": "Server uptime in hours and minutes, e.g. 3h 12m; {hours} and {minutes} are numbers", + "sourceHash": "425dc6c72de6" + }, + "web.lib.utils.uptime_minutes": { + "defaultMessage": "{minutes} min", + "description": "Server uptime in minutes, e.g. 12m; {minutes} is a number", + "sourceHash": "f70471798458" + }, + "web.lib.validation.at_least": { + "defaultMessage": "Doit être supérieur ou égal à {min}.", + "description": "Config field validation: number below the minimum; {min} is the minimum", + "sourceHash": "70e696ec6428" + }, + "web.lib.validation.at_most": { + "defaultMessage": "Doit être inférieur ou égal à {max}.", + "description": "Config field validation: number above the maximum; {max} is the maximum", + "sourceHash": "b8afdbbdca6d" + }, + "web.lib.validation.between": { + "defaultMessage": "Doit être comprise entre {min} et {max}.", + "description": "Config field validation: number outside a min and max range; {min} and {max} are the limits", + "sourceHash": "c63096e22dfb" + }, + "web.lib.validation.chars_at_least": { + "defaultMessage": "Doit comporter au moins {min} caractères.", + "description": "Config field validation: text shorter than the minimum length; {min} is the minimum number of characters", + "sourceHash": "9b2c45e97d4b" + }, + "web.lib.validation.chars_at_most": { + "defaultMessage": "Doit comporter au plus {max} caractères.", + "description": "Config field validation: text longer than the maximum length; {max} is the maximum number of characters", + "sourceHash": "36563ed527e9" + }, + "web.lib.validation.label_problem": { + "defaultMessage": "{label} : {problem}", + "description": "Config field validation: prefixes a value problem with the field name; {label} is the field's display name and {problem} is the problem sentence", + "sourceHash": "154e872af7dc" + }, + "web.lib.validation.one_of": { + "defaultMessage": "{label} doit être l'une des valeurs suivantes : {options}", + "description": "Config field validation: enum field value not in the allowed list; {label} is the field's display name and {options} is a comma-separated list of allowed values", + "sourceHash": "f447884f55dc" + }, + "web.lib.validation.required": { + "defaultMessage": "{label} est obligatoire", + "description": "Config field validation: shown under a required config field left empty; {label} is the field's display name", + "sourceHash": "ca8766082649" + }, + "web.lib.validation.true_false": { + "defaultMessage": "Doit être vrai ou faux.", + "description": "Config field validation: boolean field value is neither true nor false", + "sourceHash": "0534262e742c" + }, + "web.lib.validation.whole_number": { + "defaultMessage": "Doit être un nombre entier.", + "description": "Config field validation: integer field value is not a whole number", + "sourceHash": "5340372953b7" + }, + "web.routes.admin_logs.component_api": { + "defaultMessage": "Serveur API", + "description": "Admin logs: tab label for the API server log source", + "sourceHash": "86067e1ce53b" + }, + "web.routes.admin_logs.component_operator": { + "defaultMessage": "Opérateur", + "description": "Admin logs: tab label for the operator log source", + "sourceHash": "291101a07fe9" + }, + "web.routes.admin_logs.couldn_t_load_installation_capabilities": { + "defaultMessage": "Impossible de charger les capacités de l'installation.", + "description": "Admin logs: error message shown when installation details fail to load; a retry button follows it", + "sourceHash": "76468b1e3822" + }, + "web.routes.admin_logs.docker_compose_logs_follow": { + "defaultMessage": "docker compose logs --follow", + "description": "Admin logs: shell command shown in monospace inside the standalone hint, the Docker Compose command to stream logs; technical term, usually left untranslated", + "sourceHash": "ad23ec51b0bb" + }, + "web.routes.admin_logs.download": { + "defaultMessage": "Télécharger", + "description": "Admin logs: button that downloads the logs shown on screen as a file; keep it short", + "sourceHash": "d6eafe823591" + }, + "web.routes.admin_logs.follow": { + "defaultMessage": "Suivre", + "description": "Admin logs: label of the switch that keeps the log view following new lines as they arrive; keep it short", + "sourceHash": "641d1ef657bd" + }, + "web.routes.admin_logs.for_docker_compose_run": { + "defaultMessage": "Pour Docker Compose, exécutez", + "description": "Admin logs: opening words of the standalone hint paragraph, followed by a command in monospace and the rest of the sentence", + "sourceHash": "244c1f3732e5" + }, + "web.routes.admin_logs.from_your_panel_installation_directory": { + "defaultMessage": "depuis le répertoire d'installation de votre panneau. Les journaux de l'opérateur sont disponibles sur chaque cluster de charge de travail.", + "description": "Admin logs: end of the standalone hint, asking to run the command from the install directory; also notes operator logs are on each workload cluster", + "sourceHash": "3a03e4aadbdf" + }, + "web.routes.admin_logs.live_logs_from_the_gameplane_control": { + "defaultMessage": "Journaux en direct des pods du plan de contrôle Gameplane.", + "description": "Admin logs: description under the title of the cluster logs view, saying the lines come from the Gameplane control-plane pods", + "sourceHash": "a908a26f5b23" + }, + "web.routes.admin_logs.loading_installation": { + "defaultMessage": "Chargement de l'installation…", + "description": "Admin logs: loading text shown in place of the page while installation details are fetched", + "sourceHash": "9b3317b01b4b" + }, + "web.routes.admin_logs.log_source": { + "defaultMessage": "Source des journaux", + "description": "Admin logs: accessible name of the tab group for choosing which component's logs to show", + "sourceHash": "76a7db76dcfe" + }, + "web.routes.admin_logs.logs_for_this_standalone_panel": { + "defaultMessage": "Journaux de ce panneau autonome.", + "description": "Admin logs: description under the title in the standalone panel view, saying these logs belong to this panel only", + "sourceHash": "8387b045b0c4" + }, + "web.routes.admin_logs.n_lines": { + "defaultMessage": "{n} lignes", + "description": "Admin logs: sentence with n in ListBoxItem", + "sourceHash": "2f37d7f854c7" + }, + "web.routes.admin_logs.no_output": { + "defaultMessage": "Aucune sortie.", + "description": "Admin logs: shown in the log panel when the stream failed", + "sourceHash": "dfdba897c357" + }, + "web.routes.admin_logs.pod_currently_streamed": { + "defaultMessage": "Pod en cours de diffusion", + "description": "Admin logs: badge showing the pod whose logs are being streamed right now; keep it short", + "sourceHash": "e4cb6448f772" + }, + "web.routes.admin_logs.system_logs": { + "defaultMessage": "Journaux système", + "description": "Admin logs: page title at the top of the system logs screen; keep it short", + "sourceHash": "a6851520b7d9" + }, + "web.routes.admin_logs.tail": { + "defaultMessage": "Dernières", + "description": "Admin logs: label in front of the line-count dropdown in the log toolbar; keep it short", + "sourceHash": "ae9c3bd11f76" + }, + "web.routes.admin_logs.tail_lines": { + "defaultMessage": "Nombre de lignes récentes", + "description": "Admin logs: accessible name of the dropdown that sets how many recent lines to load", + "sourceHash": "78cdee102526" + }, + "web.routes.admin_logs.view_logs_through_your_container_runtime": { + "defaultMessage": "Consulter les journaux via votre runtime de conteneurs", + "description": "Admin logs: heading in the standalone panel view that introduces how to read logs through the container runtime", + "sourceHash": "0e5e8bb7042b" + }, + "web.routes.admin_logs.waiting_for_output": { + "defaultMessage": "En attente de sortie…", + "description": "Admin logs: shown in the log panel while no output has arrived yet", + "sourceHash": "f05fefe200f2" + }, + "web.routes.admin_settings.a_short_lowercase_identifier_letters": { + "defaultMessage": "Identifiant court en minuscules (lettres, chiffres et tirets) utilisé dans la route de connexion et le nom du Secret.", + "description": "Admin settings: hint under the provider name field: a short lowercase identifier of letters, digits and dashes, used in the login route and the Secret name", + "sourceHash": "7ed9d62127da" + }, + "web.routes.admin_settings.about": { + "defaultMessage": "À propos", + "description": "Admin settings: title of the About section card showing version details; keep it short", + "sourceHash": "4efca0d10c5f" + }, + "web.routes.admin_settings.access_token_optional": { + "defaultMessage": "Jeton d'accès (facultatif)", + "description": "Admin settings: label of the optional access token field for an ntfy notification sink", + "sourceHash": "ab881f51cf33" + }, + "web.routes.admin_settings.acme_sso": { + "defaultMessage": "Acme SSO", + "description": "Admin settings: grey example text inside the display name field, shown only while it is empty", + "sourceHash": "34f666ba4074" + }, + "web.routes.admin_settings.active_in_mods_browser": { + "defaultMessage": "Actif dans le navigateur de mods", + "description": "Admin settings: registry status when a key is configured and the provider shows in the Mods browser", + "sourceHash": "095220f86830" + }, + "web.routes.admin_settings.add": { + "defaultMessage": "Ajouter", + "description": "Admin settings: button that adds the typed group to the role; keep it short", + "sourceHash": "9fd728c66c9a" + }, + "web.routes.admin_settings.add_destination": { + "defaultMessage": "Ajouter une destination", + "description": "Admin settings: button that opens the form to add a backup destination, and the heading of that form; keep it short", + "sourceHash": "48b7df13ccd6" + }, + "web.routes.admin_settings.add_group": { + "defaultMessage": "Ajouter un groupe", + "description": "Admin settings: accessible name of the button that adds the typed group to a role; keep it short", + "sourceHash": "94901258ed02" + }, + "web.routes.admin_settings.add_identity_provider": { + "defaultMessage": "Ajouter un fournisseur d'identité", + "description": "Admin settings: heading of the card for adding a new identity provider", + "sourceHash": "dfbbad875600" + }, + "web.routes.admin_settings.add_idp_group_name": { + "defaultMessage": "Ajouter un nom de groupe IdP…", + "description": "Admin settings: grey placeholder inside the field where an identity-provider group name is typed to add it to a role", + "sourceHash": "a8a4213d56ae" + }, + "web.routes.admin_settings.add_provider": { + "defaultMessage": "Ajouter un fournisseur", + "description": "Admin settings: button that opens the form to add an identity provider, and the button in that form that submits it; keep it short", + "sourceHash": "8cd1856b03dd" + }, + "web.routes.admin_settings.add_sink": { + "defaultMessage": "Ajouter un canal", + "description": "Admin settings: heading of the add-sink form, and the button in its footer that adds the sink; keep it short", + "sourceHash": "1a70332b174f" + }, + "web.routes.admin_settings.admin": { + "defaultMessage": "Administrateur", + "description": "Admin settings: option in the default role dropdown for the admin role; keep it short", + "sourceHash": "c1c224b03cd9" + }, + "web.routes.admin_settings.admin_groups": { + "defaultMessage": "Groupes administrateurs", + "description": "Admin settings: label of the field listing the identity-provider groups that map to the admin role", + "sourceHash": "8984f73242c9" + }, + "web.routes.admin_settings.admin_settings": { + "defaultMessage": "Paramètres d'administration", + "description": "Admin settings: page title at the top of the admin settings screen; keep it short", + "sourceHash": "502d9f3ec972" + }, + "web.routes.admin_settings.agpl_3_0": { + "defaultMessage": "AGPL-3.0", + "description": "Admin settings: license name shown in the About list, usually left untranslated", + "sourceHash": "9c2e9ba90c75" + }, + "web.routes.admin_settings.always_available_modrinth_thunderstore": { + "defaultMessage": "Toujours disponibles : Modrinth, Thunderstore, Hangar, Factorio, Spigot, GitHub, uMod", + "description": "Admin settings: note under the registry list naming the registries that need no key; the names are product names, usually left untranslated", + "sourceHash": "e0022013f8d3" + }, + "web.routes.admin_settings.an_earlier_attempt": { + "defaultMessage": "une tentative précédente", + "description": "Admin settings: stands in for the time of the last successful report when the time is unknown", + "sourceHash": "d68ca429720a" + }, + "web.routes.admin_settings.anonymous_usage_metrics_help_us": { + "defaultMessage": "Les métriques d'utilisation anonymes nous aident à prioriser le travail. Les métriques étendues incluent un ID d'installation aléatoire.", + "description": "Admin settings: subtitle under Telemetry: anonymous usage metrics help prioritise work, and extended metrics include a random install ID", + "sourceHash": "52ecd346b828" + }, + "web.routes.admin_settings.api_key": { + "defaultMessage": "Clé d'API", + "description": "Admin settings: label of the API key field in the dialog that sets the key for a module registry", + "sourceHash": "16f0ee47f993" + }, + "web.routes.admin_settings.api_key_label": { + "defaultMessage": "Clé d'API — {label}", + "description": "Admin settings: sentence with label in div", + "sourceHash": "f73e61d93eeb" + }, + "web.routes.admin_settings.applied_when_no_group_matches_deny_sign": { + "defaultMessage": "Appliqué lorsqu'aucun groupe ne correspond. “Refuser la connexion” refuse les connexions sans groupe associé.", + "description": "Admin settings: hint under the default role dropdown explaining the default applies when no group matches, and that Deny sign-in refuses such logins", + "sourceHash": "964928b131fb" + }, + "web.routes.admin_settings.at_least_one_identity_provider": { + "defaultMessage": "Au moins un fournisseur d'identité doit rester activé.", + "description": "Admin settings: tooltip on an identity provider toggle that cannot be turned off", + "sourceHash": "e574337a9f49" + }, + "web.routes.admin_settings.at_least_one_identity_provider_must": { + "defaultMessage": "Au moins un fournisseur d'identité doit rester activé : le dernier fournisseur activé ne peut pas être désactivé.", + "description": "Admin settings: note under the provider list explaining that the last enabled provider cannot be turned off", + "sourceHash": "b40f084f84db" + }, + "web.routes.admin_settings.authentication": { + "defaultMessage": "Authentification", + "description": "Admin settings: title of the Authentication section card for local accounts and identity providers; keep it short", + "sourceHash": "66880d2d8216" + }, + "web.routes.admin_settings.authorization_header_optional": { + "defaultMessage": "En-tête Authorization (facultatif)", + "description": "Admin settings: label of the optional Authorization header field for a webhook notification sink", + "sourceHash": "981876203e19" + }, + "web.routes.admin_settings.azure_account_container": { + "defaultMessage": "azure:account/container", + "description": "Admin settings: example Restic URL format for Azure", + "sourceHash": "292c50fb11ee" + }, + "web.routes.admin_settings.b2_bucket": { + "defaultMessage": "b2:bucket", + "description": "Admin settings: example Restic URL format for Backblaze B2", + "sourceHash": "fbd0038aadaa" + }, + "web.routes.admin_settings.backup_destinations": { + "defaultMessage": "Destinations de sauvegarde", + "description": "Admin settings: title of the backup destinations section card for one cluster; keep it short", + "sourceHash": "6facc55bbf94" + }, + "web.routes.admin_settings.backup_destinations_are_stored_on_the": { + "defaultMessage": "Les destinations de sauvegarde sont stockées sur le cluster de charge de travail qui exécute vos serveurs.", + "description": "Admin settings: subtitle under the select-a-cluster title saying backup destinations live on the workload cluster that runs your servers", + "sourceHash": "2869cfc69e1f" + }, + "web.routes.admin_settings.basic_identity_for_this_gameplane": { + "defaultMessage": "Identité de base de cette instance Gameplane.", + "description": "Admin settings: subtitle under the General title describing the basic identity of this Gameplane instance", + "sourceHash": "8f8a62a2dc82" + }, + "web.routes.admin_settings.be_erased_only_the_credentials": { + "defaultMessage": "être effacé — seuls les identifiants que Gameplane utilise pour s'y connecter sont supprimés. Les sauvegardes existantes restent intactes ; les nouvelles sauvegardes vers cette destination échoueront tant qu'elle n'est pas ajoutée de nouveau.", + "description": "Admin settings: end of the delete dialog text: only Gameplane's credentials for the destination are removed, existing backups stay and new backups fail", + "sourceHash": "37d809768393" + }, + "web.routes.admin_settings.bearer": { + "defaultMessage": "Bearer …", + "description": "Admin settings: grey example value inside the Authorization header field, showing the Bearer prefix; shown only while it is empty", + "sourceHash": "38ab82238200" + }, + "web.routes.admin_settings.built_in_local_accounts_plus_federated": { + "defaultMessage": "Comptes locaux intégrés et fournisseurs d'identité fédérés. Les modifications prennent effet à l'enregistrement, sans redémarrage.", + "description": "Admin settings: subtitle under the Authentication title; says changes take effect on save without a restart", + "sourceHash": "101eecdcffa1" + }, + "web.routes.admin_settings.cancel": { + "defaultMessage": "Annuler", + "description": "Admin settings: button that closes an add or set form without saving; keep it short", + "sourceHash": "19766ed6ccb2" + }, + "web.routes.admin_settings.canonical_base_url_used_in_emails": { + "defaultMessage": "URL de base canonique, utilisée dans les e-mails, les webhooks et les rappels OIDC.", + "description": "Admin settings: hint under the external URL field: the canonical base URL used in emails, webhooks and OIDC callbacks (OIDC: technical term, usually left untranslated)", + "sourceHash": "9e1a43032bc7" + }, + "web.routes.admin_settings.channel": { + "defaultMessage": "Canal", + "description": "Admin settings: label of the update channel row; its value is set by the container image tags", + "sourceHash": "ce4683e7013a" + }, + "web.routes.admin_settings.choose_a_registered_cluster_above_or": { + "defaultMessage": "Choisissez un cluster enregistré ci-dessus, ou enregistrez-en un sur la page Clusters.", + "description": "Admin settings: text in the select-a-cluster card asking the user to pick a cluster above or register one on the Clusters page", + "sourceHash": "3ac79a16731f" + }, + "web.routes.admin_settings.client_id": { + "defaultMessage": "ID client", + "description": "Admin settings: label of the OAuth client ID field for the provider", + "sourceHash": "8726db013948" + }, + "web.routes.admin_settings.client_secret": { + "defaultMessage": "Secret client", + "description": "Admin settings: label of the OAuth client secret field for the provider", + "sourceHash": "4aded5faf156" + }, + "web.routes.admin_settings.configuration_changed": { + "defaultMessage": "La configuration a changé. Copiez vos modifications et rouvrez cette section avant d'enregistrer.", + "description": "Admin settings: error shown when the saved config changed under an open edit form", + "sourceHash": "3927d19b3fe4" + }, + "web.routes.admin_settings.configured": { + "defaultMessage": "Configurée", + "description": "Admin settings: badge on a mod registry whose API key is set", + "sourceHash": "84aebc69a1bf" + }, + "web.routes.admin_settings.configured_lower": { + "defaultMessage": "configuré", + "description": "Admin settings: badge on a backup destination whose password is stored", + "sourceHash": "201582247500" + }, + "web.routes.admin_settings.container_tags_select_release": { + "defaultMessage": "Les étiquettes d'image de conteneur sélectionnent la version de ce panneau.", + "description": "Admin settings: channel note on a standalone install", + "sourceHash": "c1ec11600a58" + }, + "web.routes.admin_settings.corp_sso": { + "defaultMessage": "corp-sso", + "description": "Admin settings: grey example text inside the provider name field, shown only while it is empty", + "sourceHash": "3e549be03b0d" + }, + "web.routes.admin_settings.could_not_refresh_configuration": { + "defaultMessage": "Impossible d'actualiser la configuration. Réessayez l'actualisation avant d'enregistrer.", + "description": "Admin settings: error shown when the config could not be re-fetched before a save", + "sourceHash": "6e11f182bf13" + }, + "web.routes.admin_settings.couldn_t_load_installation_capabilities": { + "defaultMessage": "Impossible de charger les capacités de l'installation.", + "description": "Admin settings: error message in the backup destinations area when installation details fail to load; a retry button follows it", + "sourceHash": "76468b1e3822" + }, + "web.routes.admin_settings.couldn_t_load_registered_clusters": { + "defaultMessage": "Impossible de charger les clusters enregistrés.", + "description": "Admin settings: error message in the backup destinations area when the list of registered clusters fails to load", + "sourceHash": "241f789c2cc1" + }, + "web.routes.admin_settings.data_handling_statement": { + "defaultMessage": "Déclaration de traitement des données", + "description": "Admin settings: link to the telemetry data-handling statement, which opens in a new tab; keep it short", + "sourceHash": "0f26aef79d22" + }, + "web.routes.admin_settings.default_events": { + "defaultMessage": "événements par défaut", + "description": "Admin settings: badge on a notification sink with no events selected, meaning the default event set applies; keep it short", + "sourceHash": "e95bf9fc3ab5" + }, + "web.routes.admin_settings.default_namespace": { + "defaultMessage": "Espace de noms par défaut", + "description": "Admin settings: label of the field for the Kubernetes namespace where new game servers are created by default", + "sourceHash": "25a345ab2b96" + }, + "web.routes.admin_settings.default_role": { + "defaultMessage": "Rôle par défaut", + "description": "Admin settings: label for the default role, the role given to users whose groups match no mapping; also a row label in the Helm provider details", + "sourceHash": "671aa0056a01" + }, + "web.routes.admin_settings.delete": { + "defaultMessage": "Supprimer", + "description": "Admin settings: confirm button of the delete backup destination dialog; keep it short", + "sourceHash": "e2d0a54968ea" + }, + "web.routes.admin_settings.delete_backup_destination": { + "defaultMessage": "Supprimer la destination de sauvegarde ?", + "description": "Admin settings: title of the confirmation dialog that asks whether to delete a backup destination", + "sourceHash": "64e89f1715e9" + }, + "web.routes.admin_settings.delete_destination": { + "defaultMessage": "Supprimer {name}", + "description": "Admin settings: aria-label of the button that deletes a backup destination; {name} is the destination name", + "sourceHash": "3cf8146e529f" + }, + "web.routes.admin_settings.delete_provider": { + "defaultMessage": "Supprimer le fournisseur {name}", + "description": "Admin settings: aria-label of the button that deletes an identity provider; {name} is the provider name", + "sourceHash": "43341bf7839c" + }, + "web.routes.admin_settings.delete_sink_name": { + "defaultMessage": "Supprimer le canal {name}", + "description": "Admin settings: aria-label of the button that deletes a notification sink; {name} is the sink name", + "sourceHash": "2261ce96df75" + }, + "web.routes.admin_settings.deliver_server_health_and_backup": { + "defaultMessage": "Envoyez l'état des serveurs et les événements de sauvegarde et de restauration vers Discord, Slack, ntfy, e-mail ou des webhooks. Les identifiants sont saisis lors de l'ajout d'un canal et stockés dans un Secret étiqueté, dans l'espace de noms du plan de contrôle.", + "description": "Admin settings: subtitle under Notifications: sinks receive server health and backup events; credentials are entered when adding a sink and stored as a labelled Secret", + "sourceHash": "ab112b66ac2b" + }, + "web.routes.admin_settings.delivery_failed": { + "defaultMessage": "échec de l'envoi", + "description": "Admin settings: fallback error when a notification test delivery fails", + "sourceHash": "9275abea043a" + }, + "web.routes.admin_settings.deny_sign_in": { + "defaultMessage": "Refuser la connexion", + "description": "Admin settings: option in the default role dropdown that refuses sign-in when no group matches; keep it short", + "sourceHash": "f42eede24d69" + }, + "web.routes.admin_settings.disable_extended_telemetry": { + "defaultMessage": "Désactiver la télémétrie étendue", + "description": "Admin settings: aria-label of the extended telemetry switch when it is on", + "sourceHash": "d9f9455a86fd" + }, + "web.routes.admin_settings.disable_sink_name": { + "defaultMessage": "Désactiver le canal {name}", + "description": "Admin settings: aria-label of the switch that turns a notification sink off; {name} is the sink name", + "sourceHash": "a08292450634" + }, + "web.routes.admin_settings.disable_telemetry": { + "defaultMessage": "Désactiver la télémétrie", + "description": "Admin settings: aria-label of the basic telemetry switch when it is on", + "sourceHash": "14ab343e54fd" + }, + "web.routes.admin_settings.disabled": { + "defaultMessage": "Désactivée", + "description": "Admin settings: badge showing the telemetry destination is disabled; keep it short", + "sourceHash": "75081b593d15" + }, + "web.routes.admin_settings.display_name_login_button": { + "defaultMessage": "Nom affiché (bouton de connexion)", + "description": "Admin settings: label of the field for the name shown on the sign-in button for this provider", + "sourceHash": "e05d69f0061c" + }, + "web.routes.admin_settings.enable_extended_telemetry": { + "defaultMessage": "Activer la télémétrie étendue", + "description": "Admin settings: aria-label of the extended telemetry switch when it is off", + "sourceHash": "51c77329fe13" + }, + "web.routes.admin_settings.enable_sink_name": { + "defaultMessage": "Activer le canal {name}", + "description": "Admin settings: aria-label of the switch that turns a notification sink on; {name} is the sink name", + "sourceHash": "11dbf2cd5c9b" + }, + "web.routes.admin_settings.enable_telemetry": { + "defaultMessage": "Activer la télémétrie", + "description": "Admin settings: aria-label of the basic telemetry switch when it is off", + "sourceHash": "115cf3dddfdb" + }, + "web.routes.admin_settings.enabled": { + "defaultMessage": "Activé", + "description": "Admin settings: status badge on a provider row showing the provider is turned on; keep it short", + "sourceHash": "92c1cdfdf4cb" + }, + "web.routes.admin_settings.events_failures_and_recovery_are_pre": { + "defaultMessage": "Événements (les échecs et les rétablissements sont présélectionnés)", + "description": "Admin settings: heading above the event checkboxes of a notification sink; failures and recovery are selected by default", + "sourceHash": "09b021ed3695" + }, + "web.routes.admin_settings.every_oidc_user_currently_signs_in_with": { + "defaultMessage": "Tous les utilisateurs OIDC se connectent actuellement avec le rôle par défaut {defaultRole}. Deux façons de modifier cela : ajouter des associations dans Surcharges de correspondance des rôles ci-dessous — elles s'appliquent dès la prochaine connexion, sans redémarrage ni nouvelle exécution de Helm — ou les définir à l'installation avec api.oidc.roleMappings.admin / .operator / .viewer dans vos valeurs Helm.", + "description": "Admin settings: sentence with defaultRole in p", + "sourceHash": "fdbc2033150b" + }, + "web.routes.admin_settings.exactly_what_the_next_report_contains": { + "defaultMessage": "Contenu exact du prochain rapport. Lecture seule.", + "description": "Admin settings: text under the preview heading saying the shown content is exactly what the next report contains and is read-only", + "sourceHash": "9d55b08c0eba" + }, + "web.routes.admin_settings.external_url": { + "defaultMessage": "URL externe", + "description": "Admin settings: label of the field for the public base URL of this instance", + "sourceHash": "8a4a1117fc22" + }, + "web.routes.admin_settings.extra_oauth_scopes_beyond": { + "defaultMessage": "Étendues OAuth supplémentaires, en plus de", + "description": "Admin settings: opening words of the hint under the scopes field, followed by example scope names and a note that scopes are space- or comma-separated", + "sourceHash": "86d8ed746bfe" + }, + "web.routes.admin_settings.failed_to_load_configuration_refresh_to": { + "defaultMessage": "Échec du chargement de la configuration. Actualisez la page pour réessayer.", + "description": "Admin settings: error text in a card when the configuration cannot be fetched; asks the user to refresh the page", + "sourceHash": "1f948598307f" + }, + "web.routes.admin_settings.failed_to_store_a_secret": { + "defaultMessage": "Échec de l'enregistrement d'un Secret", + "description": "Admin settings: fallback error when a Secret write fails", + "sourceHash": "67b5438abe01" + }, + "web.routes.admin_settings.first_it_forms_the_provider_s_oidc": { + "defaultMessage": "en premier : elle forme l'URL de redirection OIDC du fournisseur.", + "description": "Admin settings: end of the warning sentence saying the external URL forms the provider's OIDC redirect URL (OIDC: technical term, usually left untranslated)", + "sourceHash": "f97cf1613e84" + }, + "web.routes.admin_settings.from_address": { + "defaultMessage": "Adresse de l'expéditeur", + "description": "Admin settings: label of the field for the sender address of email notifications", + "sourceHash": "d465bcfa3695" + }, + "web.routes.admin_settings.full_admin_access": { + "defaultMessage": "Accès administrateur complet", + "description": "Admin settings: warning heading shown when groups are about to be mapped to the admin role", + "sourceHash": "5c1752207d69" + }, + "web.routes.admin_settings.gameplane": { + "defaultMessage": "Gameplane", + "description": "Admin settings: row label for the Gameplane version in the About list; keep it short", + "sourceHash": "ed36c32c8e77" + }, + "web.routes.admin_settings.gameplane_admins": { + "defaultMessage": "gameplane-admins", + "description": "Admin settings: grey example group name inside the admin groups field, shown only while it is empty", + "sourceHash": "0bba3d812ef2" + }, + "web.routes.admin_settings.gameplane_backup_repo": { + "defaultMessage": "gameplane-backup-repo", + "description": "Admin settings: grey example name inside the destination name field, shown only while it is empty", + "sourceHash": "3070a369c217" + }, + "web.routes.admin_settings.gameplane_example_com": { + "defaultMessage": "gameplane@example.com", + "description": "Admin settings: grey example sender address inside the from address field, shown only while it is empty", + "sourceHash": "0269196ddda3" + }, + "web.routes.admin_settings.gameplane_local_backup_destination_true": { + "defaultMessage": "gameplane.local/backup-destination=true", + "description": "Admin settings: the Secret label example in the backup destination hint", + "sourceHash": "457b27a7595b" + }, + "web.routes.admin_settings.gameplane_operators": { + "defaultMessage": "gameplane-operators", + "description": "Admin settings: grey example group name inside the operator groups field, shown only while it is empty", + "sourceHash": "e8216a1d3c22" + }, + "web.routes.admin_settings.gameplane_version_number_of_servers_and": { + "defaultMessage": "Version de Gameplane, nombre de serveurs et nombre de modèles.", + "description": "Admin settings: hint under the basic metrics switch listing what is sent: the Gameplane version, number of servers and number of templates", + "sourceHash": "6063edea33b0" + }, + "web.routes.admin_settings.gameplane_viewers": { + "defaultMessage": "gameplane-viewers", + "description": "Admin settings: grey example group name inside the viewer groups field, shown only while it is empty", + "sourceHash": "1ef766b1f0c0" + }, + "web.routes.admin_settings.general": { + "defaultMessage": "Général", + "description": "Admin settings: title of the General section card that holds the instance identity fields; keep it short", + "sourceHash": "c910d474dcd7" + }, + "web.routes.admin_settings.general_external_url": { + "defaultMessage": "Général → URL externe", + "description": "Admin settings: setting path shown in monospace inside the warning, pointing to the External URL field of the General section", + "sourceHash": "5b5f74de5630" + }, + "web.routes.admin_settings.generic_oidc": { + "defaultMessage": "OIDC générique", + "description": "Admin settings: option in the provider kind dropdown for a generic OIDC provider; keep it short", + "sourceHash": "f4b9a3888454" + }, + "web.routes.admin_settings.github_com_has_no_oidc_discovery_for": { + "defaultMessage": "GitHub.com ne prend pas en charge la découverte OIDC pour la connexion des utilisateurs : exécutez un pont OIDC tel que Dex et saisissez son URL d'émetteur ci-dessous.", + "description": "Admin settings: small note under the provider kind dropdown saying GitHub.com needs an OIDC bridge such as Dex, with its issuer URL entered below", + "sourceHash": "7925e19ce1f9" + }, + "web.routes.admin_settings.github_via_oidc_bridge": { + "defaultMessage": "GitHub (via un pont OIDC)", + "description": "Admin settings: option in the provider kind dropdown for GitHub, reached through an OIDC bridge; keep it short", + "sourceHash": "8ecd6dd077f2" + }, + "web.routes.admin_settings.google": { + "defaultMessage": "Google", + "description": "Admin settings: option in the provider kind dropdown for Google sign-in; keep it short", + "sourceHash": "ce770667e5f9" + }, + "web.routes.admin_settings.groups": { + "defaultMessage": "groups", + "description": "Admin settings: the scope name groups, shown in the scopes hint and as the grey placeholder of the groups claim field (technical term, usually left untranslated)", + "sourceHash": "4ed379d418bb" + }, + "web.routes.admin_settings.groups_added_here_will_be_mapped_to_the": { + "defaultMessage": "Les groupes ajoutés ici seront associés au rôle administrateur et disposeront d'un contrôle total du cluster dès leur prochaine connexion. Vous devrez confirmer avant l'enregistrement.", + "description": "Admin settings: warning text explaining that groups added here get full cluster control from their next login and must be confirmed before saving", + "sourceHash": "23da95eb5a49" + }, + "web.routes.admin_settings.groups_claim": { + "defaultMessage": "Revendication de groupes", + "description": "Admin settings: row label for the groups claim in the Helm provider details; keep it short", + "sourceHash": "eee6faa8c49a" + }, + "web.routes.admin_settings.groups_claim_and_default_role_are": { + "defaultMessage": "La revendication de groupes et le rôle par défaut proviennent des mêmes valeurs Helm et ne sont pas modifiables ici.", + "description": "Admin settings: note under the provider heading: groups claim and default role come from Helm values and cannot be edited here", + "sourceHash": "84d99eb2e133" + }, + "web.routes.admin_settings.groups_claim_name": { + "defaultMessage": "groups", + "description": "Admin settings: the default groups claim name", + "sourceHash": "4ed379d418bb" + }, + "web.routes.admin_settings.groups_claim_optional": { + "defaultMessage": "Revendication de groupes (facultatif)", + "description": "Admin settings: label of the optional field naming the token claim that carries the user's group memberships", + "sourceHash": "2df6239b4a4a" + }, + "web.routes.admin_settings.helm_configured_admin_mapping": { + "defaultMessage": "Association administrateur configurée via Helm", + "description": "Admin settings: warning heading shown when the admin role is mapped through Helm values", + "sourceHash": "77215c3850ce" + }, + "web.routes.admin_settings.helm_seeded_oidc_provider": { + "defaultMessage": "Fournisseur OIDC initialisé via Helm", + "description": "Admin settings: title of the card showing the OIDC provider set up through Helm values (OIDC: technical term, usually left untranslated)", + "sourceHash": "592900c36ee8" + }, + "web.routes.admin_settings.helm_seeded_role_mappings": { + "defaultMessage": "Associations de rôles initialisées via Helm", + "description": "Admin settings: label above the role mappings seeded from Helm values", + "sourceHash": "4f8a461fb4e6" + }, + "web.routes.admin_settings.hidden_from_mods_browser": { + "defaultMessage": "Masqué dans le navigateur de mods", + "description": "Admin settings: registry status when no key is configured and the provider is hidden from the Mods browser", + "sourceHash": "333954e36ee1" + }, + "web.routes.admin_settings.https_idp_example_com": { + "defaultMessage": "https://idp.example.com", + "description": "Admin settings: grey example text inside the issuer URL field, shown only while it is empty", + "sourceHash": "6e2977ae8231" + }, + "web.routes.admin_settings.id_token_claim_holding_the_user_s_group": { + "defaultMessage": "Revendication du jeton d'identité contenant les appartenances aux groupes de l'utilisateur ; par défaut {groups}.", + "description": "Admin settings: help text under the groups claim field; {groups} is the default claim name groups", + "sourceHash": "1d7df8808188" + }, + "web.routes.admin_settings.informational_only_reflects_helm": { + "defaultMessage": "Information seulement — reflète {code} depuis vos valeurs Helm.", + "description": "Admin settings: channel note when managed by Helm; {code} is the updates.channel value name", + "sourceHash": "0e1693b709fb" + }, + "web.routes.admin_settings.install_id": { + "defaultMessage": "ID d'installation", + "description": "Admin settings: label of the install ID row in the telemetry section", + "sourceHash": "19ff1c8c7933" + }, + "web.routes.admin_settings.install_id_replaced_on": { + "defaultMessage": "ID d'installation remplacé le", + "description": "Admin settings: status line starting with the date the install ID was replaced; the explanation follows it", + "sourceHash": "8647bb4c45de" + }, + "web.routes.admin_settings.instance_name": { + "defaultMessage": "Nom de l'instance", + "description": "Admin settings: label of the text field for the instance name, which shows on the Cluster page and in notifications", + "sourceHash": "0bf3adab0777" + }, + "web.routes.admin_settings.issuer_url": { + "defaultMessage": "URL de l'émetteur", + "description": "Admin settings: label of the field for the issuer URL of the identity provider", + "sourceHash": "4d03ba8a838f" + }, + "web.routes.admin_settings.kind": { + "defaultMessage": "Type", + "description": "Admin settings: label of the dropdown that chooses the provider kind, such as generic OIDC, Google or GitHub", + "sourceHash": "f5387f9bb6ed" + }, + "web.routes.admin_settings.kubernetes": { + "defaultMessage": "Kubernetes", + "description": "Admin settings: row label for the Kubernetes version in the About list, shown for the local cluster only; keep it short", + "sourceHash": "a37d07fe3004" + }, + "web.routes.admin_settings.last_attempt_failed_no_success": { + "defaultMessage": "La dernière tentative a échoué, une nouvelle tentative aura lieu. Aucun rapport n'a encore été envoyé.", + "description": "Admin settings: telemetry status after a failed report when no report was ever delivered", + "sourceHash": "97fdb3745032" + }, + "web.routes.admin_settings.last_attempt_failed_with_success": { + "defaultMessage": "La dernière tentative a échoué, une nouvelle tentative aura lieu. Dernier rapport envoyé avec succès : {when}.", + "description": "Admin settings: telemetry status after a failed report when an earlier one succeeded; {when} is the time of that report", + "sourceHash": "3133779ba2ea" + }, + "web.routes.admin_settings.last_report_sent_on": { + "defaultMessage": "Dernier rapport envoyé avec succès le {when}.", + "description": "Admin settings: telemetry status after a successful report; {when} is the time of that report or 'an earlier attempt'", + "sourceHash": "5350fe0c53b2" + }, + "web.routes.admin_settings.license": { + "defaultMessage": "Licence", + "description": "Admin settings: row label for the license in the About list; keep it short", + "sourceHash": "c011d6097bfb" + }, + "web.routes.admin_settings.like": { + "defaultMessage": "— par exemple", + "description": "Admin settings: the words for example in the scopes hint, introducing the example scope named groups", + "sourceHash": "b78fd82d1467" + }, + "web.routes.admin_settings.loading_clusters": { + "defaultMessage": "Chargement des clusters…", + "description": "Admin settings: loading text in the backup destinations area while the list of registered clusters loads", + "sourceHash": "fc84c68d8cd9" + }, + "web.routes.admin_settings.loading_configuration": { + "defaultMessage": "Chargement de la configuration…", + "description": "Admin settings: loading text shown in a card while the configuration is fetched", + "sourceHash": "bc8fd86dcffd" + }, + "web.routes.admin_settings.loading_destinations": { + "defaultMessage": "Chargement des destinations…", + "description": "Admin settings: loading text in the backup destinations card while the list is fetched", + "sourceHash": "b78eda5f3fc7" + }, + "web.routes.admin_settings.loading_installation": { + "defaultMessage": "Chargement de l'installation…", + "description": "Admin settings: loading text in the backup destinations area while installation details are fetched", + "sourceHash": "9b3317b01b4b" + }, + "web.routes.admin_settings.mail_example_com": { + "defaultMessage": "mail.example.com", + "description": "Admin settings: grey example host name inside the SMTP host field, shown only while it is empty", + "sourceHash": "406200cfd46d" + }, + "web.routes.admin_settings.map_idp_groups_comma_separated_to": { + "defaultMessage": "Associez les groupes IdP (séparés par des virgules) aux rôles du tableau de bord. Le rôle le plus élevé correspondant à un utilisateur l'emporte (administrateur > opérateur > lecteur), et les rôles se resynchronisent à chaque connexion dès qu'une association est définie.", + "description": "Admin settings: explanation under the role mapping heading: comma-separated IdP groups map to dashboard roles, the highest match wins and roles resync on each login", + "sourceHash": "283f8ba2acbf" + }, + "web.routes.admin_settings.mod_registries": { + "defaultMessage": "Registres de mods", + "description": "Admin settings: title of the Mod registries card that lists mod registries which may need an API key; keep it short", + "sourceHash": "189e782e24ab" + }, + "web.routes.admin_settings.more_events_count": { + "defaultMessage": "+{count}", + "description": "Admin settings: chip showing how many more events a notification sink has beyond the two shown; {count} is that number", + "sourceHash": "ecadd87ff0e5" + }, + "web.routes.admin_settings.name": { + "defaultMessage": "Nom", + "description": "Admin settings: label of the name field, a short identifier for the provider or sink; the same label is used in the add-sink form", + "sourceHash": "dcd1d5223f73" + }, + "web.routes.admin_settings.name_dns_label": { + "defaultMessage": "Nom (étiquette DNS)", + "description": "Admin settings: label of the destination name field, which must be a DNS label of lowercase letters, digits and dashes (DNS label: technical term, usually left untranslated)", + "sourceHash": "b86b69eeca06" + }, + "web.routes.admin_settings.needs_secret": { + "defaultMessage": "Secret requis", + "description": "Admin settings: warning badge on a notification sink row showing its credentials Secret is missing; keep it short", + "sourceHash": "050990b08475" + }, + "web.routes.admin_settings.next_report_json": { + "defaultMessage": "JSON du prochain rapport", + "description": "Admin settings: accessible name of the scrollable block that shows the next telemetry report as JSON", + "sourceHash": "f6777be116b9" + }, + "web.routes.admin_settings.next_report_preview": { + "defaultMessage": "Aperçu du prochain rapport", + "description": "Admin settings: heading of the block that previews the next telemetry report", + "sourceHash": "412ca4e41ef8" + }, + "web.routes.admin_settings.no_backup_destinations_configured_add": { + "defaultMessage": "Aucune destination de sauvegarde configurée. Ajoutez-en une pour activer les instantanés.", + "description": "Admin settings: empty-state text in the backup destinations card when none exist, asking the user to add one to enable snapshots", + "sourceHash": "60412eaecc14" + }, + "web.routes.admin_settings.no_data_of_either_kind_is_sent_and_no": { + "defaultMessage": "Aucune donnée n'est envoyée et aucune destination de télémétrie n'est contactée, quels que soient les réglages ci-dessous. Seul un opérateur peut modifier cela, lors de l'installation.", + "description": "Admin settings: text under the disabled-telemetry heading: no data is sent, no destination is contacted, and only an operator can change this at install time", + "sourceHash": "58921964162f" + }, + "web.routes.admin_settings.no_destination": { + "defaultMessage": "Aucune destination", + "description": "Admin settings: badge shown when no telemetry destination is configured; keep it short", + "sourceHash": "e67e03dade4e" + }, + "web.routes.admin_settings.no_groups_mapped_nobody_signs_in_as": { + "defaultMessage": "Aucun groupe associé — personne ne se connecte en tant que {role} via OIDC.", + "description": "Admin settings: sentence with role in div", + "sourceHash": "947ad3b19a0c" + }, + "web.routes.admin_settings.no_identity_providers_configured_yet": { + "defaultMessage": "Aucun fournisseur d'identité configuré pour le moment.", + "description": "Admin settings: empty-state text in the Authentication section shown when no identity provider has been added", + "sourceHash": "6d823cedf8ab" + }, + "web.routes.admin_settings.no_install_id_while_extended_metrics": { + "defaultMessage": "Aucun ID d'installation tant que les métriques étendues sont désactivées.", + "description": "Admin settings: hint shown in place of the install ID while extended metrics are off", + "sourceHash": "b8f9c99b7474" + }, + "web.routes.admin_settings.no_mapping": { + "defaultMessage": "(aucune association)", + "description": "Admin settings: first option in the default role dropdown, meaning no default role is set; keep it short", + "sourceHash": "a1e522ad6243" + }, + "web.routes.admin_settings.no_notification_sinks_configured": { + "defaultMessage": "Aucun canal de notification configuré.", + "description": "Admin settings: empty-state text in the Notifications section shown when no sink exists", + "sourceHash": "b94211018ee3" + }, + "web.routes.admin_settings.no_oidc_role_mappings_yet": { + "defaultMessage": "Aucune association de rôles OIDC pour le moment", + "description": "Admin settings: heading shown when the OIDC provider has no role mappings yet; the text below says how to add them", + "sourceHash": "2fdd999b86c3" + }, + "web.routes.admin_settings.no_password": { + "defaultMessage": "sans mot de passe", + "description": "Admin settings: badge on a backup destination with no stored password", + "sourceHash": "916f37ba7eb4" + }, + "web.routes.admin_settings.no_report_sent_yet": { + "defaultMessage": "Aucun rapport envoyé pour le moment.", + "description": "Admin settings: telemetry status before any report was sent", + "sourceHash": "08bb2dede349" + }, + "web.routes.admin_settings.no_telemetry_destination_configured": { + "defaultMessage": "Aucune destination de télémétrie configurée.", + "description": "Admin settings: heading of the telemetry box shown when no telemetry destination is configured", + "sourceHash": "164932ea15b3" + }, + "web.routes.admin_settings.not": { + "defaultMessage": "pas", + "description": "Admin settings: the word not, shown in bold in the delete dialog text to stress the repository is not erased", + "sourceHash": "254bb97b57f1" + }, + "web.routes.admin_settings.not_configured": { + "defaultMessage": "Non configurée", + "description": "Admin settings: badge on a mod registry whose API key is not set", + "sourceHash": "dd1841d29502" + }, + "web.routes.admin_settings.nothing_is_sent_an_operator_can_set_a": { + "defaultMessage": "Rien n'est envoyé. Un opérateur peut définir une destination personnalisée ou fournie avec l'installation.", + "description": "Admin settings: text under the no-destination heading: nothing is sent, and an operator can set a destination at install time", + "sourceHash": "39902db57f7e" + }, + "web.routes.admin_settings.nothing_to_preview_disabled": { + "defaultMessage": "Rien à prévisualiser : la télémétrie est désactivée par l'opérateur.", + "description": "Admin settings: shown in place of the telemetry preview when the operator disabled telemetry", + "sourceHash": "c4ff5415803c" + }, + "web.routes.admin_settings.nothing_to_preview_metrics_off": { + "defaultMessage": "Rien à prévisualiser : les métriques d'utilisation de base sont désactivées.", + "description": "Admin settings: shown in place of the telemetry preview when basic usage metrics are off", + "sourceHash": "ee6498d33516" + }, + "web.routes.admin_settings.nothing_to_preview_no_destination": { + "defaultMessage": "Rien à prévisualiser : aucune destination n'est configurée.", + "description": "Admin settings: shown in place of the telemetry preview when no destination is configured", + "sourceHash": "eb24a38bf4f5" + }, + "web.routes.admin_settings.notifications": { + "defaultMessage": "Notifications", + "description": "Admin settings: title of the Notifications section card that lists notification sinks; keep it short", + "sourceHash": "788011833a5a" + }, + "web.routes.admin_settings.oidc_configured_via_helm_manage_in": { + "defaultMessage": "oidc · configuré via Helm — à gérer dans values.yaml", + "description": "Admin settings: small line under a Helm-configured provider name giving its kind (OIDC, technical term, usually left untranslated) and saying it is managed in values.yaml", + "sourceHash": "3b5d5fdc35cf" + }, + "web.routes.admin_settings.openid_profile_email": { + "defaultMessage": "openid profile email", + "description": "Admin settings: example default OAuth scope names shown in monospace in the scopes hint (technical term, usually left untranslated)", + "sourceHash": "1435213d6a98" + }, + "web.routes.admin_settings.operator": { + "defaultMessage": "Opérateur", + "description": "Admin settings: option in the default role dropdown for the operator role; keep it short", + "sourceHash": "291101a07fe9" + }, + "web.routes.admin_settings.operator_groups": { + "defaultMessage": "Groupes opérateurs", + "description": "Admin settings: label of the field listing the identity-provider groups that map to the operator role", + "sourceHash": "23dbd178abfe" + }, + "web.routes.admin_settings.ops_example_com": { + "defaultMessage": "ops@example.com", + "description": "Admin settings: grey example recipient addresses inside the recipients field, shown only while it is empty", + "sourceHash": "af3c82544f64" + }, + "web.routes.admin_settings.optional_names_this_install_on_the": { + "defaultMessage": "Facultatif. Nomme cette installation sur la page Cluster et dans les notifications.", + "description": "Admin settings: hint under the instance name field saying the name is optional and where it appears", + "sourceHash": "61ca8eb8b6bf" + }, + "web.routes.admin_settings.override_the_helm_seeded_oidc_group": { + "defaultMessage": "Remplacez ici les associations de groupes OIDC initialisées via Helm, par rôle. Une modification prend effet à la prochaine connexion d'un utilisateur concerné, sans redémarrage ni réinstallation.", + "description": "Admin settings: subtitle under Role mapping overrides: change the Helm-seeded group mappings per role; changes apply at the next login without a restart", + "sourceHash": "f151cbd37b94" + }, + "web.routes.admin_settings.password_optional": { + "defaultMessage": "Mot de passe (facultatif)", + "description": "Admin settings: label of the optional SMTP password field", + "sourceHash": "a4a3dc943aa0" + }, + "web.routes.admin_settings.platform_wide_configuration_for_this": { + "defaultMessage": "Configuration globale de cette instance Gameplane.", + "description": "Admin settings: description under the page title saying these settings apply to the whole Gameplane instance", + "sourceHash": "7d4dc9c520ef" + }, + "web.routes.admin_settings.port_default_587": { + "defaultMessage": "Port (587 par défaut)", + "description": "Admin settings: label of the SMTP port field, which states 587 is the default port", + "sourceHash": "e2af02a60ec6" + }, + "web.routes.admin_settings.provider": { + "defaultMessage": "Fournisseur", + "description": "Admin settings: heading of the provider details block in the Helm-seeded card; keep it short", + "sourceHash": "472590ae974d" + }, + "web.routes.admin_settings.provider_disabled_state": { + "defaultMessage": "Désactivé", + "description": "Admin settings: state of an identity provider or toggle that is off", + "sourceHash": "75081b593d15" + }, + "web.routes.admin_settings.provider_enabled_state": { + "defaultMessage": "Activé", + "description": "Admin settings: state of an identity provider or toggle that is on", + "sourceHash": "92c1cdfdf4cb" + }, + "web.routes.admin_settings.provider_issuer_suffix": { + "defaultMessage": "· {issuer}", + "description": "Admin settings: issuer shown after the provider type; {issuer} is the issuer URL", + "sourceHash": "efcec45939c0" + }, + "web.routes.admin_settings.provider_kind": { + "defaultMessage": "Type de fournisseur", + "description": "Admin settings: accessible name of the provider kind dropdown; its options are generic OIDC, Google and GitHub", + "sourceHash": "d21bd26696da" + }, + "web.routes.admin_settings.provider_secret_suffix": { + "defaultMessage": "· {ref}", + "description": "Admin settings: Secret reference shown after the provider type when no issuer is set; {ref} is the Secret name", + "sourceHash": "5e2a8202bff3" + }, + "web.routes.admin_settings.random_and_anonymous_reset_it_to_start": { + "defaultMessage": "Aléatoire et anonyme. Réinitialisez-le pour recommencer les rapports sous un nouvel ID ; l'ancien ID n'est plus jamais envoyé.", + "description": "Admin settings: hint under the install ID: it is random and anonymous; resetting starts reporting under a new ID and the old one is never sent again", + "sourceHash": "b0925cba2c64" + }, + "web.routes.admin_settings.read_only_snapshot_of_the_identity": { + "defaultMessage": "Instantané en lecture seule du fournisseur d'identité configuré via les valeurs Helm api.oidc.* lors de l'installation. Les paramètres de connexion ne sont pas modifiables ici ; les associations de rôles peuvent être remplacées ci-dessous.", + "description": "Admin settings: subtitle under the Helm-seeded provider title: a read-only snapshot taken at install time; role mappings can be overridden below", + "sourceHash": "998c709b7001" + }, + "web.routes.admin_settings.registries_that_require_an_api_key_stay": { + "defaultMessage": "Les registres qui exigent une clé d'API restent masqués dans le navigateur de mods tant qu'une clé n'est pas configurée.", + "description": "Admin settings: subtitle under Mod registries: registries that need an API key stay hidden from the Mods browser until a key is set", + "sourceHash": "b38172d70154" + }, + "web.routes.admin_settings.remove": { + "defaultMessage": "Retirer", + "description": "Admin settings: button that removes the API key entry of a module registry; keep it short", + "sourceHash": "c3812fc4acb8" + }, + "web.routes.admin_settings.replace": { + "defaultMessage": "Remplacer", + "description": "Admin settings: button on a configured module registry that opens the dialog to replace its API key; keep it short", + "sourceHash": "95e154398a4b" + }, + "web.routes.admin_settings.replace_verb": { + "defaultMessage": "Remplacer", + "description": "Admin settings: verb on the button that replaces a stored API key", + "sourceHash": "95e154398a4b" + }, + "web.routes.admin_settings.repository_password": { + "defaultMessage": "Mot de passe du dépôt", + "description": "Admin settings: label of the password field for the Restic backup repository", + "sourceHash": "81d3f57e734c" + }, + "web.routes.admin_settings.reset_failed": { + "defaultMessage": "Échec de la réinitialisation", + "description": "Admin settings: fallback error when resetting a role mapping fails", + "sourceHash": "089cb45b70a0" + }, + "web.routes.admin_settings.reset_helm_default_role": { + "defaultMessage": "Rétablir la valeur Helm par défaut (association du rôle {role})", + "description": "Admin settings: accessible name of the button that resets one role's mapping to the Helm default; {role} is the role name", + "sourceHash": "f2e7504cd1c4" + }, + "web.routes.admin_settings.reset_id": { + "defaultMessage": "Réinitialiser l'ID", + "description": "Admin settings: button that asks to reset the install ID, and the confirm button of the reset dialog; keep it short", + "sourceHash": "b274b0ee9939" + }, + "web.routes.admin_settings.reset_install_id": { + "defaultMessage": "Réinitialiser l'ID d'installation ?", + "description": "Admin settings: title of the confirmation dialog for resetting the random install ID", + "sourceHash": "8d9aca15a8b1" + }, + "web.routes.admin_settings.reset_install_id_failed": { + "defaultMessage": "Impossible de réinitialiser l'ID d'installation", + "description": "Admin settings: fallback error text under the install ID when resetting it fails", + "sourceHash": "56e120a9e0f8" + }, + "web.routes.admin_settings.reset_to_helm_default": { + "defaultMessage": "Rétablir la valeur Helm par défaut", + "description": "Admin settings: button that resets one role's mapping to the Helm default; keep it short", + "sourceHash": "e2691e5458d8" + }, + "web.routes.admin_settings.restic_repositories_for_snapshots": { + "defaultMessage": "Dépôts Restic pour les instantanés. Stockés sous forme de Secrets Kubernetes étiquetés dans l'espace de noms configuré.", + "description": "Admin settings: subtitle under Backup destinations: Restic repositories for snapshots, stored as labelled Kubernetes Secrets (Restic: technical term, usually left untranslated)", + "sourceHash": "f9cc3a86d695" + }, + "web.routes.admin_settings.restic_url": { + "defaultMessage": "URL Restic", + "description": "Admin settings: label of the field for the Restic repository URL (Restic: technical term, usually left untranslated)", + "sourceHash": "47101b958b16" + }, + "web.routes.admin_settings.restic_url_hint": { + "defaultMessage": "Stocké sous forme de Secret étiqueté {secret}. Formats d'URL Restic : {s3}, {b2}, {azure}, etc.", + "description": "Admin settings: hint under the backup destination form; {secret} is the Secret label, {s3}, {b2} and {azure} are example Restic URL formats", + "sourceHash": "dcac27923db9" + }, + "web.routes.admin_settings.role_mapping": { + "defaultMessage": "Association de rôles", + "description": "Admin settings: heading of the part of the provider form that maps identity-provider groups to dashboard roles", + "sourceHash": "4c39d18166a4" + }, + "web.routes.admin_settings.role_mapping_group": { + "defaultMessage": "Association du rôle {role}", + "description": "Admin settings: accessible name of the group for one role's mapping; {role} is the capitalized role name", + "sourceHash": "7ef2eeb65e19" + }, + "web.routes.admin_settings.role_mapping_overrides": { + "defaultMessage": "Remplacements d'association des rôles", + "description": "Admin settings: title of the card where role mappings can be overridden per role in the dashboard; keep it short", + "sourceHash": "b8f60c3b0d86" + }, + "web.routes.admin_settings.s3_host_bucket": { + "defaultMessage": "s3:host/bucket", + "description": "Admin settings: example Restic URL format for S3", + "sourceHash": "a6be84568575" + }, + "web.routes.admin_settings.s3_s3_example_com_gameplane_bucket": { + "defaultMessage": "s3:s3.example.com/gameplane-bucket", + "description": "Admin settings: grey example Restic URL inside the URL field, pointing at an S3 bucket (S3: technical term, usually left untranslated)", + "sourceHash": "f54cf2cae3ea" + }, + "web.routes.admin_settings.save": { + "defaultMessage": "Enregistrer", + "description": "Admin settings: button that saves the module registry API key; keep it short", + "sourceHash": "1509f561f241" + }, + "web.routes.admin_settings.save_changes": { + "defaultMessage": "Enregistrer les modifications", + "description": "Admin settings: button that saves the changes in this section; keep it short", + "sourceHash": "dd0ae7a5cbcf" + }, + "web.routes.admin_settings.save_changes_first_tests": { + "defaultMessage": "Enregistrez d'abord les modifications : les tests s'exécutent sur la configuration enregistrée", + "description": "Admin settings: tooltip on a sink test button while the form has unsaved changes", + "sourceHash": "fb2119014417" + }, + "web.routes.admin_settings.save_destination": { + "defaultMessage": "Enregistrer la destination", + "description": "Admin settings: button label that saves a new backup destination", + "sourceHash": "28bc183e20fc" + }, + "web.routes.admin_settings.save_failed": { + "defaultMessage": "Échec de l'enregistrement", + "description": "Admin settings: fallback error when saving a section fails", + "sourceHash": "53ad6f999b1f" + }, + "web.routes.admin_settings.save_role_mappings": { + "defaultMessage": "Enregistrer les associations de rôles", + "description": "Admin settings: button that saves the role mapping overrides; keep it short", + "sourceHash": "40a6123945df" + }, + "web.routes.admin_settings.saved": { + "defaultMessage": "Enregistré", + "description": "Admin settings: status text beside a save button confirming the changes were saved; keep it short", + "sourceHash": "b5c120b316c2" + }, + "web.routes.admin_settings.saved_changes_take_effect_on_next_login": { + "defaultMessage": "Les modifications enregistrées prennent effet à la prochaine connexion des utilisateurs concernés, sans redémarrage ni réinstallation.", + "description": "Admin settings: footer note of the role mapping overrides card: saved changes apply at the next login for affected users, with no restart needed", + "sourceHash": "c3c2f1b7ecdf" + }, + "web.routes.admin_settings.saving": { + "defaultMessage": "Enregistrement…", + "description": "Admin settings: status text beside a save button while changes are being saved; keep it short", + "sourceHash": "23e39291d613" + }, + "web.routes.admin_settings.saving_destination": { + "defaultMessage": "Enregistrement…", + "description": "Admin settings: button label while a backup destination is being saved", + "sourceHash": "23e39291d613" + }, + "web.routes.admin_settings.scopes_optional": { + "defaultMessage": "Étendues (facultatif)", + "description": "Admin settings: label of the optional field for extra OAuth scopes", + "sourceHash": "f4d09ff950d6" + }, + "web.routes.admin_settings.select_a_workload_cluster": { + "defaultMessage": "Sélectionnez un cluster de charge de travail", + "description": "Admin settings: title of the placeholder card shown when no workload cluster is selected for backup destinations", + "sourceHash": "135026530ce2" + }, + "web.routes.admin_settings.send_basic_usage_metrics": { + "defaultMessage": "Envoyer les métriques d'utilisation de base", + "description": "Admin settings: label of the switch that turns on basic usage metrics", + "sourceHash": "1d47b39dfe9e" + }, + "web.routes.admin_settings.send_extended_usage_metrics": { + "defaultMessage": "Envoyer les métriques d'utilisation étendues", + "description": "Admin settings: label of the switch that turns on extended usage metrics; basic metrics must be on first", + "sourceHash": "e3c34ff0993c" + }, + "web.routes.admin_settings.send_test": { + "defaultMessage": "Envoyer un test", + "description": "Admin settings: button that sends a test message to a notification sink; keep it short", + "sourceHash": "22d7b11d40c9" + }, + "web.routes.admin_settings.sending_to_host": { + "defaultMessage": "Envoi vers {host}", + "description": "Admin settings: sentence with host in div", + "sourceHash": "d3dd4811764d" + }, + "web.routes.admin_settings.set": { + "defaultMessage": "Définissez", + "description": "Admin settings: opening word of the warning saying the external URL must be set first, followed by the setting path and the rest of the sentence", + "sourceHash": "b6f6f3ad07b3" + }, + "web.routes.admin_settings.set_api_key": { + "defaultMessage": "Définir la clé d'API", + "description": "Admin settings: button on an unconfigured module registry that opens the dialog to add its API key; keep it short", + "sourceHash": "71592a583990" + }, + "web.routes.admin_settings.set_verb": { + "defaultMessage": "Définir", + "description": "Admin settings: verb on the button that sets an API key for the first time", + "sourceHash": "b6f6f3ad07b3" + }, + "web.routes.admin_settings.sink_kind": { + "defaultMessage": "Type de canal", + "description": "Admin settings: accessible name of the dropdown that picks the notification sink type, such as Discord, Slack, ntfy, email or webhook", + "sourceHash": "dbe9d19db78b" + }, + "web.routes.admin_settings.sink_kind_discord": { + "defaultMessage": "Discord", + "description": "Admin settings: name of the Discord notification sink kind", + "sourceHash": "053bc65874ad" + }, + "web.routes.admin_settings.sink_kind_ntfy": { + "defaultMessage": "ntfy", + "description": "Admin settings: name of the ntfy notification sink kind; ntfy is a product name", + "sourceHash": "1d8ada3e1183" + }, + "web.routes.admin_settings.sink_kind_slack": { + "defaultMessage": "Slack", + "description": "Admin settings: name of the Slack notification sink kind", + "sourceHash": "b27fb38ba323" + }, + "web.routes.admin_settings.sink_kind_smtp": { + "defaultMessage": "SMTP", + "description": "Admin settings: name of the SMTP email notification sink kind", + "sourceHash": "c7011f116d0f" + }, + "web.routes.admin_settings.sink_kind_webhook": { + "defaultMessage": "Webhook", + "description": "Admin settings: name of the generic webhook notification sink kind", + "sourceHash": "4814f62c108d" + }, + "web.routes.admin_settings.sink_name_example": { + "defaultMessage": "team-alerts", + "description": "Admin settings: example sink name in the sink name hint", + "sourceHash": "c672e7d5e168" + }, + "web.routes.admin_settings.sink_name_hint": { + "defaultMessage": "Identifiant court en minuscules (lettres, chiffres et tirets) utilisé pour nommer le canal et son Secret, par exemple {example}.", + "description": "Admin settings: hint under the sink name field; {example} is an example sink name", + "sourceHash": "822f48db0b7c" + }, + "web.routes.admin_settings.sink_no_secret": { + "defaultMessage": "· aucun Secret configuré", + "description": "Admin settings: shown under a notification sink that has no Secret configured", + "sourceHash": "c68a45b1fa70" + }, + "web.routes.admin_settings.sink_secret_ref": { + "defaultMessage": "· Secret : {ref}", + "description": "Admin settings: secret reference shown under a notification sink; {ref} is the Secret name", + "sourceHash": "a4dbb41c1061" + }, + "web.routes.admin_settings.smtp_host": { + "defaultMessage": "Hôte SMTP", + "description": "Admin settings: label of the SMTP host field for the email notification sink", + "sourceHash": "ab328f83926c" + }, + "web.routes.admin_settings.smtp_tls_mode": { + "defaultMessage": "Mode TLS SMTP", + "description": "Admin settings: accessible name of the SMTP TLS mode dropdown; its options are STARTTLS, implicit TLS and none", + "sourceHash": "5c4bbd4601b1" + }, + "web.routes.admin_settings.space_or_comma_separated": { + "defaultMessage": ". Séparées par des espaces ou des virgules.", + "description": "Admin settings: end of the scopes hint telling the user to separate several scopes with spaces or commas", + "sourceHash": "abe9ed01e436" + }, + "web.routes.admin_settings.strong_unique_passphrase": { + "defaultMessage": "Phrase de passe forte et unique", + "description": "Admin settings: grey hint inside the repository password field, suggesting a strong unique passphrase; shown only while it is empty", + "sourceHash": "89102932b4fa" + }, + "web.routes.admin_settings.team_alerts": { + "defaultMessage": "team-alerts", + "description": "Admin settings: grey example sink name inside the name field of the add-sink form, shown only while it is empty", + "sourceHash": "c672e7d5e168" + }, + "web.routes.admin_settings.telemetry": { + "defaultMessage": "Télémétrie", + "description": "Admin settings: title of the Telemetry section card; keep it short", + "sourceHash": "e87c57f59542" + }, + "web.routes.admin_settings.telemetry_bundled_badge": { + "defaultMessage": "Intégré", + "description": "Admin settings: badge for the telemetry receiver bundled with this install", + "sourceHash": "79d3a1f108e8" + }, + "web.routes.admin_settings.telemetry_bundled_detail": { + "defaultMessage": "Le récepteur fourni avec cette installation. Les rapports restent dans votre cluster.", + "description": "Admin settings: telemetry destination description for the receiver bundled with this install", + "sourceHash": "ffdeabc20377" + }, + "web.routes.admin_settings.telemetry_custom_badge": { + "defaultMessage": "Personnalisé", + "description": "Admin settings: badge for a custom telemetry destination", + "sourceHash": "494ca78f7374" + }, + "web.routes.admin_settings.telemetry_custom_detail": { + "defaultMessage": "Destination personnalisée définie par votre opérateur. Rien n'est envoyé au projet Gameplane.", + "description": "Admin settings: telemetry destination description for a custom destination set by the operator", + "sourceHash": "3f533843eb71" + }, + "web.routes.admin_settings.telemetry_default_badge": { + "defaultMessage": "Par défaut du projet", + "description": "Admin settings: badge for the project default telemetry destination", + "sourceHash": "e8cb80e5c5cb" + }, + "web.routes.admin_settings.telemetry_default_detail": { + "defaultMessage": "Le service de télémétrie du projet Gameplane.", + "description": "Admin settings: telemetry destination description when reports go to the project default service", + "sourceHash": "d22a4c4f69bc" + }, + "web.routes.admin_settings.telemetry_extended_contents": { + "defaultMessage": "Ajoute la version de Kubernetes, la distribution, l'architecture CPU, la tranche de nombre de nœuds, le nombre de serveurs par module officiel, les fonctionnalités optionnelles utilisées et un ID d'installation aléatoire.", + "description": "Admin settings: describes what extended telemetry adds to each report", + "sourceHash": "be271755c98c" + }, + "web.routes.admin_settings.telemetry_extended_needs_basic": { + "defaultMessage": "Activez les métriques d'utilisation de base pour activer les métriques d'utilisation étendues.", + "description": "Admin settings: hint under the extended telemetry switch while basic metrics are off", + "sourceHash": "121ee3c6db20" + }, + "web.routes.admin_settings.telemetry_is_disabled_by_the_operator": { + "defaultMessage": "La télémétrie est désactivée par l'opérateur.", + "description": "Admin settings: heading of the warning box saying telemetry was turned off by the operator", + "sourceHash": "d24d81133fb1" + }, + "web.routes.admin_settings.telemetry_reset_id_description": { + "defaultMessage": "Cela remplace votre ID d'installation aléatoire {id} par un nouveau. L'ancien ID n'est plus jamais envoyé, et la destination ne peut pas relier l'ancien et le nouvel ID. Le prochain rapport est envoyé sous le nouvel ID.", + "description": "Admin settings: confirm dialog text for resetting the install ID; {id} is the current install ID, or empty", + "sourceHash": "74b3f64e2097" + }, + "web.routes.admin_settings.test_delivered": { + "defaultMessage": "✓ envoyé", + "description": "Admin settings: status shown after a notification test was delivered", + "sourceHash": "57e04ca432e4" + }, + "web.routes.admin_settings.the_destination_reported_it_was_in_use": { + "defaultMessage": " : la destination a signalé que cet ID est déjà utilisé par une autre clé.", + "description": "Admin settings: end of the install-ID status line, saying the destination reported the ID was already in use by another key", + "sourceHash": "ea94b80906dd" + }, + "web.routes.admin_settings.the_group_s_on_the_admin_row_above_were": { + "defaultMessage": "Les groupes de la ligne Administrateur ci-dessus ont été associés au rôle administrateur via les valeurs Helm, et non via le tableau de bord ; il n'y a donc pas d'étape de confirmation ici. Vérifiez qu'ils ne contiennent que des comptes de confiance : toute personne qui s'y trouve obtient un accès administrateur complet.", + "description": "Admin settings: warning text: the groups on the Admin row were mapped to admin through Helm, so there is no confirmation step; check they hold only trusted accounts", + "sourceHash": "507465a5191f" + }, + "web.routes.admin_settings.the_repository_at_this_destination_will": { + "defaultMessage": "Le dépôt de cette destination ne sera", + "description": "Admin settings: opening words of the delete dialog text, followed by the emphasised word not and the rest of the sentence", + "sourceHash": "990bcfc1de94" + }, + "web.routes.admin_settings.this_gameplane_build": { + "defaultMessage": "Ce build de Gameplane.", + "description": "Admin settings: subtitle under the About title describing the Gameplane build shown below", + "sourceHash": "97822ca6a390" + }, + "web.routes.admin_settings.tk": { + "defaultMessage": "tk_…", + "description": "Admin settings: grey example token format inside the access token field, shown only while it is empty", + "sourceHash": "33de448a4fb3" + }, + "web.routes.admin_settings.tls": { + "defaultMessage": "TLS", + "description": "Admin settings: label of the dropdown that sets how the SMTP connection uses TLS; TLS is a technical term, usually left untranslated", + "sourceHash": "d1c18ec0b109" + }, + "web.routes.admin_settings.to_comma_separated": { + "defaultMessage": "À (séparées par des virgules)", + "description": "Admin settings: label of the field listing recipient addresses, separated by commas", + "sourceHash": "02e7d8c41bfa" + }, + "web.routes.admin_settings.to_confirm": { + "defaultMessage": "pour confirmer.", + "description": "Admin settings: last words of the line asking the user to type the destination name to confirm; the name is shown in monospace before them", + "sourceHash": "a44014d57331" + }, + "web.routes.admin_settings.topic_url": { + "defaultMessage": "URL du topic", + "description": "Admin settings: label of the URL field for an ntfy sink", + "sourceHash": "07ce8b8a99bc" + }, + "web.routes.admin_settings.type": { + "defaultMessage": "Saisissez", + "description": "Admin settings: first word of the line in the delete dialog asking the user to type the destination name to confirm", + "sourceHash": "baaddf70fb5d" + }, + "web.routes.admin_settings.updates": { + "defaultMessage": "Mises à jour", + "description": "Admin settings: title of the Updates section card about how this installation is upgraded; keep it short", + "sourceHash": "22e2bada8f1c" + }, + "web.routes.admin_settings.updates_channel": { + "defaultMessage": "updates.channel", + "description": "Admin settings: the updates.channel Helm value name", + "sourceHash": "93bbd2ea2b44" + }, + "web.routes.admin_settings.updates_subtitle_helm": { + "defaultMessage": "Gameplane est mis à jour via Helm (tags d'image ou versions de chart).", + "description": "Admin settings: subtitle of the Updates card when the install is managed by Helm", + "sourceHash": "098e07b82a49" + }, + "web.routes.admin_settings.updates_subtitle_standalone": { + "defaultMessage": "Mettez à jour les images de conteneurs du panneau et de l'API pour mettre à niveau cette installation.", + "description": "Admin settings: subtitle of the Updates card on a standalone install", + "sourceHash": "83fef04a82fd" + }, + "web.routes.admin_settings.username_optional": { + "defaultMessage": "Nom d'utilisateur (facultatif)", + "description": "Admin settings: label of the optional SMTP username field", + "sourceHash": "e946bdfe2535" + }, + "web.routes.admin_settings.viewer": { + "defaultMessage": "Lecteur", + "description": "Admin settings: option in the default role dropdown for the viewer role; keep it short", + "sourceHash": "678bfa6af48b" + }, + "web.routes.admin_settings.viewer_groups": { + "defaultMessage": "Groupes lecteurs", + "description": "Admin settings: label of the field listing the identity-provider groups that map to the viewer role", + "sourceHash": "f720dd06195f" + }, + "web.routes.admin_settings.webhook_url": { + "defaultMessage": "URL du webhook", + "description": "Admin settings: label of the URL field for a webhook-style sink", + "sourceHash": "84805a7574a8" + }, + "web.routes.admin_settings.where_new_gameservers_land_by_default": { + "defaultMessage": "Où les nouveaux GameServers sont créés par défaut.", + "description": "Admin settings: hint under the default namespace field saying new GameServers are created in that namespace by default", + "sourceHash": "d3ef70e5985f" + }, + "web.routes.audit_log.access": { + "defaultMessage": "Accès", + "description": "Audit log: column heading for the access column of each event; keep it short", + "sourceHash": "ec5ba0abb717" + }, + "web.routes.audit_log.action": { + "defaultMessage": "Action", + "description": "Audit log: column heading for the action performed in each event; keep it short", + "sourceHash": "64cff1319d2f" + }, + "web.routes.audit_log.actor": { + "defaultMessage": "Acteur", + "description": "Audit log: column heading for the user who made each request; keep it short", + "sourceHash": "449995c4fe8f" + }, + "web.routes.audit_log.all_methods": { + "defaultMessage": "Toutes les méthodes", + "description": "Audit log: first option in the method dropdown, meaning no method filter; keep it short", + "sourceHash": "e601c19e94db" + }, + "web.routes.audit_log.audit_log": { + "defaultMessage": "Journal d'audit", + "description": "Audit log: page title at the top of the audit log screen; keep it short", + "sourceHash": "e4d36f9a4e22" + }, + "web.routes.audit_log.chain_verified": { + "defaultMessage": "Chaîne d'audit vérifiée, aucune altération détectée", + "description": "Audit log: banner shown when the audit hash chain verifies", + "sourceHash": "ccec7b8027f8" + }, + "web.routes.audit_log.end_of_log": { + "defaultMessage": "Fin du journal", + "description": "Audit log: label shown when every event has been loaded", + "sourceHash": "9ad38ab25b6e" + }, + "web.routes.audit_log.export_csv": { + "defaultMessage": "Exporter en CSV", + "description": "Audit log: button that downloads the currently filtered audit events as a CSV file; keep it short", + "sourceHash": "91f71c14c8d6" + }, + "web.routes.audit_log.filter_by_actor": { + "defaultMessage": "Filtrer par acteur…", + "description": "Audit log: grey placeholder of the search field that filters events by the actor who made the request", + "sourceHash": "ce781bb35ef5" + }, + "web.routes.audit_log.http_status": { + "defaultMessage": "HTTP {status}", + "description": "Audit log: tooltip with the HTTP status code; {status} is the code", + "sourceHash": "d9df8d88fd76" + }, + "web.routes.audit_log.integrity_check_failed": { + "defaultMessage": "Échec de la vérification d'intégrité : la chaîne est rompue à l'événement n° {id}", + "description": "Audit log: banner text when the chain breaks; {id} is the first event id that failed verification", + "sourceHash": "f2f73928e49a" + }, + "web.routes.audit_log.integrity_status_unavailable": { + "defaultMessage": "État d'intégrité indisponible", + "description": "Audit log: banner shown when the integrity check could not run", + "sourceHash": "d97e59bbe584" + }, + "web.routes.audit_log.ip": { + "defaultMessage": "IP", + "description": "Audit log: column heading for the IP address of each request; keep it short", + "sourceHash": "bf5505174a73" + }, + "web.routes.audit_log.length_of_length2_loaded": { + "defaultMessage": "{length} sur {length2} chargés", + "description": "Audit log: sentence with length, length2 in div", + "sourceHash": "85c9537bb9a8" + }, + "web.routes.audit_log.load_more": { + "defaultMessage": "Charger plus", + "description": "Audit log: button that loads the next page of events", + "sourceHash": "ac8991ef0101" + }, + "web.routes.audit_log.loading": { + "defaultMessage": "Chargement…", + "description": "Audit log: label of the load-more button while the next page loads", + "sourceHash": "ba3bbbe10d8b" + }, + "web.routes.audit_log.method": { + "defaultMessage": "Méthode", + "description": "Audit log: column heading for the HTTP method of each request; keep it short", + "sourceHash": "52a0f9b65b27" + }, + "web.routes.audit_log.method_and_path": { + "defaultMessage": "{method} {path}", + "description": "Audit log: tooltip with the raw HTTP method and request path; {method} is the method, {path} the path", + "sourceHash": "cf1a308a828f" + }, + "web.routes.audit_log.more_available": { + "defaultMessage": "· d'autres disponibles", + "description": "Audit log: suffix after the loaded count when older events can still be loaded", + "sourceHash": "0f8cd7d2a86e" + }, + "web.routes.audit_log.mutating_control_plane_requests_newest": { + "defaultMessage": "Requêtes de modification du plan de contrôle, les plus récentes en premier.", + "description": "Audit log: description under the title saying the list holds mutating control-plane requests, newest first", + "sourceHash": "62483425eef8" + }, + "web.routes.audit_log.no_audit_events_yet": { + "defaultMessage": "Aucun événement d'audit pour l'instant.", + "description": "Audit log: empty state when no events have been recorded", + "sourceHash": "bc542d8b313b" + }, + "web.routes.audit_log.no_events_match_the_active_filters": { + "defaultMessage": "Aucun événement ne correspond aux filtres actifs.", + "description": "Audit log: empty state when the active filters hide every event", + "sourceHash": "e1e1a2acad8a" + }, + "web.routes.audit_log.page_size_pagesize_older_events_load_on": { + "defaultMessage": "Taille de page {pageSize}. Les événements plus anciens se chargent à la demande.", + "description": "Audit log: sentence with pageSize in div", + "sourceHash": "f1f8fc62b39f" + }, + "web.routes.audit_log.re_check": { + "defaultMessage": "Vérifier à nouveau", + "description": "Audit log: button that re-runs the integrity check", + "sourceHash": "24fcca00ea50" + }, + "web.routes.audit_log.refresh": { + "defaultMessage": "Actualiser", + "description": "Audit log: button that reloads the audit events; keep it short", + "sourceHash": "0e9161011702" + }, + "web.routes.audit_log.status_all": { + "defaultMessage": "Tous", + "description": "Audit log: status filter tab that shows every event", + "sourceHash": "a52ace420f21" + }, + "web.routes.audit_log.status_filter": { + "defaultMessage": "Filtre de statut", + "description": "Audit log: accessible name of the status tabs that filter the audit events by status", + "sourceHash": "9bfe8b184f0c" + }, + "web.routes.audit_log.status_tab": { + "defaultMessage": "{label} · {count}", + "description": "Audit log: status filter tab; {label} is the filter name, {count} is the number of events in it", + "sourceHash": "4a81dd0ed1d3" + }, + "web.routes.audit_log.time": { + "defaultMessage": "Heure", + "description": "Audit log: column heading for when each event happened; keep it short", + "sourceHash": "33b93476cf59" + }, + "web.routes.backups.active": { + "defaultMessage": "Active", + "description": "Backups: column heading for the switch that turns a schedule on or off; keep it short", + "sourceHash": "92340695899b" + }, + "web.routes.backups.admin_settings": { + "defaultMessage": "paramètres d'administration", + "description": "Backups: link text inside the no-destination note that opens the admin settings screen; keep it short", + "sourceHash": "18cf9d4a1929" + }, + "web.routes.backups.back_up_now": { + "defaultMessage": "Sauvegarder maintenant", + "description": "Backups: button that opens the dialog for a one-off snapshot, and the heading of that dialog; keep it short", + "sourceHash": "02a2840b598b" + }, + "web.routes.backups.backup": { + "defaultMessage": "Sauvegarde", + "description": "Backups: column heading for the backup a restore came from; keep it short", + "sourceHash": "838557924a5c" + }, + "web.routes.backups.backups": { + "defaultMessage": "Sauvegardes", + "description": "Backups: page title at the top of the backups screen, also the name of the backups tab and its table; keep it short", + "sourceHash": "3334fee2c77f" + }, + "web.routes.backups.cancel": { + "defaultMessage": "Annuler", + "description": "Backups: button that closes the back-up-now dialog without starting a snapshot; keep it short", + "sourceHash": "19766ed6ccb2" + }, + "web.routes.backups.completed": { + "defaultMessage": "Terminée", + "description": "Backups: column heading for when a backup or restore completed; keep it short", + "sourceHash": "22a970d2e5b1" + }, + "web.routes.backups.cron": { + "defaultMessage": "Cron", + "description": "Backups: column heading for the cron expression of each schedule; keep it short", + "sourceHash": "dd9d24965dbe" + }, + "web.routes.backups.delete": { + "defaultMessage": "Supprimer", + "description": "Backups: button that starts deleting a schedule row, and the confirm button of the delete schedule dialog; keep it short", + "sourceHash": "e2d0a54968ea" + }, + "web.routes.backups.delete_schedule": { + "defaultMessage": "Supprimer la planification ?", + "description": "Backups: title of the dialog asking whether to delete a schedule", + "sourceHash": "88d3a1fd7204" + }, + "web.routes.backups.destination": { + "defaultMessage": "Destination", + "description": "Backups: accessible name of the backup destination dropdown in the back-up-now dialog", + "sourceHash": "293d404a500f" + }, + "web.routes.backups.destination_options": { + "defaultMessage": "Options de destination", + "description": "Backups: accessible name of the list of backup destinations shown when the destination dropdown is open", + "sourceHash": "ce07235d7b0c" + }, + "web.routes.backups.last_run": { + "defaultMessage": "Dernière exécution", + "description": "Backups: column heading for when a schedule last ran; keep it short", + "sourceHash": "512a48218ba2" + }, + "web.routes.backups.loading_backups": { + "defaultMessage": "Chargement des sauvegardes…", + "description": "Backups: loading text on the backups tab while backup data is fetched", + "sourceHash": "20af4b11362c" + }, + "web.routes.backups.loading_restores": { + "defaultMessage": "Chargement des restaurations…", + "description": "Backups: loading text on the restores tab while restore data is fetched", + "sourceHash": "91c493444b1d" + }, + "web.routes.backups.loading_schedules": { + "defaultMessage": "Chargement des planifications…", + "description": "Backups: loading text on the schedules tab while schedule data is fetched", + "sourceHash": "77307df7176c" + }, + "web.routes.backups.message": { + "defaultMessage": "Message", + "description": "Backups: column heading for the status message of a restore; keep it short", + "sourceHash": "2f77668a9dfb" + }, + "web.routes.backups.name": { + "defaultMessage": "Nom", + "description": "Backups: column heading for the name of each backup, schedule or restore; keep it short", + "sourceHash": "dcd1d5223f73" + }, + "web.routes.backups.new_schedule_for": { + "defaultMessage": "Nouvelle planification pour", + "description": "Backups: label above the server dropdown of the new schedule form", + "sourceHash": "621d91aacf9f" + }, + "web.routes.backups.next_run": { + "defaultMessage": "Prochaine exécution", + "description": "Backups: column heading for when a schedule runs next; keep it short", + "sourceHash": "b3c0ab96930c" + }, + "web.routes.backups.no_backup_data_available": { + "defaultMessage": "Aucune donnée de sauvegarde disponible.", + "description": "Backups: empty state when the backup list could not be loaded", + "sourceHash": "a34534f8a6b1" + }, + "web.routes.backups.no_backup_destinations_configured_add": { + "defaultMessage": "Aucune destination de sauvegarde configurée. Ajoutez-en une dans", + "description": "Backups: opening words of the note in the back-up-now dialog shown when no destination exists; the link and closing words follow it", + "sourceHash": "404e6a2883ec" + }, + "web.routes.backups.no_backups_match_the_current_filters": { + "defaultMessage": "Aucune sauvegarde ne correspond aux filtres actuels.", + "description": "Backups: empty state when filters hide every backup", + "sourceHash": "10ee0035a852" + }, + "web.routes.backups.no_backups_yet": { + "defaultMessage": "Aucune sauvegarde pour l'instant. Utilisez \"Sauvegarder maintenant\" pour créer la première.", + "description": "Backups: empty state when no backups exist; quoted text is the Back up now button label", + "sourceHash": "ebea9f42cb3d" + }, + "web.routes.backups.no_restore_data_available": { + "defaultMessage": "Aucune donnée de restauration disponible.", + "description": "Backups: restores empty state when the list could not be loaded", + "sourceHash": "60204e98f66b" + }, + "web.routes.backups.no_restores_have_been_run": { + "defaultMessage": "Aucune restauration n'a été lancée.", + "description": "Backups: restores empty state when no restore has run", + "sourceHash": "b62e76a84eb8" + }, + "web.routes.backups.no_restores_match_the_current_filters": { + "defaultMessage": "Aucune restauration ne correspond aux filtres actuels.", + "description": "Backups: restores empty state when filters hide every restore", + "sourceHash": "88413c8181f3" + }, + "web.routes.backups.no_schedule_data_available": { + "defaultMessage": "Aucune donnée de planification disponible.", + "description": "Backups: schedules empty state when the list could not be loaded", + "sourceHash": "678512a368f8" + }, + "web.routes.backups.no_schedules_configured_yet": { + "defaultMessage": "Aucune planification configurée pour l'instant.", + "description": "Backups: schedules empty state when none exist", + "sourceHash": "c8dd77068ffb" + }, + "web.routes.backups.no_schedules_match_the_current_filters": { + "defaultMessage": "Aucune planification ne correspond aux filtres actuels.", + "description": "Backups: schedules empty state when filters hide every schedule", + "sourceHash": "3edf59f081dd" + }, + "web.routes.backups.phase": { + "defaultMessage": "Phase", + "description": "Backups: column heading for the phase or state of a backup or restore; keep it short", + "sourceHash": "46342ec1eec9" + }, + "web.routes.backups.restores": { + "defaultMessage": "Restaurations", + "description": "Backups: name of the restores tab, also its table label; keep it short", + "sourceHash": "1fec560be878" + }, + "web.routes.backups.run_a_one_off_snapshot_outside_any": { + "defaultMessage": "Lancer un instantané ponctuel, hors de toute planification.", + "description": "Backups: text in the back-up-now dialog explaining that it runs a one-off snapshot outside any schedule", + "sourceHash": "50dee41ed126" + }, + "web.routes.backups.run_snapshot": { + "defaultMessage": "Lancer l'instantané", + "description": "Backups: label of the button that starts a snapshot now", + "sourceHash": "47052705260d" + }, + "web.routes.backups.schedule_active": { + "defaultMessage": "Planification active", + "description": "Backups: accessible name of the switch that turns a schedule on or off", + "sourceHash": "c1030b80ebae" + }, + "web.routes.backups.schedules": { + "defaultMessage": "Planifications", + "description": "Backups: name of the schedules tab, also its table label and the title of its panel; keep it short", + "sourceHash": "221ff19c904c" + }, + "web.routes.backups.select_a_destination": { + "defaultMessage": "Sélectionner une destination…", + "description": "Backups: grey placeholder of the destination dropdown in the back-up-now dialog, asking the user to pick a backup destination", + "sourceHash": "d9dc0577737d" + }, + "web.routes.backups.select_a_server": { + "defaultMessage": "Sélectionner un serveur…", + "description": "Backups: grey placeholder of the server dropdown, asking the user to pick a server in the backup dialog or the new schedule form", + "sourceHash": "0da661c11f32" + }, + "web.routes.backups.select_a_server_2": { + "defaultMessage": "Sélectionner un serveur", + "description": "Backups: accessible name of the server dropdown in the new schedule form", + "sourceHash": "8a953dbed756" + }, + "web.routes.backups.select_server_with_backup_access": { + "defaultMessage": "Sélectionnez un serveur disposant d'un accès aux sauvegardes.", + "description": "Backups: error shown when the chosen server has no backup permission", + "sourceHash": "70e2f870a6aa" + }, + "web.routes.backups.server": { + "defaultMessage": "Serveur", + "description": "Backups: column heading for the server a row belongs to, and the accessible name of the server dropdown in the backup dialog; keep it short", + "sourceHash": "aef7de28d529" + }, + "web.routes.backups.server_option_label": { + "defaultMessage": "{name} · {target}", + "description": "Backups: server picker option; {name} is the server name, {target} is its cluster and namespace", + "sourceHash": "36ad3069f380" + }, + "web.routes.backups.server_options": { + "defaultMessage": "Options de serveur", + "description": "Backups: accessible name of the list of servers shown when the server dropdown is open", + "sourceHash": "c20ffd9997a8" + }, + "web.routes.backups.size": { + "defaultMessage": "Taille", + "description": "Backups: column heading for the size of each backup; keep it short", + "sourceHash": "1af851907331" + }, + "web.routes.backups.snapshots_schedules_and_restores_across": { + "defaultMessage": "Instantanés, planifications et restaurations dans vos emplacements autorisés.", + "description": "Backups: subtitle under the page title saying the screen covers snapshots, schedules and restores across your authorised locations", + "sourceHash": "0c4e1cd453d9" + }, + "web.routes.backups.starting": { + "defaultMessage": "Démarrage…", + "description": "Backups: label of the Run snapshot button while the request is in flight", + "sourceHash": "bbe5fc3b9ef3" + }, + "web.routes.backups.tab_backups": { + "defaultMessage": "Sauvegardes", + "description": "Backups: label of the backups tab", + "sourceHash": "3334fee2c77f" + }, + "web.routes.backups.tab_restores": { + "defaultMessage": "Restaurations", + "description": "Backups: label of the restores tab", + "sourceHash": "1fec560be878" + }, + "web.routes.backups.tab_schedules": { + "defaultMessage": "Planifications", + "description": "Backups: label of the schedules tab", + "sourceHash": "221ff19c904c" + }, + "web.routes.backups.target": { + "defaultMessage": "Cible", + "description": "Backups: column heading for the target a restore was applied to; keep it short", + "sourceHash": "978354db0c00" + }, + "web.routes.backups.the_cron_schedule_will_be_removed": { + "defaultMessage": "La planification cron sera supprimée. Les sauvegardes existantes sont conservées ; les nouveaux instantanés de cette planification ne seront plus lancés tant qu'elle n'est pas recréée.", + "description": "Backups: text in the delete schedule dialog: the schedule is removed, existing backups stay, and new snapshots stop until it is recreated", + "sourceHash": "f8173ac895d6" + }, + "web.routes.backups.to_enable_snapshots": { + "defaultMessage": "pour activer les instantanés.", + "description": "Backups: closing words of the no-destination note after the admin settings link, ending the sentence about enabling snapshots", + "sourceHash": "165b5542b1be" + }, + "web.routes.backups.trailing_backups": { + "defaultMessage": "{shown} sur {total} {total, plural, one {sauvegarde} other {sauvegardes}}", + "description": "Backups: count shown after the filters on the backups tab; {shown} is the number listed, {total} the number loaded", + "sourceHash": "7812050af9f0" + }, + "web.routes.backups.trailing_restores": { + "defaultMessage": "{shown} sur {total} {total, plural, one {restauration} other {restaurations}}", + "description": "Backups: count shown after the filters on the restores tab; {shown} is the number listed, {total} the number loaded", + "sourceHash": "c20e80b03f67" + }, + "web.routes.backups.trailing_schedules": { + "defaultMessage": "{shown} sur {total} {total, plural, one {planification} other {planifications}}", + "description": "Backups: count shown after the filters on the schedules tab; {shown} is the number listed, {total} the number loaded", + "sourceHash": "eee16e438056" + }, + "web.routes.backups.type_name_to_confirm": { + "defaultMessage": "Saisissez {name} ({target}) pour confirmer.", + "description": "Backups: confirmation hint under the delete-schedule dialog; {name} is the schedule name in monospace, {target} is its cluster and namespace", + "sourceHash": "65f17d606581" + }, + "web.routes.cluster.add_node": { + "defaultMessage": "Ajouter un nœud", + "description": "Cluster: button that creates a node-join token so a new machine can be added to the cluster; keep it short", + "sourceHash": "1ba44456db5e" + }, + "web.routes.cluster.choose_a_registered_cluster_to_view_its": { + "defaultMessage": "Choisissez un cluster enregistré pour afficher son inventaire de nœuds. Enregistrez les clusters de charge de travail sur la page Clusters.", + "description": "Cluster: hint under the empty-state heading saying a registered cluster must be picked to see its nodes and that clusters are registered on the Clusters page", + "sourceHash": "a47ffbb0c7a3" + }, + "web.routes.cluster.cluster": { + "defaultMessage": "Cluster", + "description": "Cluster: page title of the node inventory view at the top of the page; keep it short", + "sourceHash": "c137a7f43e23" + }, + "web.routes.cluster.cluster_default": { + "defaultMessage": "Par défaut du cluster", + "description": "Cluster: badge shown instead of a storage class name when none is set, meaning new volumes use the cluster's default; keep it short", + "sourceHash": "7239777411b6" + }, + "web.routes.cluster.copy": { + "defaultMessage": "Copier", + "description": "Cluster: button that copies the join command to the clipboard; keep it short", + "sourceHash": "e21f935f11d7" + }, + "web.routes.cluster.couldn_t_load_installation_capabilities": { + "defaultMessage": "Impossible de charger les capacités d'installation.", + "description": "Cluster: error message shown when the installation capabilities cannot be fetched; a retry button appears below it", + "sourceHash": "76468b1e3822" + }, + "web.routes.cluster.couldn_t_load_registered_clusters": { + "defaultMessage": "Impossible de charger les clusters enregistrés.", + "description": "Cluster: error message shown when the list of registered clusters fails to load; a retry button appears below it", + "sourceHash": "241f789c2cc1" + }, + "web.routes.cluster.cpu": { + "defaultMessage": "CPU", + "description": "Cluster: label for CPU on a node card, used both for the core count figure and for the CPU usage bar; CPU is a technical term, usually left untranslated", + "sourceHash": "db9a4c7d4c19" + }, + "web.routes.cluster.cpu_cores": { + "defaultMessage": "{cores} cœurs", + "description": "Cluster: node CPU capacity; {cores} is the core count", + "sourceHash": "1eaed4cac057" + }, + "web.routes.cluster.dismiss": { + "defaultMessage": "Ignorer", + "description": "Cluster: button that closes the join-a-node card; keep it short", + "sourceHash": "48845bff334a" + }, + "web.routes.cluster.download_kubeconfig": { + "defaultMessage": "Télécharger le kubeconfig", + "description": "Cluster: button that downloads the cluster's kubeconfig file; keep it short; kubeconfig is a technical term, usually left untranslated", + "sourceHash": "23201c4fd9e3" + }, + "web.routes.cluster.enable_helm_note": { + "defaultMessage": "Activez {setting} dans les valeurs Helm pour générer des jetons d'adhésion de nœud et des kubeconfigs.", + "description": "Cluster: note under the action buttons when operations are disabled; {setting} is the Helm key, shown as code", + "sourceHash": "986949079a0e" + }, + "web.routes.cluster.game_data_storage_class": { + "defaultMessage": "Classe de stockage des données de jeu", + "description": "Cluster: label of the setting that names the storage class used for new game server data volumes; storage class is a technical term, usually left untranslated", + "sourceHash": "d0705e1b1d8d" + }, + "web.routes.cluster.join_a_node": { + "defaultMessage": "Rejoindre un nœud", + "description": "Cluster: heading of the card that shows the command to run on a machine that should join the cluster as a node", + "sourceHash": "3745396e2450" + }, + "web.routes.cluster.loading_clusters": { + "defaultMessage": "Chargement des clusters…", + "description": "Cluster: loading text shown while the list of registered clusters is being fetched, before a cluster can be chosen", + "sourceHash": "fc84c68d8cd9" + }, + "web.routes.cluster.loading_installation": { + "defaultMessage": "Chargement de l'installation…", + "description": "Cluster: loading text shown in place of the node inventory while the installation capabilities are being fetched", + "sourceHash": "9b3317b01b4b" + }, + "web.routes.cluster.loading_nodes_for": { + "defaultMessage": "Chargement des nœuds pour {clusterId}…", + "description": "Cluster: message of LoadingCard; {clusterId} is the cluster id", + "sourceHash": "870243ff3c2d" + }, + "web.routes.cluster.memory": { + "defaultMessage": "Mémoire", + "description": "Cluster: label of the memory usage bar on a node card, showing memory in use against the node's total memory", + "sourceHash": "c3963aedaac6" + }, + "web.routes.cluster.memory_of_capacity": { + "defaultMessage": "sur {capacity}", + "description": "Cluster: node memory capacity shown when usage is unknown; {capacity} is a formatted byte size", + "sourceHash": "81245e5822b0" + }, + "web.routes.cluster.memory_used_of_capacity": { + "defaultMessage": "{used} / {capacity}", + "description": "Cluster: node memory as used / capacity; both are formatted byte sizes", + "sourceHash": "d328f6fb7ea8" + }, + "web.routes.cluster.no_node_data_yet": { + "defaultMessage": "Aucune donnée de nœud pour l'instant.", + "description": "Cluster: empty-state heading shown when no node information has arrived for the cluster; the line below it says what to check", + "sourceHash": "3f00557afc96" + }, + "web.routes.cluster.no_nodes_are_reporting_yet_check_the": { + "defaultMessage": "Aucun nœud ne remonte d'information pour l'instant. Vérifiez la connexion à l'API et à l'opérateur.", + "description": "Cluster: hint under the empty-state heading asking the user to check the API and operator connection because no nodes report yet", + "sourceHash": "4db01473f651" + }, + "web.routes.cluster.node_inventory_for": { + "defaultMessage": "Inventaire des nœuds du cluster sélectionné : {clusterId}.", + "description": "Cluster: page subtitle shown before node data loads; {clusterId} is the cluster id", + "sourceHash": "be8dcecedd63" + }, + "web.routes.cluster.node_status_badge": { + "defaultMessage": "● {status}", + "description": "Cluster: node status badge; {status} is the node status, or Unknown when none is reported", + "sourceHash": "7c9641b1f5c8" + }, + "web.routes.cluster.nodes_load_failed": { + "defaultMessage": "Impossible de charger l'inventaire des nœuds pour {clusterId}. Vérifiez votre accès au cluster et votre connexion.", + "description": "Cluster: error card message when node inventory fails to load; {clusterId} is the cluster id", + "sourceHash": "af488f2e6374" + }, + "web.routes.cluster.op_failed": { + "defaultMessage": "Cette opération a échoué. Consultez les journaux de l'API.", + "description": "Cluster: generic error shown when a cluster operation fails", + "sourceHash": "828667119523" + }, + "web.routes.cluster.op_forbidden": { + "defaultMessage": "Vous n'avez pas la permission de faire cela.", + "description": "Cluster: error shown when the user lacks permission for a cluster operation (HTTP 403)", + "sourceHash": "ff6d26107f4d" + }, + "web.routes.cluster.op_not_enabled": { + "defaultMessage": "Les opérations sur les clusters ne sont pas activées sur cette installation (définissez clusterOps.enabled).", + "description": "Cluster: error shown when the operator has cluster operations turned off (HTTP 501)", + "sourceHash": "ce2c47b25841" + }, + "web.routes.cluster.ops_disabled_hint": { + "defaultMessage": "Les opérations sur les clusters sont désactivées sur cette installation.", + "description": "Cluster: tooltip on the node action buttons when cluster operations are off", + "sourceHash": "b3989fd48af9" + }, + "web.routes.cluster.pods": { + "defaultMessage": "Pods", + "description": "Cluster: label of the pod figure on a node card; the value shows pods in use out of the node's pod capacity, or only the capacity when usage is unknown", + "sourceHash": "9836b5eeec47" + }, + "web.routes.cluster.pods_of_capacity": { + "defaultMessage": "sur {capacity}", + "description": "Cluster: node pod capacity shown when usage is unknown; {capacity} is the pod limit", + "sourceHash": "81245e5822b0" + }, + "web.routes.cluster.pods_used_of_capacity": { + "defaultMessage": "{used}/{capacity}", + "description": "Cluster: node pod count as used/capacity; {used} and {capacity} are numbers", + "sourceHash": "c35992c9f4ae" + }, + "web.routes.cluster.remote_managed_note": { + "defaultMessage": "L'ajout de nœuds et le téléchargement des kubeconfigs sont gérés sur ce cluster distant.", + "description": "Cluster: note under the action buttons on a remote cluster", + "sourceHash": "68ea82583be5" + }, + "web.routes.cluster.role_worker": { + "defaultMessage": "worker", + "description": "Cluster: role label shown for a node that reports no Kubernetes roles", + "sourceHash": "87eba76e7f31" + }, + "web.routes.cluster.run_this_on_the_machine_you_want_to": { + "defaultMessage": "Exécutez ceci sur la machine à rejoindre (jeton valable jusqu'à {expiresAt}) :", + "description": "Cluster: sentence with expiresAt in p", + "sourceHash": "477ed0897f85" + }, + "web.routes.cluster.select_a_workload_cluster": { + "defaultMessage": "Sélectionner un cluster de charge de travail", + "description": "Cluster: heading of the empty-state card shown when no workload cluster is selected", + "sourceHash": "135026530ce2" + }, + "web.routes.cluster.set_at_install_time_via_helm_values_not": { + "defaultMessage": "Défini à l'installation via les valeurs Helm, non modifiable depuis le tableau de bord.", + "description": "Cluster: hint under the storage heading explaining that these values come from Helm values at install time and cannot be edited in the dashboard", + "sourceHash": "4b84b772e23c" + }, + "web.routes.cluster.status_not_ready": { + "defaultMessage": "Non prêt", + "description": "Cluster: node status label for a node that is not ready", + "sourceHash": "9d6ee0d551f5" + }, + "web.routes.cluster.status_ready": { + "defaultMessage": "Prêt", + "description": "Cluster: node status label for a node that is ready", + "sourceHash": "5fa7aac5375c" + }, + "web.routes.cluster.status_unknown": { + "defaultMessage": "Inconnu", + "description": "Cluster: node status shown when the node reports none", + "sourceHash": "b764cdc0eab7" + }, + "web.routes.cluster.storage": { + "defaultMessage": "Stockage", + "description": "Cluster: section heading for the storage settings that are fixed at install time; shown only to users who can read the cluster configuration", + "sourceHash": "a69c4dece144" + }, + "web.routes.cluster.storage_class_unset": { + "defaultMessage": "Non défini : les nouveaux volumes utilisent la StorageClass par défaut du cluster.", + "description": "Cluster: note under the StorageClass description when no StorageClass is set", + "sourceHash": "7dbd3e751bd6" + }, + "web.routes.cluster.storageclass_for_new_gameservers_game": { + "defaultMessage": "StorageClass pour les volumes de données de jeu des nouveaux GameServers : définie par la valeur Helm operator.gameDataStorage.storageClassName et appliquée par l'opérateur à chaque nouveau PVC ; l'API reçoit la même valeur via --game-data-storage-class uniquement pour l'afficher ici.", + "description": "Cluster: explanation under the game data storage class label; a Helm value sets it and it applies to new volumes; StorageClass is a technical term, usually left untranslated", + "sourceHash": "8cac0a4612c8" + }, + "web.routes.cluster.summary": { + "defaultMessage": "{name} · {version} · {ready}/{total} nœuds en bon état", + "description": "Cluster: page subtitle; {name} is the cluster name, {version} the Kubernetes version, {ready} and {total} the node counts", + "sourceHash": "b5652d764a6c" + }, + "web.routes.cluster.uptime": { + "defaultMessage": "Temps de fonctionnement", + "description": "Cluster: label of the uptime figure on a node card, showing how long the node has been running; keep it short", + "sourceHash": "d63ab4711473" + }, + "web.routes.clusters.administration_of_registered_locations": { + "defaultMessage": "Administration des emplacements enregistrés, de leur connectivité et de l'inventaire des nœuds.", + "description": "Clusters: subtitle under the page title saying the page covers registered locations, their connectivity and node inventory", + "sourceHash": "823fbc8b35a4" + }, + "web.routes.clusters.api": { + "defaultMessage": "API :", + "description": "Clusters: label before the API health value on a cluster card, such as Healthy or Unhealthy; the value is colored by its state; keep it short", + "sourceHash": "359ba9514abb" + }, + "web.routes.clusters.ask_administrator_for_access": { + "defaultMessage": "Demandez à votre administrateur l'accès à un cluster enregistré.", + "description": "Clusters: empty-state hint shown to users without cluster-management access", + "sourceHash": "184a0deed015" + }, + "web.routes.clusters.available_status_reports_kubernetes_api": { + "defaultMessage": "{count, plural, one {{count} cluster disponible} other {{count} clusters disponibles}} · Le statut indique la connectivité à l'API Kubernetes, et non l'état du jeu ou de la passerelle.", + "description": "Clusters: count of registered clusters followed by a note that status reflects Kubernetes API connectivity; {count} is the number of clusters", + "sourceHash": "45788e6e33ab" + }, + "web.routes.clusters.central_cluster": { + "defaultMessage": "Cluster central", + "description": "Clusters: note on the cluster card of the built-in local cluster that the panel itself runs in", + "sourceHash": "d15c5fb2e351" + }, + "web.routes.clusters.cluster_administration_requires": { + "defaultMessage": "L'administration des clusters requiert un accès à l'inventaire ou à la gestion des clusters.", + "description": "Clusters: error card shown instead of the cluster list to users who have neither cluster management nor inventory access", + "sourceHash": "f47171d4b801" + }, + "web.routes.clusters.clusters": { + "defaultMessage": "Clusters", + "description": "Clusters: page title at the top of the cluster administration page; keep it short", + "sourceHash": "92d5e4c5cd37" + }, + "web.routes.clusters.couldn_t_load_registered_clusters_try": { + "defaultMessage": "Impossible de charger les clusters enregistrés. Réessayez.", + "description": "Clusters: error card shown when the cluster list fails to load; a retry button appears below it", + "sourceHash": "c2a52a28cfb7" + }, + "web.routes.clusters.couldn_t_remove_the_cluster": { + "defaultMessage": "Impossible de supprimer l'enregistrement du cluster. Réessayez.", + "description": "Clusters: error message in the remove-cluster dialog shown when removing the registration fails; the user can try again", + "sourceHash": "8286e76eb422" + }, + "web.routes.clusters.kubernetes_serverversion": { + "defaultMessage": "Kubernetes {serverVersion}", + "description": "Clusters: sentence with serverVersion in p", + "sourceHash": "ff240e6e1c69" + }, + "web.routes.clusters.last_checked": { + "defaultMessage": "Dernière vérification", + "description": "Clusters: label before the time of the last health check on a cluster card; the relative time follows it", + "sourceHash": "0c0c351a9713" + }, + "web.routes.clusters.loading_clusters": { + "defaultMessage": "Chargement des clusters…", + "description": "Clusters: loading text shown while the list of registered clusters is being fetched", + "sourceHash": "fc84c68d8cd9" + }, + "web.routes.clusters.no_clusters_available": { + "defaultMessage": "Aucun cluster disponible", + "description": "Clusters: heading of the empty-state card shown when no cluster is registered or visible to the user", + "sourceHash": "05c2348fe3bc" + }, + "web.routes.clusters.phase_healthy": { + "defaultMessage": "Sain", + "description": "Clusters: API status label for a cluster the panel reports as healthy", + "sourceHash": "7f1e323b6272" + }, + "web.routes.clusters.phase_unhealthy": { + "defaultMessage": "Défaillant", + "description": "Clusters: API status label for a cluster the panel reports as unhealthy", + "sourceHash": "317b1fbc1e13" + }, + "web.routes.clusters.phase_unknown": { + "defaultMessage": "Inconnu", + "description": "Clusters: API status shown when the cluster reports no phase", + "sourceHash": "b764cdc0eab7" + }, + "web.routes.clusters.register_cluster": { + "defaultMessage": "Enregistrer un cluster", + "description": "Clusters: button at the top right that opens the dialog for registering a new cluster; shown only to users who can manage clusters; keep it short", + "sourceHash": "bed8e2c07c18" + }, + "web.routes.clusters.register_workload_cluster_hint": { + "defaultMessage": "Enregistrez un cluster de charge de travail pour déployer et gérer des serveurs de jeu. Ce panneau peut fonctionner sans cluster local.", + "description": "Clusters: empty-state hint shown to users who can manage clusters", + "sourceHash": "c63274f4c850" + }, + "web.routes.clusters.remove": { + "defaultMessage": "Supprimer", + "description": "Clusters: button on a cluster card that removes its registration; not shown for the local cluster; keep it short", + "sourceHash": "c3812fc4acb8" + }, + "web.routes.clusters.remove_cluster": { + "defaultMessage": "Supprimer le cluster", + "description": "Clusters: confirm button in the remove-cluster dialog, shown in a destructive style; keep it short", + "sourceHash": "c9aef9e7f146" + }, + "web.routes.clusters.remove_cluster_named": { + "defaultMessage": "Supprimer le cluster {name}", + "description": "Clusters: accessible label of the Remove button; {name} is the cluster display name", + "sourceHash": "75614f86b2a7" + }, + "web.routes.clusters.remove_title": { + "defaultMessage": "Supprimer {name} ?", + "description": "Clusters: title of the remove-cluster confirmation; {name} is the cluster name", + "sourceHash": "01c1f0250884" + }, + "web.routes.clusters.removes_this_panel_s_connection_to_the": { + "defaultMessage": "Supprime la connexion de ce panneau au cluster. Les charges de travail en cours restent sur le cluster.", + "description": "Clusters: text in the remove-cluster confirmation dialog saying the panel's connection is removed while running workloads stay on the cluster", + "sourceHash": "de344c4b75e8" + }, + "web.routes.clusters.selected": { + "defaultMessage": "Sélectionné", + "description": "Clusters: small badge on a cluster card marking the cluster currently selected; keep it short", + "sourceHash": "57fd7a0cf33f" + }, + "web.routes.clusters.view_nodes": { + "defaultMessage": "Voir les nœuds", + "description": "Clusters: button on a cluster card that opens the node inventory for that cluster; shown only when the user can view inventory; keep it short", + "sourceHash": "b3f9e14daeb6" + }, + "web.routes.clusters.view_nodes_in": { + "defaultMessage": "Voir les nœuds de {name}", + "description": "Clusters: accessible label of the View nodes button; {name} is the cluster display name", + "sourceHash": "8900b329c953" + }, + "web.routes.clusters.view_servers": { + "defaultMessage": "Voir les serveurs", + "description": "Clusters: button on a cluster card that opens the server list for that cluster; keep it short; its screen-reader name also includes the cluster name", + "sourceHash": "521aceb2f180" + }, + "web.routes.clusters.view_servers_in": { + "defaultMessage": "Voir les serveurs de {name}", + "description": "Clusters: accessible label of the View servers button; {name} is the cluster display name", + "sourceHash": "c929c983e74e" + }, + "web.routes.create_server.50gi": { + "defaultMessage": "50Gi", + "description": "Create server: example placeholder in the storage size field showing the size format; an example value, not a default", + "sourceHash": "049a2bd550a4" + }, + "web.routes.create_server.add_port_mapping": { + "defaultMessage": "Ajouter une correspondance de port", + "description": "Create server: button that adds a new empty tunnel port mapping row to the list; keep it short", + "sourceHash": "9e1b868a66ba" + }, + "web.routes.create_server.address_pool": { + "defaultMessage": "Pool d'adresses", + "description": "Create server: field label for the load balancer address pool the server draws its address from", + "sourceHash": "d741d43d889e" + }, + "web.routes.create_server.address_preference_ignored": { + "defaultMessage": "Préférence d'adresse ignorée", + "description": "Create server: warning title of a notice shown when an address pool or requested address is set but Expose is not LoadBalancer", + "sourceHash": "18f4cc1b9872" + }, + "web.routes.create_server.back": { + "defaultMessage": "Retour", + "description": "Create server: footer button that returns to the previous wizard step; keep it short", + "sourceHash": "76900f1bfd16" + }, + "web.routes.create_server.cancel": { + "defaultMessage": "Annuler", + "description": "Create server: footer button that closes the wizard without creating a server; keep it short", + "sourceHash": "19766ed6ccb2" + }, + "web.routes.create_server.category_all": { + "defaultMessage": "Tous", + "description": "Create server: template category chip that shows every template", + "sourceHash": "a52ace420f21" + }, + "web.routes.create_server.changing_location_starts_a_fresh_draft": { + "defaultMessage": "Changer d'emplacement crée un nouveau brouillon. L'emplacement choisi gère toutes les étapes de création.", + "description": "Create server: hint under the location dropdown warning that switching location starts a fresh draft and the chosen location handles all steps", + "sourceHash": "8d08dd0fc448" + }, + "web.routes.create_server.choose_a_version": { + "defaultMessage": "Choisissez une version", + "description": "Create server: heading of the version step, shown above the list of versions and loaders to pick from", + "sourceHash": "ae8211ceb8be" + }, + "web.routes.create_server.close": { + "defaultMessage": "Fermer", + "description": "Create server: accessible label of the icon-only close button in the wizard header; read by screen readers, not shown as text", + "sourceHash": "7d9eb7acb13e" + }, + "web.routes.create_server.cluster_namespace": { + "defaultMessage": "{cluster} / {namespace}", + "description": "Create server: location choice label; {cluster} is the cluster name and {namespace} the namespace", + "sourceHash": "d2efd80cd0eb" + }, + "web.routes.create_server.comma_or_space_separated": { + "defaultMessage": "Séparées par des virgules ou des espaces.", + "description": "Create server: hint under the Tailscale tags field saying the tags can be separated by commas or spaces", + "sourceHash": "ea7232f5948e" + }, + "web.routes.create_server.continue_to_stepindex": { + "defaultMessage": "Continuer vers {stepIndex}", + "description": "Create server: label of the button that moves to the next wizard step; {stepIndex} is the next step name", + "sourceHash": "98fc1d027602" + }, + "web.routes.create_server.cpu": { + "defaultMessage": "CPU", + "description": "Create server: field label for the CPU amount given to the server; technical term, usually left untranslated", + "sourceHash": "db9a4c7d4c19" + }, + "web.routes.create_server.create_server": { + "defaultMessage": "Créer le serveur", + "description": "Create server: label of the final footer button that submits the wizard and creates the server; keep it short", + "sourceHash": "cb569a5332e6" + }, + "web.routes.create_server.creating": { + "defaultMessage": "Création…", + "description": "Create server: label of the final footer button while the server is being created, shown with a spinner; keep it short", + "sourceHash": "c79ed9492e3c" + }, + "web.routes.create_server.credentials": { + "defaultMessage": "Identifiants *", + "description": "Create server: field label for the tunnel credentials; the asterisk marks the field as required", + "sourceHash": "a7903ceabb28" + }, + "web.routes.create_server.default": { + "defaultMessage": "Par défaut", + "description": "Create server: small badge on a version card marking the default version; keep it short", + "sourceHash": "21b111cbfe6e" + }, + "web.routes.create_server.default_7000_must_be_between_1_and_65535": { + "defaultMessage": "Par défaut 7000. Doit être compris entre 1 et 65535.", + "description": "Create server: hint under the frp server port field giving the default port 7000 and the allowed range from 1 to 65535", + "sourceHash": "0d73cdbaae79" + }, + "web.routes.create_server.delete": { + "defaultMessage": "Supprimer", + "description": "Create server: icon-only button that removes one tunnel port mapping row; read by screen readers; keep it short", + "sourceHash": "e2d0a54968ea" + }, + "web.routes.create_server.description": { + "defaultMessage": "Description", + "description": "Create server: field label for the optional free-text description of the new server", + "sourceHash": "526e0087cc3f" + }, + "web.routes.create_server.docs_creating_game_servers": { + "defaultMessage": "Documentation : Créer des serveurs de jeu", + "description": "Create server: link at the bottom of the wizard that opens the documentation page about creating game servers in a new tab", + "sourceHash": "4f1c64acf158" + }, + "web.routes.create_server.edit": { + "defaultMessage": "Modifier", + "description": "Create server: review step button that opens the matching wizard step so that section can be changed; keep it short", + "sourceHash": "464c4ffd019e" + }, + "web.routes.create_server.enable_tunnel": { + "defaultMessage": "Activer le tunnel", + "description": "Create server: checkbox label that turns on a tunnel for reaching the server from outside the cluster", + "sourceHash": "e6c2ed95ba0e" + }, + "web.routes.create_server.error_api_rejected": { + "defaultMessage": "L'API a rejeté la requête.", + "description": "Create server: fallback error body when the API returned no body", + "sourceHash": "aa161418c56d" + }, + "web.routes.create_server.error_create_failed": { + "defaultMessage": "Échec de la création", + "description": "Create server: error title when server creation fails", + "sourceHash": "3e05ffcf023b" + }, + "web.routes.create_server.error_create_failed_status": { + "defaultMessage": "Échec de la création ({status})", + "description": "Create server: error title for an API error; {status} is the HTTP status code", + "sourceHash": "c5716ac8c724" + }, + "web.routes.create_server.error_credential_save_failed": { + "defaultMessage": "Serveur créé, mais l'enregistrement des identifiants a échoué : {detail}. Le serveur existe avec le tunnel activé. Vous pouvez définir l'identifiant dans les paramètres réseau du serveur.", + "description": "Create server: error shown when the server was created but saving its tunnel credential failed; {detail} is the underlying error text", + "sourceHash": "6deb8ec3e637" + }, + "web.routes.create_server.error_name_taken_body": { + "defaultMessage": "Choisissez un autre nom ou ouvrez le serveur existant. Les noms doivent être uniques dans le namespace.", + "description": "Create server: error body when a server with the same name exists", + "sourceHash": "6b7f478886ee" + }, + "web.routes.create_server.error_name_taken_title": { + "defaultMessage": "Un serveur nommé {name} existe déjà", + "description": "Create server: error title when a server with the same name exists; {name} is the server name", + "sourceHash": "a408beae738d" + }, + "web.routes.create_server.error_not_permitted_body": { + "defaultMessage": "Votre rôle ne permet pas de créer des serveurs dans ce namespace.", + "description": "Create server: error body when the user's role cannot create servers", + "sourceHash": "7b5bb665390d" + }, + "web.routes.create_server.error_not_permitted_title": { + "defaultMessage": "Non autorisé", + "description": "Create server: error title when the user's role cannot create servers", + "sourceHash": "8ec5f4b7a4d4" + }, + "web.routes.create_server.error_unknown": { + "defaultMessage": "Erreur inconnue", + "description": "Create server: fallback error body when the error has no message", + "sourceHash": "27c2ccd962c2" + }, + "web.routes.create_server.expose": { + "defaultMessage": "Exposition", + "description": "Create server: field label for how the server is exposed to players; the choice is shown as option cards", + "sourceHash": "9687fa49cbab" + }, + "web.routes.create_server.expose_clusterip_hint": { + "defaultMessage": "Interne au cluster uniquement.", + "description": "Create server: help text under the ClusterIP expose option", + "sourceHash": "143125175398" + }, + "web.routes.create_server.expose_is_set_to_expose_address_pool": { + "defaultMessage": "L'exposition est définie sur {expose}. Le pool d'adresses et l'adresse demandée ne s'appliquent que lorsque l'exposition (ci-dessus) est définie sur LoadBalancer.", + "description": "Create server: note that address pool and requested address apply only to LoadBalancer; {expose} is the currently selected expose type", + "sourceHash": "05ef553a863e" + }, + "web.routes.create_server.expose_loadbalancer_hint": { + "defaultMessage": "Crée un équilibreur de charge externe.", + "description": "Create server: help text under the LoadBalancer expose option", + "sourceHash": "e831dd1571a8" + }, + "web.routes.create_server.expose_nodeport_hint": { + "defaultMessage": "Accessible via l'IP de n'importe quel nœud.", + "description": "Create server: help text under the NodePort expose option", + "sourceHash": "55236434dafb" + }, + "web.routes.create_server.factor_in_mod_plugin_overhead_minecraft": { + "defaultMessage": "Prévoyez la surcharge des mods et plugins ; Minecraft Vanilla utilise environ 1,5 GB pour 4 joueurs.", + "description": "Create server: memory hint in the YAML preview panel asking the user to allow for mod or plugin overhead; the example is about 1.5 GB for 4 Minecraft Vanilla players", + "sourceHash": "9152aa7c0bbe" + }, + "web.routes.create_server.finding_available_locations": { + "defaultMessage": "Recherche des emplacements disponibles…", + "description": "Create server: loading text shown while the list of server locations is being fetched", + "sourceHash": "9fdedd6412e5" + }, + "web.routes.create_server.frp_server_port_placeholder": { + "defaultMessage": "7000", + "description": "Create server: placeholder of the frp server port input, an example port number", + "sourceHash": "b698d86c67a2" + }, + "web.routes.create_server.frp_token_hint": { + "defaultMessage": "Votre jeton d'authentification frp", + "description": "Create server: help text under the credential input when frp is selected", + "sourceHash": "1b35ddd34b76" + }, + "web.routes.create_server.frp_token_placeholder": { + "defaultMessage": "jeton frp", + "description": "Create server: placeholder of the credential input when frp is selected", + "sourceHash": "7088d41502a6" + }, + "web.routes.create_server.game_port": { + "defaultMessage": "game-port", + "description": "Create server: example placeholder in a tunnel port mapping's port name field; an example value, not a default", + "sourceHash": "11bd30470bbb" + }, + "web.routes.create_server.hardcore_survival_realm_with_curated": { + "defaultMessage": "Monde survie hardcore avec mods sélectionnés. Sur invitation uniquement.", + "description": "Create server: example placeholder shown in the description box before the user types; an example text, not a default", + "sourceHash": "e35a82b329a3" + }, + "web.routes.create_server.hostname_optional": { + "defaultMessage": "Nom d'hôte (facultatif)", + "description": "Create server: field label for the optional hostname, shown in the Tailscale section and in the general network settings", + "sourceHash": "0cc21c11be92" + }, + "web.routes.create_server.ip_allow_list": { + "defaultMessage": "Liste d'autorisation IP", + "description": "Create server: accessible label of the multi-line IP allow-list box, read by screen readers; the same text as the visible field label", + "sourceHash": "2fcd214bc1d8" + }, + "web.routes.create_server.ip_allow_list_cidrs_optional": { + "defaultMessage": "Liste d'autorisation IP (CIDR, facultatif)", + "description": "Create server: field label for the optional IP allow-list, shown only when the expose choice is LoadBalancer; CIDR is a technical term", + "sourceHash": "edeed7cdc791" + }, + "web.routes.create_server.load_balancer_address_optional": { + "defaultMessage": "Adresse de l'équilibreur de charge (facultatif)", + "description": "Create server: label of the optional load balancer address group, which holds the address pool and requested address fields", + "sourceHash": "f90b13acd847" + }, + "web.routes.create_server.loader_loader": { + "defaultMessage": "Loader : {loader}", + "description": "Create server: loader line under a version option; {loader} is the loader name", + "sourceHash": "c0eff68071b6" + }, + "web.routes.create_server.location": { + "defaultMessage": "Emplacement", + "description": "Create server: label of the location dropdown in the wizard header, which chooses the cluster and namespace the server is created in", + "sourceHash": "15b61974b270" + }, + "web.routes.create_server.locations_unavailable": { + "defaultMessage": "Emplacements indisponibles", + "description": "Create server: title of the error card shown when the list of authorized server locations fails to load", + "sourceHash": "24c9bd1e2299" + }, + "web.routes.create_server.map_at_least_one_port_name_to_a_remote": { + "defaultMessage": "Associez au moins un nom de port à un port distant (1–65535).", + "description": "Create server: hint under the port mappings editor saying at least one port name must map to a remote port from 1 to 65535", + "sourceHash": "8e8090124245" + }, + "web.routes.create_server.max_maxcpu_largest_node": { + "defaultMessage": "Max {maxCpu} (plus grand nœud)", + "description": "Create server: hint under the CPU field; {maxCpu} is the largest CPU count one node can provide", + "sourceHash": "7b54b07b377e" + }, + "web.routes.create_server.max_maxmemgi_gib_largest_node": { + "defaultMessage": "Max {maxMemGi} GiB (plus grand nœud)", + "description": "Create server: hint under the memory field; {maxMemGi} is the largest memory in GiB one node can provide", + "sourceHash": "c6e60c555d03" + }, + "web.routes.create_server.mc_example_dev": { + "defaultMessage": "mc.example.dev", + "description": "Create server: example placeholder in the optional hostname field; an example value, not a default", + "sourceHash": "94148aba8e03" + }, + "web.routes.create_server.mc_hardcore": { + "defaultMessage": "mc-hardcore", + "description": "Create server: example placeholder shown in the server name field before the user types; an example value, not a default", + "sourceHash": "efc33167d6e4" + }, + "web.routes.create_server.memory": { + "defaultMessage": "Mémoire", + "description": "Create server: field label for the memory amount given to the server", + "sourceHash": "c3963aedaac6" + }, + "web.routes.create_server.memory_tip": { + "defaultMessage": "Conseil mémoire.", + "description": "Create server: bold label that starts the memory hint at the bottom of the YAML preview panel", + "sourceHash": "a650043e8e49" + }, + "web.routes.create_server.mounted_at_data_rwo_pvc": { + "defaultMessage": "Monté sur /data (PVC RWO).", + "description": "Create server: hint under the storage field saying the storage is mounted at /data as a RWO PVC; PVC is a technical term, usually left untranslated", + "sourceHash": "11ddbd308f6d" + }, + "web.routes.create_server.my_game_server": { + "defaultMessage": "my-game-server", + "description": "Create server: example placeholder in the playit tunnel name field; an example value, not a default", + "sourceHash": "3cf055d85d60" + }, + "web.routes.create_server.my_server": { + "defaultMessage": "my-server", + "description": "Create server: example placeholder in the Tailscale hostname field; an example value, not a default", + "sourceHash": "ae348bd0a230" + }, + "web.routes.create_server.my_tunnel_secret": { + "defaultMessage": "my-tunnel-secret", + "description": "Create server: example placeholder in the existing Secret name field; an example name, not a default", + "sourceHash": "ae89fbcaef94" + }, + "web.routes.create_server.name_of_a_load_balancer_address_pool": { + "defaultMessage": "Nom d'un pool d'adresses d'équilibreur de charge configuré par votre administrateur de cluster, plus une adresse précise facultative à demander. S'applique uniquement lorsque l'exposition est définie sur LoadBalancer ; ignoré sinon.", + "description": "Create server: hint under the address pool and requested address fields; they apply only when Expose is LoadBalancer and are ignored otherwise", + "sourceHash": "5130fc490ad7" + }, + "web.routes.create_server.name_of_a_pre_existing_kubernetes": { + "defaultMessage": "Nom d'un Secret Kubernetes existant. Laissez vide pour en créer un à partir des identifiants ci-dessus.", + "description": "Create server: hint under the existing Secret name field saying to leave it empty to create a new secret from the credentials above", + "sourceHash": "c634394876bd" + }, + "web.routes.create_server.new_game_server": { + "defaultMessage": "Nouveau serveur de jeu", + "description": "Create server: heading of the wizard dialog for creating a new game server, shown at the top left", + "sourceHash": "1fdf0188e0c8" + }, + "web.routes.create_server.no_available_location": { + "defaultMessage": "Aucun emplacement disponible", + "description": "Create server: title of the error card shown when no location is available to create a server in", + "sourceHash": "48cabbf7ff34" + }, + "web.routes.create_server.no_description": { + "defaultMessage": "Aucune description.", + "description": "Create server: shown under a template that has no description", + "sourceHash": "06a694820920" + }, + "web.routes.create_server.no_templates_match_your_search": { + "defaultMessage": "Aucun modèle ne correspond à votre recherche.", + "description": "Create server: message shown in the template list when the search text matches no template", + "sourceHash": "daae35aa0ed7" + }, + "web.routes.create_server.node_placement": { + "defaultMessage": "Placement du nœud", + "description": "Create server: field label for choosing which Kubernetes node runs the server, with automatic or pinned options", + "sourceHash": "d26952a2501a" + }, + "web.routes.create_server.node_placement_auto": { + "defaultMessage": "Auto (planificateur)", + "description": "Create server: node placement option letting the scheduler pick a node", + "sourceHash": "c2778c8fb70a" + }, + "web.routes.create_server.node_placement_auto_hint": { + "defaultMessage": "Choisit le nœud ayant le plus de mémoire libre, avec des tolérances ou sans taints.", + "description": "Create server: help text under the auto node placement option", + "sourceHash": "7af065fbeae7" + }, + "web.routes.create_server.node_placement_gpu": { + "defaultMessage": "Compatible GPU", + "description": "Create server: node placement option requiring a GPU-capable node", + "sourceHash": "69c9aa1cc53b" + }, + "web.routes.create_server.node_placement_gpu_hint": { + "defaultMessage": "Nécessite un nœud avec un plugin d'appareil GPU.", + "description": "Create server: help text under the GPU node placement option", + "sourceHash": "6a040fde2828" + }, + "web.routes.create_server.node_placement_pin": { + "defaultMessage": "Épingler sur un nœud", + "description": "Create server: node placement option pinning the server to one node", + "sourceHash": "bb946fcb1df2" + }, + "web.routes.create_server.node_placement_pin_hint": { + "defaultMessage": "Épingle sur un nœud précis (sélectionnable après création).", + "description": "Create server: help text under the pin node placement option", + "sourceHash": "7581d6c5ae22" + }, + "web.routes.create_server.one_cidr_per_line_restricts_which": { + "defaultMessage": "Un CIDR par ligne. Limite les clients autorisés à atteindre le LoadBalancer ; vide = tous autorisés.", + "description": "Create server: hint under the IP allow-list box saying one CIDR goes on each line and an empty list allows all clients to reach the LoadBalancer", + "sourceHash": "6aa166ea622b" + }, + "web.routes.create_server.or_use_existing_secret_gitops": { + "defaultMessage": "Ou utiliser un Secret existant (GitOps)", + "description": "Create server: field label for an option to reuse an existing Kubernetes Secret for the tunnel credentials instead of entering them", + "sourceHash": "f1a5ab230597" + }, + "web.routes.create_server.persistent_storage": { + "defaultMessage": "Stockage persistant", + "description": "Create server: field label for the storage size reserved for the server's data", + "sourceHash": "35a14307af85" + }, + "web.routes.create_server.pick_a_template": { + "defaultMessage": "Choisir un modèle", + "description": "Create server: preview pane title shown until a template is picked", + "sourceHash": "21a2714bf862" + }, + "web.routes.create_server.pick_the_version_and_loader_for_this": { + "defaultMessage": "Choisissez la version et le loader de ce serveur. Chaque loader conserve ses mods sur un volume distinct, ainsi un changement n'écrase jamais les autres.", + "description": "Create server: hint under the version step heading explaining that each loader keeps its own mods on a separate volume, so switching never removes other mods", + "sourceHash": "2e536e7f64b1" + }, + "web.routes.create_server.pin_a_nodeport_or_override_the_service": { + "defaultMessage": "Épinglez un NodePort ou remplacez le port du Service pour un port de modèle nommé.", + "description": "Create server: hint under the port overrides editor explaining that overrides pin a NodePort or change the Service port for a template port", + "sourceHash": "2bf57b09272d" + }, + "web.routes.create_server.playit_secret_key_hint": { + "defaultMessage": "Votre clé secrète playit.gg", + "description": "Create server: help text under the credential input when playit.gg is selected", + "sourceHash": "65ba5feeb522" + }, + "web.routes.create_server.playit_secret_key_placeholder": { + "defaultMessage": "clé secrète playit", + "description": "Create server: placeholder of the credential input when playit.gg is selected", + "sourceHash": "bbd9b609cb3f" + }, + "web.routes.create_server.pool_us_west": { + "defaultMessage": "pool-us-west", + "description": "Create server: example placeholder in the address pool name field; an example name, not a default", + "sourceHash": "647d35b425b0" + }, + "web.routes.create_server.port_mappings": { + "defaultMessage": "Correspondances de ports *", + "description": "Create server: field label for the list of frp port mappings; the asterisk marks it required", + "sourceHash": "908a85e574b4" + }, + "web.routes.create_server.port_name": { + "defaultMessage": "Nom du port", + "description": "Create server: small field label for the port name in a tunnel port mapping row", + "sourceHash": "239a365f3b9b" + }, + "web.routes.create_server.port_overrides_optional": { + "defaultMessage": "Remplacements de ports (facultatif)", + "description": "Create server: field label for optional port overrides that pin a NodePort or change the Service port for a named template port; NodePort is a technical term", + "sourceHash": "fc23c3a3a092" + }, + "web.routes.create_server.provider_frp": { + "defaultMessage": "frp", + "description": "Create server: name of the frp tunnel provider option", + "sourceHash": "0c4243c45502" + }, + "web.routes.create_server.provider_playit": { + "defaultMessage": "playit.gg", + "description": "Create server: name of the playit.gg tunnel provider option", + "sourceHash": "99186d88c8d4" + }, + "web.routes.create_server.provider_tailscale": { + "defaultMessage": "Tailscale", + "description": "Create server: name of the Tailscale tunnel provider option", + "sourceHash": "88d90e2eef33" + }, + "web.routes.create_server.relay_example_com": { + "defaultMessage": "relay.example.com", + "description": "Create server: example placeholder in the frp server address field; an example host, not a default", + "sourceHash": "c5294ed03a55" + }, + "web.routes.create_server.remote_port": { + "defaultMessage": "Port distant", + "description": "Create server: small field label for the remote port number in a tunnel port mapping row", + "sourceHash": "57d6b8baec32" + }, + "web.routes.create_server.requested_address": { + "defaultMessage": "Adresse demandée", + "description": "Create server: field label for an optional specific load balancer address the server should request", + "sourceHash": "21cc42607cf9" + }, + "web.routes.create_server.requested_address_placeholder": { + "defaultMessage": "203.0.113.50", + "description": "Create server: placeholder of the requested load balancer address input, an example IP", + "sourceHash": "3b5cd36857cb" + }, + "web.routes.create_server.review_all_sources": { + "defaultMessage": "tout", + "description": "Create server: review value for the IP allow-list when no source range is set, meaning every address is allowed", + "sourceHash": "5ef5ef0364b6" + }, + "web.routes.create_server.review_configuration": { + "defaultMessage": "Configuration", + "description": "Create server: review section title for the server name, location and resources", + "sourceHash": "b332c3492d5e" + }, + "web.routes.create_server.review_cpu": { + "defaultMessage": "CPU", + "description": "Create server: review row label for the CPU limit", + "sourceHash": "db9a4c7d4c19" + }, + "web.routes.create_server.review_credentials_configured": { + "defaultMessage": "✓ Configurés", + "description": "Create server: review value shown when tunnel credentials were entered", + "sourceHash": "728b1b67e573" + }, + "web.routes.create_server.review_credentials_secret": { + "defaultMessage": "Secret : {name}", + "description": "Create server: review value naming the existing tunnel Secret; {name} is the Secret name", + "sourceHash": "90f79161559d" + }, + "web.routes.create_server.review_expose": { + "defaultMessage": "Exposition", + "description": "Create server: review row label for how the server is exposed", + "sourceHash": "9687fa49cbab" + }, + "web.routes.create_server.review_frp_port_mappings": { + "defaultMessage": "Correspondances de ports frp", + "description": "Create server: review row label for the frp remote port mappings", + "sourceHash": "1fe1916879a9" + }, + "web.routes.create_server.review_frp_server": { + "defaultMessage": "Serveur frp", + "description": "Create server: review row label for the frp server address", + "sourceHash": "e386706a6eb1" + }, + "web.routes.create_server.review_hostname": { + "defaultMessage": "Nom d'hôte", + "description": "Create server: review row label for the server hostname", + "sourceHash": "2db533557011" + }, + "web.routes.create_server.review_ip_allow_list": { + "defaultMessage": "Liste d'autorisation IP", + "description": "Create server: review row label for the load balancer source IP allow-list", + "sourceHash": "2fcd214bc1d8" + }, + "web.routes.create_server.review_location": { + "defaultMessage": "Emplacement", + "description": "Create server: review row label for the cluster and namespace the server is created in", + "sourceHash": "15b61974b270" + }, + "web.routes.create_server.review_memory": { + "defaultMessage": "Mémoire", + "description": "Create server: review row label for the memory limit", + "sourceHash": "c3963aedaac6" + }, + "web.routes.create_server.review_name": { + "defaultMessage": "Nom", + "description": "Create server: review row label for the server name", + "sourceHash": "dcd1d5223f73" + }, + "web.routes.create_server.review_network": { + "defaultMessage": "Réseau", + "description": "Create server: review section title for the network settings", + "sourceHash": "1744b96470b5" + }, + "web.routes.create_server.review_placement": { + "defaultMessage": "Placement", + "description": "Create server: review row label for the node placement option", + "sourceHash": "4df9939944a7" + }, + "web.routes.create_server.review_playit_tunnel_name": { + "defaultMessage": "Nom du tunnel playit", + "description": "Create server: review row label for the playit.gg tunnel name", + "sourceHash": "ffb763b95cb4" + }, + "web.routes.create_server.review_port_overrides": { + "defaultMessage": "Remplacements de ports", + "description": "Create server: review row label for the port override names", + "sourceHash": "4404f3b839fa" + }, + "web.routes.create_server.review_storage": { + "defaultMessage": "Stockage", + "description": "Create server: review row label for the storage size", + "sourceHash": "a69c4dece144" + }, + "web.routes.create_server.review_tailscale_hostname": { + "defaultMessage": "Nom d'hôte Tailscale", + "description": "Create server: review row label for the Tailscale hostname", + "sourceHash": "136da3e3aa45" + }, + "web.routes.create_server.review_tailscale_tags": { + "defaultMessage": "Tags Tailscale", + "description": "Create server: review row label for the Tailscale tags", + "sourceHash": "7676030e97c5" + }, + "web.routes.create_server.review_template": { + "defaultMessage": "Modèle", + "description": "Create server: review section title and row label for the chosen game template", + "sourceHash": "0575f29df888" + }, + "web.routes.create_server.review_tunnel_credentials": { + "defaultMessage": "Identifiants du tunnel", + "description": "Create server: review row label for the tunnel credentials", + "sourceHash": "90a5e9b451be" + }, + "web.routes.create_server.review_tunnel_provider": { + "defaultMessage": "Fournisseur de tunnel", + "description": "Create server: review row label for the tunnel provider", + "sourceHash": "ecf2fc494d28" + }, + "web.routes.create_server.review_version": { + "defaultMessage": "Version", + "description": "Create server: review section title and row label for the chosen game version", + "sourceHash": "dd167905de0d" + }, + "web.routes.create_server.search": { + "defaultMessage": "Rechercher…", + "description": "Create server: placeholder text in the template search box, shown before the user types; typing filters the template list", + "sourceHash": "7336265a0467" + }, + "web.routes.create_server.search_templates": { + "defaultMessage": "Rechercher des modèles", + "description": "Create server: accessible label of the template search box, read by screen readers; the box shows a placeholder instead", + "sourceHash": "04b9835b977e" + }, + "web.routes.create_server.server_address": { + "defaultMessage": "Adresse du serveur *", + "description": "Create server: field label for the frp relay server address; the asterisk marks it required; frp is a technical term, usually left untranslated", + "sourceHash": "58f3a09c35ad" + }, + "web.routes.create_server.server_name": { + "defaultMessage": "Nom du serveur", + "description": "Create server: field label for the name of the new server; the name is used as the pod name and subdomain", + "sourceHash": "21babfed6c38" + }, + "web.routes.create_server.server_port": { + "defaultMessage": "Port du serveur", + "description": "Create server: field label for the port of the frp relay server", + "sourceHash": "426f91710cc2" + }, + "web.routes.create_server.some_locations_are_unavailable": { + "defaultMessage": "Certains emplacements sont indisponibles. Seuls les emplacements disponibles sont affichés.", + "description": "Create server: status note under the location dropdown shown when some locations are unavailable and only the available ones are listed", + "sourceHash": "c69c2f0fc859" + }, + "web.routes.create_server.source_ranges_placeholder": { + "defaultMessage": "203.0.113.0/24 10.0.0.0/8", + "description": "Create server: placeholder of the IP allow-list textarea, example CIDR ranges one per line", + "sourceHash": "163ceee1dc16" + }, + "web.routes.create_server.step_configure": { + "defaultMessage": "Configurer", + "description": "Create server: name of the configure wizard step in the step bar", + "sourceHash": "6defafa2caa6" + }, + "web.routes.create_server.step_network": { + "defaultMessage": "Réseau", + "description": "Create server: name of the network wizard step in the step bar", + "sourceHash": "1744b96470b5" + }, + "web.routes.create_server.step_review": { + "defaultMessage": "Vérification", + "description": "Create server: name of the review wizard step in the step bar", + "sourceHash": "aff0766a5290" + }, + "web.routes.create_server.step_stepindex_of_length_currentkey": { + "defaultMessage": "Étape {stepIndex} sur {length} · {currentKey}", + "description": "Create server: wizard progress line in the header; {stepIndex} is the current step number, {length} the step count, {currentKey} the current step name", + "sourceHash": "ff5cb171850b" + }, + "web.routes.create_server.step_template": { + "defaultMessage": "Modèle", + "description": "Create server: name of the template wizard step in the step bar", + "sourceHash": "0575f29df888" + }, + "web.routes.create_server.step_version": { + "defaultMessage": "Version", + "description": "Create server: name of the version wizard step in the step bar", + "sourceHash": "dd167905de0d" + }, + "web.routes.create_server.tag_game_tag_public": { + "defaultMessage": "tag:game,tag:public", + "description": "Create server: example placeholder in the Tailscale tags field showing the tag format; an example value, not a default", + "sourceHash": "7af7599931b7" + }, + "web.routes.create_server.tags_optional": { + "defaultMessage": "Tags (facultatif)", + "description": "Create server: field label for optional Tailscale tags; Tailscale is a technical term, usually left untranslated", + "sourceHash": "66e58c49d455" + }, + "web.routes.create_server.tailscale_access_is_tailnet_private": { + "defaultMessage": "L'accès Tailscale est limité au tailnet privé et n'est pas joignable depuis Internet.", + "description": "Create server: warning note in the Tailscale section saying access stays private to the tailnet and cannot be reached from the internet; Tailscale is a technical term", + "sourceHash": "ed94fee961d0" + }, + "web.routes.create_server.tailscale_auth_key_placeholder": { + "defaultMessage": "Clé d'authentification Tailscale", + "description": "Create server: placeholder of the credential input when Tailscale is selected", + "sourceHash": "935e212cf8bc" + }, + "web.routes.create_server.tailscale_token_hint": { + "defaultMessage": "Jeton OAuth Tailscale (généralement ts_{id}={secret})", + "description": "Create server: help text under the credential input when Tailscale is selected; {id} is the key id and {secret} the secret, shown as and ", + "sourceHash": "8814f19e0e61" + }, + "web.routes.create_server.template": { + "defaultMessage": "Modèle :", + "description": "Create server: label before the template name in the YAML preview header; shows a dash when no template is chosen yet", + "sourceHash": "3c8938774eb8" + }, + "web.routes.create_server.template_config": { + "defaultMessage": "Config du modèle", + "description": "Create server: small uppercase heading above the code block showing the chosen template configuration values on the review step", + "sourceHash": "beab9159511e" + }, + "web.routes.create_server.template_configuration": { + "defaultMessage": "Configuration du modèle", + "description": "Create server: small uppercase heading above the template-specific settings, shown only when the template has such settings", + "sourceHash": "22ec13d0a26b" + }, + "web.routes.create_server.the_selected_location_is_unavailable_or": { + "defaultMessage": "L'emplacement sélectionné est indisponible, ou aucun emplacement accessible n'autorise la création. Rétablissez son accès ou revenez à la liste des serveurs pour choisir un emplacement disponible.", + "description": "Create server: explanation under the no-location title; tells the user to restore access to the location or go back to the server list", + "sourceHash": "a5d139ae1548" + }, + "web.routes.create_server.tunnel_name_optional": { + "defaultMessage": "Nom du tunnel (facultatif)", + "description": "Create server: field label for the optional playit tunnel name; playit is a technical term, usually left untranslated", + "sourceHash": "9d44677c71ba" + }, + "web.routes.create_server.tunnel_port_placeholder": { + "defaultMessage": "25565", + "description": "Create server: placeholder of the tunnel remote port input, an example port number", + "sourceHash": "ab628dfe91bd" + }, + "web.routes.create_server.tunnel_provider": { + "defaultMessage": "Fournisseur de tunnel", + "description": "Create server: field label above the choice of tunnel provider, shown when the tunnel is enabled", + "sourceHash": "ecf2fc494d28" + }, + "web.routes.create_server.unable_to_load_authorized_server": { + "defaultMessage": "Impossible de charger les emplacements de serveur autorisés.", + "description": "Create server: message under the locations error title, saying the authorized server locations could not be loaded", + "sourceHash": "62a2395728f4" + }, + "web.routes.create_server.used_as_pod_name_and_subdomain": { + "defaultMessage": "Utilisé comme nom de pod et sous-domaine. En minuscules, séparés par des tirets.", + "description": "Create server: hint under the server name field saying the name becomes the pod name and subdomain and should be lowercase with dashes", + "sourceHash": "603cc3c19310" + }, + "web.routes.create_server.validation_choose_version": { + "defaultMessage": "Choisissez une version pour continuer", + "description": "Create server: validation message shown when the version step has no version chosen", + "sourceHash": "a9a851eb75dc" + }, + "web.routes.create_server.validation_cpu_memory_quantity": { + "defaultMessage": "Le CPU et la mémoire doivent être des quantités valides", + "description": "Create server: validation message shown when the CPU or memory limit is not a valid Kubernetes quantity", + "sourceHash": "02dc0e6a348c" + }, + "web.routes.create_server.validation_cpu_over_node": { + "defaultMessage": "Le CPU ne peut pas dépasser {cores} cœurs : aucun nœud n'en a davantage", + "description": "Create server: validation message shown when the CPU limit is above the largest node; {cores} is the core count of the largest node", + "sourceHash": "ff1c4e3ff288" + }, + "web.routes.create_server.validation_frp_port_mapping": { + "defaultMessage": "frp requiert au moins une correspondance de port complète", + "description": "Create server: validation message shown when frp has no complete port mapping", + "sourceHash": "13355561dfe9" + }, + "web.routes.create_server.validation_frp_server_address": { + "defaultMessage": "frp requiert une adresse de serveur", + "description": "Create server: validation message shown when frp is selected without a server address", + "sourceHash": "4b921dd9b8eb" + }, + "web.routes.create_server.validation_memory_over_node": { + "defaultMessage": "La mémoire ne peut pas dépasser {gib} GiB : aucun nœud n'en a davantage", + "description": "Create server: validation message shown when the memory limit is above the largest node; {gib} is the memory of the largest node in GiB", + "sourceHash": "e231d16165fb" + }, + "web.routes.create_server.validation_name_format": { + "defaultMessage": "Le nom doit contenir uniquement des lettres minuscules, des chiffres et des tirets (63 caractères max)", + "description": "Create server: validation message shown when the server name is not a valid Kubernetes name", + "sourceHash": "e7deb9a0e1d4" + }, + "web.routes.create_server.validation_name_required": { + "defaultMessage": "Le nom du serveur est obligatoire", + "description": "Create server: validation message shown when the server name is empty", + "sourceHash": "065750f922eb" + }, + "web.routes.create_server.validation_pick_template": { + "defaultMessage": "Choisissez un modèle de jeu pour continuer", + "description": "Create server: validation message shown when the template step has no template picked", + "sourceHash": "01b87ffe87fe" + }, + "web.routes.create_server.validation_port_range": { + "defaultMessage": "Les numéros de port doivent être compris entre 1 et 65535", + "description": "Create server: validation message shown when a frp remote port is outside 1-65535", + "sourceHash": "ccf9006b66dd" + }, + "web.routes.create_server.validation_server_port_range": { + "defaultMessage": "Le port du serveur doit être compris entre 1 et 65535", + "description": "Create server: validation message shown when the frp server port is outside 1-65535", + "sourceHash": "52dab6eceb29" + }, + "web.routes.create_server.validation_storage_quantity": { + "defaultMessage": "Le stockage doit être une quantité Kubernetes (ex. 50Gi)", + "description": "Create server: validation message shown when the storage size is not a valid Kubernetes quantity", + "sourceHash": "c54f35cae25c" + }, + "web.routes.create_server.validation_tunnel_credentials": { + "defaultMessage": "Le tunnel requiert des identifiants (saisissez une valeur ou le nom d'un secret existant)", + "description": "Create server: validation message shown when the tunnel is enabled without credentials or a secret name", + "sourceHash": "a764143ce1f2" + }, + "web.routes.create_server.vversion": { + "defaultMessage": "v{version}", + "description": "Create server: template version shown under a template card; {version} is the version string", + "sourceHash": "da95c4621978" + }, + "web.routes.create_server.yaml_pick_a_template": { + "defaultMessage": "# Choisissez un modèle pour prévisualiser le YAML.", + "description": "Create server: YAML preview comment shown until a template is picked", + "sourceHash": "49bfca55d19c" + }, + "web.routes.dashboard.all_healthy": { + "defaultMessage": "tous sains", + "description": "Dashboard: sub-label on the Nodes ready card when every node is ready", + "sourceHash": "d43c87c18cf4" + }, + "web.routes.dashboard.audit_backed_up": { + "defaultMessage": "{actor} a sauvegardé{target}", + "description": "Dashboard: activity line for a backup; {actor} is the username, {target} is the audited object preceded by a space, or empty", + "sourceHash": "1101958d6525" + }, + "web.routes.dashboard.audit_failed_sign_in": { + "defaultMessage": "{actor} n'a pas réussi à se connecter", + "description": "Dashboard: activity line for a failed sign-in; {actor} is the username", + "sourceHash": "959bb15a93a1" + }, + "web.routes.dashboard.audit_generic": { + "defaultMessage": "{actor} {verb}{target}", + "description": "Dashboard: fallback activity line; {actor} is the username, {verb} the action word, {target} the audited object preceded by a space, or empty", + "sourceHash": "5b5d512c6793" + }, + "web.routes.dashboard.audit_restarted": { + "defaultMessage": "{actor} a redémarré{target}", + "description": "Dashboard: activity line for a server restart; {actor} is the username, {target} is the audited object preceded by a space, or empty", + "sourceHash": "e0e6d120e71d" + }, + "web.routes.dashboard.audit_signed_in": { + "defaultMessage": "{actor} s'est connecté", + "description": "Dashboard: activity line for a successful sign-in; {actor} is the username", + "sourceHash": "61b8a7d8690e" + }, + "web.routes.dashboard.audit_started": { + "defaultMessage": "{actor} a démarré{target}", + "description": "Dashboard: activity line for a server start; {actor} is the username, {target} is the audited object preceded by a space, or empty", + "sourceHash": "86324ac1d1b8" + }, + "web.routes.dashboard.audit_stopped": { + "defaultMessage": "{actor} a arrêté{target}", + "description": "Dashboard: activity line for a server stop; {actor} is the username, {target} is the audited object preceded by a space, or empty", + "sourceHash": "74caa56a47eb" + }, + "web.routes.dashboard.audit_updated_user": { + "defaultMessage": "{actor} a modifié un utilisateur{target}", + "description": "Dashboard: activity line for a user change; {actor} is the username, {target} is the audited object preceded by a space, or empty", + "sourceHash": "a7c3b47cf05c" + }, + "web.routes.dashboard.audit_verb_created": { + "defaultMessage": "a créé", + "description": "Dashboard: action word in an activity line for a POST request", + "sourceHash": "406effb1e9c5" + }, + "web.routes.dashboard.audit_verb_deleted": { + "defaultMessage": "a supprimé", + "description": "Dashboard: action word in an activity line for a DELETE request", + "sourceHash": "1185f37d33b0" + }, + "web.routes.dashboard.audit_verb_updated": { + "defaultMessage": "a modifié", + "description": "Dashboard: action word in an activity line for a PUT or PATCH request", + "sourceHash": "27eb5e51506c" + }, + "web.routes.dashboard.authorized_inventory_cores": { + "defaultMessage": "cœurs de l'inventaire autorisé", + "description": "Dashboard: sub-label on the vCPUs card", + "sourceHash": "a232ee589951" + }, + "web.routes.dashboard.backups": { + "defaultMessage": "Sauvegardes", + "description": "Dashboard: label naming the backup data in the coverage status line; that line warns when backup data is partial or failed", + "sourceHash": "3334fee2c77f" + }, + "web.routes.dashboard.backups_load_failed": { + "defaultMessage": "Impossible de charger les backups. Leur état n'est pas disponible.", + "description": "Dashboard: error card when the backup list cannot be loaded", + "sourceHash": "eea881e77c7e" + }, + "web.routes.dashboard.central_gameplane_audit_activity": { + "defaultMessage": "Activité d'audit centrale de Gameplane", + "description": "Dashboard: small caption above the recent audit events card, shown only to users who can view the audit log", + "sourceHash": "d8bd5b2e98e5" + }, + "web.routes.dashboard.cluster_resources": { + "defaultMessage": "Ressources du cluster", + "description": "Dashboard: heading of the card showing CPU, memory and storage use across the clusters the user can see", + "sourceHash": "53334c23e3a7" + }, + "web.routes.dashboard.cpu": { + "defaultMessage": "CPU", + "description": "Dashboard: label of the CPU usage bar in the cluster resources card; CPU is a technical term, usually left untranslated", + "sourceHash": "db9a4c7d4c19" + }, + "web.routes.dashboard.cpu_used_of_capacity": { + "defaultMessage": "{used} / {capacity} cœurs", + "description": "Dashboard: CPU usage sub-label on the Cluster resources card; both are formatted core counts", + "sourceHash": "1d14c1f626d0" + }, + "web.routes.dashboard.create_server": { + "defaultMessage": "Créer un serveur", + "description": "Dashboard: button at the top right that opens the form to create a new game server; keep it short", + "sourceHash": "cb569a5332e6" + }, + "web.routes.dashboard.dashboard": { + "defaultMessage": "Tableau de bord", + "description": "Dashboard: page title at the top of the dashboard; keep it short", + "sourceHash": "67b696468610" + }, + "web.routes.dashboard.everything_healthy": { + "defaultMessage": "Tout semble en bon état.", + "description": "Dashboard: Needs attention message when the fleet is complete and nothing needs attention", + "sourceHash": "a58910c21f49" + }, + "web.routes.dashboard.fleet_status": { + "defaultMessage": "État du parc", + "description": "Dashboard: heading of the card showing how many game servers are running, stopped or failed and which ones need attention", + "sourceHash": "ce5bb3942443" + }, + "web.routes.dashboard.inventory": { + "defaultMessage": "Inventaire", + "description": "Dashboard: label naming the cluster inventory data in the coverage status line; that line warns when inventory is partial or failed", + "sourceHash": "758f487fe8e9" + }, + "web.routes.dashboard.inventory_unavailable": { + "defaultMessage": "inventaire indisponible", + "description": "Dashboard: sub-label on the storage and nodes cards when inventory cannot be read", + "sourceHash": "8f63484de135" + }, + "web.routes.dashboard.legend_failed": { + "defaultMessage": "En échec {count}", + "description": "Dashboard: legend label for failed servers; {count} is the number", + "sourceHash": "17b44d11b9c5" + }, + "web.routes.dashboard.legend_running": { + "defaultMessage": "En cours {count}", + "description": "Dashboard: legend label for running servers; {count} is the number", + "sourceHash": "39cd62e2d416" + }, + "web.routes.dashboard.legend_stopped": { + "defaultMessage": "Arrêtés {count}", + "description": "Dashboard: legend label for stopped servers; {count} is the number", + "sourceHash": "6b23fa7aa658" + }, + "web.routes.dashboard.loading_dashboard": { + "defaultMessage": "Chargement du tableau de bord…", + "description": "Dashboard: loading text shown in place of the dashboard cards while the data loads", + "sourceHash": "88585a23b840" + }, + "web.routes.dashboard.memory": { + "defaultMessage": "Mémoire", + "description": "Dashboard: label of the memory usage bar in the cluster resources card", + "sourceHash": "c3963aedaac6" + }, + "web.routes.dashboard.memory_used_of_capacity": { + "defaultMessage": "{used} / {capacity}", + "description": "Dashboard: memory usage sub-label on the Cluster resources card; both are formatted byte sizes", + "sourceHash": "d328f6fb7ea8" + }, + "web.routes.dashboard.meta_separator": { + "defaultMessage": "·", + "description": "Separator between items on one line of metadata", + "sourceHash": "a137f17a19a0" + }, + "web.routes.dashboard.needs_attention": { + "defaultMessage": "À traiter", + "description": "Dashboard: small uppercase label above the list of up to four servers that need attention in the fleet status card", + "sourceHash": "c1ebc7817870" + }, + "web.routes.dashboard.no_backup_data": { + "defaultMessage": "Aucune donnée de backup disponible.", + "description": "Dashboard: empty Recent backups card when the backup list is partial", + "sourceHash": "a34534f8a6b1" + }, + "web.routes.dashboard.no_backups_yet": { + "defaultMessage": "Aucun backup pour le moment.", + "description": "Dashboard: empty Recent backups card when the backup list is complete", + "sourceHash": "0d123d0dc3c6" + }, + "web.routes.dashboard.no_issues_partial": { + "defaultMessage": "Aucun problème parmi les serveurs renvoyés ; certains emplacements sont indisponibles.", + "description": "Dashboard: Needs attention message when the fleet is partial and no returned server needs attention", + "sourceHash": "1a50f08ad8fc" + }, + "web.routes.dashboard.no_node_data": { + "defaultMessage": "aucune donnée de nœud", + "description": "Dashboard: sub-label on the Nodes ready card when no nodes are reported", + "sourceHash": "3e669b1ef094" + }, + "web.routes.dashboard.no_recent_activity": { + "defaultMessage": "Aucune activité récente.", + "description": "Dashboard: empty-state text in the recent activity card shown when there are no audit events", + "sourceHash": "44b67bb3678a" + }, + "web.routes.dashboard.node_cpu": { + "defaultMessage": "cpu {percent}%", + "description": "Dashboard: CPU usage on a node row; {percent} is the rounded percentage", + "sourceHash": "18cb7791ab73" + }, + "web.routes.dashboard.node_pods": { + "defaultMessage": "{count} pods", + "description": "Dashboard: pod count on a node row; {count} is the number of pods", + "sourceHash": "eaeca5c1dd33" + }, + "web.routes.dashboard.nodes": { + "defaultMessage": "Nœuds", + "description": "Dashboard: small uppercase label above the list of up to four nodes in the cluster resources card", + "sourceHash": "7ac362063b9f" + }, + "web.routes.dashboard.nodes_need_attention": { + "defaultMessage": "à traiter", + "description": "Dashboard: sub-label on the Nodes ready card when some nodes are not ready", + "sourceHash": "ca25baf7ebfd" + }, + "web.routes.dashboard.nodes_ready": { + "defaultMessage": "Nœuds prêts", + "description": "Dashboard: label of the summary card counting ready nodes; the value shows ready nodes out of all nodes", + "sourceHash": "bb1f26839bc2" + }, + "web.routes.dashboard.nodes_ready_of_total": { + "defaultMessage": "{ready}/{total} nœuds prêts", + "description": "Dashboard: node readiness in the Cluster resources card; {ready} and {total} are counts", + "sourceHash": "bceba8635a69" + }, + "web.routes.dashboard.of_returned": { + "defaultMessage": "sur {count} renvoyés", + "description": "Dashboard: sub-label on the Running card when the server list is partial; {count} is the number of returned servers", + "sourceHash": "1b4833c2f940" + }, + "web.routes.dashboard.of_total": { + "defaultMessage": "sur {count} au total", + "description": "Dashboard: sub-label on the Running card; {count} is the total number of servers", + "sourceHash": "49d1a6b81f5c" + }, + "web.routes.dashboard.peak_players": { + "defaultMessage": "pic {count}", + "description": "Dashboard: sub-label on the Players online card with the peak player count; {count} is the peak", + "sourceHash": "555e73fa360a" + }, + "web.routes.dashboard.player_counts_unavailable": { + "defaultMessage": "{count} compteurs de joueurs de serveurs indisponibles", + "description": "Dashboard: sub-label on the Players online card when some servers report no player count; {count} is how many", + "sourceHash": "cf5bed257eaf" + }, + "web.routes.dashboard.player_status_unavailable": { + "defaultMessage": "état des joueurs indisponible", + "description": "Dashboard: sub-label on the Players online card when player counts cannot be read", + "sourceHash": "64a33b9ecd84" + }, + "web.routes.dashboard.players_online": { + "defaultMessage": "Joueurs en ligne", + "description": "Dashboard: label of the summary card for players online; the line under the number shows the peak or a note when counts are unavailable", + "sourceHash": "ae9bb529a555" + }, + "web.routes.dashboard.ready_of_total": { + "defaultMessage": "{ready}/{total}", + "description": "Dashboard: nodes ready shown as ready/total; both are counts", + "sourceHash": "43e87ceb4369" + }, + "web.routes.dashboard.reason_agent_stale": { + "defaultMessage": "Heartbeat de l'agent périmé", + "description": "Dashboard: reason shown on a server whose agent heartbeat is stale", + "sourceHash": "fd99886ea828" + }, + "web.routes.dashboard.reason_failed": { + "defaultMessage": "Échec — consultez les journaux", + "description": "Dashboard: reason shown on a server that needs attention because it failed", + "sourceHash": "8dc907fdda3e" + }, + "web.routes.dashboard.recent_activity": { + "defaultMessage": "Activité récente", + "description": "Dashboard: heading of the card listing the latest audit events; a link to the full audit log sits beside it", + "sourceHash": "6cb44b56336a" + }, + "web.routes.dashboard.recent_backups": { + "defaultMessage": "Backups récents", + "description": "Dashboard: heading of the card listing the latest backups; a link to the backups page sits beside it", + "sourceHash": "a7ca13d3519e" + }, + "web.routes.dashboard.returned_inventory_cores": { + "defaultMessage": "cœurs de l'inventaire renvoyés", + "description": "Dashboard: sub-label on the vCPUs card when the inventory is partial", + "sourceHash": "4c41d9819af5" + }, + "web.routes.dashboard.returned_nodes_only": { + "defaultMessage": "nœuds renvoyés uniquement", + "description": "Dashboard: sub-label on the Nodes ready card when the inventory is partial", + "sourceHash": "4c1c48ca5585" + }, + "web.routes.dashboard.returned_servers_count": { + "defaultMessage": "{count} serveurs renvoyés", + "description": "Dashboard: server count in the Fleet status card when the list is partial; {count} is the number of returned servers", + "sourceHash": "d9511a1e3838" + }, + "web.routes.dashboard.running": { + "defaultMessage": "En cours", + "description": "Dashboard: label of the summary card counting running game servers", + "sourceHash": "f4ccae29e1bb" + }, + "web.routes.dashboard.server_status_unavailable": { + "defaultMessage": "état des serveurs indisponible", + "description": "Dashboard: sub-label on the Running card when server data cannot be read", + "sourceHash": "ba6afe3f2925" + }, + "web.routes.dashboard.server_totals": { + "defaultMessage": "Totaux des serveurs", + "description": "Dashboard: label naming the server data in the coverage status line; that line warns when server data is partial or failed", + "sourceHash": "f39531803fe3" + }, + "web.routes.dashboard.servers_count": { + "defaultMessage": "{count} serveurs", + "description": "Dashboard: server count in the Fleet status card; {count} is the number of servers", + "sourceHash": "2810d70b5560" + }, + "web.routes.dashboard.servers_load_failed": { + "defaultMessage": "Impossible de charger les serveurs. L'état et les totaux de joueurs ne sont pas disponibles.", + "description": "Dashboard: error card when the server list cannot be loaded", + "sourceHash": "6051ff708596" + }, + "web.routes.dashboard.storage": { + "defaultMessage": "Stockage", + "description": "Dashboard: label of the storage usage bar in the cluster resources card", + "sourceHash": "a69c4dece144" + }, + "web.routes.dashboard.storage_provisioned": { + "defaultMessage": "Stockage provisionné", + "description": "Dashboard: label of the summary card with the total provisioned storage; the line under it says whether the figure covers returned or all authorized inventory", + "sourceHash": "22d021bf9a49" + }, + "web.routes.dashboard.target_and_reason": { + "defaultMessage": "{target} · {reason}", + "description": "Dashboard: line under a server that needs attention; {target} is the cluster and namespace label, {reason} the problem", + "sourceHash": "a88d2529cf38" + }, + "web.routes.dashboard.target_and_time": { + "defaultMessage": "{target} · {when}", + "description": "Dashboard: line under a backup; {target} is the cluster and namespace label, {when} the relative time", + "sourceHash": "c9443d3577c5" + }, + "web.routes.dashboard.vcpus": { + "defaultMessage": "vCPUs", + "description": "Dashboard: label of the summary card with the total vCPU count; vCPU is a technical term, usually left untranslated", + "sourceHash": "d0a4f1baaeab" + }, + "web.routes.dashboard.view_all": { + "defaultMessage": "Tout voir", + "description": "Dashboard: link in the recent activity card to the full audit log, and in the recent backups card to the backups page; keep it short", + "sourceHash": "30a64216eaea" + }, + "web.routes.dashboard.view_clusters": { + "defaultMessage": "Voir les clusters", + "description": "Dashboard: link at the top right of the cluster resources card that opens the clusters page; keep it short", + "sourceHash": "aa20ec12aeb6" + }, + "web.routes.dashboard.your_game_servers_and_resources_across": { + "defaultMessage": "Vos serveurs de jeu et ressources dans les emplacements autorisés.", + "description": "Dashboard: subtitle under the page title saying the page shows game servers and resources in the locations the user may access", + "sourceHash": "ad5dec1bb501" + }, + "web.routes.login.agpl_3_0": { + "defaultMessage": "AGPL-3.0", + "description": "Login: small badge above the side panel headline showing the license name; AGPL-3.0 is a technical term, usually left untranslated; keep it short", + "sourceHash": "9c2e9ba90c75" + }, + "web.routes.login.agpl_3_0_licensed": { + "defaultMessage": "Sous licence AGPL-3.0", + "description": "Login: footer line below the sign-in form stating the license; AGPL-3.0 is the license name, a technical term usually left untranslated", + "sourceHash": "62b00d481cf1" + }, + "web.routes.login.an_open_source_alternative_to": { + "defaultMessage": "Une alternative open source aux panneaux de jeu propriétaires : CRDs, Operators et StatefulSets jusqu'au bout.", + "description": "Login: paragraph under the side panel headline describing the product; CRDs, Operators and StatefulSets are technical terms, usually left untranslated", + "sourceHash": "48a41bc8d693" + }, + "web.routes.login.backups_auto_restart_rolling_upgrades": { + "defaultMessage": "Backups, redémarrage automatique, mises à jour progressives, RBAC. Des valeurs par défaut prêtes pour la production.", + "description": "Login: feature point on the side panel listing backups, auto-restart, rolling upgrades and RBAC as production-ready defaults; RBAC is a technical term, usually left untranslated", + "sourceHash": "b304af1ba40a" + }, + "web.routes.login.contact_your_administrator_to_reset": { + "defaultMessage": "Contactez votre administrateur pour réinitialiser votre mot de passe.", + "description": "Login: hint shown under the password field after the forgot button is pressed, telling the user to ask an administrator to reset the password", + "sourceHash": "b0d84387c045" + }, + "web.routes.login.continue_with_label": { + "defaultMessage": "Continuer avec {label}", + "description": "Login: sentence with label in Button", + "sourceHash": "af90621a9069" + }, + "web.routes.login.deploy_any_game_minecraft_valheim": { + "defaultMessage": "Déployez n'importe quel jeu. Minecraft, Valheim, Factorio, Palworld, ARK, CS2, Terraria et bien d'autres.", + "description": "Login: first feature point on the side panel saying any game can be deployed; the game names are product names, usually kept as is", + "sourceHash": "1822fc7d8637" + }, + "web.routes.login.email_or_username": { + "defaultMessage": "E-mail ou nom d'utilisateur", + "description": "Login: label of the username field on the sign-in form; the field accepts an email address or a username", + "sourceHash": "2a347f1933d4" + }, + "web.routes.login.forgot": { + "defaultMessage": "Oublié ?", + "description": "Login: small button beside the password label that shows the password-reset hint; keep it short", + "sourceHash": "761c9156320c" + }, + "web.routes.login.game_server_control_panel": { + "defaultMessage": "panneau de contrôle de serveurs de jeu", + "description": "Login: tagline under the product name next to the logo on the sign-in page", + "sourceHash": "357dab105bdc" + }, + "web.routes.login.game_server_hosting": { + "defaultMessage": "hébergement de serveurs de jeu.", + "description": "Login: second line of the large headline on the side panel, completing the phrase that starts with the first line", + "sourceHash": "d6026b32b68b" + }, + "web.routes.login.gameplane": { + "defaultMessage": "gameplane", + "description": "Login: product name shown next to the logo at the top of the sign-in page; a brand name, usually kept as is", + "sourceHash": "c48499141ee6" + }, + "web.routes.login.gitops_friendly": { + "defaultMessage": "Compatible GitOps.", + "description": "Login: opening words of the last feature point on the side panel; GitOps is a technical term, usually left untranslated; a command sample and closing words follow", + "sourceHash": "e479ed87e4d0" + }, + "web.routes.login.hide_password": { + "defaultMessage": "Masquer le mot de passe", + "description": "Login: aria-label of the button that hides the password", + "sourceHash": "a60a56c584b3" + }, + "web.routes.login.invalid_credentials": { + "defaultMessage": "Identifiants invalides", + "description": "Login: error shown when the username or password is rejected; deliberately generic", + "sourceHash": "7ec371a97f3b" + }, + "web.routes.login.just_works": { + "defaultMessage": "fonctionne simplement.", + "description": "Login: closing words after the command sample in the last feature point on the side panel", + "sourceHash": "c8f23cfd9ba5" + }, + "web.routes.login.kubectl_get_gameservers": { + "defaultMessage": "kubectl get gameservers", + "description": "Login: command sample shown in code style inside the last feature point; kubectl is a technical term, usually left untranslated", + "sourceHash": "ac76f0b444c6" + }, + "web.routes.login.kubernetes_native": { + "defaultMessage": "Kubernetes-native", + "description": "Login: first line of the large headline on the side panel of the sign-in page; Kubernetes-native is a technical term, usually left untranslated", + "sourceHash": "4bfadd77d428" + }, + "web.routes.login.network_error": { + "defaultMessage": "Erreur réseau", + "description": "Login: error shown when the sign-in request could not reach the server", + "sourceHash": "2a33d984de88" + }, + "web.routes.login.or": { + "defaultMessage": "ou", + "description": "Login: small word between the sign-in form and the single sign-on buttons; keep it short", + "sourceHash": "7175517a370b" + }, + "web.routes.login.password": { + "defaultMessage": "Mot de passe", + "description": "Login: label of the password field on the sign-in form", + "sourceHash": "e7cf3ef4f17c" + }, + "web.routes.login.scale_from_single_node_k3s_to_multi": { + "defaultMessage": "Passez d'un nœud k3s unique à du bare metal ou du cloud multi-nœuds.", + "description": "Login: feature point on the side panel about scaling from a single k3s node to multi-node bare metal or cloud; k3s is a technical term, usually left untranslated", + "sourceHash": "115e28c2595a" + }, + "web.routes.login.show_password": { + "defaultMessage": "Afficher le mot de passe", + "description": "Login: aria-label of the button that reveals the password", + "sourceHash": "6aeaa6a53d09" + }, + "web.routes.login.sign_in": { + "defaultMessage": "Connexion", + "description": "Login: main heading of the sign-in form", + "sourceHash": "bfd402b2f6f3" + }, + "web.routes.login.sign_in_2": { + "defaultMessage": "Se connecter →", + "description": "Login: main submit button of the sign-in form; a spinner shows while the sign-in runs; keep it short", + "sourceHash": "8637e0051dee" + }, + "web.routes.login.sign_in_with_safe_mode_custom_styling": { + "defaultMessage": "Se connecter en mode sans échec (personnalisation désactivée)", + "description": "Login: link below the sign-in form that signs in with safe mode, which turns off custom styling", + "sourceHash": "917adf7fafeb" + }, + "web.routes.login.welcome_to_gameplane": { + "defaultMessage": "Bienvenue sur Gameplane.", + "description": "Login: short line under the sign-in heading that greets the user", + "sourceHash": "4e3f356c5ad0" + }, + "web.routes.modules.all_categories": { + "defaultMessage": "Toutes les catégories", + "description": "Modules: category filter chip that shows every category", + "sourceHash": "9d5097a83710" + }, + "web.routes.modules.all_sources": { + "defaultMessage": "Toutes les sources", + "description": "Modules: source filter chip that shows every source", + "sourceHash": "08e774c5bacc" + }, + "web.routes.modules.catalog_entry_the_entry_can_be_re": { + "defaultMessage": "du catalogue. L'entrée pourra être réimportée plus tard.", + "description": "Modules: end of the remove-upload dialog text saying the catalog entry can be re-uploaded later", + "sourceHash": "e98d200aa4b7" + }, + "web.routes.modules.create_module": { + "defaultMessage": "Créer un module", + "description": "Modules: button that opens the dialog to build a new module; keep it short", + "sourceHash": "6fd9db6db036" + }, + "web.routes.modules.deletes_the_module_resource_and_its": { + "defaultMessage": "Supprime la ressource Module et son GameTemplate géré. Les serveurs de jeu qui utilisent ce template doivent être supprimés au préalable.", + "description": "Modules: explanation in the uninstall dialog: deletes the Module resource and its managed GameTemplate, and game servers using it must be removed first", + "sourceHash": "6b45f75e73c8" + }, + "web.routes.modules.deletes_the_uploaded_bundle_backing_the": { + "defaultMessage": "Supprime le bundle importé à l'origine de l'entrée", + "description": "Modules: opening words of the remove-upload dialog text, followed by the module name in monospace and the rest of the sentence", + "sourceHash": "2e498b491afb" + }, + "web.routes.modules.failed_to_load_module_catalog": { + "defaultMessage": "Échec du chargement du catalogue de modules.", + "description": "Modules: error text shown when the module catalog fails to load; a retry button sits next to it", + "sourceHash": "14c07eaff22a" + }, + "web.routes.modules.failed_to_load_module_sources": { + "defaultMessage": "Échec du chargement des sources de modules.", + "description": "Modules: error text shown when the module sources panel fails to load; a retry button sits next to it", + "sourceHash": "a0edcc84eaac" + }, + "web.routes.modules.loading_module_catalog": { + "defaultMessage": "Chargement du catalogue de modules…", + "description": "Modules: loading text shown in place of the catalog while the module list is fetched", + "sourceHash": "3f3e5dfa001b" + }, + "web.routes.modules.manage_sources": { + "defaultMessage": "Gérer les sources", + "description": "Modules: button that shows or hides the module sources panel; keep it short", + "sourceHash": "b00d1920b294" + }, + "web.routes.modules.modules": { + "defaultMessage": "Modules", + "description": "Modules: page title at the top of the modules screen; keep it short", + "sourceHash": "76c86c4c3243" + }, + "web.routes.modules.no_modules_in_any_catalog_yet": { + "defaultMessage": "Aucun module dans les catalogues pour le moment. Ouvrez Gérer les sources pour ajouter une source ou vérifier son état de synchronisation.", + "description": "Modules: empty state when no catalog has any module", + "sourceHash": "c20737e8ccb4" + }, + "web.routes.modules.no_modules_match_the_current_filter": { + "defaultMessage": "Aucun module ne correspond au filtre actuel.", + "description": "Modules: empty state when the search or filters hide every module", + "sourceHash": "2616bf713cc5" + }, + "web.routes.modules.pre_packaged_game_server_templates": { + "defaultMessage": "Modèles de serveurs de jeu prêts à l'emploi, issus de vos sources de modules configurées.", + "description": "Modules: subtitle under the title: pre-packaged game-server templates pulled from your configured module sources", + "sourceHash": "1f29acbd0b61" + }, + "web.routes.modules.remove_upload": { + "defaultMessage": "Supprimer l'import", + "description": "Modules: confirm button of the remove-upload dialog; keep it short", + "sourceHash": "b57e3abc42c8" + }, + "web.routes.modules.remove_uploaded_module": { + "defaultMessage": "Supprimer le module importé ?", + "description": "Modules: title of the remove-upload ConfirmDialog", + "sourceHash": "39b9aa358035" + }, + "web.routes.modules.search_modules": { + "defaultMessage": "Rechercher des modules…", + "description": "Modules: grey placeholder of the search field that filters the module list by name", + "sourceHash": "ed4cdf519e64" + }, + "web.routes.modules.search_modules_2": { + "defaultMessage": "Rechercher des modules", + "description": "Modules: accessible name of the module search field", + "sourceHash": "a67f6147d439" + }, + "web.routes.modules.uninstall": { + "defaultMessage": "Désinstaller", + "description": "Modules: confirm button of the uninstall dialog that removes the module; keep it short", + "sourceHash": "fe199528850a" + }, + "web.routes.modules.uninstall_title": { + "defaultMessage": "Désinstaller {name} ?", + "description": "Modules: title of the uninstall ConfirmDialog; {name} is the module display name", + "sourceHash": "1725586dc91f" + }, + "web.routes.modules.upload_module": { + "defaultMessage": "Importer un module", + "description": "Modules: button that opens the dialog to upload a module bundle; keep it short", + "sourceHash": "803f9a75009b" + }, + "web.routes.not_found.go_to_dashboard": { + "defaultMessage": "Aller au tableau de bord", + "description": "Not found: link styled as a button under the explanation that returns the user to the dashboard; keep it short", + "sourceHash": "f085ee865703" + }, + "web.routes.not_found.page_not_found": { + "defaultMessage": "Page introuvable", + "description": "Not found: heading of the 404 page saying the address matches no page", + "sourceHash": "a469ab4ca4e5" + }, + "web.routes.not_found.the_page_you_re_looking_for_doesn_t": { + "defaultMessage": "La page que vous recherchez n'existe pas ou a été déplacée.", + "description": "Not found: explanation under the heading saying the page does not exist or has moved", + "sourceHash": "20633ff5dbba" + }, + "web.routes.server_detail.loading": { + "defaultMessage": "Chargement…", + "description": "Server detail: loading text shown in the body of a tab while that tab's content is still loading", + "sourceHash": "ba3bbbe10d8b" + }, + "web.routes.server_detail.loading_server": { + "defaultMessage": "Chargement du serveur…", + "description": "Server detail: loading text shown while the server and the user's access to it are being fetched", + "sourceHash": "93df6445ed1a" + }, + "web.routes.server_detail.ns_namespace": { + "defaultMessage": "ns : {namespace}", + "description": "Server detail: header line naming the namespace; {namespace} is the namespace name", + "sourceHash": "15958a4c21c3" + }, + "web.routes.server_detail.open_console": { + "defaultMessage": "Ouvrir la console", + "description": "Server detail: header button that switches to the console tab; shown only when a console is available; keep it short", + "sourceHash": "f4063d1b2a07" + }, + "web.routes.server_detail.restart": { + "defaultMessage": "Redémarrer", + "description": "Server detail: header button that restarts the running server; disabled without control access; keep it short", + "sourceHash": "6b983a81e5e8" + }, + "web.routes.server_detail.server_detail_tabs": { + "defaultMessage": "Onglets du détail du serveur", + "description": "Server detail: accessible label of the row of server tabs, read by screen readers; not shown as text", + "sourceHash": "b6ac85d80133" + }, + "web.routes.server_detail.server_failed_to_start": { + "defaultMessage": "Le serveur n'a pas pu démarrer.", + "description": "Server detail: shown when the server failed to start and gives no reason", + "sourceHash": "78eca9f4eb28" + }, + "web.routes.server_detail.site_cluster": { + "defaultMessage": "site : {cluster}", + "description": "Server detail: header line naming the cluster; {cluster} is the cluster name", + "sourceHash": "43778d690ab3" + }, + "web.routes.server_detail.start": { + "defaultMessage": "Démarrer", + "description": "Server detail: header button that starts a stopped server; keep it short", + "sourceHash": "e4bb9f1ece9a" + }, + "web.routes.server_detail.stop": { + "defaultMessage": "Arrêter", + "description": "Server detail: header button that stops the running server, or puts a sleeping server to sleep fully; keep it short", + "sourceHash": "cae7d57bc067" + }, + "web.routes.server_detail.tab_backups": { + "defaultMessage": "Backups", + "description": "Server detail: label of the Backups tab", + "sourceHash": "3334fee2c77f" + }, + "web.routes.server_detail.tab_capture": { + "defaultMessage": "Capture", + "description": "Server detail: label of the Capture tab", + "sourceHash": "1900b478a586" + }, + "web.routes.server_detail.tab_console": { + "defaultMessage": "Console", + "description": "Server detail: label of the Console tab", + "sourceHash": "29a40861bafe" + }, + "web.routes.server_detail.tab_events": { + "defaultMessage": "Événements", + "description": "Server detail: label of the Events tab", + "sourceHash": "8d14f6e72de8" + }, + "web.routes.server_detail.tab_files": { + "defaultMessage": "Fichiers", + "description": "Server detail: label of the Files tab", + "sourceHash": "abc7e9892806" + }, + "web.routes.server_detail.tab_logs": { + "defaultMessage": "Journaux", + "description": "Server detail: label of the Logs tab", + "sourceHash": "ea2100dc89ae" + }, + "web.routes.server_detail.tab_modpacks": { + "defaultMessage": "Modpacks", + "description": "Server detail: label of the Modpacks tab", + "sourceHash": "fa8a67ceb251" + }, + "web.routes.server_detail.tab_mods": { + "defaultMessage": "Mods", + "description": "Server detail: label of the Mods tab", + "sourceHash": "6e58b577a084" + }, + "web.routes.server_detail.tab_overview": { + "defaultMessage": "Vue d'ensemble", + "description": "Server detail: label of the Overview tab", + "sourceHash": "d4b1ea5708dd" + }, + "web.routes.server_detail.tab_players": { + "defaultMessage": "Joueurs", + "description": "Server detail: label of the Players tab", + "sourceHash": "84e12ac655dd" + }, + "web.routes.server_detail.tab_settings": { + "defaultMessage": "Paramètres", + "description": "Server detail: label of the Settings tab", + "sourceHash": "74a883a037bc" + }, + "web.routes.server_detail.this_server_changed_while_it_was_being": { + "defaultMessage": "Ce serveur a changé pendant son chargement. Rechargez son identité actuelle avant de continuer.", + "description": "Server detail: error card shown when the server's identity no longer matches the one the link was for; asks the user to reload its current identity", + "sourceHash": "2c8039637895" + }, + "web.routes.server_detail.this_server_could_not_be_loaded_at_its": { + "defaultMessage": "Ce serveur n'a pas pu être chargé à son emplacement enregistré. Vérifiez vos accès et la connexion du site.", + "description": "Server detail: error card shown when the server or the user's access to it cannot be loaded at its recorded location; a retry button is on the card", + "sourceHash": "174ec93ac583" + }, + "web.routes.server_detail.up_uptime": { + "defaultMessage": "actif depuis {uptime}", + "description": "Server detail: header line with uptime; {uptime} is the formatted uptime", + "sourceHash": "5453281f95f7" + }, + "web.routes.server_detail.wake": { + "defaultMessage": "Réveiller", + "description": "Server detail: header button that wakes a sleeping server; disabled without control access; keep it short", + "sourceHash": "33a7e908344d" + }, + "web.routes.servers.actions": { + "defaultMessage": "Actions", + "description": "Servers: column heading for the row buttons such as start, stop and restart; keep it short", + "sourceHash": "ff8059dc6752" + }, + "web.routes.servers.all": { + "defaultMessage": "Tous", + "description": "Servers: status filter tab that shows all servers, followed by their count; keep it short", + "sourceHash": "a52ace420f21" + }, + "web.routes.servers.authorized_inventory_cores": { + "defaultMessage": "cœurs de l'inventaire autorisé", + "description": "Servers: sub-label on the vCPUs card", + "sourceHash": "a232ee589951" + }, + "web.routes.servers.authorized_inventory_nodes": { + "defaultMessage": "nœuds de l'inventaire autorisé", + "description": "Servers: sub-label on the inventory-nodes card", + "sourceHash": "7b3a2aa17bbd" + }, + "web.routes.servers.cpu": { + "defaultMessage": "CPU", + "description": "Servers: column heading for CPU use per server; CPU is a technical term, usually left untranslated; keep it short", + "sourceHash": "db9a4c7d4c19" + }, + "web.routes.servers.cpu_cores": { + "defaultMessage": "{cores} cœurs", + "description": "Servers: CPU usage in cores when no limit is set; {cores} is the core count with two decimals", + "sourceHash": "1eaed4cac057" + }, + "web.routes.servers.cpu_percent": { + "defaultMessage": "{percent}%", + "description": "Servers: CPU usage as a percent of the limit; {percent} is the rounded number", + "sourceHash": "02a35530e04a" + }, + "web.routes.servers.create_server": { + "defaultMessage": "Créer un serveur", + "description": "Servers: button at the top right that opens the form to create a new game server; shown only to users who can create servers; keep it short", + "sourceHash": "cb569a5332e6" + }, + "web.routes.servers.filter": { + "defaultMessage": "Filtrer", + "description": "Servers: button that opens the filter panel, with a count of applied filters; keep it short", + "sourceHash": "638e249f4a15" + }, + "web.routes.servers.game": { + "defaultMessage": "Jeu", + "description": "Servers: column heading for the game each server runs; keep it short", + "sourceHash": "5c412a262086" + }, + "web.routes.servers.inventory": { + "defaultMessage": "Inventaire", + "description": "Servers: label of the coverage status for the cluster inventory data; it warns when the inventory is partial or failed", + "sourceHash": "758f487fe8e9" + }, + "web.routes.servers.inventory_nodes": { + "defaultMessage": "Nœuds de l'inventaire", + "description": "Servers: label of the summary card counting the nodes in the cluster inventory", + "sourceHash": "f8b877751182" + }, + "web.routes.servers.inventory_unavailable": { + "defaultMessage": "inventaire indisponible", + "description": "Servers: sub-label on the storage and inventory-nodes cards when inventory cannot be read", + "sourceHash": "8f63484de135" + }, + "web.routes.servers.loading": { + "defaultMessage": "Chargement…", + "description": "Servers: text shown in a card while the server list is loading, in place of the list", + "sourceHash": "ba3bbbe10d8b" + }, + "web.routes.servers.loading_servers": { + "defaultMessage": "Chargement…", + "description": "Servers: table empty state while servers load", + "sourceHash": "ba3bbbe10d8b" + }, + "web.routes.servers.location": { + "defaultMessage": "Emplacement", + "description": "Servers: label of the location filter in the filter panel; the choices are the clusters the user can access", + "sourceHash": "15b61974b270" + }, + "web.routes.servers.manage_your_game_servers_across_all": { + "defaultMessage": "Gérez vos serveurs de jeu dans tous les emplacements autorisés.", + "description": "Servers: subtitle under the page title saying the list covers game servers in all locations the user may access", + "sourceHash": "de1a9812b987" + }, + "web.routes.servers.memory": { + "defaultMessage": "Mémoire", + "description": "Servers: column heading for memory use per server; keep it short", + "sourceHash": "c3963aedaac6" + }, + "web.routes.servers.memory_percent": { + "defaultMessage": "{percent}%", + "description": "Servers: memory usage as a percent of the limit; {percent} is the rounded number", + "sourceHash": "02a35530e04a" + }, + "web.routes.servers.name": { + "defaultMessage": "Nom", + "description": "Servers: column heading for the server name; keep it short", + "sourceHash": "dcd1d5223f73" + }, + "web.routes.servers.no_data_for_filter": { + "defaultMessage": "Aucune donnée de serveur disponible pour ce filtre.", + "description": "Servers: empty-list message when server data is unavailable or partial", + "sourceHash": "5e568a9f8428" + }, + "web.routes.servers.no_node_data": { + "defaultMessage": "aucune donnée de nœud", + "description": "Servers: sub-label on the inventory-nodes card when no nodes are reported", + "sourceHash": "3e669b1ef094" + }, + "web.routes.servers.no_servers_match": { + "defaultMessage": "Aucun serveur ne correspond.", + "description": "Servers: empty-list message when no servers match the filter", + "sourceHash": "4ee3dc20fad4" + }, + "web.routes.servers.node": { + "defaultMessage": "Nœud", + "description": "Servers: column heading for the cluster node each server runs on; keep it short", + "sourceHash": "e93372533f32" + }, + "web.routes.servers.of_returned": { + "defaultMessage": "sur {count} renvoyés", + "description": "Servers: sub-label on the Running card when the server list is partial; {count} is the number of returned servers", + "sourceHash": "1b4833c2f940" + }, + "web.routes.servers.of_total": { + "defaultMessage": "sur {count} au total", + "description": "Servers: sub-label on the Running card; {count} is the total number of servers", + "sourceHash": "49d1a6b81f5c" + }, + "web.routes.servers.peak_players": { + "defaultMessage": "pic {count}", + "description": "Servers: sub-label on the Players online card with the peak player count; {count} is the peak", + "sourceHash": "555e73fa360a" + }, + "web.routes.servers.player_counts_unavailable": { + "defaultMessage": "{count} compteurs de joueurs de serveurs indisponibles", + "description": "Servers: sub-label on the Players online card when some servers report no player count; {count} is how many", + "sourceHash": "cf5bed257eaf" + }, + "web.routes.servers.player_status_unavailable": { + "defaultMessage": "état des joueurs indisponible", + "description": "Servers: sub-label on the Players online card when player counts cannot be read", + "sourceHash": "64a33b9ecd84" + }, + "web.routes.servers.players": { + "defaultMessage": "Joueurs", + "description": "Servers: column heading for the number of players online on each server; keep it short", + "sourceHash": "84e12ac655dd" + }, + "web.routes.servers.players_of_max": { + "defaultMessage": "{players}/{max}", + "description": "Servers: player count as online/max; both are numbers", + "sourceHash": "bf607bc3c6a8" + }, + "web.routes.servers.players_online": { + "defaultMessage": "Joueurs en ligne", + "description": "Servers: label of the summary card for players online; the line under the number shows the peak or a note when counts are unavailable", + "sourceHash": "ae9bb529a555" + }, + "web.routes.servers.restart": { + "defaultMessage": "Redémarrer", + "description": "Servers: tooltip of the icon button that restarts a server; keep it short", + "sourceHash": "6b983a81e5e8" + }, + "web.routes.servers.returned_inventory_cores": { + "defaultMessage": "cœurs de l'inventaire renvoyés", + "description": "Servers: sub-label on the vCPUs card when the inventory is partial", + "sourceHash": "4c41d9819af5" + }, + "web.routes.servers.returned_nodes_only": { + "defaultMessage": "nœuds renvoyés uniquement", + "description": "Servers: sub-label on the inventory-nodes card when the inventory is partial", + "sourceHash": "4c1c48ca5585" + }, + "web.routes.servers.running": { + "defaultMessage": "En cours", + "description": "Servers: 'Running' is both a summary card label counting running servers and a status filter tab that lists them with their count; keep it short", + "sourceHash": "f4ccae29e1bb" + }, + "web.routes.servers.search": { + "defaultMessage": "Rechercher…", + "description": "Servers: placeholder inside the search box shown on narrow screens; the short form of the search prompt", + "sourceHash": "7336265a0467" + }, + "web.routes.servers.search_servers": { + "defaultMessage": "Rechercher des serveurs…", + "description": "Servers: placeholder text inside the search box above the server list on wide screens, shown while the box is empty", + "sourceHash": "b11d96510005" + }, + "web.routes.servers.search_servers_2": { + "defaultMessage": "Rechercher des serveurs", + "description": "Servers: screen-reader name of the server search box on wide and narrow screens; not shown on screen", + "sourceHash": "3d7d709f953e" + }, + "web.routes.servers.server_list": { + "defaultMessage": "Liste des serveurs", + "description": "Servers: hidden name of the server list table, read only by screen readers", + "sourceHash": "3695909456c1" + }, + "web.routes.servers.server_results": { + "defaultMessage": "Résultats des serveurs", + "description": "Servers: label of the coverage status for the server data behind the list; it warns when the data is partial or failed", + "sourceHash": "d78e0b99df57" + }, + "web.routes.servers.server_status_filter": { + "defaultMessage": "Filtre de statut des serveurs", + "description": "Servers: hidden name of the row of status filter tabs, read only by screen readers", + "sourceHash": "2325249053d8" + }, + "web.routes.servers.server_status_unavailable": { + "defaultMessage": "état des serveurs indisponible", + "description": "Servers: sub-label on the Running card when server data cannot be read", + "sourceHash": "ba6afe3f2925" + }, + "web.routes.servers.servers": { + "defaultMessage": "Serveurs", + "description": "Servers: page title of the server list page; keep it short", + "sourceHash": "68d7beb6df59" + }, + "web.routes.servers.start": { + "defaultMessage": "Démarrer", + "description": "Servers: tooltip of the icon button that starts a stopped server; keep it short", + "sourceHash": "e4bb9f1ece9a" + }, + "web.routes.servers.status": { + "defaultMessage": "Statut", + "description": "Servers: column heading for the server state, such as running or stopped; keep it short", + "sourceHash": "920e413c7d41" + }, + "web.routes.servers.stop": { + "defaultMessage": "Arrêter", + "description": "Servers: tooltip of the icon button that stops a running server; keep it short", + "sourceHash": "cae7d57bc067" + }, + "web.routes.servers.stopped": { + "defaultMessage": "Arrêtés", + "description": "Servers: status filter tab that shows only stopped servers, followed by their count; keep it short", + "sourceHash": "1a4f630ac1b6" + }, + "web.routes.servers.storage_provisioned": { + "defaultMessage": "Stockage provisionné", + "description": "Servers: label of the summary card with the total provisioned storage; the line under it says whether it covers returned or all authorized inventory", + "sourceHash": "22d021bf9a49" + }, + "web.routes.servers.vcpus": { + "defaultMessage": "vCPUs", + "description": "Servers: label of the summary card with the total vCPU count; vCPU is a technical term, usually left untranslated", + "sourceHash": "d0a4f1baaeab" + }, + "web.routes.servers.wake": { + "defaultMessage": "Réveiller", + "description": "Servers: tooltip of the icon button that wakes a sleeping server; keep it short", + "sourceHash": "33a7e908344d" + }, + "web.routes.share.asleep": { + "defaultMessage": "En veille", + "description": "Share: status badge under the server name when the server is asleep; keep it short", + "sourceHash": "60135e8f5cbb" + }, + "web.routes.share.connect_at": { + "defaultMessage": "SE CONNECTER À", + "description": "Share: small label above the server address on the share page; keep it short", + "sourceHash": "5bed26b684ca" + }, + "web.routes.share.copy_address": { + "defaultMessage": "Copier l'adresse", + "description": "Share: tooltip of the icon button that copies the server address to the clipboard; keep it short", + "sourceHash": "7c4e5224f9d4" + }, + "web.routes.share.gameplane": { + "defaultMessage": "gameplane", + "description": "Share: product name above the shared server title on the public share page; a brand name, usually kept as is", + "sourceHash": "c48499141ee6" + }, + "web.routes.share.host_port": { + "defaultMessage": "{host}:{port}", + "description": "Share: connect address shown as host:port; {host} is the server host name and {port} is its port number", + "sourceHash": "ce62fe9d5d52" + }, + "web.routes.share.link_not_available": { + "defaultMessage": "Lien indisponible", + "description": "Share: heading of the error page shown when the shared link is invalid or cannot be used", + "sourceHash": "158d5c7851a7" + }, + "web.routes.share.not_exposed": { + "defaultMessage": "Non exposé", + "description": "Share: text shown in place of the server address when the server has no address to share", + "sourceHash": "441818ccc4c3" + }, + "web.routes.share.online": { + "defaultMessage": "En ligne", + "description": "Share: status badge under the server name on the share page, shown when the server is running; keep it short", + "sourceHash": "0d21bd52022c" + }, + "web.routes.share.players": { + "defaultMessage": "JOUEURS", + "description": "Share: small label above the players-online line on the share page", + "sourceHash": "a1e4b72d1fe8" + }, + "web.routes.share.playersonline_players_online": { + "defaultMessage": "{playersOnline} joueurs en ligne", + "description": "Share: sentence with playersOnline in div", + "sourceHash": "3fca18e3db8d" + }, + "web.routes.share.shared_server_link": { + "defaultMessage": "lien de serveur partagé", + "description": "Share: caption under the product name on the public share page, identifying the page as a shared server link", + "sourceHash": "ca70da992b92" + }, + "web.routes.share.start_server": { + "defaultMessage": "Démarrer le serveur", + "description": "Share: button label that starts the shared server", + "sourceHash": "7b7da6f8ab38" + }, + "web.routes.share.starting": { + "defaultMessage": "Démarrage…", + "description": "Share: status badge with a spinner under the server name while the server is starting; keep it short", + "sourceHash": "82b93630a921" + }, + "web.routes.share.starting_ellipsis": { + "defaultMessage": "Démarrage…", + "description": "Share: start button label while the start request is in flight", + "sourceHash": "82b93630a921" + }, + "web.routes.share.the_server_is_starting_up_this_usually": { + "defaultMessage": "Le serveur démarre. Cela prend généralement une à deux minutes ; cette page se met à jour toute seule.", + "description": "Share: explanation under the starting badge saying startup takes a minute or two and the page updates by itself", + "sourceHash": "2311bd54df1f" + }, + "web.routes.share.this_link_may_be_invalid_expired_or": { + "defaultMessage": "Ce lien est peut-être invalide, expiré ou révoqué. Demandez à la personne qui l'a partagé de vous en envoyer un nouveau.", + "description": "Share: explanation under the link-not-available heading telling the visitor to ask the person who shared the link for a new one", + "sourceHash": "8f37a7e1793f" + }, + "web.routes.share.this_server_is_asleep_right_now_check": { + "defaultMessage": "Ce serveur est en veille pour le moment. Revenez plus tard ou demandez au propriétaire du serveur de le démarrer.", + "description": "Share: explanation under the asleep badge telling the visitor to check back later or ask the server owner to start the server", + "sourceHash": "e412be4414aa" + }, + "web.routes.share.this_server_is_asleep_to_save_resources": { + "defaultMessage": "Ce serveur est en veille pour économiser des ressources. Démarrez-le, il sera prêt dans une ou deux minutes.", + "description": "Share: explanation under the asleep badge saying the server sleeps to save resources and takes a minute or two to be ready after it is started", + "sourceHash": "a8b39e8e27c8" + }, + "web.routes.share.try_again_shortly": { + "defaultMessage": "Réessayer bientôt", + "description": "Share: start button label while a retry is scheduled after a failed start", + "sourceHash": "c1415c8e2080" + }, + "web.routes.tabs.backups.actions": { + "defaultMessage": "Actions", + "description": "Backups tab: column heading for the row action buttons at the end of the backups table; keep it short", + "sourceHash": "ff8059dc6752" + }, + "web.routes.tabs.backups.back_up_now": { + "defaultMessage": "Sauvegarder maintenant", + "description": "Backups: label of the button that starts a backup immediately", + "sourceHash": "02a2840b598b" + }, + "web.routes.tabs.backups.backup_now_hint_many_destinations": { + "defaultMessage": "Plusieurs destinations configurées : utilisez la page Backups pour en choisir une.", + "description": "Backups tab: hint under the Back up now button when several backup destinations exist; points to the dashboard Backups page to choose one", + "sourceHash": "cb2e40b4a4e3" + }, + "web.routes.tabs.backups.backup_now_hint_no_destination": { + "defaultMessage": "Aucune destination de backup configurée. Ajoutez-en une dans les paramètres d'administration.", + "description": "Backups tab: hint under the Back up now button when no backup destination exists; points to admin settings", + "sourceHash": "a2502204eb21" + }, + "web.routes.tabs.backups.backups": { + "defaultMessage": "Backups", + "description": "Backups tab: stat card label for the number of backups; also the section heading above the backup list", + "sourceHash": "3334fee2c77f" + }, + "web.routes.tabs.backups.backups_table": { + "defaultMessage": "Tableau des backups", + "description": "Backups tab: accessible label of the backups table, read by screen readers; not shown as text", + "sourceHash": "d39505cf094f" + }, + "web.routes.tabs.backups.completed": { + "defaultMessage": "Terminé", + "description": "Backups tab: column heading for when each backup finished in the backups table; keep it short", + "sourceHash": "22a970d2e5b1" + }, + "web.routes.tabs.backups.last_backup": { + "defaultMessage": "Dernier backup", + "description": "Backups tab: stat card label for the time of the most recent backup; the value shows that time relative to now, or Unavailable", + "sourceHash": "8462b632f3ab" + }, + "web.routes.tabs.backups.managed_by_server_settings": { + "defaultMessage": "Géré par les paramètres du serveur", + "description": "Backups tab: small badge on a schedule row meaning the schedule is set by the server settings and not edited here; keep it short", + "sourceHash": "2dc4a8a70036" + }, + "web.routes.tabs.backups.name": { + "defaultMessage": "Nom", + "description": "Backups tab: column heading for the backup name in the backups table; keep it short", + "sourceHash": "dcd1d5223f73" + }, + "web.routes.tabs.backups.new_schedule": { + "defaultMessage": "Nouvelle planification", + "description": "Backups tab: button that opens the form to create a new backup schedule; keep it short", + "sourceHash": "3bfe90620277" + }, + "web.routes.tabs.backups.next": { + "defaultMessage": "Prochain :", + "description": "Backups tab: label in front of the next run time shown on a backup schedule row", + "sourceHash": "5693e770dd67" + }, + "web.routes.tabs.backups.next_backup": { + "defaultMessage": "Prochain backup", + "description": "Backups tab: stat card label for when the next scheduled backup will run; the value shows a relative time or a dash", + "sourceHash": "dc1d9aa1c0c7" + }, + "web.routes.tabs.backups.no_schedules_yet": { + "defaultMessage": "Aucune planification pour le moment.", + "description": "Backups tab: empty-state text in the schedules section, shown when the server has no backup schedules", + "sourceHash": "8841ef2b4040" + }, + "web.routes.tabs.backups.phase": { + "defaultMessage": "Phase", + "description": "Backups tab: column heading for the current phase of each backup in the backups table; keep it short", + "sourceHash": "46342ec1eec9" + }, + "web.routes.tabs.backups.recent_restores": { + "defaultMessage": "Restaurations récentes", + "description": "Backups tab: section heading above the list of recent restores; shown only when there are restores", + "sourceHash": "c688cf4c4dda" + }, + "web.routes.tabs.backups.restore": { + "defaultMessage": "Restore", + "description": "Backups tab: row button that opens the restore dialog for one backup; disabled when the backup cannot be restored; keep it short", + "sourceHash": "a76e13b98392" + }, + "web.routes.tabs.backups.restore_completed_relative": { + "defaultMessage": "· {time}", + "description": "Backups: restore row suffix; {time} is how long ago the restore completed", + "sourceHash": "2104a06ea560" + }, + "web.routes.tabs.backups.schedules": { + "defaultMessage": "Planifications", + "description": "Backups tab: section heading above the list of backup schedules for this server", + "sourceHash": "221ff19c904c" + }, + "web.routes.tabs.backups.size": { + "defaultMessage": "Taille", + "description": "Backups tab: column heading for the size of each backup in the backups table; keep it short", + "sourceHash": "1af851907331" + }, + "web.routes.tabs.backups.starting": { + "defaultMessage": "Démarrage…", + "description": "Backups: label of the Back up now button while the backup is being started", + "sourceHash": "bbe5fc3b9ef3" + }, + "web.routes.tabs.backups.this_server_has_multiple_backup": { + "defaultMessage": "Ce serveur a plusieurs planifications de backup — envisagez de les regrouper pour éviter les doublons.", + "description": "Backups tab: warning note shown when the server has more than one backup schedule, advising to combine them to avoid duplicate backups", + "sourceHash": "cf71a4bb2270" + }, + "web.routes.tabs.backups.total_size": { + "defaultMessage": "Taille totale", + "description": "Backups tab: stat card label for the combined size of all backups of this server", + "sourceHash": "51cdf6b25609" + }, + "web.routes.tabs.backups.unavailable": { + "defaultMessage": "Indisponible", + "description": "Backups: stat value shown when the backup or schedule data could not be loaded", + "sourceHash": "ca1844969742" + }, + "web.routes.tabs.console.loading_console": { + "defaultMessage": "Chargement de la console…", + "description": "Console tab: loading text shown while the console is being prepared", + "sourceHash": "4e9b7abcba33" + }, + "web.routes.tabs.console.no_console_available": { + "defaultMessage": "Aucune console disponible", + "description": "Console tab: title of the error card shown when the game template provides no console", + "sourceHash": "39fdec93b6d4" + }, + "web.routes.tabs.console.pty_attached": { + "defaultMessage": "— attaché —", + "description": "Console: line written into the terminal console when the terminal attaches to the server; the dashes are part of the text", + "sourceHash": "97dc1708eb31" + }, + "web.routes.tabs.console.pty_detached": { + "defaultMessage": "— détaché —", + "description": "Console: line written into the terminal console when the terminal detaches from the server; the dashes are part of the text", + "sourceHash": "547557862910" + }, + "web.routes.tabs.console.rcon_connected": { + "defaultMessage": "— connecté —", + "description": "Console: line written into the RCON console terminal when the connection opens; the dashes are part of the text", + "sourceHash": "c2a325426d76" + }, + "web.routes.tabs.console.rcon_disconnected": { + "defaultMessage": "— déconnecté —", + "description": "Console: line written into the RCON console terminal when the connection closes; the dashes are part of the text", + "sourceHash": "eb0c934f5df7" + }, + "web.routes.tabs.console.status_connecting": { + "defaultMessage": "connexion…", + "description": "Console: connection status badge while the console stream connects", + "sourceHash": "4b6394535d80" + }, + "web.routes.tabs.console.status_live": { + "defaultMessage": "EN DIRECT", + "description": "Console: connection status badge when the console stream is connected", + "sourceHash": "35e0d0360a0a" + }, + "web.routes.tabs.console.status_offline": { + "defaultMessage": "hors ligne", + "description": "Console: connection status badge when the console stream is closed", + "sourceHash": "8e2c7ac50813" + }, + "web.routes.tabs.console.status_reconnecting": { + "defaultMessage": "reconnexion…", + "description": "Console: connection status badge while the console stream reconnects", + "sourceHash": "9e2607d85619" + }, + "web.routes.tabs.console.this_game_template_doesn_t_expose_a": { + "defaultMessage": "Ce template de jeu n'expose pas de console.", + "description": "Console tab: message under the no-console title explaining that the game template does not expose a console", + "sourceHash": "10b0d0488dc0" + }, + "web.routes.tabs.console_shell.clear_terminal": { + "defaultMessage": "Vider le terminal", + "description": "Console shell: icon-only button that clears the console output; read by screen readers; keep it short", + "sourceHash": "953dec81dcda" + }, + "web.routes.tabs.console_shell.download_terminal_buffer": { + "defaultMessage": "Télécharger le tampon du terminal", + "description": "Console shell: icon-only button that downloads the console output as a file; read by screen readers; keep it short", + "sourceHash": "b33ceeb9b80c" + }, + "web.routes.tabs.console_shell.send": { + "defaultMessage": "Envoyer", + "description": "Console shell: button that sends the typed command to the server; disabled while the command box is empty; keep it short", + "sourceHash": "f6f4688ff23d" + }, + "web.routes.tabs.console_shell.toggle_fullscreen": { + "defaultMessage": "Basculer en plein écran", + "description": "Console shell: icon-only button that switches the console between normal and full-screen view; read by screen readers; keep it short", + "sourceHash": "c13ff73b9098" + }, + "web.routes.tabs.console_shell.type_a_command_recalls_history": { + "defaultMessage": "Saisissez une commande… (↑/↓ pour l'historique)", + "description": "Console shell: placeholder text in the command box, shown before typing; the arrow keys recall earlier commands", + "sourceHash": "087e988c4f5e" + }, + "web.routes.tabs.events.empty_all": { + "defaultMessage": "Aucun événement pour le moment.", + "description": "Events: shown when the server has no events", + "sourceHash": "80c652c4eeec" + }, + "web.routes.tabs.events.empty_info": { + "defaultMessage": "Aucun événement d'information.", + "description": "Events: shown when there are no info events", + "sourceHash": "9df70abab434" + }, + "web.routes.tabs.events.empty_warnings": { + "defaultMessage": "Aucun avertissement ni erreur.", + "description": "Events: shown when there are no warning or error events", + "sourceHash": "aeab012d130d" + }, + "web.routes.tabs.events.events": { + "defaultMessage": "Événements", + "description": "Events tab: heading of the events card that lists the server's events, with filter buttons beside it", + "sourceHash": "8d14f6e72de8" + }, + "web.routes.tabs.events.filter_all": { + "defaultMessage": "Tous", + "description": "Events: filter button showing every event", + "sourceHash": "a52ace420f21" + }, + "web.routes.tabs.events.filter_info": { + "defaultMessage": "Info", + "description": "Events: filter button showing info events only", + "sourceHash": "170322a32f3c" + }, + "web.routes.tabs.events.filter_warnings": { + "defaultMessage": "Avertissements", + "description": "Events: filter button showing warning and error events only", + "sourceHash": "0e04cd10f9af" + }, + "web.routes.tabs.files.back_to_files": { + "defaultMessage": "Retour aux fichiers", + "description": "Files tab: icon-only button that returns from a file view to the file tree; read by screen readers", + "sourceHash": "f911e3dbe7ed" + }, + "web.routes.tabs.files.cancel": { + "defaultMessage": "Annuler", + "description": "Files tab: button in the name prompt dialog that closes it without creating anything; keep it short", + "sourceHash": "19766ed6ccb2" + }, + "web.routes.tabs.files.config_yaml": { + "defaultMessage": "config.yaml", + "description": "Files tab: example placeholder in the new file name field; an example name, not a default", + "sourceHash": "d8d0422389f0" + }, + "web.routes.tabs.files.create": { + "defaultMessage": "Créer", + "description": "Files tab: confirm button of the new file and new folder prompts that creates the item; keep it short", + "sourceHash": "4759498ac2a7" + }, + "web.routes.tabs.files.delete": { + "defaultMessage": "Supprimer", + "description": "Files tab: red button that deletes the selected file or folder, and the confirm button of the delete dialog; keep it short", + "sourceHash": "e2d0a54968ea" + }, + "web.routes.tabs.files.delete_entry_aria": { + "defaultMessage": "Supprimer {name}", + "description": "Files: aria-label of the delete button on a file or folder row; {name} is the file or folder name", + "sourceHash": "3cf8146e529f" + }, + "web.routes.tabs.files.delete_entry_title": { + "defaultMessage": "Supprimer {name} ?", + "description": "Files: title of the confirm dialog for deleting a file or folder; {name} is the file or folder name", + "sourceHash": "a4b982a9e0bc" + }, + "web.routes.tabs.files.delete_file_body": { + "defaultMessage": "Cette action supprime définitivement le fichier du volume de données du serveur. Elle est irréversible.", + "description": "Files: body of the confirm dialog for deleting a file", + "sourceHash": "ff99b9bdbe77" + }, + "web.routes.tabs.files.delete_folder_body": { + "defaultMessage": "Cette action supprime définitivement le dossier et tout ce qu'il contient du volume de données du serveur. Elle est irréversible.", + "description": "Files: body of the confirm dialog for deleting a folder", + "sourceHash": "40eae244b529" + }, + "web.routes.tabs.files.discard_unsaved_changes": { + "defaultMessage": "Vous avez des modifications non enregistrées. Les abandonner et continuer ?", + "description": "Files: confirm prompt shown before discarding unsaved file edits", + "sourceHash": "2c47f188bf8e" + }, + "web.routes.tabs.files.download": { + "defaultMessage": "Télécharger", + "description": "Files tab: button that downloads the selected file to the user's computer; keep it short", + "sourceHash": "d6eafe823591" + }, + "web.routes.tabs.files.empty_folder": { + "defaultMessage": "Dossier vide", + "description": "Files tab: text shown in the file list when the current folder has no entries", + "sourceHash": "ca1fd4548524" + }, + "web.routes.tabs.files.file_name": { + "defaultMessage": "Nom du fichier", + "description": "Files tab: field label for the name of a new file in the new file prompt", + "sourceHash": "3efed3de9d4b" + }, + "web.routes.tabs.files.files": { + "defaultMessage": "Fichiers", + "description": "Files tab: label beside the back button in the file pane header, naming the files area", + "sourceHash": "abc7e9892806" + }, + "web.routes.tabs.files.folder_name": { + "defaultMessage": "Nom du dossier", + "description": "Files tab: field label for the name of a new folder in the new folder prompt", + "sourceHash": "14d34edf50ef" + }, + "web.routes.tabs.files.modified": { + "defaultMessage": "· modifié", + "description": "Files tab: warning marker shown beside the open file name when it has unsaved edits", + "sourceHash": "e2109d543007" + }, + "web.routes.tabs.files.my_folder": { + "defaultMessage": "mon-dossier", + "description": "Files tab: example placeholder in the new folder name field; an example name, not a default", + "sourceHash": "48b510ddef56" + }, + "web.routes.tabs.files.names_can_t_contain_or_be_or": { + "defaultMessage": "Les noms ne peuvent pas contenir \"/\" ni être \".\" ou \"..\".", + "description": "Files tab: validation error under a file or folder name field when the name contains a slash or is a single or double dot", + "sourceHash": "d7b0f47525eb" + }, + "web.routes.tabs.files.new_file": { + "defaultMessage": "Nouveau fichier", + "description": "Files tab: button that opens the prompt to create a new file, and the title of that prompt dialog; keep it short", + "sourceHash": "44dedd0f97fe" + }, + "web.routes.tabs.files.new_folder": { + "defaultMessage": "Nouveau dossier", + "description": "Files tab: button that opens the prompt to create a new folder, and the title of that prompt dialog; keep it short", + "sourceHash": "cf28f49ec597" + }, + "web.routes.tabs.files.parent_directory": { + "defaultMessage": "..", + "description": "Files: row that opens the parent folder of the current folder", + "sourceHash": "5ec1f7e700f3" + }, + "web.routes.tabs.files.path_separator": { + "defaultMessage": "/", + "description": "Files: slash drawn between breadcrumb segments", + "sourceHash": "8a5edab28263" + }, + "web.routes.tabs.files.refresh": { + "defaultMessage": "Actualiser", + "description": "Files tab: icon-only button that reloads the file list; read by screen readers; keep it short", + "sourceHash": "0e9161011702" + }, + "web.routes.tabs.files.root_crumb": { + "defaultMessage": "/", + "description": "Files: breadcrumb button that jumps to the server data root, shown as a slash", + "sourceHash": "8a5edab28263" + }, + "web.routes.tabs.files.save": { + "defaultMessage": "Enregistrer", + "description": "Files tab: button that saves edits to the open file; shows a spinner while saving; keep it short", + "sourceHash": "1509f561f241" + }, + "web.routes.tabs.files.select_a_file_to_edit": { + "defaultMessage": "Sélectionnez un fichier à modifier.", + "description": "Files tab: empty-state text in the editor pane shown when no file is selected", + "sourceHash": "c0e9ac91b043" + }, + "web.routes.tabs.files.upload": { + "defaultMessage": "Importer", + "description": "Files tab: button that uploads a file to the current folder; shows a spinner while the upload runs; keep it short", + "sourceHash": "865e89de78d9" + }, + "web.routes.tabs.files.working": { + "defaultMessage": "En cours…", + "description": "Files: label of the dialog submit button while the request is running", + "sourceHash": "5474eef8d0f1" + }, + "web.routes.tabs.logs.connected_waiting_for_output": { + "defaultMessage": "Connecté — en attente de sortie…", + "description": "Logs: shown once the log stream is connected and no output has arrived yet", + "sourceHash": "7603ae5a811e" + }, + "web.routes.tabs.logs.connecting_to_log_stream": { + "defaultMessage": "Connexion au flux de journaux…", + "description": "Logs: shown while the log stream connection is being opened", + "sourceHash": "636c9c7b82de" + }, + "web.routes.tabs.logs.container_output": { + "defaultMessage": "Sortie du conteneur", + "description": "Logs tab: tab name for the container output of the pod, the setup and startup output; when no log file exists it is shown as plain text", + "sourceHash": "70cc2d2159a0" + }, + "web.routes.tabs.logs.couldn_t_reach_the_agent_to_tail_the": { + "defaultMessage": "Impossible de joindre l'agent pour suivre le fichier de journal du jeu. La sortie du conteneur reste disponible.", + "description": "Logs tab: message under the game log unavailable title saying the agent could not be reached and container output is still available", + "sourceHash": "9d57a6c3d50c" + }, + "web.routes.tabs.logs.download": { + "defaultMessage": "Télécharger", + "description": "Logs tab: button that downloads the log as a file; keep it short", + "sourceHash": "d6eafe823591" + }, + "web.routes.tabs.logs.downloading_game_files_and_starting_up": { + "defaultMessage": "Téléchargement des fichiers du jeu et démarrage. Le premier démarrage peut prendre quelques minutes ; la sortie d'installation s'affiche ici au fur et à mesure qu'elle arrive du conteneur.", + "description": "Logs tab: message shown while the server starts for the first time, saying install output streams here from the container", + "sourceHash": "159dec9b65b9" + }, + "web.routes.tabs.logs.filter": { + "defaultMessage": "filtrer…", + "description": "Logs tab: placeholder in the search box that filters the log lines by the text typed", + "sourceHash": "0c823e78f0d3" + }, + "web.routes.tabs.logs.follow_the_pod_s_setup_game_container": { + "defaultMessage": "Suit la sortie de configuration du pod et du conteneur de jeu (installation et démarrage)", + "description": "Logs tab: tooltip on the plain container output label explaining it holds the pod setup and game container output for install and startup", + "sourceHash": "5610438fb227" + }, + "web.routes.tabs.logs.game_log": { + "defaultMessage": "Journal du jeu", + "description": "Logs tab: tab name for the game log file written by the game itself", + "sourceHash": "e3a5d3ab99e5" + }, + "web.routes.tabs.logs.game_log_unavailable": { + "defaultMessage": "Journal du jeu indisponible", + "description": "Logs tab: title of the notice shown when the game log file cannot be read", + "sourceHash": "93fe026e865c" + }, + "web.routes.tabs.logs.level_all": { + "defaultMessage": "Tous", + "description": "Logs: tab label of the log level filter that shows every level", + "sourceHash": "a52ace420f21" + }, + "web.routes.tabs.logs.level_count": { + "defaultMessage": "{count}", + "description": "Logs: number of lines shown after a log level tab label; {count} is that count", + "sourceHash": "9307b2fa1323" + }, + "web.routes.tabs.logs.level_debug": { + "defaultMessage": "Debug", + "description": "Logs: tab label of the DEBUG log level filter", + "sourceHash": "1a03bd2fd107" + }, + "web.routes.tabs.logs.level_error": { + "defaultMessage": "Erreur", + "description": "Logs: tab label of the ERROR log level filter", + "sourceHash": "54a0e8c17ebb" + }, + "web.routes.tabs.logs.level_info": { + "defaultMessage": "Info", + "description": "Logs: tab label of the INFO log level filter", + "sourceHash": "170322a32f3c" + }, + "web.routes.tabs.logs.level_warn": { + "defaultMessage": "Avertissement", + "description": "Logs: tab label of the WARN log level filter", + "sourceHash": "8448814dc5ca" + }, + "web.routes.tabs.logs.lines": { + "defaultMessage": "lignes", + "description": "Logs tab: word lines after the count shown beside the log; the number is how many lines remain after filtering", + "sourceHash": "5ea44c3961f1" + }, + "web.routes.tabs.logs.log_level": { + "defaultMessage": "Niveau de journal", + "description": "Logs tab: accessible label of the tabs that filter log lines by level, with an All option; not shown as text", + "sourceHash": "886e60232b66" + }, + "web.routes.tabs.logs.log_source": { + "defaultMessage": "Source du journal", + "description": "Logs tab: accessible label of the tabs that choose between container output and game log; not shown as text", + "sourceHash": "76a7db76dcfe" + }, + "web.routes.tabs.logs.no_container_output_was_captured_check": { + "defaultMessage": "Aucune sortie de conteneur capturée. Consultez les événements de la vue d'ensemble pour les erreurs de téléchargement d'image ou de planification.", + "description": "Logs tab: explanation under the empty log state saying no container output was captured and pointing to the Overview events for pull or scheduling errors", + "sourceHash": "9c9a3f02f9b0" + }, + "web.routes.tabs.logs.server_failed_to_start": { + "defaultMessage": "Le serveur n'a pas pu démarrer.", + "description": "Logs: shown when the server failed to start and gives no reason", + "sourceHash": "78eca9f4eb28" + }, + "web.routes.tabs.logs.starting_the_server": { + "defaultMessage": "Démarrage du serveur…", + "description": "Logs: shown while the server is starting and no progress message is available yet", + "sourceHash": "d4536e008e22" + }, + "web.routes.tabs.logs.use_container_output": { + "defaultMessage": "Utiliser la sortie du conteneur", + "description": "Logs tab: button that switches the log view to container output; keep it short", + "sourceHash": "131f47918284" + }, + "web.routes.tabs.modpacks.banner_installed": { + "defaultMessage": "{title} installé — {count, plural, one {# mod} other {# mods}}.", + "description": "Modpacks: banner shown after a modpack's dependency mods are installed; {title} is the modpack title, {count} the number of mods installed", + "sourceHash": "76652b0e008e" + }, + "web.routes.tabs.modpacks.banner_set": { + "defaultMessage": "Modpack {title} défini. Le serveur redémarre pour l'installer.", + "description": "Modpacks: banner shown after a modpack is set on the server; {title} is the modpack title", + "sourceHash": "1763d7284d89" + }, + "web.routes.tabs.modpacks.browse_modpacks": { + "defaultMessage": "Parcourir les modpacks", + "description": "Modpacks tab: heading of the section where modpacks are browsed from a registry", + "sourceHash": "ee1646014610" + }, + "web.routes.tabs.modpacks.category_adventure": { + "defaultMessage": "Aventure", + "description": "Modpacks: category filter chip for adventure modpacks", + "sourceHash": "9a3c16424750" + }, + "web.routes.tabs.modpacks.category_magic": { + "defaultMessage": "Magie", + "description": "Modpacks: category filter chip for magic modpacks", + "sourceHash": "ed1a5a81b812" + }, + "web.routes.tabs.modpacks.category_multiplayer": { + "defaultMessage": "Multijoueur", + "description": "Modpacks: category filter chip for multiplayer modpacks", + "sourceHash": "563370a7b0ef" + }, + "web.routes.tabs.modpacks.category_optimization": { + "defaultMessage": "Optimisé", + "description": "Modpacks: category filter chip for optimization modpacks", + "sourceHash": "a0348c11ea88" + }, + "web.routes.tabs.modpacks.category_technology": { + "defaultMessage": "Technologie", + "description": "Modpacks: category filter chip for technology modpacks", + "sourceHash": "d0da7ab8bc92" + }, + "web.routes.tabs.modpacks.dismiss": { + "defaultMessage": "fermer", + "description": "Modpacks tab: icon-only button that closes the result banner; read by screen readers; keep it short", + "sourceHash": "5880072b27d4" + }, + "web.routes.tabs.modpacks.error_not_permitted": { + "defaultMessage": "Votre rôle ne permet pas de gérer ce serveur.", + "description": "Modpacks: error shown when the user's role cannot manage this server", + "sourceHash": "58602129606a" + }, + "web.routes.tabs.modpacks.error_request_failed": { + "defaultMessage": "échec de la requête", + "description": "Modpacks: fallback error text when the error has no message", + "sourceHash": "fbbc1b5ba864" + }, + "web.routes.tabs.modpacks.error_request_failed_status": { + "defaultMessage": "échec de la requête ({status})", + "description": "Modpacks: fallback error text for an API error with no body; {status} is the HTTP status code", + "sourceHash": "f6c6e4104a40" + }, + "web.routes.tabs.modpacks.install": { + "defaultMessage": "Installer", + "description": "Modpacks: label of the button that installs a modpack", + "sourceHash": "569ca49f4aaf" + }, + "web.routes.tabs.modpacks.installing": { + "defaultMessage": "Installation…", + "description": "Modpacks: label of the install button while this modpack is installing", + "sourceHash": "530bcc355f0a" + }, + "web.routes.tabs.modpacks.installing_a_modpack_either_pins_it_on": { + "defaultMessage": "Installer un modpack l'épingle sur le serveur (et le redémarre) ou ajoute tous ses mods, selon le registre.", + "description": "Modpacks tab: hint under the modpacks heading saying an install either pins the modpack and restarts the server, or adds all its mods, depending on the registry", + "sourceHash": "f1bcb7792cdd" + }, + "web.routes.tabs.modpacks.requires_operator_role": { + "defaultMessage": "Nécessite le rôle opérateur", + "description": "Modpacks: tooltip on a disabled install control for users without the operator role", + "sourceHash": "e454d6eabdee" + }, + "web.routes.tabs.mods.a_registry": { + "defaultMessage": "un registre", + "description": "Mods: fallback that reads 'a registry' when the provider has no name", + "sourceHash": "ebdfd1cc34de" + }, + "web.routes.tabs.mods.accepted_extensions": { + "defaultMessage": "Acceptés : {extensions} · dépose dans le volume de mods du loader actif", + "description": "Mods: upload hint listing the accepted file extensions; {extensions} is a comma-separated list of extensions", + "sourceHash": "17ef4fbd60e3" + }, + "web.routes.tabs.mods.add": { + "defaultMessage": "Ajouter", + "description": "Mods: label of the button that adds a mod to the list", + "sourceHash": "9fd728c66c9a" + }, + "web.routes.tabs.mods.added": { + "defaultMessage": "Ajouté", + "description": "Mods: shown when a mod is already in the list (chip or button label)", + "sourceHash": "6b02e0d363a4" + }, + "web.routes.tabs.mods.allowed_hosts": { + "defaultMessage": "Hôtes autorisés :", + "description": "Mods tab: hint under the download URL form; the list after the label shows the host names the URL may come from", + "sourceHash": "cf892aa8dc29" + }, + "web.routes.tabs.mods.available": { + "defaultMessage": "disponible", + "description": "Mods tab: word after the latest version number in the update badge, meaning a newer version is available; keep it short", + "sourceHash": "ddd9818abacf" + }, + "web.routes.tabs.mods.back_to_installed_mods": { + "defaultMessage": "Retour aux mods installés", + "description": "Mods tab: accessible label of the back button that returns to the installed mods list; read by screen readers; keep it short", + "sourceHash": "dc345d7906a9" + }, + "web.routes.tabs.mods.back_to_selected_mods": { + "defaultMessage": "Retour aux mods sélectionnés", + "description": "Mods tab: accessible label of the back button that returns from the registry browser to the selected mods list; keep it short", + "sourceHash": "d2cfda0312e3" + }, + "web.routes.tabs.mods.banner_installed": { + "defaultMessage": "{name} installé", + "description": "Mods: banner shown after a mod is installed; {name} is the mod name", + "sourceHash": "fe0cd721497d" + }, + "web.routes.tabs.mods.banner_removed": { + "defaultMessage": "{name} supprimé", + "description": "Mods: banner shown after a mod is removed; {name} is the mod file name", + "sourceHash": "ad6173859d5e" + }, + "web.routes.tabs.mods.banner_saved_restart": { + "defaultMessage": "Enregistré — le serveur redémarrera pour appliquer {count, plural, one {# mod} other {# mods}}.", + "description": "Mods: banner shown after the mod list is saved; {count} is the number of mods applied on restart", + "sourceHash": "7ef17bac67fc" + }, + "web.routes.tabs.mods.banner_updated_count": { + "defaultMessage": "{count, plural, one {# mod mis à jour} other {# mods mis à jour}}", + "description": "Mods: banner shown after updating mods; {count} is the number of mods updated", + "sourceHash": "c3c404a7d802" + }, + "web.routes.tabs.mods.banner_updated_to": { + "defaultMessage": "Mis à jour vers {name}", + "description": "Mods: banner shown after a mod is updated to a new file; {name} is the mod name", + "sourceHash": "09f4f367a189" + }, + "web.routes.tabs.mods.banner_uploaded": { + "defaultMessage": "{name} importé", + "description": "Mods: banner shown after a mod file is uploaded; {name} is the mod name", + "sourceHash": "12296be9896d" + }, + "web.routes.tabs.mods.browse": { + "defaultMessage": "Parcourir", + "description": "Mods tab: word Browse that starts the registry browse heading and button; the registry name follows it; keep it short", + "sourceHash": "3227aa966625" + }, + "web.routes.tabs.mods.cancel": { + "defaultMessage": "Annuler", + "description": "Mods tab: ghost button that closes the download URL or upload form without installing; keep it short", + "sourceHash": "19766ed6ccb2" + }, + "web.routes.tabs.mods.category_adventure": { + "defaultMessage": "Aventure", + "description": "Mods: category filter chip for adventure mods", + "sourceHash": "9a3c16424750" + }, + "web.routes.tabs.mods.category_library": { + "defaultMessage": "Bibliothèque", + "description": "Mods: category filter chip for library mods", + "sourceHash": "dc20b3d5d2cd" + }, + "web.routes.tabs.mods.category_magic": { + "defaultMessage": "Magie", + "description": "Mods: category filter chip for magic mods", + "sourceHash": "ed1a5a81b812" + }, + "web.routes.tabs.mods.category_optimization": { + "defaultMessage": "Performance", + "description": "Mods: category filter chip for performance mods", + "sourceHash": "442aded87a55" + }, + "web.routes.tabs.mods.category_storage": { + "defaultMessage": "Stockage", + "description": "Mods: category filter chip for storage mods", + "sourceHash": "a69c4dece144" + }, + "web.routes.tabs.mods.category_technology": { + "defaultMessage": "Tech", + "description": "Mods: category filter chip for technology mods", + "sourceHash": "d0da7ab8bc92" + }, + "web.routes.tabs.mods.category_utility": { + "defaultMessage": "Utilitaires", + "description": "Mods: category filter chip for utility mods", + "sourceHash": "9450d833fa77" + }, + "web.routes.tabs.mods.category_worldgen": { + "defaultMessage": "Génération du monde", + "description": "Mods: category filter chip for world generation mods", + "sourceHash": "bcf2c9079992" + }, + "web.routes.tabs.mods.check_updates": { + "defaultMessage": "Vérifier les mises à jour", + "description": "Mods: label of the button that checks installed mods for updates", + "sourceHash": "2e6f94a6f87f" + }, + "web.routes.tabs.mods.checking": { + "defaultMessage": "Vérification…", + "description": "Mods: label of the check-for-updates button while checking", + "sourceHash": "ec963ffc911b" + }, + "web.routes.tabs.mods.couldnt_load": { + "defaultMessage": "Impossible de charger les mods", + "description": "Mods: heading shown when the mod list fails to load", + "sourceHash": "259d4af0e78c" + }, + "web.routes.tabs.mods.discard": { + "defaultMessage": "Abandonner", + "description": "Mods tab: footer button that throws away unsaved mod list changes; disabled when nothing changed; keep it short", + "sourceHash": "eb1a70e39274" + }, + "web.routes.tabs.mods.dismiss": { + "defaultMessage": "fermer", + "description": "Mods tab: icon-only button that closes the message banner; read by screen readers; keep it short", + "sourceHash": "5880072b27d4" + }, + "web.routes.tabs.mods.download_url": { + "defaultMessage": "URL de téléchargement", + "description": "Mods tab: field label for the download URL of a mod; the red asterisk marks the field as required", + "sourceHash": "28951f9c69ca" + }, + "web.routes.tabs.mods.drop_mod_file": { + "defaultMessage": "Déposez un fichier de mod ici, ou cliquez pour parcourir", + "description": "Mods: prompt in the upload drop zone when no file is chosen", + "sourceHash": "cd67e77d8f78" + }, + "web.routes.tabs.mods.empty_hint_browse": { + "defaultMessage": "Utilisez “Installer un mod” pour chercher dans un registre, ajouter un mod depuis une URL ou importer un fichier.", + "description": "Mods: hint under the empty list when the user can browse, install from a URL and upload", + "sourceHash": "a33e709a3906" + }, + "web.routes.tabs.mods.empty_hint_install": { + "defaultMessage": "Utilisez “Installer un mod” pour ajouter un mod depuis une URL ou importer un fichier.", + "description": "Mods: hint under the empty list when the user can install from a URL or upload", + "sourceHash": "a13834497d16" + }, + "web.routes.tabs.mods.empty_hint_upload": { + "defaultMessage": "Utilisez “Installer un mod” pour importer un fichier.", + "description": "Mods: hint under the empty list when the user can only upload a file", + "sourceHash": "d1ad6a6896ca" + }, + "web.routes.tabs.mods.error_not_permitted": { + "defaultMessage": "Votre rôle ne permet pas de gérer les mods.", + "description": "Mods: error shown when the user's role cannot manage mods", + "sourceHash": "4401fc01014b" + }, + "web.routes.tabs.mods.error_request_failed": { + "defaultMessage": "échec de la requête", + "description": "Mods: fallback error text when the error has no message", + "sourceHash": "fbbc1b5ba864" + }, + "web.routes.tabs.mods.error_request_failed_status": { + "defaultMessage": "échec de la requête ({status})", + "description": "Mods: fallback error text for an API error with no body; {status} is the HTTP status code", + "sourceHash": "f6c6e4104a40" + }, + "web.routes.tabs.mods.error_retry_separator": { + "defaultMessage": "·", + "description": "Mods: separator between the list error and the retry button", + "sourceHash": "a137f17a19a0" + }, + "web.routes.tabs.mods.filename": { + "defaultMessage": "Nom du fichier", + "description": "Mods tab: field label for the optional file name the mod is saved under", + "sourceHash": "971b974235a8" + }, + "web.routes.tabs.mods.from_the_server_it_will_be_gone_on_the": { + "defaultMessage": "du serveur ? Il sera supprimé au prochain redémarrage.", + "description": "Mods tab: rest of the removal confirmation sentence after the mod name, saying the mod will be gone on the next restart", + "sourceHash": "0a57c853719d" + }, + "web.routes.tabs.mods.https_cdn_modrinth_com_mod_jar": { + "defaultMessage": "https://cdn.modrinth.com/…/mod.jar", + "description": "Mods tab: example placeholder in the download URL field showing the expected form; an example address, not a default", + "sourceHash": "1494bff7d72f" + }, + "web.routes.tabs.mods.id_id": { + "defaultMessage": "ID {id}", + "description": "Mods: label showing a mod's registry ID; {id} is the ID", + "sourceHash": "ac76cc7fdc0b" + }, + "web.routes.tabs.mods.install": { + "defaultMessage": "Installer", + "description": "Mods: label of the button that installs a mod", + "sourceHash": "569ca49f4aaf" + }, + "web.routes.tabs.mods.install_hint_browse": { + "defaultMessage": "Parcourez un registre et installez pour la version et le loader de ce serveur.", + "description": "Mods: hint under the install heading in browse mode", + "sourceHash": "2a08d609019f" + }, + "web.routes.tabs.mods.install_hint_upload": { + "defaultMessage": "Importez un fichier de mod depuis votre ordinateur dans le répertoire mods du serveur.", + "description": "Mods: hint under the install heading in upload mode", + "sourceHash": "d2fddfc8bc49" + }, + "web.routes.tabs.mods.install_hint_url": { + "defaultMessage": "Téléchargez un mod depuis une URL dans le répertoire mods du serveur.", + "description": "Mods: hint under the install heading in URL download mode", + "sourceHash": "031ea980bc68" + }, + "web.routes.tabs.mods.install_mod": { + "defaultMessage": "Installer un mod", + "description": "Mods tab: button that opens the form to add a mod; disabled for users without the operator role; keep it short", + "sourceHash": "db3370a43178" + }, + "web.routes.tabs.mods.install_mods": { + "defaultMessage": "Installer des mods", + "description": "Mods tab: heading of the page for installing mods, shown above the install options", + "sourceHash": "6439ff3ddb72" + }, + "web.routes.tabs.mods.installed_count": { + "defaultMessage": "{count} installés", + "description": "Mods: heading with the number of installed mods; {count} is that number", + "sourceHash": "5b9b7385cee6" + }, + "web.routes.tabs.mods.installed_mods": { + "defaultMessage": "Mods installés", + "description": "Mods tab: visible text of the back button that returns to the installed mods list; keep it short", + "sourceHash": "0c5391923000" + }, + "web.routes.tabs.mods.installing": { + "defaultMessage": "Installation…", + "description": "Mods: label of the install button while the mod installs", + "sourceHash": "530bcc355f0a" + }, + "web.routes.tabs.mods.lands_in_loader_volume": { + "defaultMessage": "Déposé dans le volume de mods du loader actif", + "description": "Mods: upload hint saying the file lands in the active loader's mod volume", + "sourceHash": "54d1ee193fe6" + }, + "web.routes.tabs.mods.loading": { + "defaultMessage": "Chargement…", + "description": "Mods: heading shown while the mod list loads", + "sourceHash": "ba3bbbe10d8b" + }, + "web.routes.tabs.mods.loading_versions": { + "defaultMessage": "Chargement des versions…", + "description": "Mods tab: loading text in a mod row's version area while the list of versions is fetched", + "sourceHash": "9780e2c99c1b" + }, + "web.routes.tabs.mods.marked_for_removal": { + "defaultMessage": "Marqué pour suppression", + "description": "Mods tab: badge on a mod row showing it is marked for removal and will be removed when saved; keep it short", + "sourceHash": "41f5c48631b2" + }, + "web.routes.tabs.mods.mode_browse_registry": { + "defaultMessage": "Parcourir le registre", + "description": "Mods: install panel tab that browses a mod registry", + "sourceHash": "f18144f60c3e" + }, + "web.routes.tabs.mods.mode_from_url": { + "defaultMessage": "Depuis une URL", + "description": "Mods: install panel tab that installs a mod from a URL", + "sourceHash": "8d71559c9817" + }, + "web.routes.tabs.mods.mode_upload_file": { + "defaultMessage": "Importer un fichier", + "description": "Mods: install panel tab that uploads a mod file", + "sourceHash": "d99bcd78b586" + }, + "web.routes.tabs.mods.modified_relative": { + "defaultMessage": "· {time}", + "description": "Mods: suffix after a mod's version showing when it was modified; {time} is how long ago", + "sourceHash": "2104a06ea560" + }, + "web.routes.tabs.mods.must_be_an_http_s_url": { + "defaultMessage": "Doit être une URL http(s).", + "description": "Mods tab: validation error under the download URL field when the text is not an http or https address", + "sourceHash": "e96e1dfb0294" + }, + "web.routes.tabs.mods.new_version": { + "defaultMessage": "nouvelle version", + "description": "Mods: stand-in for the latest version number when it is unknown", + "sourceHash": "30e4af48343e" + }, + "web.routes.tabs.mods.no_compatible_files": { + "defaultMessage": "Aucun fichier compatible.", + "description": "Mods tab: text in a mod row's version area when no file fits the server's version and loader", + "sourceHash": "88bed053e063" + }, + "web.routes.tabs.mods.no_mods_installed": { + "defaultMessage": "Aucun mod installé.", + "description": "Mods tab: empty-state text shown when no mods are installed; a hint below it explains how to add one", + "sourceHash": "06423755f449" + }, + "web.routes.tabs.mods.no_mods_selected": { + "defaultMessage": "Aucun mod sélectionné.", + "description": "Mods tab: empty-state text in the selected mods list when no mods are selected", + "sourceHash": "b6b928275568" + }, + "web.routes.tabs.mods.optional_derived_from_the_url": { + "defaultMessage": "facultatif — déduit de l’URL", + "description": "Mods tab: placeholder in the file name field saying the name is taken from the URL when the field is left empty", + "sourceHash": "d72b9925e1ea" + }, + "web.routes.tabs.mods.or_add_a_mod_id": { + "defaultMessage": "ou ajoutez un identifiant de mod.", + "description": "Mods tab: end of the empty-state hint that tells the user to browse a registry or add a mod ID", + "sourceHash": "6e225d8d477d" + }, + "web.routes.tabs.mods.paste_mod_id": { + "defaultMessage": "Collez un identifiant de mod…", + "description": "Mods: placeholder of the mod ID input when the provider has no name", + "sourceHash": "7171ec7e7986" + }, + "web.routes.tabs.mods.paste_provider_mod_id": { + "defaultMessage": "Collez un identifiant de mod {provider}…", + "description": "Mods: placeholder of the mod ID input when the provider has a name; {provider} is the provider name", + "sourceHash": "e0ee8de518c4" + }, + "web.routes.tabs.mods.placed_outside_the_panel_no_update": { + "defaultMessage": "Placé hors du panneau — pas de vérification des mises à jour", + "description": "Mods tab: accessible label of the grey tag on a mod placed outside the panel; the tag tells the user update checks do not run for it", + "sourceHash": "9a66689e4b57" + }, + "web.routes.tabs.mods.provider_mod_ids": { + "defaultMessage": "Identifiants de mods {provider}", + "description": "Mods: subtitle line naming the mod provider whose mod IDs are listed; {provider} is the provider name", + "sourceHash": "461ded921f6b" + }, + "web.routes.tabs.mods.refresh": { + "defaultMessage": "Actualiser", + "description": "Mods tab: icon-only button that reloads the mod list and checks for updates; read by screen readers; keep it short", + "sourceHash": "0e9161011702" + }, + "web.routes.tabs.mods.registry": { + "defaultMessage": "registre", + "description": "Mods: fallback name of the mod registry when the provider has no name", + "sourceHash": "872491a30d60" + }, + "web.routes.tabs.mods.remove": { + "defaultMessage": "Supprimer", + "description": "Mods tab: the word Remove, used as the confirm button of the removal dialog, as the start of its sentence, and as a row button marking a mod for removal; keep it short", + "sourceHash": "c3812fc4acb8" + }, + "web.routes.tabs.mods.remove_mod": { + "defaultMessage": "Supprimer le mod", + "description": "Mods tab: title of the confirmation dialog that asks before a mod is removed from the server", + "sourceHash": "5e305a099a37" + }, + "web.routes.tabs.mods.remove_mod_aria": { + "defaultMessage": "Supprimer {name}", + "description": "Mods: aria-label of the remove button on a mod row; {name} is the mod name", + "sourceHash": "e6a3c4a1250a" + }, + "web.routes.tabs.mods.required_marker": { + "defaultMessage": "*", + "description": "Mods: marker shown after the label of a required field", + "sourceHash": "684888c0ebb1" + }, + "web.routes.tabs.mods.requires_operator_role": { + "defaultMessage": "Nécessite le rôle opérateur", + "description": "Mods: tooltip on a disabled control for users without the operator role", + "sourceHash": "e454d6eabdee" + }, + "web.routes.tabs.mods.retry": { + "defaultMessage": "réessayer", + "description": "Mods: label of the retry button after a failed list load", + "sourceHash": "06b29bb31881" + }, + "web.routes.tabs.mods.save_changes": { + "defaultMessage": "Enregistrer les modifications", + "description": "Mods: label of the button that saves the mod list", + "sourceHash": "dd0ae7a5cbcf" + }, + "web.routes.tabs.mods.saving": { + "defaultMessage": "Enregistrement…", + "description": "Mods: label of the save button while the mod list is saving", + "sourceHash": "23e39291d613" + }, + "web.routes.tabs.mods.saving_restarts_the_server_to_apply_the": { + "defaultMessage": "L’enregistrement redémarre le serveur pour appliquer la nouvelle liste de mods.", + "description": "Mods tab: warning note above the save bar saying that saving restarts the server to apply the new mod list", + "sourceHash": "0b107daa8386" + }, + "web.routes.tabs.mods.selected_count": { + "defaultMessage": "{count} sélectionnés", + "description": "Mods: heading with how many mods are selected; {count} is that number", + "sourceHash": "529aacfdfd2b" + }, + "web.routes.tabs.mods.selected_mods": { + "defaultMessage": "Mods sélectionnés", + "description": "Mods tab: visible text of the back button in the registry browser, and the heading of the selected mods list; keep it short", + "sourceHash": "2218390c16e5" + }, + "web.routes.tabs.mods.this_game_s_server_downloads_its_own": { + "defaultMessage": "Le serveur de ce jeu télécharge ses propres mods. Sélectionnez-les ici : Gameplane transmet la liste au serveur au lancement.", + "description": "Mods tab: info note at the top of the by-ID mods view saying the game server downloads its own mods and Gameplane passes the list at launch", + "sourceHash": "ead4904d423c" + }, + "web.routes.tabs.mods.this_registry_s_downloads_need_your_own": { + "defaultMessage": "Les téléchargements de ce registre nécessitent vos propres identifiants de compte ajoutés à l’URL", + "description": "Mods tab: tooltip on the Use URL form button explaining the registry needs the user's own account credentials appended to the URL", + "sourceHash": "4225bb56aeaf" + }, + "web.routes.tabs.mods.undo": { + "defaultMessage": "Annuler", + "description": "Mods tab: row button that cancels a pending removal of a mod; keep it short", + "sourceHash": "a8283ade3185" + }, + "web.routes.tabs.mods.unmanaged": { + "defaultMessage": "non géré", + "description": "Mods tab: grey tag on a mod that was placed outside the panel, meaning no update checks run for it; keep it short", + "sourceHash": "183b72743e9b" + }, + "web.routes.tabs.mods.update": { + "defaultMessage": "Mettre à jour", + "description": "Mods tab: button on a mod row that updates it to the newer version; keep it short", + "sourceHash": "c1c1009d3f37" + }, + "web.routes.tabs.mods.update_all_count": { + "defaultMessage": "Tout mettre à jour ({count})", + "description": "Mods: label of the button that updates every mod; {count} is how many mods have an update", + "sourceHash": "990cf4176472" + }, + "web.routes.tabs.mods.updates_available": { + "defaultMessage": "· {count, plural, one {# mise à jour disponible} other {# mises à jour disponibles}}", + "description": "Mods: shown beside the installed count when mods have updates; {count} is the number of mods with an update", + "sourceHash": "a7d4e331eac2" + }, + "web.routes.tabs.mods.updating": { + "defaultMessage": "Mise à jour…", + "description": "Mods: label of the update-all button while updates run", + "sourceHash": "dfe40efe921f" + }, + "web.routes.tabs.mods.upload": { + "defaultMessage": "Importer", + "description": "Mods: label of the button that uploads a mod file", + "sourceHash": "865e89de78d9" + }, + "web.routes.tabs.mods.uploading": { + "defaultMessage": "Importation…", + "description": "Mods: label of the upload button while the file uploads", + "sourceHash": "5ce44dd77dae" + }, + "web.routes.tabs.mods.uploads_are_recorded_as_provider_upload": { + "defaultMessage": "Les imports sont enregistrés avec le fournisseur “upload” dans le manifeste — ils ne sont jamais vérifiés pour les mises à jour.", + "description": "Mods tab: note under the upload field saying uploaded mods are recorded with the upload provider and are never checked for updates", + "sourceHash": "41ae6989ecbd" + }, + "web.routes.tabs.mods.use_url_form": { + "defaultMessage": "Utiliser le formulaire URL", + "description": "Mods tab: button that moves a mod to the URL form so the user can add their own credentials; it is not a one-click install; keep it short", + "sourceHash": "e99ef84583d3" + }, + "web.routes.tabs.mods.version": { + "defaultMessage": "Version", + "description": "Mods tab: accessible label of the version dropdown in a mod row; not shown as text", + "sourceHash": "dd167905de0d" + }, + "web.routes.tabs.mods.version_file_suffix": { + "defaultMessage": "· {filename}", + "description": "Mods: suffix on a version option naming its file; {filename} is the file name", + "sourceHash": "6e83134bf303" + }, + "web.routes.tabs.overview.cluster_address": { + "defaultMessage": "Adresse du cluster", + "description": "Overview tab: small heading above the cluster-internal address group, shown when it differs from the external address", + "sourceHash": "5045fc44e020" + }, + "web.routes.tabs.overview.connection": { + "defaultMessage": "Connexion", + "description": "Overview tab: small uppercase heading of the connection card that lists the server's addresses", + "sourceHash": "639a40e82b9a" + }, + "web.routes.tabs.overview.copy": { + "defaultMessage": "Copier", + "description": "Overview tab: icon-only button tooltip that copies the address to the clipboard; keep it short", + "sourceHash": "e21f935f11d7" + }, + "web.routes.tabs.overview.cpu": { + "defaultMessage": "CPU", + "description": "Overview tab: label of the CPU usage card; technical term, usually left untranslated", + "sourceHash": "db9a4c7d4c19" + }, + "web.routes.tabs.overview.cpu_cores": { + "defaultMessage": "{cores} cœurs", + "description": "Overview: CPU value in cores when no CPU limit is set; {cores} is the cores in use", + "sourceHash": "1eaed4cac057" + }, + "web.routes.tabs.overview.cpu_of_cores": { + "defaultMessage": "{used} / {total} cœurs", + "description": "Overview: CPU usage against its limit in cores; {used} is the cores in use, {total} the CPU limit", + "sourceHash": "124d7ca65cf4" + }, + "web.routes.tabs.overview.disk": { + "defaultMessage": "Disque", + "description": "Overview tab: label of the disk usage card", + "sourceHash": "340e0cf3bfa8" + }, + "web.routes.tabs.overview.external_address": { + "defaultMessage": "Adresse externe", + "description": "Overview tab: label of the row with the public address of the server, shown without a port", + "sourceHash": "edce211fa286" + }, + "web.routes.tabs.overview.host": { + "defaultMessage": "Hôte", + "description": "Overview tab: label of the host name row in an address group", + "sourceHash": "4a823118b9ba" + }, + "web.routes.tabs.overview.host_port_suffix": { + "defaultMessage": ":{port}", + "description": "Overview: port shown after the host of an endpoint; {port} is the port number", + "sourceHash": "87b677323b5f" + }, + "web.routes.tabs.overview.length_listed": { + "defaultMessage": "{length} affichés", + "description": "Overview: count of listed items in a section heading; {length} is how many items are listed", + "sourceHash": "b7526900ba07" + }, + "web.routes.tabs.overview.length_more": { + "defaultMessage": "+ {length} de plus", + "description": "Overview: list footer showing hidden items; {length} is how many more items exist", + "sourceHash": "4aefc660c8d2" + }, + "web.routes.tabs.overview.loading": { + "defaultMessage": "Chargement…", + "description": "Overview tab: loading text shown until the server status is available", + "sourceHash": "ba3bbbe10d8b" + }, + "web.routes.tabs.overview.memory": { + "defaultMessage": "Mémoire", + "description": "Overview tab: label of the memory usage card", + "sourceHash": "c3963aedaac6" + }, + "web.routes.tabs.overview.no_events_yet_lifecycle_backup_and": { + "defaultMessage": "Aucun événement pour l’instant. L’activité du cycle de vie, des sauvegardes et des agents apparaîtra ici.", + "description": "Overview tab: empty-state text in the recent events card shown when there are no events yet", + "sourceHash": "01e3499397d6" + }, + "web.routes.tabs.overview.no_players_connected": { + "defaultMessage": "Aucun joueur connecté.", + "description": "Overview tab: text in the players card shown when no player is connected", + "sourceHash": "e5c214fd56ce" + }, + "web.routes.tabs.overview.online_online_names_not_yet_available": { + "defaultMessage": "{online} en ligne · noms pas encore disponibles.", + "description": "Overview: note under the players card; {online} is the number of players online", + "sourceHash": "609394aa57ec" + }, + "web.routes.tabs.overview.player_count_unknown": { + "defaultMessage": "Nombre de joueurs inconnu.", + "description": "Overview tab: text in the players card shown when the online player count cannot be read", + "sourceHash": "3e250378b6af" + }, + "web.routes.tabs.overview.player_list_not_supported_for_this_game": { + "defaultMessage": "Liste des joueurs non prise en charge pour ce jeu.", + "description": "Overview tab: text in the players card saying this game does not provide a player list", + "sourceHash": "3940e9389c34" + }, + "web.routes.tabs.overview.players_online": { + "defaultMessage": "Joueurs en ligne ·", + "description": "Overview tab: heading of the players card, followed by the online player count or a dash when the count is unknown", + "sourceHash": "626a0a8a6632" + }, + "web.routes.tabs.overview.pool_pool": { + "defaultMessage": "pool : {pool}", + "description": "Overview: load balancer address pool line; {pool} is the pool name", + "sourceHash": "41daa749f4fd" + }, + "web.routes.tabs.overview.port": { + "defaultMessage": "Port", + "description": "Overview tab: label of the port number row in the cluster address group", + "sourceHash": "72e9a59f5a1d" + }, + "web.routes.tabs.overview.ready_condition_reason_message": { + "defaultMessage": "Condition Ready : {reason} — {message}", + "description": "Overview: alert text for the Ready condition; {reason} is the condition reason and {message} the condition message", + "sourceHash": "b6a084167104" + }, + "web.routes.tabs.overview.recent_events": { + "defaultMessage": "Événements récents", + "description": "Overview tab: small uppercase heading of the recent events card", + "sourceHash": "8ecce94dc301" + }, + "web.routes.tabs.overview.tailnet_only": { + "defaultMessage": "Tailnet uniquement", + "description": "Overview tab: warning badge beside a tunnel address meaning it is reachable only inside the tailnet; keep it short", + "sourceHash": "ff2929f8f28d" + }, + "web.routes.tabs.overview.tunnel": { + "defaultMessage": "Tunnel", + "description": "Overview tab: label of the tunnel address row, shown only when a tunnel endpoint exists", + "sourceHash": "cf23f35d540d" + }, + "web.routes.tabs.overview.used_of_total": { + "defaultMessage": "{used} / {total}", + "description": "Overview: memory or disk usage against its limit; {used} is the amount in use, {total} the limit", + "sourceHash": "d77a43cd175e" + }, + "web.routes.tabs.overview.view_all": { + "defaultMessage": "Tout afficher", + "description": "Overview tab: link-style button in the recent events heading that opens the full events list; keep it short", + "sourceHash": "30a64216eaea" + }, + "web.routes.tabs.overview.waiting_for_tunnel_address": { + "defaultMessage": "En attente de l’adresse du tunnel…", + "description": "Overview: endpoint text shown while the tunnel has not yet published an address", + "sourceHash": "35df5faab820" + }, + "web.routes.tabs.overview.waiting_on_storage_the_server_can_t": { + "defaultMessage": "En attente du stockage — le serveur ne peut pas encore être créé", + "description": "Overview tab: title of the alert shown while the server waits for storage before it can be created; the line below gives the ready condition", + "sourceHash": "39620eec90ed" + }, + "web.routes.tabs.players.action_add": { + "defaultMessage": "ajouter", + "description": "Players: lowercase verb for adding a player to the whitelist, shown in the status line", + "sourceHash": "7e9e5ac30f22" + }, + "web.routes.tabs.players.action_ban": { + "defaultMessage": "bannir", + "description": "Players: lowercase verb for the ban action, shown in the status line", + "sourceHash": "b2a96c3d3fc2" + }, + "web.routes.tabs.players.action_failed": { + "defaultMessage": "échec de l’action", + "description": "Players: error shown when a player action fails and the error has no message", + "sourceHash": "af266d85e290" + }, + "web.routes.tabs.players.action_kick": { + "defaultMessage": "expulser", + "description": "Players: lowercase verb for the kick action, shown in the status line", + "sourceHash": "0db10f2c2f33" + }, + "web.routes.tabs.players.action_player_ok": { + "defaultMessage": "{action} {player} ok", + "description": "Players: status line after a moderation action succeeds; {action} is the action verb, {player} the player name", + "sourceHash": "f12766f027f9" + }, + "web.routes.tabs.players.action_remove": { + "defaultMessage": "retirer", + "description": "Players: lowercase verb for removing a player from the whitelist, shown in the status line", + "sourceHash": "7e5608ab6100" + }, + "web.routes.tabs.players.action_unban": { + "defaultMessage": "débannir", + "description": "Players: lowercase verb for the unban action, shown in the status line", + "sourceHash": "de73c984dc0d" + }, + "web.routes.tabs.players.add": { + "defaultMessage": "Ajouter", + "description": "Players tab: button that adds the typed player name to the whitelist; keep it short", + "sourceHash": "9fd728c66c9a" + }, + "web.routes.tabs.players.add_player_to_whitelist": { + "defaultMessage": "Ajouter un joueur à la liste blanche…", + "description": "Players tab: placeholder in the box used to add a player name to the whitelist", + "sourceHash": "6970b303762a" + }, + "web.routes.tabs.players.ban": { + "defaultMessage": "Bannir", + "description": "Players: verb for banning a player (label and confirm dialog)", + "sourceHash": "520ed297c96f" + }, + "web.routes.tabs.players.ban_player_aria": { + "defaultMessage": "Bannir {player}", + "description": "Players: aria-label of the ban button on a player row; {player} is the player name", + "sourceHash": "394d14bc18e4" + }, + "web.routes.tabs.players.banned": { + "defaultMessage": "Bannis", + "description": "Players tab: stat card label for banned players and heading of the collapsible banned list, followed by a count; keep it short", + "sourceHash": "05eb210744d1" + }, + "web.routes.tabs.players.banning": { + "defaultMessage": "Bannissement", + "description": "Players: progress verb shown while banning a player", + "sourceHash": "62c872843dec" + }, + "web.routes.tabs.players.by_source": { + "defaultMessage": "par {source}", + "description": "Players: source of a ban shown under the player; {source} is who issued the ban", + "sourceHash": "7517f78a9c56" + }, + "web.routes.tabs.players.cancel": { + "defaultMessage": "Annuler", + "description": "Players tab: button that closes the kick or ban confirmation without acting; keep it short", + "sourceHash": "19766ed6ccb2" + }, + "web.routes.tabs.players.count_in_parens": { + "defaultMessage": "({count})", + "description": "Players: entry count shown in parentheses after a list heading; {count} is the number of entries", + "sourceHash": "ae7cbb64a012" + }, + "web.routes.tabs.players.count_unknown": { + "defaultMessage": "Nombre de joueurs inconnu", + "description": "Players: heading when the online player count is unknown", + "sourceHash": "c63406a6e501" + }, + "web.routes.tabs.players.dismiss": { + "defaultMessage": "fermer", + "description": "Players tab: small text button that closes the result message; keep it short", + "sourceHash": "5880072b27d4" + }, + "web.routes.tabs.players.kick": { + "defaultMessage": "Expulser", + "description": "Players: verb for kicking a player (button label and confirm dialog)", + "sourceHash": "37ca3cfb5d85" + }, + "web.routes.tabs.players.kicking": { + "defaultMessage": "Expulsion", + "description": "Players: progress verb shown while kicking a player", + "sourceHash": "1479215c83d9" + }, + "web.routes.tabs.players.loading": { + "defaultMessage": "Chargement…", + "description": "Players: shown while the player list is loading", + "sourceHash": "ba3bbbe10d8b" + }, + "web.routes.tabs.players.nobody_is_banned": { + "defaultMessage": "Aucun joueur banni.", + "description": "Players tab: empty-state text in the banned section when no player is banned", + "sourceHash": "6a2e8016d3d6" + }, + "web.routes.tabs.players.nobody_online": { + "defaultMessage": "Personne en ligne.", + "description": "Players tab: empty-state text in the player list when nobody is online", + "sourceHash": "e6d1ea2af3c4" + }, + "web.routes.tabs.players.online": { + "defaultMessage": "En ligne", + "description": "Players tab: stat card label for the number of online players; keep it short", + "sourceHash": "0d21bd52022c" + }, + "web.routes.tabs.players.online_count": { + "defaultMessage": "{online} en ligne", + "description": "Players: heading with the online player count; {online} is the number of players online", + "sourceHash": "278dc281f27c" + }, + "web.routes.tabs.players.online_of_max": { + "defaultMessage": "{online} / {max}", + "description": "Players: online player count against the server maximum; {online} is the current count, {max} the maximum", + "sourceHash": "ec5e5652faca" + }, + "web.routes.tabs.players.online_of_max_heading": { + "defaultMessage": "{online} / {max} en ligne", + "description": "Players: heading with the online count and the player slot limit; {online} is the number of players online and {max} the slot limit", + "sourceHash": "c76bce7f9d45" + }, + "web.routes.tabs.players.player_id_in_parens": { + "defaultMessage": "({playerId})", + "description": "Players: player ID shown in parentheses after the player name; {playerId} is the ID element", + "sourceHash": "4adbfa9c0c87" + }, + "web.routes.tabs.players.player_moderation_isn_t_supported_for": { + "defaultMessage": "La modération des joueurs n’est pas prise en charge pour ce jeu.", + "description": "Players tab: text shown when this game does not support player moderation such as bans", + "sourceHash": "423d59d646a0" + }, + "web.routes.tabs.players.question_mark": { + "defaultMessage": " ?", + "description": "Players: question mark that ends the confirm sentence", + "sourceHash": "8a8de823d5ed" + }, + "web.routes.tabs.players.reason_optional_max_256_chars": { + "defaultMessage": "Motif (facultatif, 256 caractères max)", + "description": "Players tab: placeholder in the reason box of the kick or ban confirmation, saying the reason is optional and limited to 256 characters", + "sourceHash": "7a14b40b474e" + }, + "web.routes.tabs.players.refresh": { + "defaultMessage": "Actualiser", + "description": "Players tab: tooltip on the refresh icon, same action as the Refresh players button", + "sourceHash": "0e9161011702" + }, + "web.routes.tabs.players.refresh_players": { + "defaultMessage": "Actualiser les joueurs", + "description": "Players tab: icon-only button that reloads the player list; read by screen readers; keep it short", + "sourceHash": "8ee9b84684b6" + }, + "web.routes.tabs.players.remove_from_whitelist": { + "defaultMessage": "Retirer de la liste blanche", + "description": "Players tab: tooltip on the icon button that removes a player from the whitelist; the same action is named in the button's accessible label", + "sourceHash": "c3186151971b" + }, + "web.routes.tabs.players.remove_from_whitelist_aria": { + "defaultMessage": "Retirer {player} de la liste blanche", + "description": "Players: aria-label of the button removing a player from the whitelist; {player} is the player name", + "sourceHash": "c9cdf325f7be" + }, + "web.routes.tabs.players.source_reason_separator": { + "defaultMessage": "·", + "description": "Players: separator between the ban source and the ban reason", + "sourceHash": "a137f17a19a0" + }, + "web.routes.tabs.players.unban": { + "defaultMessage": "Débannir", + "description": "Players tab: row button that lifts a ban on one player; the text is also its accessible label with the player name; keep it short", + "sourceHash": "25fb5989510d" + }, + "web.routes.tabs.players.unban_player_aria": { + "defaultMessage": "Débannir {player}", + "description": "Players: aria-label of the unban button on a banned player row; {player} is the player name", + "sourceHash": "b374c63007c6" + }, + "web.routes.tabs.players.verbing_ellipsis": { + "defaultMessage": "{verbing}…", + "description": "Players: confirm button label while the action runs; {verbing} is the progress verb Kicking or Banning", + "sourceHash": "cce4b52a263b" + }, + "web.routes.tabs.players.whitelist": { + "defaultMessage": "Liste blanche", + "description": "Players tab: collapsible heading of the whitelist section; the count in brackets follows when the list is loaded", + "sourceHash": "8559afa62d46" + }, + "web.routes.tabs.players.whitelist_is_empty": { + "defaultMessage": "La liste blanche est vide.", + "description": "Players tab: empty-state text in the whitelist section when no player is whitelisted", + "sourceHash": "35d62adbf285" + }, + "web.routes.tabs.players.whitelist_player_ok": { + "defaultMessage": "liste blanche {op} {player} ok", + "description": "Players: status line after a whitelist change succeeds; {op} is add or remove, {player} the player name", + "sourceHash": "19deddab1282" + }, + "web.routes.tabs.players.whitelisted": { + "defaultMessage": "Sur la liste blanche", + "description": "Players tab: stat card label for the number of whitelisted players; shown only when the game supports a whitelist", + "sourceHash": "766225e4e0a6" + }, + "web.routes.tabs.settings.access.add": { + "defaultMessage": "Ajouter", + "description": "Button that adds the typed username as a collaborator", + "sourceHash": "9fd728c66c9a" + }, + "web.routes.tabs.settings.access.add_collaborator_by_username": { + "defaultMessage": "Ajouter un collaborateur par nom d’utilisateur…", + "description": "Settings access section: placeholder in the box for adding a collaborator by username; pressing Enter adds them", + "sourceHash": "c191be242dac" + }, + "web.routes.tabs.settings.access.collaborators": { + "defaultMessage": "Collaborateurs", + "description": "Label for the list of users who can manage this server", + "sourceHash": "439c0bddcded" + }, + "web.routes.tabs.settings.access.collaborators_get_full_control_of_this": { + "defaultMessage": "Les collaborateurs ont un contrôle total de ce serveur (console, fichiers, paramètres). Ils ne peuvent ni transférer la propriété ni modifier cette liste.", + "description": "Settings access section: hint under the Collaborators label saying collaborators get full control of the server but cannot transfer ownership or edit this list", + "sourceHash": "47baa8bdb093" + }, + "web.routes.tabs.settings.access.collaborators_were_modified_outside_the": { + "defaultMessage": "Les collaborateurs ont été modifiés en dehors du tableau de bord.", + "description": "Settings access section: warning shown when the collaborators list was changed outside the dashboard", + "sourceHash": "7e37180dfcc0" + }, + "web.routes.tabs.settings.access.failed": { + "defaultMessage": "échec", + "description": "Access: fallback error shown when changing collaborators fails and the server gives no message", + "sourceHash": "5d28a90f4498" + }, + "web.routes.tabs.settings.access.loading": { + "defaultMessage": "Chargement…", + "description": "Settings access section: loading text shown while the server access details are fetched", + "sourceHash": "ba3bbbe10d8b" + }, + "web.routes.tabs.settings.access.none_yet": { + "defaultMessage": "Aucun pour l’instant", + "description": "Settings access section: empty-state text in the collaborators area when there are no collaborators", + "sourceHash": "adb85cf22bfc" + }, + "web.routes.tabs.settings.access.owner": { + "defaultMessage": "Propriétaire", + "description": "Label for the server owner's username", + "sourceHash": "4b1b8aa3608a" + }, + "web.routes.tabs.settings.access.remove_collaborator": { + "defaultMessage": "Supprimer {name}", + "description": "Access: aria-label of the remove button on a collaborator chip; {name} is the collaborator's username", + "sourceHash": "e6a3c4a1250a" + }, + "web.routes.tabs.settings.backups.active": { + "defaultMessage": "Active", + "description": "Backups: status label next to the suspend switch when off", + "sourceHash": "92340695899b" + }, + "web.routes.tabs.settings.backups.automatically_create_backups_on_a_cron": { + "defaultMessage": "Créer automatiquement des sauvegardes selon une planification cron. L’opérateur gère la ressource BackupSchedule.", + "description": "Settings backups section: hint under the scheduled backups switch saying backups are created on a cron schedule and the operator manages the schedule resource", + "sourceHash": "e39128303e1b" + }, + "web.routes.tabs.settings.backups.backup_destination": { + "defaultMessage": "Destination de sauvegarde", + "description": "Backups: accessible label of the list of backup destinations shown when the destination picker opens; not shown as text", + "sourceHash": "b2055a887c17" + }, + "web.routes.tabs.settings.backups.configure_a_backup_destination_first": { + "defaultMessage": "Configurez d’abord une destination de sauvegarde.", + "description": "Settings backups section: note shown when no backup destination exists yet, so scheduled backups cannot be set up", + "sourceHash": "0f91edc7e49a" + }, + "web.routes.tabs.settings.backups.destination": { + "defaultMessage": "Destination", + "description": "Backups: field label for the backup destination picker, which opens a list of configured destinations", + "sourceHash": "293d404a500f" + }, + "web.routes.tabs.settings.backups.disabled": { + "defaultMessage": "Désactivé", + "description": "Backups: status label next to the scheduled backups switch when off", + "sourceHash": "75081b593d15" + }, + "web.routes.tabs.settings.backups.enable_scheduled_backups": { + "defaultMessage": "Activer les sauvegardes planifiées", + "description": "Settings backups section: label of the switch that turns scheduled backups on or off; the same text is its accessible name", + "sourceHash": "74d6c8b7548a" + }, + "web.routes.tabs.settings.backups.enabled": { + "defaultMessage": "Activé", + "description": "Backups: status label next to the scheduled backups switch when on", + "sourceHash": "92c1cdfdf4cb" + }, + "web.routes.tabs.settings.backups.retention_policy": { + "defaultMessage": "Politique de rétention", + "description": "Backups: small heading above the retention fields that decide how many backups are kept", + "sourceHash": "49bd31dc520d" + }, + "web.routes.tabs.settings.backups.schedule_cron": { + "defaultMessage": "Planification (cron)", + "description": "Settings backups section: label of the cron expression field that sets when backups run; the hint shows an example expression", + "sourceHash": "58f2d2a2337f" + }, + "web.routes.tabs.settings.backups.schedule_placeholder": { + "defaultMessage": "0 */6 * * *", + "description": "Backups: example cron schedule shown as the schedule input placeholder", + "sourceHash": "16ddb6345476" + }, + "web.routes.tabs.settings.backups.select_destination": { + "defaultMessage": "Sélectionner une destination…", + "description": "Backups: placeholder of the backup destination picker when none is chosen", + "sourceHash": "92b9351fbae6" + }, + "web.routes.tabs.settings.backups.suspend_schedule": { + "defaultMessage": "Suspendre la planification", + "description": "Backups: label of the switch that pauses scheduled backups; the same text is its accessible name", + "sourceHash": "1aabc8c4decb" + }, + "web.routes.tabs.settings.backups.suspended": { + "defaultMessage": "Suspendue", + "description": "Backups: status label next to the suspend switch when on", + "sourceHash": "e392a3891c07" + }, + "web.routes.tabs.settings.backups.when_enabled_scheduled_backups_will_not": { + "defaultMessage": "Une fois activé, les sauvegardes planifiées ne s’exécutent plus, mais la planification est conservée.", + "description": "Backups: hint under the suspend switch saying scheduled backups stop running while it is on, but the schedule itself is kept", + "sourceHash": "0735cc741f0a" + }, + "web.routes.tabs.settings.backups.when_to_run_backups_example_0_6_for": { + "defaultMessage": "Moment d’exécution des sauvegardes. Exemple : '0 */6 * * *' pour toutes les 6 heures.", + "description": "Backups: hint under the cron schedule field saying when backups run, with the example 0 */6 * * * for every 6 hours", + "sourceHash": "d89cb53270ed" + }, + "web.routes.tabs.settings.correct_invalid_before_saving": { + "defaultMessage": "Corrigez les paramètres invalides avant d’enregistrer.", + "description": "Settings: error shown when saving is attempted while some settings are invalid", + "sourceHash": "16977fba846f" + }, + "web.routes.tabs.settings.danger.delete": { + "defaultMessage": "Supprimer", + "description": "Button that opens the delete server confirmation", + "sourceHash": "e2d0a54968ea" + }, + "web.routes.tabs.settings.danger.delete_server": { + "defaultMessage": "Supprimer le serveur", + "description": "Danger: title of the danger row that deletes the whole server, including its persistent volume; the text below warns it cannot be undone", + "sourceHash": "7e9464a23e43" + }, + "web.routes.tabs.settings.danger.delete_server_body": { + "defaultMessage": "Supprime le GameServer et son volume persistant. Cette action est irréversible.", + "description": "Danger: explanation under the Delete server row", + "sourceHash": "40c04974c3bd" + }, + "web.routes.tabs.settings.danger.transfer": { + "defaultMessage": "Transférer…", + "description": "Button that opens the transfer ownership dialog", + "sourceHash": "d3078978d524" + }, + "web.routes.tabs.settings.danger.transfer_ownership": { + "defaultMessage": "Transférer la propriété", + "description": "Danger: title of the danger row that hands the server to another user; the text after it explains the user keeps access only as a collaborator", + "sourceHash": "3667f939d52f" + }, + "web.routes.tabs.settings.danger.transfer_ownership_body": { + "defaultMessage": "Confiez ce serveur à un autre utilisateur. Vous ne gardez l’accès que si vous restez collaborateur.", + "description": "Danger: explanation under the Transfer ownership row", + "sourceHash": "36d0b60c711f" + }, + "web.routes.tabs.settings.danger.wipe_world": { + "defaultMessage": "Effacer le monde…", + "description": "Button that opens the wipe world data confirmation", + "sourceHash": "d181159270c7" + }, + "web.routes.tabs.settings.danger.wipe_world_body": { + "defaultMessage": "Supprime toutes les données du volume de données et redémarre le serveur sur un monde neuf. Faites d’abord une sauvegarde.", + "description": "Danger: explanation under the Wipe world data row", + "sourceHash": "05951afe8e73" + }, + "web.routes.tabs.settings.danger.wipe_world_data": { + "defaultMessage": "Effacer les données du monde", + "description": "Danger: title of the danger row that deletes all world data and restarts on a fresh world", + "sourceHash": "2cca819060c6" + }, + "web.routes.tabs.settings.discard": { + "defaultMessage": "Abandonner", + "description": "Settings tab: footer button that throws away unsaved settings edits; disabled when nothing changed; keep it short", + "sourceHash": "eb1a70e39274" + }, + "web.routes.tabs.settings.env_vars.actions": { + "defaultMessage": "Actions", + "description": "Table column header for the per-row action buttons", + "sourceHash": "ff8059dc6752" + }, + "web.routes.tabs.settings.env_vars.add_from_secret": { + "defaultMessage": "Ajouter depuis un secret", + "description": "Button that adds an environment variable whose value is read from a Secret", + "sourceHash": "e4fe54be01a6" + }, + "web.routes.tabs.settings.env_vars.add_variable": { + "defaultMessage": "Ajouter une variable", + "description": "Button that adds a literal environment variable row", + "sourceHash": "e10f539e38e1" + }, + "web.routes.tabs.settings.env_vars.duplicate_name": { + "defaultMessage": "Nom en double", + "description": "Env vars: validation error under the variable name box when another variable already uses that name", + "sourceHash": "72e44e2a40e5" + }, + "web.routes.tabs.settings.env_vars.environment_variables": { + "defaultMessage": "Variables d’environnement", + "description": "Env vars: accessible label of the environment variables table, read by screen readers; not shown as text", + "sourceHash": "aac7246f5e97" + }, + "web.routes.tabs.settings.env_vars.key": { + "defaultMessage": "clé", + "description": "Env vars: placeholder in an environment variable row's key box, shown before typing; the value box beside it has its own placeholder", + "sourceHash": "2c70e12b7a06" + }, + "web.routes.tabs.settings.env_vars.literal": { + "defaultMessage": "Valeur littérale", + "description": "Option label for an environment variable with a literal value typed in the dashboard", + "sourceHash": "a6230c2a81ab" + }, + "web.routes.tabs.settings.env_vars.must_match_a_z_a_z0_9": { + "defaultMessage": "Doit correspondre à [A-Z_][A-Z0-9_]*", + "description": "Env vars: validation error under the variable name box when the name does not match the allowed upper-case pattern", + "sourceHash": "82da2b78efc0" + }, + "web.routes.tabs.settings.env_vars.name": { + "defaultMessage": "Nom", + "description": "Table column header for an environment variable's name", + "sourceHash": "dcd1d5223f73" + }, + "web.routes.tabs.settings.env_vars.no_environment_variables_click_below_to": { + "defaultMessage": "Aucune variable d’environnement. Cliquez ci-dessous pour en ajouter une.", + "description": "Env vars: empty-state text shown when the server has no environment variables; points to the add button below", + "sourceHash": "4b8b068b0ef2" + }, + "web.routes.tabs.settings.env_vars.remove": { + "defaultMessage": "Supprimer", + "description": "Button that removes this environment variable row", + "sourceHash": "c3812fc4acb8" + }, + "web.routes.tabs.settings.env_vars.secret": { + "defaultMessage": "Secret", + "description": "Option label for an environment variable whose value comes from a Kubernetes Secret", + "sourceHash": "7e32a729b122" + }, + "web.routes.tabs.settings.env_vars.secret_key": { + "defaultMessage": "Clé du secret", + "description": "Env vars: accessible label of the box that holds the key inside the Kubernetes Secret for a secret-backed variable; not shown as text", + "sourceHash": "f47a99ebcf29" + }, + "web.routes.tabs.settings.env_vars.secret_name": { + "defaultMessage": "Nom du secret", + "description": "Env vars: accessible label of the box that holds the Kubernetes Secret name for a secret-backed variable; not shown as text", + "sourceHash": "5cdf573b8913" + }, + "web.routes.tabs.settings.env_vars.secret_name_2": { + "defaultMessage": "nom-du-secret", + "description": "Env vars: example placeholder in the Secret name box showing the expected format; an example value, not a default", + "sourceHash": "433144222dd7" + }, + "web.routes.tabs.settings.env_vars.type": { + "defaultMessage": "Type", + "description": "Table column header for an environment variable's source type (literal or secret)", + "sourceHash": "baaddf70fb5d" + }, + "web.routes.tabs.settings.env_vars.value": { + "defaultMessage": "Valeur", + "description": "Table column header for an environment variable's value", + "sourceHash": "8e37953d23da" + }, + "web.routes.tabs.settings.env_vars.value_2": { + "defaultMessage": "valeur", + "description": "Env vars: placeholder in a variable row's value box, shown before typing a literal value", + "sourceHash": "cd42404d52ad" + }, + "web.routes.tabs.settings.env_vars.var_name": { + "defaultMessage": "NOM_VARIABLE", + "description": "Env vars: example placeholder in the variable name box, showing the naming style; an example name, not a default", + "sourceHash": "f896c636df42" + }, + "web.routes.tabs.settings.env_vars.variable_name": { + "defaultMessage": "Nom de la variable", + "description": "Env vars: accessible label of the variable name box in a row of the environment variables table; not shown as text", + "sourceHash": "0b64682bead3" + }, + "web.routes.tabs.settings.env_vars.variable_value": { + "defaultMessage": "Valeur de la variable", + "description": "Env vars: accessible label of the box that holds the value of a variable row; not shown as text", + "sourceHash": "f94d74f14494" + }, + "web.routes.tabs.settings.game_config.game_configuration": { + "defaultMessage": "Configuration du jeu", + "description": "Game config: heading of the game configuration section, shown at the top of the section", + "sourceHash": "748837ff0cd2" + }, + "web.routes.tabs.settings.game_config.no_longer_in_the_template": { + "defaultMessage": "Plus présent dans le modèle", + "description": "Game config: note on a saved setting that the game template no longer offers; shown next to a Remove button", + "sourceHash": "15f23f6410f1" + }, + "web.routes.tabs.settings.game_config.remove": { + "defaultMessage": "Supprimer", + "description": "Button that removes this config value override", + "sourceHash": "c3812fc4acb8" + }, + "web.routes.tabs.settings.game_config.saving_changes_restarts_the_server_so": { + "defaultMessage": "L’enregistrement des modifications redémarre le serveur pour les appliquer.", + "description": "Game config: warning note saying that saving changes restarts the server so they apply; shown to users who can write the settings", + "sourceHash": "c08cd2689fa0" + }, + "web.routes.tabs.settings.game_config.settings_from_this_server_s_game": { + "defaultMessage": "Paramètres issus du modèle de ce serveur. L’enregistrement redémarre le serveur.", + "description": "Game config: hint under the heading saying these settings come from the game template and that saving restarts the server", + "sourceHash": "5efd94b0f6f2" + }, + "web.routes.tabs.settings.game_config.the_operator_rejected_this_configuration": { + "defaultMessage": "L’opérateur a rejeté cette configuration", + "description": "Game config: title of the danger alert shown when the operator rejects the configuration; the detail line below gives the reason", + "sourceHash": "59c4885e7f90" + }, + "web.routes.tabs.settings.game_config.this_template_has_no_configurable": { + "defaultMessage": "Ce modèle n’a aucun paramètre configurable.", + "description": "Game config: empty-state text shown when the game template has no configurable settings", + "sourceHash": "9ff154b75c10" + }, + "web.routes.tabs.settings.gameconfig.hidden": { + "defaultMessage": "Masqué", + "description": "GameConfig: placeholder of a redacted config value input that is hidden from view", + "sourceHash": "7e6fefff0f0a" + }, + "web.routes.tabs.settings.general.add": { + "defaultMessage": "Ajouter", + "description": "Button that adds the typed label key and value", + "sourceHash": "9fd728c66c9a" + }, + "web.routes.tabs.settings.general.description": { + "defaultMessage": "Description", + "description": "General: field label for the free-text description of the server", + "sourceHash": "526e0087cc3f" + }, + "web.routes.tabs.settings.general.free_form_kubernetes_labels": { + "defaultMessage": "Labels Kubernetes libres.", + "description": "General: hint under the labels field saying labels are free-form Kubernetes labels", + "sourceHash": "ded91b19f0d8" + }, + "web.routes.tabs.settings.general.image_override": { + "defaultMessage": "Remplacement de l’image", + "description": "General: field label for an optional container image that replaces the template's image", + "sourceHash": "0d40c7aecf4b" + }, + "web.routes.tabs.settings.general.key": { + "defaultMessage": "clé", + "description": "General: placeholder in the label key box of the new label row, shown before typing", + "sourceHash": "2c70e12b7a06" + }, + "web.routes.tabs.settings.general.key_value_separator": { + "defaultMessage": "=", + "description": "General: separator shown between a label key and its value", + "sourceHash": "380918b946a5" + }, + "web.routes.tabs.settings.general.labels": { + "defaultMessage": "Labels", + "description": "General: field label for the list of Kubernetes labels set on the server", + "sourceHash": "934b8899c3d9" + }, + "web.routes.tabs.settings.general.long_standing_survival_realm_invite_only": { + "defaultMessage": "Royaume de survie de longue durée. Sur invitation uniquement.", + "description": "General: example placeholder in the description box before typing; an example text, not a default", + "sourceHash": "fe3e08ff44d1" + }, + "web.routes.tabs.settings.general.name": { + "defaultMessage": "Nom", + "description": "General: field label for the server name, which is fixed and cannot be edited here", + "sourceHash": "dcd1d5223f73" + }, + "web.routes.tabs.settings.general.pin_a_specific_game_container_image": { + "defaultMessage": "Épinglez une image de conteneur de jeu précise. Laissez vide pour utiliser l’image par défaut du modèle.", + "description": "General: hint under the image override field; leave it blank to use the template's default image", + "sourceHash": "17ee0f4afcc5" + }, + "web.routes.tabs.settings.general.pinned_to_the_gameserver_uid_cannot": { + "defaultMessage": "Épinglé à l’UID du GameServer ; modification impossible.", + "description": "General: hint under the server name field saying the name is pinned to the game server's unique ID and cannot be changed", + "sourceHash": "9e70c2202668" + }, + "web.routes.tabs.settings.general.remove_label": { + "defaultMessage": "Supprimer le label", + "description": "General: tooltip and accessible label of the icon button that removes one label row; keep it short", + "sourceHash": "d2c6d89109d3" + }, + "web.routes.tabs.settings.general.shown_in_the_dashboard_server_list": { + "defaultMessage": "Affiché dans la liste des serveurs du tableau de bord.", + "description": "General: hint under the description field saying the text is shown in the dashboard server list", + "sourceHash": "b9560cceef4b" + }, + "web.routes.tabs.settings.general.template": { + "defaultMessage": "Modèle", + "description": "General: field label for the game template the server was created from; the field is read-only", + "sourceHash": "0575f29df888" + }, + "web.routes.tabs.settings.general.template_image": { + "defaultMessage": "(image du modèle)", + "description": "General: placeholder of the image override input when the template image is unknown", + "sourceHash": "f61478c86406" + }, + "web.routes.tabs.settings.general.value": { + "defaultMessage": "valeur", + "description": "General: placeholder in the label value box of the new label row, shown before typing", + "sourceHash": "cd42404d52ad" + }, + "web.routes.tabs.settings.lifecycle.a_game_that_reports_no_player_count": { + "defaultMessage": "Un jeu qui ne remonte aucun nombre de joueurs ne passera jamais en veille.", + "description": "Lifecycle: warning note that a game reporting no player count will never sleep, since idle detection cannot work", + "sourceHash": "b8d936e777b7" + }, + "web.routes.tabs.settings.lifecycle.a_wake_window_never_restarts_a_server": { + "defaultMessage": "Une fenêtre de réveil ne redémarre jamais un serveur arrêté manuellement.", + "description": "Lifecycle: note that a wake window does not restart a server the user stopped by hand", + "sourceHash": "40979529414f" + }, + "web.routes.tabs.settings.lifecycle.add_wake_window": { + "defaultMessage": "Ajouter une fenêtre de réveil", + "description": "Button that adds a wake window cron row (up to 8)", + "sourceHash": "fb259abc7957" + }, + "web.routes.tabs.settings.lifecycle.auto_restart": { + "defaultMessage": "Redémarrage automatique", + "description": "Lifecycle: field label for the switch that restarts the server automatically; the same text is its accessible name", + "sourceHash": "8551382b1d1e" + }, + "web.routes.tabs.settings.lifecycle.disabled": { + "defaultMessage": "Désactivé", + "description": "Lifecycle: status text next to the idle auto-sleep switch when off", + "sourceHash": "75081b593d15" + }, + "web.routes.tabs.settings.lifecycle.enable_idle_auto_sleep": { + "defaultMessage": "Activer la mise en veille automatique", + "description": "Lifecycle: switch that turns idle auto-sleep on or off; the text is its accessible name, not shown as text", + "sourceHash": "1e5a4a940f31" + }, + "web.routes.tabs.settings.lifecycle.from_template": { + "defaultMessage": "depuis le modèle", + "description": "Badge showing that a health probe value comes from the template, not a per-server override", + "sourceHash": "1cd031062620" + }, + "web.routes.tabs.settings.lifecycle.grace_placeholder": { + "defaultMessage": "120", + "description": "Lifecycle: example termination grace period in seconds, shown as the input placeholder", + "sourceHash": "2abaca4911e6" + }, + "web.routes.tabs.settings.lifecycle.health_probes": { + "defaultMessage": "Sondes de santé", + "description": "Lifecycle: field label for the per-server overrides of the health probe timing", + "sourceHash": "53f23697d50a" + }, + "web.routes.tabs.settings.lifecycle.idle_auto_sleep": { + "defaultMessage": "Mise en veille automatique", + "description": "Lifecycle: field label for the idle auto-sleep setting that scales the server to zero when nobody is playing", + "sourceHash": "c4c9dfe6259d" + }, + "web.routes.tabs.settings.lifecycle.idle_auto_sleep_enabled": { + "defaultMessage": "Mise en veille automatique activée", + "description": "Lifecycle: status text next to the idle auto-sleep switch when on", + "sourceHash": "0308eb38fa02" + }, + "web.routes.tabs.settings.lifecycle.idle_minutes_placeholder": { + "defaultMessage": "30", + "description": "Lifecycle: example idle sleep duration in minutes, shown as the input placeholder", + "sourceHash": "624b60c58c9d" + }, + "web.routes.tabs.settings.lifecycle.idle_sleep_after": { + "defaultMessage": "Mise en veille après", + "description": "Lifecycle: accessible label of the minutes box for the idle sleep delay; not shown as text", + "sourceHash": "11a251ca2a2a" + }, + "web.routes.tabs.settings.lifecycle.minutes_of_zero_players_5_1440": { + "defaultMessage": "minutes sans joueur (5–1440)", + "description": "Lifecycle: unit text after the minutes box, saying the value counts minutes of zero players and must be from 5 to 1440", + "sourceHash": "bd5f4fc45a21" + }, + "web.routes.tabs.settings.lifecycle.must_be_a_five_field_cron_expression_e": { + "defaultMessage": "Doit être une expression cron à cinq champs (ex. 0 9 * * 1-5).", + "description": "Lifecycle: validation error under a wake window when its text is not a five-field cron expression; the example 0 9 * * 1-5 is shown", + "sourceHash": "3c5a8e7054ed" + }, + "web.routes.tabs.settings.lifecycle.must_be_an_integer_between_0_and_600": { + "defaultMessage": "Doit être un entier compris entre 0 et 600.", + "description": "Lifecycle: validation error under the grace period field when the value is not a whole number from 0 to 600", + "sourceHash": "53a8505c1cd9" + }, + "web.routes.tabs.settings.lifecycle.must_be_an_integer_between_5_and_1440": { + "defaultMessage": "Doit être un entier compris entre 5 et 1440.", + "description": "Lifecycle: validation error under the idle minutes box when the value is not a whole number from 5 to 1440", + "sourceHash": "2eb1c2af9351" + }, + "web.routes.tabs.settings.lifecycle.not_defined_by_the_template": { + "defaultMessage": ": non défini par le modèle.", + "description": "Lifecycle: text after a probe kind name, shown when neither the template nor this server defines that probe", + "sourceHash": "8b378f98d202" + }, + "web.routes.tabs.settings.lifecycle.per_server_overrides_for_the_template_s": { + "defaultMessage": "Surcharges par serveur pour le délai des sondes de disponibilité, de vivacité et de démarrage du modèle. L’action de sonde est héritée du modèle.", + "description": "Lifecycle: hint under the health probes label explaining the overrides for probe timing; the probe action comes from the template", + "sourceHash": "f6eb5ecd5372" + }, + "web.routes.tabs.settings.lifecycle.pod_desired_running": { + "defaultMessage": "Le pod doit être en cours d’exécution", + "description": "Lifecycle: status text next to the auto-restart switch when on", + "sourceHash": "0ce42ec8d730" + }, + "web.routes.tabs.settings.lifecycle.pod_suspended": { + "defaultMessage": "Le pod est suspendu", + "description": "Lifecycle: status text next to the auto-restart switch when off", + "sourceHash": "7619c641fca8" + }, + "web.routes.tabs.settings.lifecycle.probe_failure_threshold": { + "defaultMessage": "Seuil d’échec", + "description": "Lifecycle: label of the health probe failure threshold input", + "sourceHash": "8390731230e8" + }, + "web.routes.tabs.settings.lifecycle.probe_field_aria": { + "defaultMessage": "{kind} {label}", + "description": "Lifecycle: aria-label of a health probe timing input; {kind} is the probe kind (readiness, liveness or startup) and {label} is the field name", + "sourceHash": "9b2d32e8e149" + }, + "web.routes.tabs.settings.lifecycle.probe_initial_delay": { + "defaultMessage": "Délai initial", + "description": "Lifecycle: label of the health probe initial delay input", + "sourceHash": "c79b82aad8d7" + }, + "web.routes.tabs.settings.lifecycle.probe_kind_liveness": { + "defaultMessage": "vivacité", + "description": "Lifecycle: name of the liveness health probe, shown as a lowercase label (CSS capitalizes it)", + "sourceHash": "f43c5c4c7519" + }, + "web.routes.tabs.settings.lifecycle.probe_kind_readiness": { + "defaultMessage": "disponibilité", + "description": "Lifecycle: name of the readiness health probe, shown as a lowercase label (CSS capitalizes it)", + "sourceHash": "0e06b8449679" + }, + "web.routes.tabs.settings.lifecycle.probe_kind_startup": { + "defaultMessage": "démarrage", + "description": "Lifecycle: name of the startup health probe, shown as a lowercase label (CSS capitalizes it)", + "sourceHash": "8e7e3ff9c84f" + }, + "web.routes.tabs.settings.lifecycle.probe_period": { + "defaultMessage": "Période", + "description": "Lifecycle: label of the health probe period input", + "sourceHash": "6e795d4d3cc2" + }, + "web.routes.tabs.settings.lifecycle.probe_success_threshold": { + "defaultMessage": "Seuil de réussite", + "description": "Lifecycle: label of the health probe success threshold input", + "sourceHash": "1aa1464a1d10" + }, + "web.routes.tabs.settings.lifecycle.probe_timeout": { + "defaultMessage": "Délai d’expiration", + "description": "Lifecycle: label of the health probe timeout input", + "sourceHash": "70594d932950" + }, + "web.routes.tabs.settings.lifecycle.remove": { + "defaultMessage": "Supprimer", + "description": "Button that removes this wake window", + "sourceHash": "c3812fc4acb8" + }, + "web.routes.tabs.settings.lifecycle.reset_to_template": { + "defaultMessage": "Rétablir le modèle", + "description": "Button that discards the per-server probe override and restores the template probe", + "sourceHash": "7b544dfbb54e" + }, + "web.routes.tabs.settings.lifecycle.seconds": { + "defaultMessage": "secondes", + "description": "Unit label after the termination grace period input", + "sourceHash": "59f006d63bd0" + }, + "web.routes.tabs.settings.lifecycle.seconds_the_operator_waits_for_the": { + "defaultMessage": "Secondes pendant lesquelles l’opérateur attend la fin de la séquence d’arrêt du modèle avant de réduire le pod. Plage 0–600 ; 30 par défaut. Sans effet si le modèle ne déclare aucune séquence d’arrêt.", + "description": "Lifecycle: hint under the termination grace period field giving the allowed range 0 to 600 and the default of 30 seconds", + "sourceHash": "9cbe548e9545" + }, + "web.routes.tabs.settings.lifecycle.sleep_after": { + "defaultMessage": "Veille après", + "description": "Lifecycle: small label in front of the minutes box that sets how long the server waits with zero players before sleeping", + "sourceHash": "828986727f1b" + }, + "web.routes.tabs.settings.lifecycle.standard_five_field_cron_max_8_entries": { + "defaultMessage": "Cron standard à cinq champs. 8 entrées maximum.", + "description": "Lifecycle: hint under the wake windows list saying the entries use standard five-field cron and at most 8 are allowed", + "sourceHash": "d513b9094b75" + }, + "web.routes.tabs.settings.lifecycle.termination_grace_period": { + "defaultMessage": "Délai de grâce à l’arrêt", + "description": "Lifecycle: field label for the number of seconds given to the server to stop cleanly before it is shut down", + "sourceHash": "5a2eee9ddf20" + }, + "web.routes.tabs.settings.lifecycle.wake_window_aria": { + "defaultMessage": "Fenêtre de réveil {n}", + "description": "Lifecycle: aria-label of a wake window cron input; {n} is the 1-based position of the window", + "sourceHash": "c29b66f99b16" + }, + "web.routes.tabs.settings.lifecycle.wake_window_placeholder": { + "defaultMessage": "0 9 * * 1-5", + "description": "Lifecycle: example wake window cron expression, shown as the input placeholder", + "sourceHash": "22b9fd458d10" + }, + "web.routes.tabs.settings.lifecycle.wake_windows": { + "defaultMessage": "Fenêtres de réveil", + "description": "Lifecycle: field label for the list of wake windows, the times the server is woken on a schedule", + "sourceHash": "f1cdf6d4be8c" + }, + "web.routes.tabs.settings.lifecycle.when_enabled_scales_the_server_to_zero": { + "defaultMessage": "Une fois activé, le serveur est mis à zéro réplique après le nombre de minutes configuré sans joueur. Une fenêtre de réveil cron ou un :wake explicite le relance.", + "description": "Lifecycle: hint under the idle auto-sleep label explaining the server sleeps after zero players for the set time and wake windows bring it back", + "sourceHash": "6de6cd328c30" + }, + "web.routes.tabs.settings.lifecycle.when_off_the_statefulset_is_scaled_to": { + "defaultMessage": "Désactivé, le StatefulSet est mis à zéro (suspendu). Les données sont conservées.", + "description": "Lifecycle: hint under the auto-restart switch saying that when it is off the server is suspended and its data is kept", + "sourceHash": "9a0aa8691eb4" + }, + "web.routes.tabs.settings.loading": { + "defaultMessage": "Chargement…", + "description": "Settings tab: loading text shown while the settings are fetched or a settings section is still loading", + "sourceHash": "ba3bbbe10d8b" + }, + "web.routes.tabs.settings.network_capture.captures_are_automatically_deleted": { + "defaultMessage": "Les captures sont supprimées automatiquement après cette durée. Laissez vide pour utiliser la valeur par défaut du cluster.", + "description": "Network capture: description under the Retention Window heading saying captures are deleted after this window, and blank uses the cluster default", + "sourceHash": "1cde08478a1e" + }, + "web.routes.tabs.settings.network_capture.cluster_maximum_7_days": { + "defaultMessage": "Maximum du cluster : 7 jours ({seconds} secondes) — une valeur par défaut fondée sur la limitation de stockage, et non une obligation légale.", + "description": "Network capture: hint under the retention window input stating the cluster maximum; {seconds} is the maximum in seconds, formatted with thousands separators", + "sourceHash": "10a7c0bd1893" + }, + "web.routes.tabs.settings.network_capture.disabled": { + "defaultMessage": "Désactivée", + "description": "Network capture: status label next to the enable switch when capture is off", + "sourceHash": "75081b593d15" + }, + "web.routes.tabs.settings.network_capture.enable_capture": { + "defaultMessage": "Activer la capture", + "description": "Label for the switch that turns network capture on or off", + "sourceHash": "cc6cea7d7e2a" + }, + "web.routes.tabs.settings.network_capture.enabled": { + "defaultMessage": "Activée", + "description": "Network capture: status label next to the enable switch when capture is on", + "sourceHash": "92c1cdfdf4cb" + }, + "web.routes.tabs.settings.network_capture.enter_a_positive_number": { + "defaultMessage": "Saisissez un nombre positif.", + "description": "Network capture: validation error under the retention window input when the value is not a positive number", + "sourceHash": "06d87795ff9c" + }, + "web.routes.tabs.settings.network_capture.exceeds_cluster_maximum": { + "defaultMessage": "Dépasse le maximum du cluster de 7 jours ({seconds} secondes).", + "description": "Network capture: validation error when the retention window is longer than the cluster maximum; {seconds} is the maximum in seconds, formatted with thousands separators", + "sourceHash": "c4115342cb31" + }, + "web.routes.tabs.settings.network_capture.learn_more": { + "defaultMessage": "En savoir plus", + "description": "Network capture: link at the bottom of the capture section that opens the capture documentation", + "sourceHash": "1445799c033a" + }, + "web.routes.tabs.settings.network_capture.network_packet_capture_requires_admin": { + "defaultMessage": "La capture de paquets réseau nécessite un accès administrateur. Les captures contiennent des données réelles des joueurs (adresses IP, chat, identifiants) et ne sont pas anonymisées.", + "description": "Network capture: warning under the capture status saying packet capture needs admin access and captures hold real player data that is not redacted", + "sourceHash": "7b9fc293eb8a" + }, + "web.routes.tabs.settings.network_capture.records_raw_network_protocol_traffic": { + "defaultMessage": "Enregistre le trafic brut du protocole réseau depuis un conteneur sidecar, pour téléchargement et analyse ultérieurs.", + "description": "Network capture: description under the Enable Capture heading saying it records raw network traffic from a sidecar for later download and analysis", + "sourceHash": "3a1f273d60eb" + }, + "web.routes.tabs.settings.network_capture.retention_window": { + "defaultMessage": "Durée de conservation", + "description": "Section heading for how long captures are kept before automatic deletion", + "sourceHash": "8e439d3dae6a" + }, + "web.routes.tabs.settings.network_capture.retention_window_unit": { + "defaultMessage": "Unité de la durée de conservation", + "description": "Label and accessible name for the retention window unit picker (seconds, minutes, hours, days)", + "sourceHash": "3cb61c9f52be" + }, + "web.routes.tabs.settings.network_capture.retention_window_value": { + "defaultMessage": "Valeur de la durée de conservation", + "description": "Network capture: accessible label of the box for the retention window amount; not shown as text; its placeholder is the default amount", + "sourceHash": "9e20c2033e1e" + }, + "web.routes.tabs.settings.network_capture.turning_this_off_stops_any_running": { + "defaultMessage": "La désactivation arrête toute capture en cours et bloque immédiatement les nouvelles. Le conteneur de capture reste dans le pod, inactif, jusqu’à la prochaine recréation du pod — Kubernetes n’a pas d’API pour supprimer un conteneur éphémère.", + "description": "Network capture: hint under the enabled status saying that turning capture off stops running captures and blocks new ones, though the idle capture container stays until the pod is recreated", + "sourceHash": "b6347103f00d" + }, + "web.routes.tabs.settings.network_capture.unit_days": { + "defaultMessage": "jours", + "description": "Network capture: retention window unit option, days", + "sourceHash": "ab51004e9d71" + }, + "web.routes.tabs.settings.network_capture.unit_hours": { + "defaultMessage": "heures", + "description": "Network capture: retention window unit option, hours", + "sourceHash": "404314b1f4bd" + }, + "web.routes.tabs.settings.network_capture.unit_minutes": { + "defaultMessage": "minutes", + "description": "Network capture: retention window unit option, minutes", + "sourceHash": "90e63d85fa1a" + }, + "web.routes.tabs.settings.network_capture.unit_seconds": { + "defaultMessage": "secondes", + "description": "Network capture: retention window unit option, seconds", + "sourceHash": "59f006d63bd0" + }, + "web.routes.tabs.settings.network_capture.you_don_t_have_permission_to_change": { + "defaultMessage": "Vous n’avez pas la permission de modifier les paramètres de capture de ce serveur.", + "description": "Network capture: warning shown to users who cannot change the capture settings for this server", + "sourceHash": "8f4b945c4237" + }, + "web.routes.tabs.settings.networking.a_specific_address_to_request_from_the": { + "defaultMessage": "Une adresse précise à demander au pool. Elle doit être libre : le gestionnaire d’adresses rejette les adresses déjà utilisées. Laissez vide sauf si vous avez besoin d’une adresse précise.", + "description": "Networking: hint under the requested address field saying the address must be free and should be left blank unless a specific address is needed", + "sourceHash": "0fdecde21878" + }, + "web.routes.tabs.settings.networking.add": { + "defaultMessage": "Ajouter", + "description": "Button that adds the typed annotation key and value", + "sourceHash": "9fd728c66c9a" + }, + "web.routes.tabs.settings.networking.add_port_mapping": { + "defaultMessage": "Ajouter un mappage de port", + "description": "Button that adds an empty frp port mapping row", + "sourceHash": "9e1b868a66ba" + }, + "web.routes.tabs.settings.networking.address_and_pool_currently_bound_to": { + "defaultMessage": "Adresse et pool actuellement liés à ce serveur par l’opérateur.", + "description": "Networking: hint under the current assignment label saying the address and pool are bound to this server by the operator", + "sourceHash": "1ff35847107a" + }, + "web.routes.tabs.settings.networking.address_in_use": { + "defaultMessage": "L’adresse demandée {address} est déjà utilisée par GameServer \"{name}\".", + "description": "Networking: status text when the requested address is taken by another GameServer; {address} is the IP address and {name} is the other server's name; link wraps that server's name and links to it", + "sourceHash": "bf6ad783fa94" + }, + "web.routes.tabs.settings.networking.address_pool": { + "defaultMessage": "Pool d’adresses", + "description": "Networking: field label for the load balancer address pool name; the hint says leaving it blank lets the address manager choose", + "sourceHash": "d741d43d889e" + }, + "web.routes.tabs.settings.networking.address_pool_and_requested_address_only": { + "defaultMessage": "L’option Exposition est définie sur {expose}. Le pool d’adresses et l’adresse demandée ne prennent effet que lorsque l’option Exposition (ci-dessus) est définie sur LoadBalancer.", + "description": "Networking: note under the ignored address preference banner; {expose} is the current Expose type, such as ClusterIP", + "sourceHash": "05ef553a863e" + }, + "web.routes.tabs.settings.networking.address_preference_ignored": { + "defaultMessage": "Préférence d’adresse ignorée", + "description": "Networking: notice title shown when an address preference is set but Expose is not LoadBalancer, so the preference has no effect; the line below names the Expose value", + "sourceHash": "18f4cc1b9872" + }, + "web.routes.tabs.settings.networking.address_status": { + "defaultMessage": "Statut de l’adresse", + "description": "Networking: field label for the status of the address assignment, taken from the server's address condition", + "sourceHash": "63ce613d3e7d" + }, + "web.routes.tabs.settings.networking.allow_list_placeholder": { + "defaultMessage": "203.0.113.0/24 10.0.0.0/8", + "description": "Networking: example CIDR ranges, one per line, shown as the LoadBalancer IP allow-list placeholder", + "sourceHash": "163ceee1dc16" + }, + "web.routes.tabs.settings.networking.annotation_value_placeholder": { + "defaultMessage": "valeur", + "description": "Networking: placeholder of the value input when adding a service annotation", + "sourceHash": "cd42404d52ad" + }, + "web.routes.tabs.settings.networking.badge_assigned": { + "defaultMessage": "Attribuée", + "description": "Networking: address status badge when the address is assigned", + "sourceHash": "8191888dd97c" + }, + "web.routes.tabs.settings.networking.badge_error": { + "defaultMessage": "Erreur", + "description": "Networking: address status badge when address assignment failed", + "sourceHash": "54a0e8c17ebb" + }, + "web.routes.tabs.settings.networking.badge_ignored": { + "defaultMessage": "Ignorée", + "description": "Networking: address status badge when the address preference is ignored in the current expose mode", + "sourceHash": "7d648f5b47f3" + }, + "web.routes.tabs.settings.networking.badge_pending": { + "defaultMessage": "En attente", + "description": "Networking: address status badge when the address assignment is still pending", + "sourceHash": "331551b0de41" + }, + "web.routes.tabs.settings.networking.cancel": { + "defaultMessage": "Annuler", + "description": "Button that closes the tunnel credential form without saving", + "sourceHash": "19766ed6ccb2" + }, + "web.routes.tabs.settings.networking.comma_separated_tags_optional": { + "defaultMessage": "Tags séparés par des virgules (facultatif).", + "description": "Networking: hint under the Tailscale tags field saying tags are comma-separated and optional", + "sourceHash": "83a98f07f7ce" + }, + "web.routes.tabs.settings.networking.configured": { + "defaultMessage": "Configuré", + "description": "Networking: status text beside the tunnel credentials, shown when credentials are already stored", + "sourceHash": "84aebc69a1bf" + }, + "web.routes.tabs.settings.networking.credential_label_frp": { + "defaultMessage": "jeton frp", + "description": "Networking: name of the frp credential, shown as the credential input placeholder and in the help text", + "sourceHash": "7088d41502a6" + }, + "web.routes.tabs.settings.networking.credential_label_playit": { + "defaultMessage": "clé secrète playit", + "description": "Networking: name of the playit.gg credential, shown as the credential input placeholder and in the help text", + "sourceHash": "bbd9b609cb3f" + }, + "web.routes.tabs.settings.networking.credential_label_tailscale": { + "defaultMessage": "clé d’authentification Tailscale", + "description": "Networking: name of the Tailscale credential, shown as the credential input placeholder and in the help text", + "sourceHash": "935e212cf8bc" + }, + "web.routes.tabs.settings.networking.current_assignment": { + "defaultMessage": "Attribution actuelle", + "description": "Networking: field label for the address and pool the operator has currently bound to this server", + "sourceHash": "5e510b0f42f4" + }, + "web.routes.tabs.settings.networking.e_g_203_0_113_50": { + "defaultMessage": "ex. 203.0.113.50", + "description": "Networking: example placeholder in the requested address box; an example address, not a default", + "sourceHash": "c33ff15b1dde" + }, + "web.routes.tabs.settings.networking.enable_tunnel": { + "defaultMessage": "Activer le tunnel", + "description": "Networking: switch that turns a relay tunnel on or off; the text is its accessible name, not shown as text", + "sourceHash": "e6c2ed95ba0e" + }, + "web.routes.tabs.settings.networking.expose": { + "defaultMessage": "Exposition", + "description": "Networking: field label for the service type that exposes the game pod; keeps applying when a tunnel is enabled", + "sourceHash": "9687fa49cbab" + }, + "web.routes.tabs.settings.networking.expose_cluster_ip": { + "defaultMessage": "ClusterIP (dans le cluster uniquement)", + "description": "Networking: Expose option label for ClusterIP, a service reachable only inside the cluster", + "sourceHash": "df574fea848b" + }, + "web.routes.tabs.settings.networking.expose_hostport": { + "defaultMessage": "Hostport", + "description": "Networking: Expose option label for Hostport, a port bound on the node host", + "sourceHash": "0909afb9f701" + }, + "web.routes.tabs.settings.networking.expose_load_balancer": { + "defaultMessage": "LoadBalancer", + "description": "Networking: Expose option label for LoadBalancer (Kubernetes service type name)", + "sourceHash": "6103cff5c53f" + }, + "web.routes.tabs.settings.networking.expose_node_port": { + "defaultMessage": "NodePort", + "description": "Networking: Expose option label for NodePort (Kubernetes service type name)", + "sourceHash": "2a4730517ac2" + }, + "web.routes.tabs.settings.networking.from_pool_pool": { + "defaultMessage": "depuis le pool ''{pool}'", + "description": "Networking: sentence with pool in span", + "sourceHash": "9154255f028b" + }, + "web.routes.tabs.settings.networking.frp_port_mapping_required": { + "defaultMessage": "Au moins un mappage de port est requis pour frp", + "description": "Networking: validation error when no frp port mapping is configured", + "sourceHash": "4faf4a6f92b0" + }, + "web.routes.tabs.settings.networking.frp_server_address": { + "defaultMessage": "Adresse du serveur frp.", + "description": "Networking: hint under the frp server address field; frp is a technical term, usually left untranslated", + "sourceHash": "6c5dbcad49c9" + }, + "web.routes.tabs.settings.networking.frp_server_address_required": { + "defaultMessage": "L'adresse du serveur est requise pour frp", + "description": "Networking: validation error when the frp server address is empty", + "sourceHash": "071d8316fe54" + }, + "web.routes.tabs.settings.networking.frp_server_port_default_7000": { + "defaultMessage": "Port du serveur frp (par défaut 7000).", + "description": "Networking: hint under the frp server port field giving the default port 7000", + "sourceHash": "14df87886591" + }, + "web.routes.tabs.settings.networking.frp_server_port_placeholder": { + "defaultMessage": "7000", + "description": "Networking: default frp server port shown as the server port input placeholder", + "sourceHash": "b698d86c67a2" + }, + "web.routes.tabs.settings.networking.game": { + "defaultMessage": "jeu", + "description": "Networking: example placeholder in a frp port mapping's name box; an example value, not a default", + "sourceHash": "6ca5cab77e70" + }, + "web.routes.tabs.settings.networking.game_server": { + "defaultMessage": "game-server", + "description": "Networking: example placeholder in the Tailscale hostname box; an example value, not a default", + "sourceHash": "cd8f588bc820" + }, + "web.routes.tabs.settings.networking.hostname": { + "defaultMessage": "Nom d'hôte", + "description": "Networking: field label for an optional hostname, used for Tailscale or for ingress and external-dns annotations", + "sourceHash": "2db533557011" + }, + "web.routes.tabs.settings.networking.hostname_in_your_tailnet_optional": { + "defaultMessage": "Nom d'hôte dans votre tailnet (facultatif).", + "description": "Networking: hint under the Tailscale hostname field saying it is the hostname in the tailnet and is optional", + "sourceHash": "77952a60df89" + }, + "web.routes.tabs.settings.networking.key_value_separator": { + "defaultMessage": "=", + "description": "Networking: separator shown between an annotation key and its value", + "sourceHash": "380918b946a5" + }, + "web.routes.tabs.settings.networking.loadbalancer_ip_allow_list": { + "defaultMessage": "Liste d'autorisation des IP LoadBalancer", + "description": "Networking: field label for the LoadBalancer IP allow-list, shown only when Expose is LoadBalancer; the accessible name of its text box is the same", + "sourceHash": "6be02faa8498" + }, + "web.routes.tabs.settings.networking.mc_example_com": { + "defaultMessage": "mc.example.com", + "description": "Networking: example placeholder in the general hostname field; an example value, not a default", + "sourceHash": "36d237f1c848" + }, + "web.routes.tabs.settings.networking.merged_into_the_service_useful_for": { + "defaultMessage": "Fusionnées dans le Service. Utile pour la configuration du LB cloud ou d'external-dns.", + "description": "Networking: hint under the service annotations field saying they are merged into the Service and are useful for cloud load balancer or external-dns setup", + "sourceHash": "51fe0990cef4" + }, + "web.routes.tabs.settings.networking.my_server": { + "defaultMessage": "my-server", + "description": "Networking: example placeholder in the playit tunnel name box; an example value, not a default", + "sourceHash": "ae348bd0a230" + }, + "web.routes.tabs.settings.networking.name_and_remote_port_for_each_game_port": { + "defaultMessage": "Nom et port distant pour chaque port de jeu.", + "description": "Networking: hint under the port mappings heading saying each entry holds a name and the remote port for each game port", + "sourceHash": "3731fda83dad" + }, + "web.routes.tabs.settings.networking.name_of_a_load_balancer_address_pool": { + "defaultMessage": "Nom d'un pool d'adresses de load balancer configuré par l'administrateur du cluster. Laisser vide pour que le gestionnaire d'adresses choisisse.", + "description": "Networking: hint under the address pool field saying the pool is configured by the cluster admin and blank lets the address manager choose", + "sourceHash": "83e07aec082f" + }, + "web.routes.tabs.settings.networking.name_of_the_tunnel_on_playit_gg": { + "defaultMessage": "Nom du tunnel sur playit.gg.", + "description": "Networking: hint under the playit tunnel name field saying it is the name of the tunnel on playit.gg; playit is a technical term", + "sourceHash": "165dfd2f22fc" + }, + "web.routes.tabs.settings.networking.no_address_manager_configured": { + "defaultMessage": "Aucun gestionnaire d'adresses configuré", + "description": "Networking: warning title shown when the cluster has no address manager; the line below says the pool and address preference will be saved but not applied", + "sourceHash": "f226f5d23024" + }, + "web.routes.tabs.settings.networking.one_cidr_per_line_restricts_which": { + "defaultMessage": "Un CIDR par ligne. Limite les clients qui peuvent atteindre le LoadBalancer ; vide autorise tous les clients.", + "description": "Networking: hint under the IP allow-list saying one CIDR goes on each line and an empty list allows all clients", + "sourceHash": "6aa166ea622b" + }, + "web.routes.tabs.settings.networking.optional_dns_name_for_ingress_external": { + "defaultMessage": "Nom DNS facultatif pour les annotations d'ingress / external-dns.", + "description": "Networking: hint under the Hostname field of the general network settings, saying it is an optional DNS name for ingress or external-dns annotations", + "sourceHash": "0b8c58629721" + }, + "web.routes.tabs.settings.networking.pin_a_nodeport_or_override_the_service": { + "defaultMessage": "Figer un NodePort ou remplacer le port du Service pour un port de modèle nommé.", + "description": "Networking: hint under the port overrides label explaining that overrides pin a NodePort or change the Service port for a template port", + "sourceHash": "2bf57b09272d" + }, + "web.routes.tabs.settings.networking.pool_name": { + "defaultMessage": "pool-name", + "description": "Networking: example placeholder in the address pool box; an example name, not a default", + "sourceHash": "daac3122ab1f" + }, + "web.routes.tabs.settings.networking.port_mapping_name_required": { + "defaultMessage": "Mappage de port {index} : le nom est obligatoire", + "description": "Networking: validation error when a port mapping has no name; {index} is the 1-based position of the mapping", + "sourceHash": "493238c27a74" + }, + "web.routes.tabs.settings.networking.port_mapping_remote_port_range": { + "defaultMessage": "Mappage de port {index} : le port distant doit être compris entre 1 et 65535", + "description": "Networking: validation error when a port mapping's remote port is out of range; {index} is the 1-based position of the mapping", + "sourceHash": "0fa39fe38c37" + }, + "web.routes.tabs.settings.networking.port_mappings": { + "defaultMessage": "Mappages de ports", + "description": "Networking: heading of the frp port mappings list; the line under it explains each entry names a game port and its remote port", + "sourceHash": "08400200337f" + }, + "web.routes.tabs.settings.networking.port_name": { + "defaultMessage": "Nom du port", + "description": "Column label for the game port name of a port mapping", + "sourceHash": "239a365f3b9b" + }, + "web.routes.tabs.settings.networking.port_overrides": { + "defaultMessage": "Remplacements de ports", + "description": "Networking: field label for the port overrides list that pins a NodePort or changes the Service port of a template port", + "sourceHash": "4404f3b839fa" + }, + "web.routes.tabs.settings.networking.provider": { + "defaultMessage": "Fournisseur", + "description": "Networking: field label for the tunnel provider picker, shown when a tunnel is enabled", + "sourceHash": "472590ae974d" + }, + "web.routes.tabs.settings.networking.provider_and_credential_required": { + "defaultMessage": "Fournisseur et valeur d'identifiant requis", + "description": "Networking: error shown when a tunnel credential is saved without a provider or a value", + "sourceHash": "fa55f85b15f0" + }, + "web.routes.tabs.settings.networking.provider_stored_in_a_kubernetes_secret": { + "defaultMessage": "{provider} : stocké dans un Secret Kubernetes, jamais affiché.", + "description": "Networking: sentence with provider in span", + "sourceHash": "64c6c61c0b9f" + }, + "web.routes.tabs.settings.networking.public_address_assigned_at_runtime": { + "defaultMessage": "Adresse publique attribuée à l'exécution", + "description": "Networking: title of the info note for playit saying the public address is assigned at runtime", + "sourceHash": "fe4b00f6a2d3" + }, + "web.routes.tabs.settings.networking.reflects_the_gameserver_s": { + "defaultMessage": "Reflète la condition AddressAssignment du GameServer.", + "description": "Networking: hint under the address status label saying it reflects the game server's address assignment condition", + "sourceHash": "c7cebb936762" + }, + "web.routes.tabs.settings.networking.relay_example_com": { + "defaultMessage": "relay.example.com", + "description": "Networking: example placeholder in the frp server address box; an example host, not a default", + "sourceHash": "c5294ed03a55" + }, + "web.routes.tabs.settings.networking.remote_port": { + "defaultMessage": "Port distant", + "description": "Column label for the remote port of a port mapping", + "sourceHash": "57d6b8baec32" + }, + "web.routes.tabs.settings.networking.remote_port_placeholder": { + "defaultMessage": "8080", + "description": "Networking: example remote port shown as the port mapping input placeholder", + "sourceHash": "6c237681e709" + }, + "web.routes.tabs.settings.networking.remove": { + "defaultMessage": "Retirer", + "description": "Networking: icon-only button that removes one port mapping or key-value entry; read by screen readers; keep it short", + "sourceHash": "c3812fc4acb8" + }, + "web.routes.tabs.settings.networking.remove_credential_failed": { + "defaultMessage": "Échec de la suppression de l'identifiant", + "description": "Networking: fallback error shown when removing a tunnel credential fails", + "sourceHash": "0817f8327efd" + }, + "web.routes.tabs.settings.networking.remove_tunnel_credentials": { + "defaultMessage": "Retirer les identifiants du tunnel", + "description": "Networking: icon-only button that removes the stored tunnel credentials; read by screen readers; keep it short", + "sourceHash": "f9d0e9f6b1db" + }, + "web.routes.tabs.settings.networking.replace": { + "defaultMessage": "Remplacer", + "description": "Button that replaces the saved tunnel credential with a new one", + "sourceHash": "95e154398a4b" + }, + "web.routes.tabs.settings.networking.requested_address": { + "defaultMessage": "Adresse demandée", + "description": "Networking: field label for an optional specific address to request from the address pool", + "sourceHash": "21cc42607cf9" + }, + "web.routes.tabs.settings.networking.route_players_through_a_relay_so_they": { + "defaultMessage": "Faire passer les joueurs par un relais pour qu'ils puissent se connecter sans redirection de port ni IP publique.", + "description": "Networking: hint under the Enable tunnel switch explaining a relay lets players connect without port forwarding or a public IP", + "sourceHash": "2be51186d4d1" + }, + "web.routes.tabs.settings.networking.save_credential": { + "defaultMessage": "Enregistrer l'identifiant", + "description": "Button that saves the tunnel credential", + "sourceHash": "a88c299ab989" + }, + "web.routes.tabs.settings.networking.save_credential_failed": { + "defaultMessage": "Échec de l'enregistrement de l'identifiant", + "description": "Networking: fallback error shown when saving a tunnel credential fails", + "sourceHash": "b8f28b2acdbe" + }, + "web.routes.tabs.settings.networking.saving": { + "defaultMessage": "Enregistrement...", + "description": "Save button label while the tunnel credential is being saved", + "sourceHash": "dc85af8f2b1d" + }, + "web.routes.tabs.settings.networking.secret_secretname": { + "defaultMessage": "Secret : {secretName}", + "description": "Networking: sentence with secretName in div", + "sourceHash": "d96201466704" + }, + "web.routes.tabs.settings.networking.server_address": { + "defaultMessage": "Adresse du serveur", + "description": "Networking: field label for the frp relay server address", + "sourceHash": "8be89710b2f1" + }, + "web.routes.tabs.settings.networking.server_port": { + "defaultMessage": "Port du serveur", + "description": "Networking: field label for the port of the frp relay server", + "sourceHash": "426f91710cc2" + }, + "web.routes.tabs.settings.networking.service_annotations": { + "defaultMessage": "Annotations du Service", + "description": "Networking: field label for the optional annotations added to the Kubernetes Service", + "sourceHash": "20c1315df8a1" + }, + "web.routes.tabs.settings.networking.service_type_fronting_the_game_pod": { + "defaultMessage": "Type de Service exposant le pod de jeu. S'applique toujours lorsqu'un tunnel est activé.", + "description": "Networking: hint under the Expose field saying the service type fronts the game pod and still applies when a tunnel is enabled", + "sourceHash": "f4a10b574d74" + }, + "web.routes.tabs.settings.networking.tag_server_tag_minecraft": { + "defaultMessage": "tag:server, tag:minecraft", + "description": "Networking: example placeholder in the Tailscale tags box showing the tag format; an example value, not a default", + "sourceHash": "c7f3120ba190" + }, + "web.routes.tabs.settings.networking.tags": { + "defaultMessage": "Tags", + "description": "Networking: field label for the optional Tailscale tags", + "sourceHash": "1331275bc537" + }, + "web.routes.tabs.settings.networking.tailnet_only_not_public": { + "defaultMessage": "Tailnet uniquement, non public", + "description": "Networking: warning title shown for the Tailscale provider; the line below says the server is reachable only from the Tailscale network", + "sourceHash": "3ac9698b3c3f" + }, + "web.routes.tabs.settings.networking.the_server_becomes_reachable_only_from": { + "defaultMessage": "Le serveur n'est accessible que depuis votre réseau Tailscale, jamais depuis l'internet public.", + "description": "Networking: text under the Tailnet only warning saying the server is reachable only from the user's Tailscale network, never from the public internet", + "sourceHash": "38ab1a7e0a22" + }, + "web.routes.tabs.settings.networking.the_tunnel_s_public_address_will_be": { + "defaultMessage": "L'adresse publique du tunnel sera attribuée par playit.gg et affichée sur la carte de connexion.", + "description": "Networking: text under the playit info note saying playit.gg assigns the public address, which then shows on the connection card", + "sourceHash": "c6ea4450f964" + }, + "web.routes.tabs.settings.networking.this_cluster_has_no_metallb_or_cilium": { + "defaultMessage": "Ce cluster n'a pas de gestionnaire d'adresses MetalLB ou Cilium. Votre pool et votre préférence d'adresse seront enregistrés mais jamais appliqués.", + "description": "Networking: note under the no-address-manager warning; without MetalLB or Cilium the pool and address preference are saved but never applied", + "sourceHash": "571256ad9e30" + }, + "web.routes.tabs.settings.networking.tunnel_credentials": { + "defaultMessage": "Identifiants du tunnel", + "description": "Label for the tunnel credential field and its status section", + "sourceHash": "90a5e9b451be" + }, + "web.routes.tabs.settings.networking.tunnel_credentials_required": { + "defaultMessage": "Les identifiants du tunnel sont requis", + "description": "Networking: validation error when a tunnel is enabled without credentials", + "sourceHash": "2e85fc792165" + }, + "web.routes.tabs.settings.networking.tunnel_name": { + "defaultMessage": "Nom du tunnel", + "description": "Label for the playit.gg tunnel name", + "sourceHash": "f929e0dc06bd" + }, + "web.routes.tabs.settings.placement.affinity": { + "defaultMessage": "Affinité", + "description": "Label for the affinity JSON editor", + "sourceHash": "5a2e54bbcb99" + }, + "web.routes.tabs.settings.placement.affinity_must_be_object": { + "defaultMessage": "Doit être un objet JSON", + "description": "Placement: validation error when the affinity JSON is not an object", + "sourceHash": "3c8b003bc4a9" + }, + "web.routes.tabs.settings.placement.invalid_json": { + "defaultMessage": "JSON non valide", + "description": "Placement: validation error when a tolerations or affinity field is not valid JSON", + "sourceHash": "f60c824125e6" + }, + "web.routes.tabs.settings.placement.pod_affinity_anti_affinity_and_node": { + "defaultMessage": "Contraintes d'affinité et d'anti-affinité de pods et d'affinité de nœuds. Objet JSON (facultatif).", + "description": "Placement: hint under the Affinity label saying the field takes an optional JSON object with pod and node affinity constraints", + "sourceHash": "6b5dac3ef7c5" + }, + "web.routes.tabs.settings.placement.pod_tolerations_for_kubernetes_node": { + "defaultMessage": "Tolérances de pods pour les taints de nœuds Kubernetes. Tableau d'objets de tolérance (facultatif).", + "description": "Placement: hint under the Tolerations label saying the field takes an optional JSON array of toleration objects for Kubernetes node taints", + "sourceHash": "546aaae64496" + }, + "web.routes.tabs.settings.placement.tolerations": { + "defaultMessage": "Tolérances", + "description": "Label for the tolerations JSON editor", + "sourceHash": "db0748ecfbcf" + }, + "web.routes.tabs.settings.placement.tolerations_must_be_array": { + "defaultMessage": "Doit être un tableau JSON", + "description": "Placement: validation error when the tolerations JSON is not an array", + "sourceHash": "0a4615b18dac" + }, + "web.routes.tabs.settings.reload": { + "defaultMessage": "Recharger", + "description": "Settings tab: inline button in the footer warning that reloads the latest server settings; keep it short", + "sourceHash": "bdc090ec61e3" + }, + "web.routes.tabs.settings.resources.10gi": { + "defaultMessage": "10Gi", + "description": "Resources: example placeholder in the storage size box; an example value, not a default", + "sourceHash": "a6a6a53775e9" + }, + "web.routes.tabs.settings.resources.allowvolumeexpansion_true": { + "defaultMessage": "allowVolumeExpansion: true", + "description": "Resources: Kubernetes StorageClass field name shown in the storage size hint", + "sourceHash": "1980baabd43d" + }, + "web.routes.tabs.settings.resources.cpu_cores": { + "defaultMessage": "Cœurs CPU", + "description": "Resources: field label for the number of CPU cores reserved for the server; CPU is a technical term, usually left untranslated", + "sourceHash": "5dbed0bb8927" + }, + "web.routes.tabs.settings.resources.fast_ssd": { + "defaultMessage": "fast-ssd", + "description": "Resources: example placeholder in the StorageClass box; an example class name, not a default", + "sourceHash": "a21fbe70b3f9" + }, + "web.routes.tabs.settings.resources.invalid_quantity_e_g_10gi": { + "defaultMessage": "Quantité non valide (par ex. \"10Gi\").", + "description": "Resources: validation error under the storage size box when the value is not a valid quantity such as 10Gi", + "sourceHash": "9f47b2e0f69c" + }, + "web.routes.tabs.settings.resources.k8s_quantity_e_g_10gi_200gi": { + "defaultMessage": "Quantité K8s (par ex. 10Gi, 200Gi).", + "description": "Resources: hint under the storage size field saying the value is a Kubernetes quantity such as 10Gi or 200Gi; Kubernetes is a technical term", + "sourceHash": "5df66a711422" + }, + "web.routes.tabs.settings.resources.leave_blank_to_use_the_cluster_default": { + "defaultMessage": "Laisser vide pour utiliser la valeur par défaut du cluster.", + "description": "Resources: hint under the StorageClass field saying a blank value uses the cluster default storage class", + "sourceHash": "9b1e131c4d2c" + }, + "web.routes.tabs.settings.resources.memory_gib": { + "defaultMessage": "Mémoire (GiB)", + "description": "Resources: field label for the memory amount in GiB reserved for the server", + "sourceHash": "e360625ba4be" + }, + "web.routes.tabs.settings.resources.resizing_requires_a_storageclass_with": { + "defaultMessage": "Le redimensionnement nécessite une StorageClass avec {setting}.", + "description": "Resources: hint under the storage size field saying resizing needs a StorageClass that allows volume expansion; {setting} is the allowVolumeExpansion: true field, shown in monospace", + "sourceHash": "e31bc11004d7" + }, + "web.routes.tabs.settings.resources.sets_requests_limits_to_the_same_value": { + "defaultMessage": "Définit requests=limits à la même valeur (QoS Guaranteed).", + "description": "Resources: hint under the CPU cores field saying the request and limit are set to the same value, which gives the Guaranteed QoS class; QoS is a technical term", + "sourceHash": "44bd85735b1b" + }, + "web.routes.tabs.settings.resources.sets_requests_limits_to_the_same_value_2": { + "defaultMessage": "Définit requests=limits à la même valeur.", + "description": "Resources: hint under the memory field saying the request and limit are set to the same value", + "sourceHash": "ef5f1f2e057e" + }, + "web.routes.tabs.settings.resources.storage_size": { + "defaultMessage": "Taille de stockage", + "description": "Resources: field label for the size of the server's persistent storage; the accessible name of its box is the same", + "sourceHash": "a5ba48674229" + }, + "web.routes.tabs.settings.resources.storageclass": { + "defaultMessage": "StorageClass", + "description": "Resources: field label for the optional storage class that provides the server's storage; the hint below says blank uses the cluster default", + "sourceHash": "ca92c8a29fb8" + }, + "web.routes.tabs.settings.retry_reload": { + "defaultMessage": "Réessayer le rechargement", + "description": "Settings tab: inline button shown after a failed reload that tries the reload again; keep it short", + "sourceHash": "3a1dfdb0163e" + }, + "web.routes.tabs.settings.save_changes": { + "defaultMessage": "Enregistrer les modifications", + "description": "Settings: label of the button that saves the server settings", + "sourceHash": "dd0ae7a5cbcf" + }, + "web.routes.tabs.settings.save_failed": { + "defaultMessage": "échec de l'enregistrement", + "description": "Settings: error shown when saving the server settings fails and the error has no message", + "sourceHash": "802f2bef7c23" + }, + "web.routes.tabs.settings.saved": { + "defaultMessage": "Enregistré.", + "description": "Settings tab: footer status text shown briefly after the settings are saved", + "sourceHash": "fca046ccd586" + }, + "web.routes.tabs.settings.saving": { + "defaultMessage": "Enregistrement…", + "description": "Settings: label of the save button while the save is running", + "sourceHash": "23e39291d613" + }, + "web.routes.tabs.settings.section_danger_zone": { + "defaultMessage": "Zone dangereuse", + "description": "Settings: name of the Danger zone section in the settings tab list", + "sourceHash": "fd8b8dae4421" + }, + "web.routes.tabs.settings.section_environment": { + "defaultMessage": "Environnement", + "description": "Settings: name of the Environment section in the settings tab list", + "sourceHash": "9e471951a1b4" + }, + "web.routes.tabs.settings.section_game_configuration": { + "defaultMessage": "Configuration du jeu", + "description": "Settings: name of the Game configuration section in the settings tab list", + "sourceHash": "748837ff0cd2" + }, + "web.routes.tabs.settings.section_general": { + "defaultMessage": "Général", + "description": "Settings: name of the General section in the settings tab list", + "sourceHash": "c910d474dcd7" + }, + "web.routes.tabs.settings.section_lifecycle": { + "defaultMessage": "Cycle de vie", + "description": "Settings: name of the Lifecycle section in the settings tab list", + "sourceHash": "46459b1fa919" + }, + "web.routes.tabs.settings.section_network_capture": { + "defaultMessage": "Capture réseau", + "description": "Settings: name of the Network capture section in the settings tab list", + "sourceHash": "72c6f4a9c6ae" + }, + "web.routes.tabs.settings.section_networking": { + "defaultMessage": "Réseau", + "description": "Settings: name of the Networking section in the settings tab list", + "sourceHash": "204de4b6d5a3" + }, + "web.routes.tabs.settings.section_placement": { + "defaultMessage": "Placement", + "description": "Settings: name of the Placement section in the settings tab list", + "sourceHash": "4df9939944a7" + }, + "web.routes.tabs.settings.section_rbac_access": { + "defaultMessage": "RBAC et accès", + "description": "Settings: name of the RBAC and access section in the settings tab list", + "sourceHash": "0a8f235c085b" + }, + "web.routes.tabs.settings.section_resources": { + "defaultMessage": "Ressources", + "description": "Settings: name of the Resources section in the settings tab list", + "sourceHash": "e89b30aa1dc3" + }, + "web.routes.tabs.settings.section_scheduled_backups": { + "defaultMessage": "Sauvegardes planifiées", + "description": "Settings: name of the Scheduled backups section in the settings tab list", + "sourceHash": "2c1320018844" + }, + "web.routes.tabs.settings.section_share_links": { + "defaultMessage": "Liens de partage", + "description": "Settings: name of the Share links section in the settings tab list", + "sourceHash": "16ce9683d9ed" + }, + "web.routes.tabs.settings.section_version": { + "defaultMessage": "Version", + "description": "Settings: name of the Version section in the settings tab list", + "sourceHash": "dd167905de0d" + }, + "web.routes.tabs.settings.server_changed_since_you_opened_this": { + "defaultMessage": "Le serveur a changé depuis l'ouverture de cette page.", + "description": "Settings tab: warning in the footer saying the server changed after the page was opened; followed by a Reload button and the sentence after it", + "sourceHash": "5526f30b91d5" + }, + "web.routes.tabs.settings.share_links.allow_starting_the_server": { + "defaultMessage": "Autoriser le démarrage du serveur", + "description": "Share links: switch that lets the link holder wake the server from sleep; the text is its accessible name", + "sourceHash": "6185b2553ea9" + }, + "web.routes.tabs.settings.share_links.anyone_using_this_link_will_immediately": { + "defaultMessage": "Toute personne utilisant ce lien perdra immédiatement l'accès à la page d'état de {serverName}. Cette action est irréversible.", + "description": "Share links: sentence with serverName in Description", + "sourceHash": "5740eed209a2" + }, + "web.routes.tabs.settings.share_links.anyone_with_this_link_can_check_the": { + "defaultMessage": "Toute personne disposant de ce lien peut consulter l'état du serveur et son adresse de connexion sans se connecter.", + "description": "Share links: description under the Create share link heading saying anyone with the link can view the server status and address without signing in", + "sourceHash": "c7c2228dad32" + }, + "web.routes.tabs.settings.share_links.can_start": { + "defaultMessage": "Démarrage possible", + "description": "Table column header for whether the share link allows starting the server", + "sourceHash": "603ed8baefa8" + }, + "web.routes.tabs.settings.share_links.can_start_value": { + "defaultMessage": "Démarrage possible", + "description": "Share links: table cell for a link that allows starting the server", + "sourceHash": "603ed8baefa8" + }, + "web.routes.tabs.settings.share_links.cancel": { + "defaultMessage": "Annuler", + "description": "Button that closes the share link dialog without changing anything", + "sourceHash": "19766ed6ccb2" + }, + "web.routes.tabs.settings.share_links.copied": { + "defaultMessage": "Copié !", + "description": "Share links: copy button label briefly shown after the link was copied", + "sourceHash": "ea61bc15688d" + }, + "web.routes.tabs.settings.share_links.copy_link": { + "defaultMessage": "Copier le lien", + "description": "Share links: button that copies the created share link", + "sourceHash": "dbf362d4f210" + }, + "web.routes.tabs.settings.share_links.copy_url": { + "defaultMessage": "Copier l'URL", + "description": "Share links: icon-only button in the created-link box that copies the URL; read by screen readers; keep it short", + "sourceHash": "b26d1037f1c2" + }, + "web.routes.tabs.settings.share_links.create_a_link_so_a_friend_without_a": { + "defaultMessage": "Créez un lien pour qu'un ami sans compte Gameplane puisse consulter l'état de ce serveur et s'y connecter, sans lui donner accès au tableau de bord.", + "description": "Share links: empty-state text shown when the server has no share links, explaining a link lets someone check status without dashboard access", + "sourceHash": "d98c06e7779e" + }, + "web.routes.tabs.settings.share_links.create_failed": { + "defaultMessage": "Échec de la création du lien", + "description": "Share links: fallback error shown when creating a link fails", + "sourceHash": "85892fd35527" + }, + "web.routes.tabs.settings.share_links.create_link": { + "defaultMessage": "Créer un lien", + "description": "Button that opens the create-link dialog", + "sourceHash": "e6b850cff6af" + }, + "web.routes.tabs.settings.share_links.create_link_submit": { + "defaultMessage": "Créer un lien", + "description": "Share links: create dialog submit button", + "sourceHash": "e6b850cff6af" + }, + "web.routes.tabs.settings.share_links.create_share_link_for_servername": { + "defaultMessage": "Créer un lien de partage pour {serverName}", + "description": "Share links: sentence with serverName in ModalHeading", + "sourceHash": "e1610862b926" + }, + "web.routes.tabs.settings.share_links.created": { + "defaultMessage": "Créé", + "description": "Table column header for the share link creation date", + "sourceHash": "d70b9e24bca2" + }, + "web.routes.tabs.settings.share_links.creating": { + "defaultMessage": "Création…", + "description": "Share links: create dialog submit button while the link is being created", + "sourceHash": "c79ed9492e3c" + }, + "web.routes.tabs.settings.share_links.done": { + "defaultMessage": "Terminé", + "description": "Button that closes the link-created dialog", + "sourceHash": "11a6767d5674" + }, + "web.routes.tabs.settings.share_links.expires": { + "defaultMessage": "Expire", + "description": "Table column header for the share link expiry date", + "sourceHash": "f6725f3af08a" + }, + "web.routes.tabs.settings.share_links.expires_in": { + "defaultMessage": "Expire dans", + "description": "Label for the expiry picker in the create-link dialog", + "sourceHash": "de9967524f05" + }, + "web.routes.tabs.settings.share_links.expires_on": { + "defaultMessage": "Expire le", + "description": "Label for the custom expiry date input in the create-link dialog", + "sourceHash": "71712ec2f7ba" + }, + "web.routes.tabs.settings.share_links.expiry_15_days": { + "defaultMessage": "15 jours", + "description": "Share links: create dialog expiry option, a link valid for 15 days", + "sourceHash": "c801ee548dac" + }, + "web.routes.tabs.settings.share_links.expiry_30_days": { + "defaultMessage": "30 jours", + "description": "Share links: create dialog expiry option, a link valid for 30 days", + "sourceHash": "ffd7280513ce" + }, + "web.routes.tabs.settings.share_links.expiry_60_days": { + "defaultMessage": "60 jours", + "description": "Share links: create dialog expiry option, a link valid for 60 days", + "sourceHash": "600a4e11044e" + }, + "web.routes.tabs.settings.share_links.expiry_90_days": { + "defaultMessage": "90 jours", + "description": "Share links: create dialog expiry option, a link valid for 90 days", + "sourceHash": "38825b0f646b" + }, + "web.routes.tabs.settings.share_links.expiry_custom": { + "defaultMessage": "Personnalisée", + "description": "Share links: create dialog expiry option, pick an explicit expiry date", + "sourceHash": "494ca78f7374" + }, + "web.routes.tabs.settings.share_links.expiry_never": { + "defaultMessage": "Sans expiration", + "description": "Share links: create dialog expiry option, the link never expires", + "sourceHash": "fe06351db8da" + }, + "web.routes.tabs.settings.share_links.gameplane_stores_only_a_one_way_hash_of": { + "defaultMessage": "Gameplane ne stocke qu'un hachage à sens unique du jeton : il ne peut donc pas être affiché ni récupéré après la fermeture de cette fenêtre. Copiez-le maintenant.", + "description": "Share links: warning text under the title saying only a hash of the token is stored, so the link cannot be shown again and must be copied now", + "sourceHash": "7d505791b397" + }, + "web.routes.tabs.settings.share_links.let_people_without_a_gameplane_account": { + "defaultMessage": "Permettez aux personnes sans compte Gameplane de consulter l'état et l'adresse de connexion de ce serveur. Seul le propriétaire peut créer ou révoquer des liens.", + "description": "Share links: hint under the Share links heading saying people without an account can check the status and address, and only the owner can create or revoke links", + "sourceHash": "58127d4cbb18" + }, + "web.routes.tabs.settings.share_links.long_lived_link_it_stays_valid_for_over": { + "defaultMessage": "Lien de longue durée : il reste valide plus d'un an sauf si vous le révoquez.", + "description": "Share links: warning under the expiry date field shown when the chosen date is more than a year away", + "sourceHash": "d9184eef554c" + }, + "web.routes.tabs.settings.share_links.never": { + "defaultMessage": "Jamais", + "description": "Share links: table cell for a link's expiry when the link never expires", + "sourceHash": "6300ef800bb8" + }, + "web.routes.tabs.settings.share_links.no_share_links_yet": { + "defaultMessage": "Aucun lien de partage pour le moment", + "description": "Empty-state heading when the server has no share links", + "sourceHash": "7fb2dcf4a058" + }, + "web.routes.tabs.settings.share_links.revoke": { + "defaultMessage": "Révoquer", + "description": "Table row button that revokes this share link", + "sourceHash": "87e6d00bbf53" + }, + "web.routes.tabs.settings.share_links.revoke_failed": { + "defaultMessage": "Échec de la révocation du lien", + "description": "Share links: fallback error shown when revoking a link fails", + "sourceHash": "c271eafef2bc" + }, + "web.routes.tabs.settings.share_links.revoke_link_submit": { + "defaultMessage": "Révoquer le lien", + "description": "Share links: revoke dialog submit button", + "sourceHash": "da371ee1e141" + }, + "web.routes.tabs.settings.share_links.revoke_this_share_link": { + "defaultMessage": "Révoquer ce lien de partage ?", + "description": "Share links: title of the confirmation dialog that asks before a share link is revoked", + "sourceHash": "2b8b774e5866" + }, + "web.routes.tabs.settings.share_links.revoking": { + "defaultMessage": "Révocation…", + "description": "Share links: revoke dialog submit button while the link is being revoked", + "sourceHash": "1a36f21b3cfe" + }, + "web.routes.tabs.settings.share_links.send_this_to_your_friend_it_works": { + "defaultMessage": "Envoyez-le à votre ami. Il fonctionne sans compte Gameplane.", + "description": "Share links: description in the created-link dialog telling the user to send the link to a friend, who needs no Gameplane account", + "sourceHash": "4958340ed5f4" + }, + "web.routes.tabs.settings.share_links.share_link_created": { + "defaultMessage": "Lien de partage créé", + "description": "Share links: heading of the dialog shown after a share link is created", + "sourceHash": "8d0174715812" + }, + "web.routes.tabs.settings.share_links.share_links": { + "defaultMessage": "Liens de partage", + "description": "Share links: section heading and table label for the list of share links; the table name is read by screen readers", + "sourceHash": "16ce9683d9ed" + }, + "web.routes.tabs.settings.share_links.status": { + "defaultMessage": "Statut", + "description": "Table column header for the share link status (Active, Expired or Revoked)", + "sourceHash": "920e413c7d41" + }, + "web.routes.tabs.settings.share_links.status_active": { + "defaultMessage": "Actif", + "description": "Share links: status chip for a link that still works", + "sourceHash": "92340695899b" + }, + "web.routes.tabs.settings.share_links.status_expired": { + "defaultMessage": "Expiré", + "description": "Share links: status chip for a link past its expiry date", + "sourceHash": "424a2551d356" + }, + "web.routes.tabs.settings.share_links.status_revoked": { + "defaultMessage": "Révoqué", + "description": "Share links: status chip for a link the owner revoked", + "sourceHash": "f6f738d04392" + }, + "web.routes.tabs.settings.share_links.the_link_holder_can_wake_servername": { + "defaultMessage": "Le détenteur du lien peut réveiller {serverName} depuis la veille. Sans cela, il ne peut que consulter son état.", + "description": "Share links: sentence with serverName in Description", + "sourceHash": "1db4d4098974" + }, + "web.routes.tabs.settings.share_links.this_link_works_until_you_revoke_it": { + "defaultMessage": "Ce lien fonctionne jusqu'à ce que vous le révoquiez.", + "description": "Share links: warning under the expiry picker shown when the link never expires; it works until revoked", + "sourceHash": "17ff874d7586" + }, + "web.routes.tabs.settings.share_links.view_only": { + "defaultMessage": "Lecture seule", + "description": "Share links: table cell for a link that only allows viewing the server status", + "sourceHash": "9b4c6c8590e9" + }, + "web.routes.tabs.settings.share_links.you_will_not_see_this_link_again": { + "defaultMessage": "Vous ne reverrez pas ce lien", + "description": "Share links: bold warning title in the created-link dialog saying the link cannot be seen again", + "sourceHash": "d7a63702cbb9" + }, + "web.routes.tabs.settings.to_discard_your_edits_and_load_the": { + "defaultMessage": "pour abandonner vos modifications et charger la version la plus récente.", + "description": "Settings tab: sentence after the Reload button in the footer warning, saying reloading discards edits and loads the latest settings", + "sourceHash": "4a142e5cf151" + }, + "web.routes.tabs.settings.unsaved_changes": { + "defaultMessage": "Modifications non enregistrées", + "description": "Settings tab: footer status text shown when the settings form has edits that are not saved yet", + "sourceHash": "a710c2b90913" + }, + "web.routes.tabs.settings.version.clear_override": { + "defaultMessage": "Effacer la substitution", + "description": "Button that removes the custom image override so the version image is used", + "sourceHash": "46fdbf5fdaa5" + }, + "web.routes.tabs.settings.version.default": { + "defaultMessage": "Par défaut", + "description": "Badge marking the template's default version", + "sourceHash": "21b111cbfe6e" + }, + "web.routes.tabs.settings.version.downgrading_across_game_versions_may_be": { + "defaultMessage": "Revenir à une version antérieure du jeu peut ne pas être pris en charge par les données de monde du jeu : faites d'abord une sauvegarde.", + "description": "Version: warning under the version section saying a downgrade may not work with the game's world data, so a backup should be taken first", + "sourceHash": "685564abc0b3" + }, + "web.routes.tabs.settings.version.each_loader_keeps_its_own_mod_volume_a": { + "defaultMessage": "Chaque loader conserve son propre volume de mods : les mods d'un loader sont conservés et restaurés si vous y revenez plus tard.", + "description": "Version: info note under the version choices saying each loader keeps its own mod volume, so its mods come back if you switch to it again", + "sourceHash": "2dd841c198d1" + }, + "web.routes.tabs.settings.version.game_meta": { + "defaultMessage": "jeu {version}", + "description": "Version: metadata line for a version entry naming its game version; {version} is the game version number", + "sourceHash": "4c37a38b7872" + }, + "web.routes.tabs.settings.version.game_version": { + "defaultMessage": "Version du jeu", + "description": "Version: heading of the game version section, where the version and loader are picked", + "sourceHash": "583311d68a42" + }, + "web.routes.tabs.settings.version.loader_meta": { + "defaultMessage": "loader {loader}", + "description": "Version: metadata line for a version entry naming its mod loader; {loader} is the loader name", + "sourceHash": "f89a96dda22f" + }, + "web.routes.tabs.settings.version.no_catalog": { + "defaultMessage": "Ce modèle n'a pas de catalogue de versions : le serveur utilise toujours l'image du modèle.", + "description": "Version: note shown when the template has no version catalog", + "sourceHash": "d18b4bb06b7e" + }, + "web.routes.tabs.settings.version.no_catalog_image_overridden": { + "defaultMessage": "Ce modèle n'a pas de catalogue de versions : le serveur utilise toujours l'image du modèle (actuellement remplacée ci-dessous, dans Général).", + "description": "Version: note shown when the template has no version catalog and the image is overridden in General", + "sourceHash": "1d4d4b37be98" + }, + "web.routes.tabs.settings.version.pick_a_version_loader_from_the_template": { + "defaultMessage": "Choisissez une version et un loader dans le catalogue du modèle. L'enregistrement redémarre le serveur sur la nouvelle version.", + "description": "Version: hint under the game version heading saying the version and loader come from the template catalog and saving restarts the server", + "sourceHash": "cc740141c69e" + }, + "web.routes.tabs.settings.version.the_selected_version_s_image_is_ignored": { + "defaultMessage": "Substitution d'image active ({image}) : l'image de la version sélectionnée est ignorée jusqu'à ce qu'elle soit effacée.", + "description": "Version: warning under the version list when a custom image override is set; {image} is the override image reference, shown in monospace", + "sourceHash": "88fd25d38c16" + }, + "web.routes.tabs.settings.you_need_permission_to_change_this": { + "defaultMessage": "Vous n'avez pas l'autorisation de modifier les paramètres de ce serveur.", + "description": "Settings tab: footer text shown to users who can view but not change this server's settings", + "sourceHash": "7e3993e00ca4" + }, + "web.routes.theme_settings.a_stylesheet_layered_on_top_of_the": { + "defaultMessage": "Une feuille de style superposée au thème de base actif.", + "description": "Theme settings: subtitle under the custom CSS overlay title: a stylesheet layered on top of the active base theme", + "sourceHash": "33d4288952db" + }, + "web.routes.theme_settings.accent_amber": { + "defaultMessage": "Ambre", + "description": "Theme settings: name of the Amber accent swatch (also its accessible name)", + "sourceHash": "a5cccbcead7d" + }, + "web.routes.theme_settings.accent_blue": { + "defaultMessage": "Bleu", + "description": "Theme settings: name of the Blue accent swatch (also its accessible name)", + "sourceHash": "ec7d56a01607" + }, + "web.routes.theme_settings.accent_color_and_surface_tone_for_the": { + "defaultMessage": "Couleur d'accent et ton de surface du tableau de bord.", + "description": "Theme settings: subtitle under the custom colors title: accent color and surface tone for the dashboard", + "sourceHash": "1f5c77b5087b" + }, + "web.routes.theme_settings.accent_cyan": { + "defaultMessage": "Cyan", + "description": "Theme settings: name of the Cyan accent swatch (also its accessible name)", + "sourceHash": "e95bb222e07f" + }, + "web.routes.theme_settings.accent_emerald": { + "defaultMessage": "Émeraude", + "description": "Theme settings: name of the Emerald accent swatch (also its accessible name)", + "sourceHash": "af1ccb31304d" + }, + "web.routes.theme_settings.accent_orange": { + "defaultMessage": "Orange", + "description": "Theme settings: name of the Orange accent swatch (also its accessible name)", + "sourceHash": "78e7771b8b46" + }, + "web.routes.theme_settings.accent_purple": { + "defaultMessage": "Violet", + "description": "Theme settings: name of the Purple accent swatch (also its accessible name)", + "sourceHash": "7d465fb9b931" + }, + "web.routes.theme_settings.accent_rose": { + "defaultMessage": "Rose", + "description": "Theme settings: name of the Rose accent swatch (also its accessible name)", + "sourceHash": "e10d850954a8" + }, + "web.routes.theme_settings.accepts_gameplane_theme_v1_documents": { + "defaultMessage": "Accepte les documents gameplane-theme v1.", + "description": "Theme settings: hint under the paste area saying it accepts gameplane-theme v1 documents", + "sourceHash": "43545518234c" + }, + "web.routes.theme_settings.accepts_gameplane_theme_v1_json": { + "defaultMessage": "Accepte gameplane-theme v1 (.json)", + "description": "Theme settings: hint beside the choose-file button saying the file must be a gameplane-theme v1 JSON document", + "sourceHash": "5a18b6045b1d" + }, + "web.routes.theme_settings.appearance_mode": { + "defaultMessage": "Mode d'affichage", + "description": "Theme settings: title of the appearance mode card, also its accessible group name; keep it short", + "sourceHash": "3a6b5f459e07" + }, + "web.routes.theme_settings.apply_import": { + "defaultMessage": "Appliquer l'import", + "description": "Theme settings: button that applies the imported theme; keep it short", + "sourceHash": "454d876bf250" + }, + "web.routes.theme_settings.choose_a_preset_theme": { + "defaultMessage": "Choisissez un thème prédéfini :", + "description": "Theme settings: label above the preset choices in the preset theme card", + "sourceHash": "79b8d1aaaac0" + }, + "web.routes.theme_settings.choose_file": { + "defaultMessage": "Choisir un fichier…", + "description": "Theme settings: button that opens the file picker to choose a theme file; keep it short", + "sourceHash": "5c6623857cc7" + }, + "web.routes.theme_settings.choose_the_base_preset_for_your": { + "defaultMessage": "Choisissez le thème de base de votre tableau de bord.", + "description": "Theme settings: subtitle under the preset theme title: choose the base preset for the dashboard", + "sourceHash": "5349ed9eea2f" + }, + "web.routes.theme_settings.clipboard_unavailable": { + "defaultMessage": "Presse-papiers indisponible : sélectionnez le texte et copiez-le manuellement.", + "description": "Theme settings: status note when the clipboard write failed", + "sourceHash": "f77bd7239b5b" + }, + "web.routes.theme_settings.contrast_guard": { + "defaultMessage": "Garde-fou de contraste", + "description": "Theme settings: heading of the warning shown when the accent and surface colors fail the WCAG AA contrast check", + "sourceHash": "beaeaf0a8f5b" + }, + "web.routes.theme_settings.contrast_result": { + "defaultMessage": "Contraste {ratio}:1 — {verdict} WCAG AA", + "description": "Theme settings: accent/surface contrast result; {ratio} is the contrast ratio to one decimal place and {verdict} is Passes or Fails", + "sourceHash": "08deef1982ac" + }, + "web.routes.theme_settings.copied_to_clipboard": { + "defaultMessage": "Copié dans le presse-papiers", + "description": "Theme settings: status note after the export JSON was copied", + "sourceHash": "d37078fe6a1f" + }, + "web.routes.theme_settings.copy_or_download_your_saved_theme": { + "defaultMessage": "Copiez ou téléchargez votre profil de thème enregistré, ou appliquez un document gameplane-theme v1.", + "description": "Theme settings: subtitle under the export and import title: copy or download the saved theme, or apply a theme document", + "sourceHash": "909e032aa640" + }, + "web.routes.theme_settings.copy_to_clipboard": { + "defaultMessage": "Copier dans le presse-papiers", + "description": "Theme settings: button that copies the theme export to the clipboard; keep it short", + "sourceHash": "b749e205302c" + }, + "web.routes.theme_settings.css_bytes_of_limit": { + "defaultMessage": "{used} / {max}", + "description": "Theme settings: custom CSS byte counter; {used} is the bytes in use and {max} the byte limit, both already formatted", + "sourceHash": "24f398410580" + }, + "web.routes.theme_settings.custom_colors": { + "defaultMessage": "Couleurs personnalisées", + "description": "Theme settings: name of the custom colors option and the title of its card; keep it short", + "sourceHash": "2f7518c7ae0e" + }, + "web.routes.theme_settings.custom_css": { + "defaultMessage": "CSS personnalisé", + "description": "Theme settings: label of the custom CSS text area", + "sourceHash": "9d3b9a0e78ea" + }, + "web.routes.theme_settings.custom_css_2": { + "defaultMessage": "CSS personnalisé :", + "description": "Theme settings: label before the custom CSS summary in the import preview; the size or none follows it", + "sourceHash": "c1328c9e5195" + }, + "web.routes.theme_settings.custom_css_modifies_application": { + "defaultMessage": "Le CSS personnalisé modifie directement l'apparence de l'application. Si l'interface devient inutilisable, récupérez-la via le", + "description": "Theme settings: opening sentence of the custom CSS warning, ending with the words recover via the, followed by the safe-mode parameter", + "sourceHash": "7652a79290ff" + }, + "web.routes.theme_settings.custom_css_overlay": { + "defaultMessage": "Superposition CSS personnalisée", + "description": "Theme settings: title of the custom CSS overlay card; keep it short", + "sourceHash": "c71e0c53fb04" + }, + "web.routes.theme_settings.custom_css_recovery": { + "defaultMessage": "Récupération du CSS personnalisé", + "description": "Theme settings: heading of the warning box about recovering the interface if custom CSS makes it unusable", + "sourceHash": "a020307f1b33" + }, + "web.routes.theme_settings.download_gameplane_theme_json": { + "defaultMessage": "Télécharger gameplane-theme.json", + "description": "Theme settings: button that downloads the theme export as a gameplane-theme.json file; keep it short, the file name is usually left untranslated", + "sourceHash": "3862e7651a5b" + }, + "web.routes.theme_settings.editor_error_external_import": { + "defaultMessage": "Le chargement de ressources externes n'est pas autorisé (@import). Collez plutôt le contenu directement.", + "description": "Theme settings: custom CSS error for an @import rule when its line cannot be located", + "sourceHash": "0b00c5d3261b" + }, + "web.routes.theme_settings.editor_error_external_line": { + "defaultMessage": "Le chargement de ressources externes n'est pas autorisé (ligne {line} : `{text}`). Collez plutôt le contenu directement.", + "description": "Theme settings: custom CSS error for an @import or external url() on a known line; {line} is the line number and {text} the offending source line", + "sourceHash": "53798cad9309" + }, + "web.routes.theme_settings.editor_error_external_url": { + "defaultMessage": "Le chargement de ressources externes n'est pas autorisé (url() externe). Collez plutôt le contenu directement.", + "description": "Theme settings: custom CSS error for an external url() when its line cannot be located", + "sourceHash": "ed233d0c2573" + }, + "web.routes.theme_settings.editor_error_html_delimiter": { + "defaultMessage": "Les balises HTML {style} et {script} ne sont pas autorisées dans le CSS personnalisé.", + "description": "Theme settings: custom CSS error for HTML style or script tags; {style} and {script} are the literal tags", + "sourceHash": "b790616a5ed2" + }, + "web.routes.theme_settings.editor_error_max_length": { + "defaultMessage": "Le CSS personnalisé dépasse la limite de {max} octets.", + "description": "Theme settings: custom CSS error when the text is longer than the limit; {max} is the formatted byte limit", + "sourceHash": "6faf8cb4fbb0" + }, + "web.routes.theme_settings.editor_error_unbalanced_braces": { + "defaultMessage": "Erreur de syntaxe : accolades non équilibrées, chaque {open} doit avoir un {close} correspondant.", + "description": "Theme settings: custom CSS syntax error; {open} and {close} are the brace characters { and }", + "sourceHash": "c6f4b17e0b27" + }, + "web.routes.theme_settings.enable_custom_css_overlay": { + "defaultMessage": "Activer la superposition CSS personnalisée", + "description": "Theme settings: label and accessible name of the switch that turns the custom CSS overlay on", + "sourceHash": "42b95e4f0fe3" + }, + "web.routes.theme_settings.export_import": { + "defaultMessage": "Export / Import", + "description": "Theme settings: title of the export and import card; keep it short", + "sourceHash": "566eef188649" + }, + "web.routes.theme_settings.import_applied": { + "defaultMessage": "Import appliqué", + "description": "Theme settings: status text confirming the imported theme was applied; keep it short", + "sourceHash": "fbf4233b7620" + }, + "web.routes.theme_settings.import_css_bytes": { + "defaultMessage": "{size} octets", + "description": "Theme settings: import preview size of the imported custom CSS; {size} is the formatted byte count", + "sourceHash": "6a73dcfb3830" + }, + "web.routes.theme_settings.import_css_none": { + "defaultMessage": "aucun", + "description": "Theme settings: import preview value when the imported document has no custom CSS", + "sourceHash": "140bedbf9c3f" + }, + "web.routes.theme_settings.import_preview": { + "defaultMessage": "Aperçu de l'import", + "description": "Theme settings: heading of the preview box that shows what an import will apply", + "sourceHash": "ed144b77c0d7" + }, + "web.routes.theme_settings.light_dark_or_follow_your_system": { + "defaultMessage": "Clair, sombre, ou suivre la préférence de votre système.", + "description": "Theme settings: subtitle under the appearance mode title: light, dark, or follow the system preference", + "sourceHash": "a51a1a925b01" + }, + "web.routes.theme_settings.loading_theme_preferences": { + "defaultMessage": "Chargement des préférences de thème…", + "description": "Theme settings: loading text shown while the saved theme preferences are fetched", + "sourceHash": "b8c361c6a7fe" + }, + "web.routes.theme_settings.mode_dark": { + "defaultMessage": "Sombre", + "description": "Theme settings: label of the Dark appearance mode", + "sourceHash": "60acc53f13a5" + }, + "web.routes.theme_settings.mode_light": { + "defaultMessage": "Clair", + "description": "Theme settings: label of the Light appearance mode", + "sourceHash": "dbcd5e7bb7a0" + }, + "web.routes.theme_settings.mode_system": { + "defaultMessage": "Système", + "description": "Theme settings: label of the System appearance mode, which follows the OS setting", + "sourceHash": "6725e7bbcd28" + }, + "web.routes.theme_settings.paste_a_gameplane_theme_v1_document": { + "defaultMessage": "Collez un document gameplane-theme v1…", + "description": "Theme settings: grey placeholder inside the paste area showing that a gameplane-theme v1 document goes here; format name, usually left untranslated", + "sourceHash": "0fa232a45307" + }, + "web.routes.theme_settings.paste_theme_json": { + "defaultMessage": "Coller le JSON du thème", + "description": "Theme settings: label of the text area where a theme document is pasted to import it", + "sourceHash": "de5edd65874f" + }, + "web.routes.theme_settings.personal_theme_presets_colors_custom": { + "defaultMessage": "Thèmes prédéfinis personnels, couleurs, CSS personnalisé et portabilité des thèmes.", + "description": "Theme settings: description under the page title: personal presets, colors, custom CSS and theme import and export", + "sourceHash": "b462a11c0359" + }, + "web.routes.theme_settings.preset": { + "defaultMessage": "Thème prédéfini :", + "description": "Theme settings: label before the preset name in the import preview; the preset name follows it", + "sourceHash": "4336d942df19" + }, + "web.routes.theme_settings.preset_legacy_orange": { + "defaultMessage": "Orange historique", + "description": "Theme settings: name of the Legacy Orange preset theme", + "sourceHash": "5bba2f3ce14a" + }, + "web.routes.theme_settings.preset_legacy_orange_desc": { + "defaultMessage": "Gameplane d'origine", + "description": "Theme settings: one-line description of the Legacy Orange preset theme", + "sourceHash": "77810f273573" + }, + "web.routes.theme_settings.preset_modern_pink": { + "defaultMessage": "Rose moderne", + "description": "Theme settings: name of the Modern Pink preset theme", + "sourceHash": "82f7af2996f0" + }, + "web.routes.theme_settings.preset_modern_pink_desc": { + "defaultMessage": "Marque HeroUI moderne", + "description": "Theme settings: one-line description of the Modern Pink preset theme", + "sourceHash": "39838f7023d8" + }, + "web.routes.theme_settings.preset_theme": { + "defaultMessage": "Thème prédéfini", + "description": "Theme settings: title of the preset theme card, also the accessible name of its preset choice group; keep it short", + "sourceHash": "9c6edbad2ebb" + }, + "web.routes.theme_settings.preview_button": { + "defaultMessage": "Bouton d'aperçu", + "description": "Theme settings: sample button drawn in the chosen colors to preview contrast; its text is only a sample label", + "sourceHash": "eb3d71fdaaf2" + }, + "web.routes.theme_settings.primary_accent": { + "defaultMessage": "Accent principal", + "description": "Theme settings: label of the primary accent swatches, also their accessible group name; keep it short", + "sourceHash": "408316dfd882" + }, + "web.routes.theme_settings.primary_accent_color_picker": { + "defaultMessage": "Sélecteur de couleur de l'accent principal", + "description": "Theme settings: accessible name of the colour picker for the primary accent", + "sourceHash": "76a7a6202fee" + }, + "web.routes.theme_settings.primary_accent_hex_value": { + "defaultMessage": "Valeur hexadécimale de l'accent principal", + "description": "Theme settings: accessible name of the text field where the primary accent is typed as a hex value", + "sourceHash": "090e4d917c6b" + }, + "web.routes.theme_settings.reset": { + "defaultMessage": "Réinitialiser", + "description": "Theme settings: confirm button of the reset-to-defaults dialog; keep it short", + "sourceHash": "daee7606b339" + }, + "web.routes.theme_settings.reset_failed": { + "defaultMessage": "Échec de la réinitialisation", + "description": "Theme settings: fallback error shown when resetting the theme fails", + "sourceHash": "089cb45b70a0" + }, + "web.routes.theme_settings.reset_to_defaults": { + "defaultMessage": "Rétablir les valeurs par défaut", + "description": "Theme settings: button that opens the reset-to-defaults confirmation; keep it short", + "sourceHash": "0953e0d5167d" + }, + "web.routes.theme_settings.reset_to_defaults_2": { + "defaultMessage": "Rétablir les valeurs par défaut ?", + "description": "Theme settings: title of the reset-to-defaults confirmation dialog", + "sourceHash": "37ba78770ece" + }, + "web.routes.theme_settings.rrggbb": { + "defaultMessage": "#RRGGBB", + "description": "Theme settings: grey placeholder showing the hex colour format in the hex fields, usually left untranslated", + "sourceHash": "7c13bc44f391" + }, + "web.routes.theme_settings.safe_mode_1": { + "defaultMessage": "?safe-mode=1", + "description": "Theme settings: URL parameter shown in monospace in the custom CSS warning (technical term, usually left untranslated)", + "sourceHash": "053e2097a1a9" + }, + "web.routes.theme_settings.save": { + "defaultMessage": "Enregistrer", + "description": "Theme settings: button label that saves the theme draft", + "sourceHash": "1509f561f241" + }, + "web.routes.theme_settings.saved": { + "defaultMessage": "Enregistré", + "description": "Theme settings: status text beside the save button confirming the theme was saved; keep it short", + "sourceHash": "b5c120b316c2" + }, + "web.routes.theme_settings.saving": { + "defaultMessage": "Enregistrement…", + "description": "Theme settings: Save button label while the save request is in flight", + "sourceHash": "23e39291d613" + }, + "web.routes.theme_settings.set_by_your_surface_color": { + "defaultMessage": "Définie par la couleur de votre surface", + "description": "Theme settings: note shown when custom colors are active, saying the appearance mode is set by the surface color", + "sourceHash": "197b3ca75d18" + }, + "web.routes.theme_settings.sharing_galleries_and_organization": { + "defaultMessage": "Les galeries de partage et les thèmes imposés par l'organisation ne font pas partie de cette fonctionnalité.", + "description": "Theme settings: note at the bottom saying sharing galleries and organisation-enforced themes are not part of this feature", + "sourceHash": "67aa82c8f728" + }, + "web.routes.theme_settings.surface_charcoal": { + "defaultMessage": "Charbon", + "description": "Theme settings: name of the Charcoal surface tone", + "sourceHash": "2632c885aa43" + }, + "web.routes.theme_settings.surface_crisp_light": { + "defaultMessage": "Clair net", + "description": "Theme settings: name of the Crisp Light surface tone", + "sourceHash": "0b0b3fe3407e" + }, + "web.routes.theme_settings.surface_dark_slate": { + "defaultMessage": "Ardoise foncée", + "description": "Theme settings: name of the Dark Slate surface tone", + "sourceHash": "ab00ae674b7f" + }, + "web.routes.theme_settings.surface_midnight": { + "defaultMessage": "Minuit", + "description": "Theme settings: name of the Midnight surface tone", + "sourceHash": "aa996cf21f0d" + }, + "web.routes.theme_settings.surface_tone": { + "defaultMessage": "Ton de surface", + "description": "Theme settings: label of the surface tone choices, also their accessible group name; keep it short", + "sourceHash": "dc88ffb60a84" + }, + "web.routes.theme_settings.surface_tone_color_picker": { + "defaultMessage": "Sélecteur de couleur du ton de surface", + "description": "Theme settings: accessible name of the colour picker for the surface tone", + "sourceHash": "5592387e0cdc" + }, + "web.routes.theme_settings.surface_tone_hex_value": { + "defaultMessage": "Valeur hexadécimale du ton de surface", + "description": "Theme settings: accessible name of the text field where the surface tone is typed as a hex value", + "sourceHash": "3cdbb6354804" + }, + "web.routes.theme_settings.the_selected_accent_and_surface": { + "defaultMessage": "La combinaison d'accent et de surface sélectionnée ne respecte pas WCAG AA ({toFixed}:1). Choisissez un accent plus foncé ou un ton de surface plus foncé.", + "description": "Theme settings: sentence with toFixed in p", + "sourceHash": "1c7034ffd810" + }, + "web.routes.theme_settings.theme_appearance": { + "defaultMessage": "Thème et apparence", + "description": "Theme settings: page title, Theme & Appearance, at the top of the screen; keep it short", + "sourceHash": "33947e018bc1" + }, + "web.routes.theme_settings.theme_export_file": { + "defaultMessage": "Fichier d'export du thème", + "description": "Theme settings: accessible name of the hidden file picker that chooses a theme JSON file", + "sourceHash": "2dc0aff13f0b" + }, + "web.routes.theme_settings.this_deletes_your_custom_colors_and": { + "defaultMessage": "Cela supprime vos couleurs personnalisées et votre CSS personnalisé, et restaure le thème prédéfini sélectionné. Continuer ?", + "description": "Theme settings: description in the reset confirmation: deletes custom colors and custom CSS, restores the selected preset, and asks whether to continue", + "sourceHash": "d21452731bf3" + }, + "web.routes.theme_settings.url_parameter_the_safe_mode_keyboard": { + "defaultMessage": "Paramètre d'URL, raccourci clavier du mode sans échec ou lien ‘Se connecter en mode sans échec’ sur la page de connexion.", + "description": "Theme settings: end of the custom CSS warning naming the recovery options: the URL parameter, the safe-mode keyboard shortcut, or the sign-in link on the login page", + "sourceHash": "d25ab8f808fe" + }, + "web.routes.theme_settings.verdict_fails": { + "defaultMessage": "Échec", + "description": "Theme settings: contrast verdict when the WCAG AA check fails", + "sourceHash": "c5d507708563" + }, + "web.routes.theme_settings.verdict_passes": { + "defaultMessage": "Réussi", + "description": "Theme settings: contrast verdict when the WCAG AA check passes", + "sourceHash": "d9720c0030b1" + }, + "web.routes.theme_settings.your_custom_colors_and_css_are_kept_and": { + "defaultMessage": "Vos couleurs et votre CSS personnalisés sont conservés et peuvent être réappliqués plus tard.", + "description": "Theme settings: note under the preset choices: custom colors and CSS are kept and can be re-applied later", + "sourceHash": "9bddd6374bb1" + }, + "web.routes.theme_settings.your_rules_are_applied_on_top_of_the": { + "defaultMessage": "Vos règles sont appliquées par-dessus le thème de base actif et le remplacent ; les éléments que vous ne ciblez pas suivent le thème de base.", + "description": "Theme settings: note under the overlay switch: your rules are applied over the base theme and win; elements you do not target follow the base theme", + "sourceHash": "35938bed92fc" + }, + "web.routes.theme_settings.your_saved_accent_and_surface": { + "defaultMessage": "Votre accent et votre surface enregistrés", + "description": "Theme settings: caption under the colour preview, naming the accent and surface colors you saved", + "sourceHash": "468e69cb8349" + }, + "web.routes.users.account_is_oidc_managed": { + "defaultMessage": "Compte géré par OIDC", + "description": "Users: tooltip explaining that an OIDC account's password is managed elsewhere", + "sourceHash": "168a24094d7e" + }, + "web.routes.users.actions": { + "defaultMessage": "Actions", + "description": "Users: accessible name of the actions column, which holds the menu for each row; keep it short", + "sourceHash": "ff8059dc6752" + }, + "web.routes.users.actions_for_user": { + "defaultMessage": "Actions pour {name}", + "description": "Users: aria-label of the row actions menu; {name} is the username", + "sourceHash": "aac6defa1846" + }, + "web.routes.users.add": { + "defaultMessage": "Ajouter", + "description": "Users: button that adds the grant to the user; keep it short", + "sourceHash": "9fd728c66c9a" + }, + "web.routes.users.additional_grants_apply_only_to_their": { + "defaultMessage": "Les autorisations supplémentaires ne s'appliquent qu'à leur cluster et leur namespace désignés.", + "description": "Users: hint after the primary role note: additional grants apply only to their named cluster and namespace", + "sourceHash": "df7b4c7607fe" + }, + "web.routes.users.all_namespaces": { + "defaultMessage": "tous les namespaces", + "description": "Users: grant namespace shown when the grant covers every namespace", + "sourceHash": "05088eed627a" + }, + "web.routes.users.all_namespaces_button": { + "defaultMessage": "Tous les namespaces", + "description": "Users: button that fills the namespace field with all namespaces; keep it short", + "sourceHash": "50c3cb6e2284" + }, + "web.routes.users.all_namespaces_requires_workload": { + "defaultMessage": "Tous les namespaces nécessite un rôle ne contenant que des autorisations de charge de travail du cluster. Les autorisations d'administration centrale ne sont pas autorisées.", + "description": "Users: grant error when all namespaces is entered for a role with central administration permissions", + "sourceHash": "78dc59c7ee78" + }, + "web.routes.users.all_permissions": { + "defaultMessage": "toutes les autorisations", + "description": "Users: role card summary when the role grants every permission", + "sourceHash": "2d8ae8b5c65e" + }, + "web.routes.users.audit_log": { + "defaultMessage": "Journal d'audit", + "description": "Users: link button to the audit log screen, shown only to users allowed to audit; keep it short", + "sourceHash": "e4d36f9a4e22" + }, + "web.routes.users.built_in": { + "defaultMessage": "intégré", + "description": "Users: small badge marking a built-in role; keep it short", + "sourceHash": "5c73a5c73dc1" + }, + "web.routes.users.cancel": { + "defaultMessage": "Annuler", + "description": "Users: button that closes a dialog without making changes; keep it short", + "sourceHash": "19766ed6ccb2" + }, + "web.routes.users.cannot_delete": { + "defaultMessage": "Suppression impossible", + "description": "Users: label of the delete button when the signed-in user is the account", + "sourceHash": "798d0eb038bc" + }, + "web.routes.users.cluster_namespace_grants": { + "defaultMessage": "Autorisations de cluster et de namespace", + "description": "Users: small heading of the cluster and namespace grants section for a user", + "sourceHash": "eaaa437090b0" + }, + "web.routes.users.cluster_option": { + "defaultMessage": "{displayName} ({name})", + "description": "Users: cluster picker option; {displayName} is the cluster's display name, {name} its id", + "sourceHash": "86ac9567b7fe" + }, + "web.routes.users.couldn_t_load_installation_capabilities": { + "defaultMessage": "Impossible de charger les capacités de l'installation.", + "description": "Users: error text in the namespace grants area when installation details fail to load", + "sourceHash": "76468b1e3822" + }, + "web.routes.users.create_user": { + "defaultMessage": "Créer l'utilisateur", + "description": "Users: submit button label in the invite-user dialog", + "sourceHash": "f06da128c342" + }, + "web.routes.users.created": { + "defaultMessage": "Créé", + "description": "Users: column heading for when each user account was created; keep it short", + "sourceHash": "d70b9e24bca2" + }, + "web.routes.users.delete": { + "defaultMessage": "Supprimer", + "description": "Users: button that opens the delete dialog for a role; keep it short", + "sourceHash": "e2d0a54968ea" + }, + "web.routes.users.delete_role": { + "defaultMessage": "Supprimer le rôle", + "description": "Users: confirm button of the delete role dialog; keep it short", + "sourceHash": "ac18d11a2f26" + }, + "web.routes.users.delete_role_name": { + "defaultMessage": "Supprimer le rôle {name} ?", + "description": "Users: sentence with name in ModalHeading", + "sourceHash": "e3d40b120ce2" + }, + "web.routes.users.delete_user": { + "defaultMessage": "Supprimer l'utilisateur", + "description": "Users: menu item that opens the delete dialog for a user, and the dialog button that deletes the user; keep it short", + "sourceHash": "58363fd94240" + }, + "web.routes.users.delete_username": { + "defaultMessage": "Supprimer {username} ?", + "description": "Users: sentence with username in ModalHeading", + "sourceHash": "ecff4bcae4d8" + }, + "web.routes.users.edit": { + "defaultMessage": "Modifier", + "description": "Users: button that opens the edit dialog for a role other than admin; keep it short", + "sourceHash": "464c4ffd019e" + }, + "web.routes.users.edit_user": { + "defaultMessage": "Modifier l'utilisateur", + "description": "Users: menu item that opens the edit dialog for a user; keep it short", + "sourceHash": "05b88d18fc0d" + }, + "web.routes.users.failed_to_load_users": { + "defaultMessage": "Échec du chargement des utilisateurs.", + "description": "Users: error title shown when the user list cannot be loaded", + "sourceHash": "29647e1097e8" + }, + "web.routes.users.for_remote_node_inventory_create_a_role": { + "defaultMessage": "Pour l'inventaire des nœuds distants, créez un rôle dans l'onglet Rôles avec “Voir les nœuds, la version et le stockage”, puis accordez-le à tous les namespaces de ce cluster.", + "description": "Users: hint under the grant form on remote node inventory: create a role with View nodes, version, and storage, then grant it to all namespaces on that cluster", + "sourceHash": "9b33930dfd2f" + }, + "web.routes.users.grant_cluster": { + "defaultMessage": "Cluster de l'autorisation", + "description": "Users: accessible name of the cluster dropdown for adding a grant", + "sourceHash": "630bd42b11fe" + }, + "web.routes.users.grant_namespace": { + "defaultMessage": "Namespace de l'autorisation", + "description": "Users: accessible name of the namespace field for a new grant", + "sourceHash": "fa1188bf8765" + }, + "web.routes.users.grant_panel": { + "defaultMessage": "panneau", + "description": "Users: grant cluster shown for a standalone panel grant", + "sourceHash": "320e00e73a7d" + }, + "web.routes.users.grant_role": { + "defaultMessage": "Rôle de l'autorisation", + "description": "Users: accessible name of the role dropdown for a new grant", + "sourceHash": "4549a77f3b6c" + }, + "web.routes.users.identity_providers": { + "defaultMessage": "Fournisseurs d'identité", + "description": "Users: name of the Identity providers tab; keep it short", + "sourceHash": "3965375163f1" + }, + "web.routes.users.in": { + "defaultMessage": "dans", + "description": "Users: small connecting word in a grant row, between the role and the namespace", + "sourceHash": "582967534d0f" + }, + "web.routes.users.invite_user": { + "defaultMessage": "Inviter un utilisateur", + "description": "Users: button that opens the invite-user dialog; keep it short", + "sourceHash": "6ac487a0242c" + }, + "web.routes.users.loading_installation": { + "defaultMessage": "Chargement de l'installation…", + "description": "Users: loading text in the namespace grants area while installation details are fetched", + "sourceHash": "9b3317b01b4b" + }, + "web.routes.users.local_cluster": { + "defaultMessage": "Cluster local", + "description": "Users: option in the grant cluster dropdown for the local cluster; keep it short", + "sourceHash": "e6b4327de6ae" + }, + "web.routes.users.local_primary_role_managed_above": { + "defaultMessage": "Le rôle principal local est géré ci-dessus. Choisissez un namespace pour une autorisation locale supplémentaire.", + "description": "Users: grant error when all namespaces is entered for the local cluster", + "sourceHash": "c86e91f95fbb" + }, + "web.routes.users.manage_access_to_the_gameplane_control": { + "defaultMessage": "Gérer l'accès au plan de contrôle Gameplane.", + "description": "Users: description under the title saying the page manages access to the Gameplane control plane", + "sourceHash": "c9c3fa291929" + }, + "web.routes.users.managed_outside_this_editor": { + "defaultMessage": "Géré en dehors de cet éditeur", + "description": "Users: grant row label for a binding this editor cannot change", + "sourceHash": "5953d49081f7" + }, + "web.routes.users.namespace": { + "defaultMessage": "namespace", + "description": "Users: grey placeholder of the namespace field in the grants form, showing the expected entry", + "sourceHash": "9359f3bb2efa" + }, + "web.routes.users.new_role": { + "defaultMessage": "Nouveau rôle", + "description": "Users: button that opens the dialog to create a new role; keep it short", + "sourceHash": "500f2f9d3956" + }, + "web.routes.users.no_entries": { + "defaultMessage": "Aucune entrée.", + "description": "Users: empty-state text shown when the table has no rows", + "sourceHash": "feed98ce586a" + }, + "web.routes.users.oidc_identity_providers_configured_in": { + "defaultMessage": "Les fournisseurs d'identité OIDC configurés dans les valeurs Helm apparaissent ici. La configuration depuis l'interface est prévue pour la v1.1.", + "description": "Users: note on the identity providers tab: OIDC providers from Helm values appear here and UI setup is planned for v1.1 (OIDC: technical term, usually left untranslated)", + "sourceHash": "c06e3829860f" + }, + "web.routes.users.on": { + "defaultMessage": "sur", + "description": "Users: small connecting word in a grant row, between the namespace and the cluster", + "sourceHash": "b8d31e852725" + }, + "web.routes.users.permission_count": { + "defaultMessage": "{count, plural, one {{count} autorisation} other {{count} autorisations}}", + "description": "Users: role card summary of the number of permissions; {count} is the number of permissions", + "sourceHash": "81127ad9295f" + }, + "web.routes.users.primary_role_managed_above": { + "defaultMessage": "Rôle principal (géré ci-dessus)", + "description": "Users: grant row label for the primary role, which is edited above", + "sourceHash": "080bfa06cec9" + }, + "web.routes.users.primary_role_manages_local_access": { + "defaultMessage": "Le rôle principal ci-dessus gère l'accès local.", + "description": "Users: note under the grants heading on cluster installs", + "sourceHash": "fa23e987b022" + }, + "web.routes.users.primary_role_manages_panel_access": { + "defaultMessage": "Le rôle principal ci-dessus gère l'accès au panneau.", + "description": "Users: note under the grants heading on standalone installs", + "sourceHash": "018f6cc52ea6" + }, + "web.routes.users.provider": { + "defaultMessage": "Fournisseur", + "description": "Users: column heading for the sign-in provider of each user; keep it short", + "sourceHash": "472590ae974d" + }, + "web.routes.users.provider_local": { + "defaultMessage": "Local", + "description": "Users: badge for an account that signs in with a local password", + "sourceHash": "8c31e6e72230" + }, + "web.routes.users.provider_oidc": { + "defaultMessage": "OIDC", + "description": "Users: badge for an account that signs in through OIDC", + "sourceHash": "99eafce66157" + }, + "web.routes.users.provider_pending": { + "defaultMessage": "En attente", + "description": "Users: badge for an invited account that has not signed in yet", + "sourceHash": "331551b0de41" + }, + "web.routes.users.remove_binding": { + "defaultMessage": "Retirer {role} dans {namespace} sur {cluster}", + "description": "Users: aria-label of the remove button on a grant; {role} is the role name, {namespace} the namespace, {cluster} the cluster", + "sourceHash": "bf2adf04b347" + }, + "web.routes.users.request_failed_status": { + "defaultMessage": "Échec de la requête ({status})", + "description": "Users: fallback error text when the API sends no message; {status} is the HTTP status code", + "sourceHash": "d5f9abf9557a" + }, + "web.routes.users.reset_password": { + "defaultMessage": "Réinitialiser le mot de passe", + "description": "Users: menu item that opens password reset for a user; disabled for OIDC-managed accounts; keep it short", + "sourceHash": "e0edfeb3a71a" + }, + "web.routes.users.reset_password_oidc_label": { + "defaultMessage": "Réinitialiser le mot de passe : compte géré par OIDC", + "description": "Users: aria-label of the disabled reset-password item on an OIDC account", + "sourceHash": "13e47dcfd55a" + }, + "web.routes.users.role": { + "defaultMessage": "Rôle", + "description": "Users: column heading for the role of each user; keep it short", + "sourceHash": "14736a2eb9f4" + }, + "web.routes.users.roles": { + "defaultMessage": "Rôles", + "description": "Users: name of the Roles tab, followed by a count in brackets; keep it short", + "sourceHash": "c25337055464" + }, + "web.routes.users.search_placeholder": { + "defaultMessage": "Rechercher…", + "description": "Users: placeholder of the search box on the other tabs", + "sourceHash": "7336265a0467" + }, + "web.routes.users.search_users": { + "defaultMessage": "Rechercher des utilisateurs", + "description": "Users: accessible name of the search field that filters the list in the open tab", + "sourceHash": "e4bb77af4bc5" + }, + "web.routes.users.search_users_placeholder": { + "defaultMessage": "Rechercher des utilisateurs…", + "description": "Users: placeholder of the search box on the users tab", + "sourceHash": "02b756f19e7b" + }, + "web.routes.users.select_a_workload_cluster": { + "defaultMessage": "Sélectionner un cluster de charge de travail", + "description": "Users: grey placeholder of the cluster dropdown in the grants form, asking the user to pick a workload cluster", + "sourceHash": "135026530ce2" + }, + "web.routes.users.service_accounts": { + "defaultMessage": "Comptes de service", + "description": "Users: name of the Service accounts tab; keep it short", + "sourceHash": "1642d9e225e6" + }, + "web.routes.users.service_accounts_machine_to_machine_api": { + "defaultMessage": "Les comptes de service (jetons d'API machine à machine) sont prévus pour la v1.1.", + "description": "Users: note on the service accounts tab: machine-to-machine API tokens are planned for v1.1", + "sourceHash": "537483acfe36" + }, + "web.routes.users.set_new_password": { + "defaultMessage": "Définir un nouveau mot de passe", + "description": "Users: submit button label in the reset-password dialog", + "sourceHash": "6ee6d9db4278" + }, + "web.routes.users.tab_count": { + "defaultMessage": "({count})", + "description": "Users: item count shown after a tab name; {count} is the number of items", + "sourceHash": "ae7cbb64a012" + }, + "web.routes.users.their_sessions_will_be_revoked_and_they": { + "defaultMessage": "Leurs sessions seront révoquées et leur accès supprimé immédiatement. Cette action est irréversible.", + "description": "Users: text in the delete dialog: their sessions are revoked, they lose access immediately, and this cannot be undone", + "sourceHash": "21fe54176b88" + }, + "web.routes.users.this_can_t_be_undone_roles_assigned_to": { + "defaultMessage": "Cette action est irréversible. Les rôles attribués à un utilisateur ne peuvent pas être supprimés.", + "description": "Users: text in the delete role dialog: this cannot be undone, and roles still assigned to a user cannot be deleted", + "sourceHash": "62ca7c7c20c2" + }, + "web.routes.users.user": { + "defaultMessage": "Utilisateur", + "description": "Users: column heading for the user name; keep it short", + "sourceHash": "b512d97e7cbf" + }, + "web.routes.users.users": { + "defaultMessage": "Utilisateurs", + "description": "Users: name of the Users tab, followed by a count in brackets; keep it short", + "sourceHash": "6b0cc904d081" + }, + "web.routes.users.users_list": { + "defaultMessage": "Liste des utilisateurs", + "description": "Users: accessible name of the users table", + "sourceHash": "3a7a8193fcb2" + }, + "web.routes.users.users_rbac": { + "defaultMessage": "Utilisateurs et RBAC", + "description": "Users: page title, Users & RBAC, at the top of the users screen; keep it short", + "sourceHash": "0c6ed78342e6" + }, + "web.routes.users.you": { + "defaultMessage": "vous", + "description": "Users: small badge next to your own user name; keep it short", + "sourceHash": "bb0347a468d9" + }, + "web.routes.users.you_can_t_delete_your_own_account": { + "defaultMessage": "Vous ne pouvez pas supprimer votre propre compte.", + "description": "Users: message in the delete dialog shown when you try to delete your own account", + "sourceHash": "89bdbfc74ea8" + }, + "web.settings.compact_language_menu": { + "defaultMessage": "Options de langue", + "description": "Accessible name of the language list opened from the compact language picker on the sign-in and shared-link pages.", + "sourceHash": "7af104f7d3e9" + }, + "web.settings.compact_language_trigger": { + "defaultMessage": "Langue", + "description": "Accessible name of the compact language menu button in the corner of the sign-in and shared-link cards.", + "sourceHash": "a4fe65264ef7" + }, + "web.settings.language.automatic": { + "defaultMessage": "Automatique (langue du navigateur)", + "description": "Language option that follows the browser language on the Theme & Appearance settings page", + "sourceHash": "7c9b647030ba" + }, + "web.settings.language.group_label": { + "defaultMessage": "Langue", + "description": "Accessible name of the language radio group on the Theme & Appearance settings page", + "sourceHash": "a4fe65264ef7" + }, + "web.settings.language.percent_translated": { + "defaultMessage": "{percent} traduit", + "description": "Share of the dashboard text translated into a language, shown beside each language option", + "sourceHash": "e0c131593bf2" + }, + "web.settings.language.save_failed": { + "defaultMessage": "Impossible d'enregistrer votre langue.", + "description": "Error shown on the Theme & Appearance settings page when the language cannot be saved", + "sourceHash": "7c55078490a8" + }, + "web.settings.language.subtitle": { + "defaultMessage": "Choisissez la langue du tableau de bord. Votre choix est enregistré sur votre compte et vous suit sur chaque navigateur et appareil.", + "description": "Subtitle under the personal Language card title on the Theme & Appearance settings page", + "sourceHash": "67ae9b336c05" + }, + "web.settings.language.title": { + "defaultMessage": "Langue", + "description": "Title of the personal Language card on the Theme & Appearance settings page", + "sourceHash": "a4fe65264ef7" + }, + "web.settings.language.untranslated_note": { + "defaultMessage": "Le texte qui n'a pas encore été traduit s'affiche en anglais.", + "description": "Note under the language options explaining untranslated text falls back to English", + "sourceHash": "fe44d8e6187c" + } +} diff --git a/i18n/locales/fr/meta.json b/i18n/locales/fr/meta.json new file mode 100644 index 000000000..99c6bba65 --- /dev/null +++ b/i18n/locales/fr/meta.json @@ -0,0 +1,6 @@ +{ + "tag": "fr", + "nativeName": "Français", + "englishName": "French", + "direction": "ltr" +} From 0c42555cc425f241834934caf0a46998db2ed336 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 18 Oct 2026 13:17:49 +0000 Subject: [PATCH 02/24] =?UTF-8?q?test(web):=20expect=20the=20French=20pick?= =?UTF-8?q?er=20label=20after=20choosing=20Fran=C3=A7ais?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The build now compiles a real French catalog, so the compact language trigger is named "Langue" once French is chosen. Signed off by valgul. Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: Claude --- web/src/routes/Login_language.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/routes/Login_language.test.tsx b/web/src/routes/Login_language.test.tsx index c2e9d2a72..3c28c156d 100644 --- a/web/src/routes/Login_language.test.tsx +++ b/web/src/routes/Login_language.test.tsx @@ -83,6 +83,6 @@ describe("LoginPage compact language picker (T045)", () => { const stored = JSON.parse(localStorage.getItem("gameplane-language") ?? "{}") as { choice?: string }; expect(stored.choice).toBe("fr"); - expect(await screen.findByRole("button", { name: "Language" })).toHaveTextContent("Français"); + expect(await screen.findByRole("button", { name: "Langue" })).toHaveTextContent("Français"); }); }); From 10fe054daede7fcc456c4faef05b265c32e67042 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 20 Oct 2026 17:58:44 +0000 Subject: [PATCH 03/24] feat(i18n): translate French entries that were still in English Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: Claude --- i18n/locales/fr/app.json | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/i18n/locales/fr/app.json b/i18n/locales/fr/app.json index fca382371..862caab82 100644 --- a/i18n/locales/fr/app.json +++ b/i18n/locales/fr/app.json @@ -50,7 +50,7 @@ "sourceHash": "ca566c8968e7" }, "audit.noun.backup": { - "defaultMessage": "backup", + "defaultMessage": "sauvegarde", "description": "Audit log: object type in an action label, a backup", "sourceHash": "54d00d867758" }, @@ -80,7 +80,7 @@ "sourceHash": "9bd535ac63d7" }, "audit.noun.restore": { - "defaultMessage": "restore", + "defaultMessage": "restauration", "description": "Audit log: object type in an action label, a restore", "sourceHash": "f329e3a317ee" }, @@ -1340,7 +1340,7 @@ "sourceHash": "e4d36f9a4e22" }, "web.components.app_layout.backups": { - "defaultMessage": "Backups", + "defaultMessage": "Sauvegardes", "description": "Sidebar: navigation link to backups", "sourceHash": "3334fee2c77f" }, @@ -8865,7 +8865,7 @@ "sourceHash": "cae7d57bc067" }, "web.routes.server_detail.tab_backups": { - "defaultMessage": "Backups", + "defaultMessage": "Sauvegardes", "description": "Server detail: label of the Backups tab", "sourceHash": "3334fee2c77f" }, @@ -9310,7 +9310,7 @@ "sourceHash": "a2502204eb21" }, "web.routes.tabs.backups.backups": { - "defaultMessage": "Backups", + "defaultMessage": "Sauvegardes", "description": "Backups tab: stat card label for the number of backups; also the section heading above the backup list", "sourceHash": "3334fee2c77f" }, @@ -9370,7 +9370,7 @@ "sourceHash": "c688cf4c4dda" }, "web.routes.tabs.backups.restore": { - "defaultMessage": "Restore", + "defaultMessage": "Restaurer", "description": "Backups tab: row button that opens the restore dialog for one backup; disabled when the backup cannot be restored; keep it short", "sourceHash": "a76e13b98392" }, @@ -9965,7 +9965,7 @@ "sourceHash": "a69c4dece144" }, "web.routes.tabs.mods.category_technology": { - "defaultMessage": "Tech", + "defaultMessage": "Technologie", "description": "Mods: category filter chip for technology mods", "sourceHash": "d0da7ab8bc92" }, @@ -10515,7 +10515,7 @@ "sourceHash": "0db10f2c2f33" }, "web.routes.tabs.players.action_player_ok": { - "defaultMessage": "{action} {player} ok", + "defaultMessage": "{action} {player} : OK", "description": "Players: status line after a moderation action succeeds; {action} is the action verb, {player} the player name", "sourceHash": "f12766f027f9" }, @@ -11830,7 +11830,7 @@ "sourceHash": "c7f3120ba190" }, "web.routes.tabs.settings.networking.tags": { - "defaultMessage": "Tags", + "defaultMessage": "Étiquettes", "description": "Networking: field label for the optional Tailscale tags", "sourceHash": "1331275bc537" }, @@ -12560,7 +12560,7 @@ "sourceHash": "42b95e4f0fe3" }, "web.routes.theme_settings.export_import": { - "defaultMessage": "Export / Import", + "defaultMessage": "Exportation / Importation", "description": "Theme settings: title of the export and import card; keep it short", "sourceHash": "566eef188649" }, From 3a23ccbc7d6cfb043891d66ae6f2f4f4312ec15b Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 20 Oct 2026 22:00:15 +0000 Subject: [PATCH 04/24] test(e2e): add French language e2e tests (spec 019 T035, T037, T062, T092) - T035 TestAPI_I18n_LanguagePref (operator bucket): PUT /users/me/language saves, null clears, unlisted tag returns the unknown_language problem body. - T062 TestAPI_I18n_NotifyLanguage (multicluster): fr and default sinks send a test event to an in-cluster busybox webhook receiver; checks language, title and text, and that the Event fields are unchanged. - T092 TestAPI_I18n_InstanceDefault (multicluster): admin sets the instance default, GET /auth/locale reflects it, unlisted and empty values are rejected, viewer gets 403. Shares a mutex with T062 for the global default. - T037 web/e2e/specs/live/i18n.spec.ts: French switch with typed input kept, reload and second context, login and share pickers, 0%-fixture subtest skipped unless VITE_I18N_FIXTURES is set for the live server. - Tasks T035, T062, T092 marked done. T037 stays open (fixture subtest blocked in CI). Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: Claude --- .../019-localization-language-packs/tasks.md | 6 +- test/e2e/buckets.sh | 9 +- test/e2e/fixtures/notify-sink.yaml | 65 ++++ test/e2e/i18n_instance_default_e2e_test.go | 104 ++++++ test/e2e/i18n_language_pref_e2e_test.go | 116 +++++++ test/e2e/i18n_notify_language_e2e_test.go | 298 ++++++++++++++++++ web/e2e/specs/live/i18n.spec.ts | 231 ++++++++++++++ 7 files changed, 825 insertions(+), 4 deletions(-) create mode 100644 test/e2e/fixtures/notify-sink.yaml create mode 100644 test/e2e/i18n_instance_default_e2e_test.go create mode 100644 test/e2e/i18n_language_pref_e2e_test.go create mode 100644 test/e2e/i18n_notify_language_e2e_test.go create mode 100644 web/e2e/specs/live/i18n.spec.ts diff --git a/specs/019-localization-language-packs/tasks.md b/specs/019-localization-language-packs/tasks.md index 683337cfb..54e936c1d 100644 --- a/specs/019-localization-language-packs/tasks.md +++ b/specs/019-localization-language-packs/tasks.md @@ -87,7 +87,7 @@ description: "Task list for 019 App Localization & Community Language Packs" ### Tests for User Story 1 -- [ ] T035 [P] [US1] Go API E2E `test/e2e/i18n_language_pref_e2e_test.go`: `PUT /api/v1/users/me/language` `fr` → `GET /users/me/preferences` returns `language: "fr"`; `null` clears; unknown tag → 400 with `server.i18n.unknown_language` problem body; register in `test/e2e/buckets.sh` (bucket `api-auth`, respect its login budget) +- [X] T035 [P] [US1] Go API E2E `test/e2e/i18n_language_pref_e2e_test.go`: `PUT /api/v1/users/me/language` `fr` → `GET /users/me/preferences` returns `language: "fr"`; `null` clears; unknown tag → 400 with `server.i18n.unknown_language` problem body; register in `test/e2e/buckets.sh` (bucket `api-auth`, respect its login budget) - [X] T036 [P] [US1] Go API E2E `test/e2e/i18n_auth_locale_e2e_test.go`: unauthenticated `GET /auth/locale` returns exactly `{"defaultLanguage": …}` and no other keys (rule 3); register in `test/e2e/buckets.sh` - [ ] T037 [P] [US1] Playwright `web/e2e/specs/live/i18n.spec.ts`: switch to French in personal settings with create-server dialog open and a typed name (kept, no reload, ``), reload and second browser context keep French with no English flash, login and share pages offer a picker remembered per browser, 0%-fixture language shows English everywhere (SC-008) @@ -159,7 +159,7 @@ description: "Task list for 019 App Localization & Community Language Packs" ### Tests for User Story 5 - [X] T061 [P] [US5] Go API E2E `test/e2e/i18n_problem_json_e2e_test.go`: forbidden-namespace and validation errors return byte-identical text/plain bodies without the Accept header and problem bodies with `detail`, `messageId`, `args` with it; failed login returns only `server.auth.invalid_credentials` for both unknown user and wrong password (FR-030); register in `test/e2e/buckets.sh` -- [ ] T062 [P] [US5] Go API E2E `test/e2e/i18n_notify_language_e2e_test.go`: webhook sink with `language: "fr"` and one without; test send to both via `POST /api/v1/admin/notifications/sinks/{name}/test`; first payload has `language: "fr"` and French `title`/`text`, second uses instance default; all pre-existing payload fields unchanged; register in `test/e2e/buckets.sh` +- [X] T062 [P] [US5] Go API E2E `test/e2e/i18n_notify_language_e2e_test.go`: webhook sink with `language: "fr"` and one without; test send to both via `POST /api/v1/admin/notifications/sinks/{name}/test`; first payload has `language: "fr"` and French `title`/`text`, second uses instance default; all pre-existing payload fields unchanged; register in `test/e2e/buckets.sh` - [X] T063 [P] [US5] envtest `operator/internal/controller/gameserver_envtest_test.go` additions: `status.conditionArgs` set with the condition and cleared when the reason no longer has args (done in the new file `gameserver_condition_args_envtest_test.go`, next to the condition-args code) ### Implementation for User Story 5 @@ -226,7 +226,7 @@ description: "Task list for 019 App Localization & Community Language Packs" **Independent Test**: quickstart.md scenario 4. -- [ ] T092 [P] [US7] Go API E2E `test/e2e/i18n_instance_default_e2e_test.go`: `PUT /api/v1/admin/config/i18n` `{defaultLanguage:"fr"}` → `GET /auth/locale` returns `fr`; unlisted tag → 400 `server.i18n.unlisted_language`; non-admin → 403; register in `test/e2e/buckets.sh` +- [X] T092 [P] [US7] Go API E2E `test/e2e/i18n_instance_default_e2e_test.go`: `PUT /api/v1/admin/config/i18n` `{defaultLanguage:"fr"}` → `GET /auth/locale` returns `fr`; unlisted tag → 400 `server.i18n.unlisted_language`; non-admin → 403; register in `test/e2e/buckets.sh` - [X] T093 [US7] Add config section `i18n` with `validateI18n` (listed languages only) to the validators map in `api/internal/handlers/config.go`; register `server.i18n.unlisted_language`; tests `config_validators_test.go` - [X] T094 [US7] DESIGN (local machine, Pencil MCP): add a Default language field to admin settings in `design.pen`; export touched nodes to `assets/design-export/`. *Done note (redrawn 2026-10-15): on `WZdnw` the General card and a new Language card sit in a Panel Column; the Language card has the Default language select and its own Save changes button, because the value is saved to the separate config section `i18n`.* - [X] T095 [US7] Implement the Default language field in `web/src/routes/AdminSettings.tsx` per T094; tests `AdminSettings_sections.test.tsx` *Done note: Language card under General, saved to config section `i18n`; save errors go through `errorTextIntl`, so a 400 `server.i18n.unlisted_language` shows the catalog text.* diff --git a/test/e2e/buckets.sh b/test/e2e/buckets.sh index d60a26f14..a72843855 100755 --- a/test/e2e/buckets.sh +++ b/test/e2e/buckets.sh @@ -105,6 +105,7 @@ TestAddressPool_ExplicitAddressRequest TestAddressPool_AddressInUseConflict TestAddressPool_PoolExhausted TestAPI_EventStreamAndRoleEdits_FollowCallerPermissions +TestAPI_I18n_LanguagePref TestE2E_ResticRepoReady EOF } @@ -338,7 +339,11 @@ EOF # e2e-admin login (account removal also uses fake-OIDC sign-ins, which every # e2e.sh cluster deploys), and TestAPI_I18n_ProblemJSON costs one admin login, # one viewer login and one failed-login pair, bringing this job to 5 admin -# logins (ceiling ~7). All run against the primary cluster. api-auth, +# logins (ceiling ~7). TestAPI_I18n_NotifyLanguage (T062) and +# TestAPI_I18n_InstanceDefault (T092) add one e2e-admin login each, bringing +# this job to 7 (ceiling ~7); TestAPI_I18n_InstanceDefault also takes one +# viewer login. TestAPI_I18n_LanguagePref (T035) sits in operator for the same +# login-budget reason. All run against the primary cluster. api-auth, # api-roles, api-mods, api-rbac and api-agent are at their ceilings (see their # comments); do not add to them. bucket_multicluster() { cat <<'EOF' @@ -346,6 +351,8 @@ TestAPI_AccountRemoval_RevokesSharesAndAllowsSSOReprovision TestAPI_ModuleUpload_ExtractionStaysWithinBudget TestAPI_I18n_ProblemJSON TestAPI_I18n_ModuleLocales +TestAPI_I18n_NotifyLanguage +TestAPI_I18n_InstanceDefault TestMultiCluster_ClusterDispatchAndScopedRBAC TestMultiCluster_GatewayParity EOF diff --git a/test/e2e/fixtures/notify-sink.yaml b/test/e2e/fixtures/notify-sink.yaml new file mode 100644 index 000000000..110088c24 --- /dev/null +++ b/test/e2e/fixtures/notify-sink.yaml @@ -0,0 +1,65 @@ +# Spec 019 T062: a webhook receiver for the notification-sink e2e test +# (TestAPI_I18n_NotifyLanguage). busybox httpd serves one CGI script that +# appends each request's query string and JSON body to /tmp/hooks.log and +# answers 204, so a successful test-send implies the log line was written. +# The test reads the log with `kubectl exec ... cat /tmp/hooks.log`. +apiVersion: v1 +kind: ConfigMap +metadata: + name: e2e-notify-sink-cgi + namespace: gameplane-system +data: + hook: | + #!/bin/sh + { printf '%s\t' "$QUERY_STRING"; head -c "${CONTENT_LENGTH:-0}"; echo; } >> /tmp/hooks.log + printf 'Status: 204 No Content\r\n\r\n' +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: e2e-notify-sink + namespace: gameplane-system + labels: { app.kubernetes.io/name: e2e-notify-sink } +spec: + replicas: 1 + selector: + matchLabels: { app.kubernetes.io/name: e2e-notify-sink } + template: + metadata: + labels: { app.kubernetes.io/name: e2e-notify-sink } + spec: + securityContext: + runAsNonRoot: true + runAsUser: 65534 + seccompProfile: { type: RuntimeDefault } + containers: + - name: sink + # Same pinned tag the other e2e fixtures use (oras-push-job.yaml). + image: busybox:1.36 + command: ["busybox", "httpd", "-f", "-p", "8080", "-h", "/www"] + securityContext: + allowPrivilegeEscalation: false + capabilities: { drop: [ALL] } + ports: + - { name: http, containerPort: 8080, protocol: TCP } + readinessProbe: + tcpSocket: { port: http } + periodSeconds: 2 + volumeMounts: + - { name: cgi, mountPath: /www/cgi-bin, readOnly: true } + - { name: tmp, mountPath: /tmp } + volumes: + - name: cgi + configMap: { name: e2e-notify-sink-cgi, defaultMode: 0755 } + - name: tmp + emptyDir: {} +--- +apiVersion: v1 +kind: Service +metadata: + name: e2e-notify-sink + namespace: gameplane-system +spec: + selector: { app.kubernetes.io/name: e2e-notify-sink } + ports: + - { name: http, port: 8080, targetPort: http, protocol: TCP } diff --git a/test/e2e/i18n_instance_default_e2e_test.go b/test/e2e/i18n_instance_default_e2e_test.go new file mode 100644 index 000000000..963c11b22 --- /dev/null +++ b/test/e2e/i18n_instance_default_e2e_test.go @@ -0,0 +1,104 @@ +//go:build e2e + +package e2e + +import ( + "encoding/json" + "net/http" + "strings" + "testing" +) + +// TestAPI_I18n_InstanceDefault proves the instance default language (spec 019, +// T092): an admin can set it through PUT /admin/config/i18n, the unauthenticated +// GET /auth/locale reflects it, an unlisted or empty value is rejected without +// writing, and a viewer cannot change it. The default is process-wide, so the +// test holds i18nInstanceDefaultMu for its whole body and restores the prior +// value on cleanup. +// +// Budget: one e2e-admin login plus one viewer login, in the multicluster +// bucket for the login budget (see buckets.sh). +func TestAPI_I18n_InstanceDefault(t *testing.T) { + t.Parallel() + + envInstance.BootstrapAdmin(t, adminUsername, adminPassword) + admin := envInstance.APIClient(t, adminUsername, adminPassword) + // Close is registered first so it runs last: every cleanup below uses admin. + t.Cleanup(admin.Close) + + i18nInstanceDefaultMu.Lock() + t.Cleanup(i18nInstanceDefaultMu.Unlock) + + username, password, id := envInstance.CreateUser(t, admin, "viewer", "e2e-i18n-default") + t.Cleanup(func() { + r, _, _ := admin.Delete("/users/" + id) + if r != nil { + _ = r.Body.Close() + } + }) + viewer := envInstance.APIClient(t, username, password) + t.Cleanup(viewer.Close) + + // Restore the prior i18n section, or en when it was absent. + prior, hadPrior := i18nConfigSection(t, admin, "i18n") + t.Cleanup(func() { + body := prior + if !hadPrior { + body = json.RawMessage(`{"defaultLanguage":"en"}`) + } + r, _, _ := admin.Do(http.MethodPut, "/admin/config/i18n", body) + if r != nil { + _ = r.Body.Close() + } + }) + + port, stop := envInstance.PortForward(t, "gameplane-system", "svc/gameplane-api", 80) + defer stop() + + // The admin sets a listed default; the unauthenticated endpoint reflects it. + resp, body, err := admin.Do(http.MethodPut, "/admin/config/i18n", map[string]string{"defaultLanguage": "fr"}) + if err != nil { + t.Fatalf("PUT /admin/config/i18n fr: %v", err) + } + if resp.StatusCode != http.StatusOK { + t.Fatalf("PUT /admin/config/i18n fr: status=%d body=%s", resp.StatusCode, string(body)) + } + if got := i18nAuthLocale(t, port); got != "fr" { + t.Fatalf("/auth/locale defaultLanguage=%q after PUT fr, want fr", got) + } + + // An unlisted tag is rejected with the registry problem document. + status, ct, raw := rawDo(t, admin, http.MethodPut, "/admin/config/i18n", + map[string]string{"defaultLanguage": "tlh"}, i18nProblemAccept) + if status != http.StatusBadRequest { + t.Fatalf("PUT /admin/config/i18n tlh: status=%d body=%q", status, string(raw)) + } + doc := decodeProblemBody(t, "PUT /admin/config/i18n tlh", ct, raw) + requireProblemFields(t, "PUT /admin/config/i18n tlh", doc, http.StatusBadRequest, + "server.i18n.unlisted_language", "language is not available: tlh") + if args, ok := doc["args"].(map[string]any); !ok || args["language"] != "tlh" { + t.Errorf("PUT /admin/config/i18n tlh: args=%v, want language=tlh", doc["args"]) + } + + // An empty default is rejected as plain text. + status, _, raw = rawDo(t, admin, http.MethodPut, "/admin/config/i18n", + map[string]string{"defaultLanguage": ""}, "") + if status != http.StatusBadRequest || strings.TrimSpace(string(raw)) != "defaultLanguage is required" { + t.Errorf("PUT /admin/config/i18n empty: status=%d body=%q, want 400 \"defaultLanguage is required\"", status, string(raw)) + } + + // Neither rejected write changed the default. + if got := i18nAuthLocale(t, port); got != "fr" { + t.Fatalf("/auth/locale defaultLanguage=%q after rejected writes, want fr", got) + } + + // A viewer cannot change the instance default. + status, _, raw = rawDo(t, viewer, http.MethodPut, "/admin/config/i18n", + map[string]string{"defaultLanguage": "en"}, "") + if status != http.StatusForbidden { + t.Fatalf("viewer PUT /admin/config/i18n: status=%d body=%q, want 403", status, string(raw)) + } + if got := i18nAuthLocale(t, port); got != "fr" { + t.Fatalf("/auth/locale defaultLanguage=%q after viewer 403, want fr", got) + } +} diff --git a/test/e2e/i18n_language_pref_e2e_test.go b/test/e2e/i18n_language_pref_e2e_test.go new file mode 100644 index 000000000..a592bf088 --- /dev/null +++ b/test/e2e/i18n_language_pref_e2e_test.go @@ -0,0 +1,116 @@ +//go:build e2e + +package e2e + +import ( + "encoding/json" + "net/http" + "strings" + "testing" +) + +// i18nLanguagePrefs is the language slice of GET /users/me/preferences. +type i18nLanguagePrefs struct { + Language *string `json:"language"` +} + +// i18nGetLanguage reads the caller's saved language from +// GET /users/me/preferences. A nil result means "no saved choice". +func i18nGetLanguage(t *testing.T, cli *APIClient) *string { + t.Helper() + resp, body, err := cli.Get("/users/me/preferences") + if err != nil { + t.Fatalf("GET /users/me/preferences: %v", err) + } + if resp.StatusCode != http.StatusOK { + t.Fatalf("GET /users/me/preferences: status=%d body=%s", resp.StatusCode, string(body)) + } + var prefs i18nLanguagePrefs + if err := json.Unmarshal(body, &prefs); err != nil { + t.Fatalf("decode preferences: %v\n%s", err, string(body)) + } + return prefs.Language +} + +// TestAPI_I18n_LanguagePref proves PUT /users/me/language (spec 019, T035): +// a listed tag is saved and reads back through the preferences payload, null +// clears it, and a rejected tag returns the registry problem document without +// writing anything. It acts only on the admin's own account and resets the +// language to null on cleanup, so later live specs run in English again. +// +// Budget: one e2e-admin login. It lives in the operator bucket for the login +// budget, not by subject, the same precedent as the EventStream test there. +func TestAPI_I18n_LanguagePref(t *testing.T) { + t.Parallel() + + envInstance.BootstrapAdmin(t, adminUsername, adminPassword) + admin := envInstance.APIClient(t, adminUsername, adminPassword) + // Registered before the reset so the reset runs while the client is open + // (cleanups run last-in first-out). + t.Cleanup(admin.Close) + t.Cleanup(func() { + r, _, _ := admin.Do(http.MethodPut, "/users/me/language", map[string]any{"language": nil}) + if r != nil { + _ = r.Body.Close() + } + }) + + // A listed tag is saved and echoed back by the preferences payload. + resp, body, err := admin.Do(http.MethodPut, "/users/me/language", map[string]any{"language": "fr"}) + if err != nil { + t.Fatalf("PUT /users/me/language fr: %v", err) + } + if resp.StatusCode != http.StatusNoContent { + t.Fatalf("PUT /users/me/language fr: status=%d body=%s", resp.StatusCode, string(body)) + } + if got := i18nGetLanguage(t, admin); got == nil || *got != "fr" { + t.Fatalf("language after PUT fr = %v, want fr", got) + } + + // Null clears the saved choice. + resp, body, err = admin.Do(http.MethodPut, "/users/me/language", map[string]any{"language": nil}) + if err != nil { + t.Fatalf("PUT /users/me/language null: %v", err) + } + if resp.StatusCode != http.StatusNoContent { + t.Fatalf("PUT /users/me/language null: status=%d body=%s", resp.StatusCode, string(body)) + } + if got := i18nGetLanguage(t, admin); got != nil { + t.Fatalf("language after PUT null = %q, want nil", *got) + } + + // Plain-text callers keep the byte-identical body for a bad body shape. + status, _, raw := rawDo(t, admin, http.MethodPut, "/users/me/language", map[string]any{}, "") + if status != http.StatusBadRequest || strings.TrimSpace(string(raw)) != "language is required" { + t.Errorf("PUT /users/me/language without key: status=%d body=%q, want 400 \"language is required\"", status, string(raw)) + } + status, _, raw = rawDo(t, admin, http.MethodPut, "/users/me/language", map[string]any{"language": 5}, "") + if status != http.StatusBadRequest || strings.TrimSpace(string(raw)) != "language must be a string or null" { + t.Errorf("PUT /users/me/language non-string: status=%d body=%q, want 400 \"language must be a string or null\"", status, string(raw)) + } + + // An unlisted tag is rejected with the localizable problem document. + const unknown = "xx" + status, ct, raw := rawDo(t, admin, http.MethodPut, "/users/me/language", map[string]any{"language": unknown}, i18nProblemAccept) + if status != http.StatusBadRequest { + t.Fatalf("PUT /users/me/language xx (problem): status=%d body=%q", status, string(raw)) + } + doc := decodeProblemBody(t, "PUT /users/me/language xx", ct, raw) + requireProblemFields(t, "PUT /users/me/language xx", doc, http.StatusBadRequest, + "server.i18n.unknown_language", "unknown language: xx") + args, ok := doc["args"].(map[string]any) + if !ok || args["language"] != unknown { + t.Errorf("PUT /users/me/language xx: args=%v, want language=%q", doc["args"], unknown) + } + + // Plain-text callers get the same message as plain text. + status, _, raw = rawDo(t, admin, http.MethodPut, "/users/me/language", map[string]any{"language": unknown}, "") + if status != http.StatusBadRequest || strings.TrimSpace(string(raw)) != "unknown language: xx" { + t.Errorf("PUT /users/me/language xx (plain): status=%d body=%q, want 400 \"unknown language: xx\"", status, string(raw)) + } + + // The rejected writes must not have saved anything. + if got := i18nGetLanguage(t, admin); got != nil { + t.Fatalf("language after rejected PUT = %q, want nil", *got) + } +} diff --git a/test/e2e/i18n_notify_language_e2e_test.go b/test/e2e/i18n_notify_language_e2e_test.go new file mode 100644 index 000000000..d6c767a63 --- /dev/null +++ b/test/e2e/i18n_notify_language_e2e_test.go @@ -0,0 +1,298 @@ +//go:build e2e + +package e2e + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "path/filepath" + "strings" + "sync" + "testing" + "time" +) + +// i18nInstanceDefaultMu serializes the two tests that depend on the instance +// default language: TestAPI_I18n_NotifyLanguage (T062), which needs the +// default to be "en" while its sinks send, and TestAPI_I18n_InstanceDefault +// (T092), which changes it. Both hold the lock for their whole body and +// restore the default before releasing it. +var i18nInstanceDefaultMu sync.Mutex + +// i18nAuthLocale reads the unauthenticated GET /auth/locale through a +// port-forward to the API and returns its defaultLanguage. The endpoint +// returns exactly one key, so any other shape fails the test. +func i18nAuthLocale(t *testing.T, port int) string { + t.Helper() + req, err := http.NewRequestWithContext(t.Context(), http.MethodGet, + fmt.Sprintf("http://127.0.0.1:%d/auth/locale", port), nil) + if err != nil { + t.Fatalf("build /auth/locale request: %v", err) + } + resp, err := http.DefaultClient.Do(req) + if err != nil { + t.Fatalf("GET /auth/locale: %v", err) + } + defer func() { _ = resp.Body.Close() }() + body, err := io.ReadAll(resp.Body) + if err != nil { + t.Fatalf("read /auth/locale: %v", err) + } + if resp.StatusCode != http.StatusOK { + t.Fatalf("GET /auth/locale: status %d, body=%s", resp.StatusCode, body) + } + var got map[string]any + if err := json.Unmarshal(body, &got); err != nil { + t.Fatalf("decode /auth/locale: %v; body=%s", err, body) + } + if len(got) != 1 { + t.Fatalf("/auth/locale: want exactly one key, got %d; body=%s", len(got), body) + } + lang, ok := got["defaultLanguage"].(string) + if !ok { + t.Fatalf("/auth/locale: defaultLanguage missing or not a string; body=%s", body) + } + return lang +} + +// i18nConfigSection returns one section of GET /admin/config/ and whether it +// is present. +func i18nConfigSection(t *testing.T, admin *APIClient, section string) (json.RawMessage, bool) { + t.Helper() + resp, body, err := admin.Get("/admin/config/") + if err != nil { + t.Fatalf("GET /admin/config/: %v", err) + } + if resp.StatusCode != http.StatusOK { + t.Fatalf("GET /admin/config/: status=%d body=%s", resp.StatusCode, string(body)) + } + var all map[string]json.RawMessage + if err := json.Unmarshal(body, &all); err != nil { + t.Fatalf("decode /admin/config/: %v\n%s", err, string(body)) + } + v, ok := all[section] + return v, ok +} + +// i18nNotifyExpect is the payload contract for one test-send (spec 019, T062). +type i18nNotifyExpect struct { + sink string + query string + language string + eventWord string + detail string +} + +// i18nCheckNotifyPayload asserts one receiver log body against the test-send +// contract: the Event fields are unchanged (type, ts, test, optional instance), +// and the additive language, title and text carry the rendered strings. +func i18nCheckNotifyPayload(t *testing.T, want i18nNotifyExpect, raw string) { + t.Helper() + var payload map[string]any + if err := json.Unmarshal([]byte(raw), &payload); err != nil { + t.Fatalf("%s: decode payload: %v; body=%s", want.sink, err, raw) + } + + // Only the Event fields and the three additive fields may appear. + allowed := map[string]bool{"type": true, "ts": true, "test": true, "instance": true, + "language": true, "title": true, "text": true} + for key := range payload { + if !allowed[key] { + t.Errorf("%s: unexpected payload key %q; body=%s", want.sink, key, raw) + } + } + for _, key := range []string{"type", "ts", "test", "language", "title", "text"} { + if _, ok := payload[key]; !ok { + t.Errorf("%s: payload missing %q; body=%s", want.sink, key, raw) + } + } + if payload["type"] != "test" { + t.Errorf("%s: type=%v, want test", want.sink, payload["type"]) + } + if payload["test"] != true { + t.Errorf("%s: test=%v, want true", want.sink, payload["test"]) + } + ts, _ := payload["ts"].(string) + if _, err := time.Parse(time.RFC3339, ts); err != nil { + t.Errorf("%s: ts=%q is not RFC3339: %v", want.sink, ts, err) + } + if payload["language"] != want.language { + t.Errorf("%s: language=%v, want %q", want.sink, payload["language"], want.language) + } + + // The [instance] part is not asserted: it depends on general.instanceName. + title, _ := payload["title"].(string) + text, _ := payload["text"].(string) + if !strings.HasPrefix(title, "[") || !strings.HasSuffix(title, " "+want.eventWord) { + t.Errorf("%s: title=%q, want \"[instance] %s\"", want.sink, title, want.eventWord) + } + if wantText := title + " — " + want.detail; text != wantText { + t.Errorf("%s: text=%q, want %q", want.sink, text, wantText) + } +} + +// TestAPI_I18n_NotifyLanguage proves that a notification sink's language +// selects the rendered title and text of a test-send (spec 019, T062), and +// that the pre-existing payload fields are unchanged. A sink with language +// "fr" renders French; a sink with no language follows the instance default, +// which is "en". +// +// The test-send delivers to an in-cluster webhook receiver (fixtures/ +// notify-sink.yaml) that logs each request's query and body. Budget: one +// e2e-admin login, in the multicluster bucket for the login budget. +func TestAPI_I18n_NotifyLanguage(t *testing.T) { + t.Parallel() + + envInstance.BootstrapAdmin(t, adminUsername, adminPassword) + admin := envInstance.APIClient(t, adminUsername, adminPassword) + // Close is registered first so it runs last: every cleanup below uses admin. + t.Cleanup(admin.Close) + + i18nInstanceDefaultMu.Lock() + t.Cleanup(i18nInstanceDefaultMu.Unlock) + + // Receiver: a hard gate. The rollout waits for the TCP readiness probe, so + // the test-send cannot race the receiver's startup. + fixture, err := filepath.Abs(filepath.Join("fixtures", "notify-sink.yaml")) + if err != nil { + t.Fatalf("resolve notify-sink fixture: %v", err) + } + envInstance.ApplyYAML(t, "notify-sink.yaml") + t.Cleanup(func() { + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + _, _ = envInstance.Kubectl(ctx, "delete", "-f", fixture, "--ignore-not-found") + }) + if out, err := envInstance.Kubectl(t.Context(), "rollout", "status", "-n", "gameplane-system", + "deploy/e2e-notify-sink", "--timeout=2m"); err != nil { + t.Fatalf("notify sink receiver not ready: %v\n%s", err, out) + } + + // Restore the notifications section (or an empty one if it was absent). + prior, hadPrior := i18nConfigSection(t, admin, "notifications") + t.Cleanup(func() { + body := prior + if !hadPrior { + body = json.RawMessage(`{"sinks":[]}`) + } + r, _, _ := admin.Do(http.MethodPut, "/admin/config/notifications", body) + if r != nil { + _ = r.Body.Close() + } + }) + + sinks := []struct{ name, query string }{ + {"e2e-notify-fr", "sink=fr"}, + {"e2e-notify-default", "sink=default"}, + } + for _, s := range sinks { + name := s.name + t.Cleanup(func() { + r, _, _ := admin.Delete("/admin/notifications/sinks/" + name + "/secret") + if r != nil { + _ = r.Body.Close() + } + }) + } + + // Secrets hold each sink's URL; configRef must name the returned Secret. + for _, s := range sinks { + url := fmt.Sprintf("http://e2e-notify-sink.gameplane-system.svc.cluster.local:8080/cgi-bin/hook?%s", s.query) + resp, body, err := admin.Do(http.MethodPut, "/admin/notifications/sinks/"+s.name+"/secret", + map[string]string{"kind": "webhook", "url": url, "authorization": ""}) + if err != nil { + t.Fatalf("PUT secret %s: %v", s.name, err) + } + if resp.StatusCode != http.StatusOK { + t.Fatalf("PUT secret %s: status=%d body=%s", s.name, resp.StatusCode, string(body)) + } + var got struct { + Name string `json:"name"` + } + if err := json.Unmarshal(body, &got); err != nil { + t.Fatalf("decode secret response %s: %v\n%s", s.name, err, string(body)) + } + if want := "gameplane-notify-" + s.name; got.Name != want { + t.Fatalf("secret name=%q, want %q", got.Name, want) + } + } + + // The fr sink names French explicitly; the default sink omits language. + // Both stay disabled so the notifier never delivers real events here. + resp, body, err := admin.Do(http.MethodPut, "/admin/config/notifications", map[string]any{ + "sinks": []map[string]any{ + {"name": "e2e-notify-fr", "kind": "webhook", "enabled": false, + "configRef": "gameplane-notify-e2e-notify-fr", "language": "fr"}, + {"name": "e2e-notify-default", "kind": "webhook", "enabled": false, + "configRef": "gameplane-notify-e2e-notify-default"}, + }, + }) + if err != nil { + t.Fatalf("PUT notifications config: %v", err) + } + if resp.StatusCode != http.StatusOK { + t.Fatalf("PUT notifications config: status=%d body=%s", resp.StatusCode, string(body)) + } + + // Race R1 guard: the default-sink expectation below is the instance + // default, so read it just before sending and require "en". + port, stop := envInstance.PortForward(t, "gameplane-system", "svc/gameplane-api", 80) + defer stop() + defaultTag := i18nAuthLocale(t, port) + if defaultTag != "en" { + t.Fatalf("instance default language = %q, want en before the test-send", defaultTag) + } + + for _, s := range sinks { + resp, body, err := admin.Post("/admin/notifications/sinks/"+s.name+"/test", nil) + if err != nil { + t.Fatalf("POST test %s: %v", s.name, err) + } + if resp.StatusCode != http.StatusOK { + t.Fatalf("POST test %s: status=%d body=%s", s.name, resp.StatusCode, string(body)) + } + var delivered struct { + Delivered bool `json:"delivered"` + } + if err := json.Unmarshal(body, &delivered); err != nil || !delivered.Delivered { + t.Fatalf("POST test %s: delivered not true; body=%s", s.name, string(body)) + } + } + + // The test-send is synchronous, so each receiver line already exists. + // Eventually only absorbs the log write racing the response on a loaded runner. + var logText string + envInstance.Eventually(t, 30*time.Second, func() (bool, string) { + out, err := envInstance.KubectlExec(t, "gameplane-system", "deploy/e2e-notify-sink", "cat", "/tmp/hooks.log") + if err != nil { + return false, fmt.Sprintf("cat /tmp/hooks.log: %v: %s", err, out) + } + logText = out + return strings.Contains(out, "sink=fr\t") && strings.Contains(out, "sink=default\t"), + fmt.Sprintf("log lacks both sinks: %q", out) + }) + + // The last matching line wins in case an earlier run left lines behind. + bodyFor := map[string]string{} + for _, line := range strings.Split(logText, "\n") { + query, body, ok := strings.Cut(line, "\t") + if ok { + bodyFor[query] = body + } + } + + i18nCheckNotifyPayload(t, i18nNotifyExpect{ + sink: "e2e-notify-fr", query: "sink=fr", language: "fr", + eventWord: "notification de test", + detail: "Ceci est une notification de test de Gameplane. Votre destination est correctement configurée.", + }, bodyFor["sink=fr"]) + + i18nCheckNotifyPayload(t, i18nNotifyExpect{ + sink: "e2e-notify-default", query: "sink=default", language: defaultTag, + eventWord: "test notification", + detail: "This is a test notification from Gameplane. Your sink is wired up correctly.", + }, bodyFor["sink=default"]) +} diff --git a/web/e2e/specs/live/i18n.spec.ts b/web/e2e/specs/live/i18n.spec.ts new file mode 100644 index 000000000..2bdd808e1 --- /dev/null +++ b/web/e2e/specs/live/i18n.spec.ts @@ -0,0 +1,231 @@ +import { test, expect } from "@playwright/test"; +import type { APIRequestContext, Page } from "@playwright/test"; +import { apiSend, loginIfNeeded, seedTemplate } from "./_seed"; + +// Live: spec 019 T037, language switching end to end on the real backend. +// Covers: +// A. Switching language keeps the create-server wizard's typed input and +// does not reload the page. +// B. A reload and a new browser context keep French, with no English flash. +// C. The logged-out login and share pages offer a picker that is remembered +// per browser, and a fresh logged-out context starts in English. +// D. A 0%-complete fixture language shows English text everywhere (SC-008). +// It runs only when the live web server was started with +// VITE_I18N_FIXTURES=1; otherwise it is reported as skipped. +// +// The specs share one admin account, and the language choice is saved to that +// account. The suite runs serially and resets the account language to null +// when it finishes, so later live specs see English again. + +const ADMIN_STATE = "./e2e/.auth/storage.json"; +// Empty storage state for logged-out contexts (no cookies, no origins). +const EMPTY_STATE: { cookies: []; origins: [] } = { cookies: [], origins: [] }; + +// Window extensions used as markers across a reload or a language switch. +type I18nWindow = Window & { __i18nMark?: number; __englishSeen?: boolean }; + +// resetAccountLanguage clears the admin's saved language, so the shared account +// falls back to the browser language and then the instance default. +async function resetAccountLanguage(request: APIRequestContext): Promise { + const res = await apiSend(request, "PUT", "/users/me/language", { language: null }); + if (!res.ok()) { + throw new Error(`reset account language: ${res.status()} ${await res.text().catch(() => "")}`); + } +} + +// pickFrench opens the compact picker on a logged-out page and chooses +// Français. The trigger is named "Language" in English and "Langue" in French. +async function pickFrenchFromCompactPicker(page: Page): Promise { + await page.getByRole("button", { name: /^(Langue|Language)$/ }).click(); + await page.getByRole("menu", { name: "Options de langue" }).getByText("Français").click(); +} + +test.describe("live: i18n", () => { + test.skip( + process.env.GAMEPLANE_E2E_TARGET !== "live", + "live-only — language switching is covered by the mock specs", + ); + + // Every test changes the admin's language, so the order matters. + test.describe.configure({ mode: "serial" }); + + test.afterAll(async ({ playwright }) => { + const ctx = await playwright.request.newContext({ + baseURL: "http://localhost:5173", + storageState: ADMIN_STATE, + }); + try { + await resetAccountLanguage(ctx); + } finally { + await ctx.dispose(); + } + }); + + test("switching language keeps the wizard input and does not reload", async ({ page, request }) => { + const stamp = Date.now(); + const templateName = `e2e-pw-i18n-tpl-${stamp}`; + const serverName = `e2e-pw-i18n-${stamp}`; + const template = await seedTemplate(request, templateName); + try { + // The template is preselected by the query string. The Configure step, + // which holds the server name, is the next step. + await page.goto(`/servers/new?template=${templateName}`); + await page.waitForLoadState("domcontentloaded"); + await loginIfNeeded(page); + + await page.getByRole("button", { name: /^Continue to/ }).click(); + const nameInput = page.getByPlaceholder("mc-hardcore"); + await expect(nameInput).toBeVisible({ timeout: 15_000 }); + await nameInput.fill(serverName); + await page.evaluate(() => { + (window as I18nWindow).__i18nMark = 1; + }); + + // Switch the account language from a second page of the same browser. + const second = await page.context().newPage(); + try { + await second.goto("/settings/theme"); + await second.waitForLoadState("domcontentloaded"); + await second.getByRole("radio", { name: /Français/ }).click(); + + const secondHtml = second.locator("html"); + await expect(secondHtml).toHaveAttribute("lang", "fr", { timeout: 15_000 }); + await expect(secondHtml).toHaveAttribute("dir", "ltr"); + await expect(secondHtml).toHaveAttribute("data-i18n-loaded", "fr"); + } finally { + await second.close(); + } + + // Back on the wizard page: the typed input and the marker survive, and the + // French label replaces the English one without a reload. + await page.bringToFront(); + await expect(page.locator("html")).toHaveAttribute("lang", "fr", { timeout: 15_000 }); + await expect(page.getByText("Nom du serveur")).toBeVisible(); + await expect(nameInput).toHaveValue(serverName); + const mark = await page.evaluate(() => (window as I18nWindow).__i18nMark); + expect(mark).toBe(1); + } finally { + await template.cleanup(request); + } + }); + + test("reload and a new browser context keep French without an English flash", async ({ page, browser }) => { + // Watch the DOM from before React renders. A later "Create server" text is + // the English label leaking into the French page. + await page.addInitScript(() => { + const w = window as I18nWindow; + w.__englishSeen = false; + const watch = () => { + const observer = new MutationObserver(() => { + if (/Create server/.test(document.body?.textContent ?? "")) { + w.__englishSeen = true; + } + }); + observer.observe(document.documentElement, { childList: true, subtree: true, characterData: true }); + }; + if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", watch); + } else { + watch(); + } + }); + + await page.goto("/servers"); + await page.waitForLoadState("domcontentloaded"); + await loginIfNeeded(page); + + await page.reload({ waitUntil: "domcontentloaded" }); + await expect(page.locator("html")).toHaveAttribute("lang", "fr"); + await expect(page.getByText("Créer un serveur").first()).toBeVisible({ timeout: 15_000 }); + expect(await page.evaluate(() => (window as I18nWindow).__englishSeen)).toBe(false); + + // A new browser context has no local storage, so the account choice decides. + const ctx2 = await browser.newContext({ storageState: ADMIN_STATE }); + try { + const p2 = await ctx2.newPage(); + await p2.goto("/servers"); + await p2.waitForLoadState("domcontentloaded"); + await expect(p2.locator("html")).toHaveAttribute("lang", "fr", { timeout: 15_000 }); + await expect(p2.getByText("Créer un serveur").first()).toBeVisible({ timeout: 15_000 }); + } finally { + await ctx2.close(); + } + }); + + test.describe("logged out", () => { + // Logged-out contexts, as login-and-shell.spec.ts does, so the shared admin + // session is not touched. + test.use({ storageState: EMPTY_STATE }); + + test("login and share pages offer a picker, remembered per browser", async ({ page, browser }) => { + await page.goto("/login"); + await page.waitForLoadState("domcontentloaded"); + await pickFrenchFromCompactPicker(page); + + await expect(page.locator("html")).toHaveAttribute("lang", "fr"); + await expect(page.getByRole("textbox", { name: /E-mail ou nom d'utilisateur/ })).toBeVisible(); + await expect(page.getByRole("button", { name: /^Connexion$/ })).toBeVisible(); + + try { + // The browser choice persists across a reload. + await page.reload({ waitUntil: "domcontentloaded" }); + await expect(page.locator("html")).toHaveAttribute("lang", "fr"); + const stored = await page.evaluate(() => window.localStorage.getItem("gameplane-language")); + expect(JSON.parse(stored ?? "null")).toMatchObject({ choice: "fr", applied: "fr" }); + + // A fresh logged-out context has no stored choice, so it starts in English. + const fresh = await browser.newContext({ storageState: EMPTY_STATE }); + try { + const freshPage = await fresh.newPage(); + await freshPage.goto("/login"); + await expect(freshPage.getByRole("button", { name: /^Sign in$/ })).toBeVisible(); + await expect(freshPage.locator("html")).toHaveAttribute("lang", "en"); + } finally { + await fresh.close(); + } + + // The share page shows the same picker in its invalid-token state. + await page.goto("/share/e2e-pw-i18n-missing"); + await page.waitForLoadState("domcontentloaded"); + await pickFrenchFromCompactPicker(page); + await expect(page.locator("html")).toHaveAttribute("lang", "fr"); + } finally { + await page.evaluate(() => { + try { + window.localStorage.removeItem("gameplane-language"); + } catch { + // Storage can be blocked; the context is discarded at the end anyway. + } + }); + } + }); + }); + + test("a 0% fixture language shows English everywhere (SC-008)", async ({ page, request }) => { + try { + await page.goto("/settings/theme"); + await page.waitForLoadState("domcontentloaded"); + await expect(page.getByRole("radio", { name: /Français/ })).toBeVisible({ timeout: 15_000 }); + + // The fixture language exists only when the web server was started with + // VITE_I18N_FIXTURES=1 (vite.config.ts). The CI live job does not set it, + // so this check reports the subtest as skipped rather than passed. + const fixture = page.getByRole("radio", { name: /Fixture/ }); + if ((await fixture.count()) === 0) { + test.skip(true, "VITE_I18N_FIXTURES is not set for the live web server, so the zz fixture language is absent"); + } + + await fixture.click(); + await expect(page.locator("html")).toHaveAttribute("lang", "zz", { timeout: 15_000 }); + + // Every string falls back to English, and no raw message id is shown. + await page.goto("/servers"); + await page.waitForLoadState("domcontentloaded"); + await expect(page.getByText("Create server").first()).toBeVisible({ timeout: 15_000 }); + const visible = await page.locator("body").innerText(); + expect(visible.split("\n").some((line) => /^web\.[a-z_.]+$/.test(line.trim()))).toBe(false); + } finally { + await resetAccountLanguage(request); + } + }); +}); From 317638be0222c4c65a23708424ddbd787be49470 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 20 Oct 2026 22:02:21 +0000 Subject: [PATCH 05/24] test(e2e): fix French e2e tests after review - i18n.spec.ts: the compact picker menu is named in the page language, so match both 'Language options' and 'Options de langue' on the first visit. - i18n.spec.ts: the first page does not learn of a language change made in a second page (no refetch on focus, no cross-tab sync), so replay a reconnect until the account query refetches before asserting French. Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: Claude --- web/e2e/specs/live/i18n.spec.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/web/e2e/specs/live/i18n.spec.ts b/web/e2e/specs/live/i18n.spec.ts index 2bdd808e1..c46fca054 100644 --- a/web/e2e/specs/live/i18n.spec.ts +++ b/web/e2e/specs/live/i18n.spec.ts @@ -37,7 +37,8 @@ async function resetAccountLanguage(request: APIRequestContext): Promise { // Français. The trigger is named "Language" in English and "Langue" in French. async function pickFrenchFromCompactPicker(page: Page): Promise { await page.getByRole("button", { name: /^(Langue|Language)$/ }).click(); - await page.getByRole("menu", { name: "Options de langue" }).getByText("Français").click(); + // The menu is named in the page's current language, English on a first visit. + await page.getByRole("menu", { name: /^(Language options|Options de langue)$/ }).getByText("Français").click(); } test.describe("live: i18n", () => { @@ -99,8 +100,17 @@ test.describe("live: i18n", () => { // Back on the wizard page: the typed input and the marker survive, and the // French label replaces the English one without a reload. await page.bringToFront(); - await expect(page.locator("html")).toHaveAttribute("lang", "fr", { timeout: 15_000 }); - await expect(page.getByText("Nom du serveur")).toBeVisible(); + // The dashboard does not refetch the account on focus and has no cross-tab + // channel, so replay a network reconnect (offline then online) until the + // stale "me" query refetches and the new language applies in place. + await expect(async () => { + await page.evaluate(() => { + window.dispatchEvent(new Event("offline")); + window.dispatchEvent(new Event("online")); + }); + await expect(page.locator("html")).toHaveAttribute("lang", "fr", { timeout: 2_000 }); + }).toPass({ timeout: 30_000 }); + await expect(page.getByText("Nom du serveur").first()).toBeVisible(); await expect(nameInput).toHaveValue(serverName); const mark = await page.evaluate(() => (window as I18nWindow).__i18nMark); expect(mark).toBe(1); From ce88178b89187a9edf8244103338576202516724 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 20 Oct 2026 22:04:14 +0000 Subject: [PATCH 06/24] docs(specs): tick 019 T104 coverage gates confirmed on CI Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: Claude --- modules | 2 +- specs/019-localization-language-packs/tasks.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules b/modules index 10fbd51b7..8f6690e41 160000 --- a/modules +++ b/modules @@ -1 +1 @@ -Subproject commit 10fbd51b786cb02ccbc519f9d384b4391640ca6b +Subproject commit 8f6690e4114155bf845c47791ab41f8e7f320ee8 diff --git a/specs/019-localization-language-packs/tasks.md b/specs/019-localization-language-packs/tasks.md index 54e936c1d..f67e98b52 100644 --- a/specs/019-localization-language-packs/tasks.md +++ b/specs/019-localization-language-packs/tasks.md @@ -250,7 +250,7 @@ description: "Task list for 019 App Localization & Community Language Packs" - [X] T101 [P] Update `docs/architecture.md`, `docs/agent-architecture.md` (task → doc map entry for i18n) and `docs/module-authoring.md` (module translations section) - [X] T102 [P] Add a CHANGELOG / release-notes entry describing language selection, French, problem+json negotiation (opt-in) and module translations - [ ] T103 Run quickstart.md scenarios 1–13 against a `make dev-up` cluster and record results in the PR description -- [ ] T104 Confirm the coverage gates (`i18n` 90, api 80, operator 72, gp-module 80, web thresholds) pass on CI; add tests where they do not (never lower thresholds) +- [X] T104 Confirm the coverage gates (`i18n` 90, api 80, operator 72, gp-module 80, web thresholds) pass on CI; add tests where they do not (never lower thresholds) — confirmed: PR 668 run 38957645731 passed the go (i18n|api|operator|gp-module) threshold gates and the web job; PR 745 web L95.87 S94.37 B89.27 F91.79 - [ ] T105 After merge and with every task complete or withdrawn: `git mv specs/019-localization-language-packs specs/done_019-localization-language-packs` and update in-repo references (Constitution IV, rule 16) --- From fa1cb46c6124c5bfc3e3c5a0a82cb5abe3ad682e Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 20 Oct 2026 22:04:54 +0000 Subject: [PATCH 07/24] chore: restore modules pointer to 10fbd51 The previous commit moved it to an unmerged module branch by mistake; the bump waits until module PR 52 merges. Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: Claude --- modules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules b/modules index 8f6690e41..10fbd51b7 160000 --- a/modules +++ b/modules @@ -1 +1 @@ -Subproject commit 8f6690e4114155bf845c47791ab41f8e7f320ee8 +Subproject commit 10fbd51b786cb02ccbc519f9d384b4391640ca6b From 9089eec0f1169a80138efee311664b26726700b9 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 20 Oct 2026 22:22:23 +0000 Subject: [PATCH 08/24] test(e2e): close response bodies in the French e2e tests Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: Claude --- test/e2e/i18n_instance_default_e2e_test.go | 3 +++ test/e2e/i18n_language_pref_e2e_test.go | 10 ++++++++++ test/e2e/i18n_notify_language_e2e_test.go | 14 ++++++++++++++ 3 files changed, 27 insertions(+) diff --git a/test/e2e/i18n_instance_default_e2e_test.go b/test/e2e/i18n_instance_default_e2e_test.go index 963c11b22..914d92b42 100644 --- a/test/e2e/i18n_instance_default_e2e_test.go +++ b/test/e2e/i18n_instance_default_e2e_test.go @@ -57,6 +57,9 @@ func TestAPI_I18n_InstanceDefault(t *testing.T) { // The admin sets a listed default; the unauthenticated endpoint reflects it. resp, body, err := admin.Do(http.MethodPut, "/admin/config/i18n", map[string]string{"defaultLanguage": "fr"}) + if resp != nil { + defer func() { _ = resp.Body.Close() }() + } if err != nil { t.Fatalf("PUT /admin/config/i18n fr: %v", err) } diff --git a/test/e2e/i18n_language_pref_e2e_test.go b/test/e2e/i18n_language_pref_e2e_test.go index a592bf088..e7185c224 100644 --- a/test/e2e/i18n_language_pref_e2e_test.go +++ b/test/e2e/i18n_language_pref_e2e_test.go @@ -19,6 +19,9 @@ type i18nLanguagePrefs struct { func i18nGetLanguage(t *testing.T, cli *APIClient) *string { t.Helper() resp, body, err := cli.Get("/users/me/preferences") + if resp != nil { + defer func() { _ = resp.Body.Close() }() + } if err != nil { t.Fatalf("GET /users/me/preferences: %v", err) } @@ -57,6 +60,9 @@ func TestAPI_I18n_LanguagePref(t *testing.T) { // A listed tag is saved and echoed back by the preferences payload. resp, body, err := admin.Do(http.MethodPut, "/users/me/language", map[string]any{"language": "fr"}) + if resp != nil { + defer func() { _ = resp.Body.Close() }() + } if err != nil { t.Fatalf("PUT /users/me/language fr: %v", err) } @@ -69,6 +75,10 @@ func TestAPI_I18n_LanguagePref(t *testing.T) { // Null clears the saved choice. resp, body, err = admin.Do(http.MethodPut, "/users/me/language", map[string]any{"language": nil}) + if resp != nil { + r := resp + defer func() { _ = r.Body.Close() }() + } if err != nil { t.Fatalf("PUT /users/me/language null: %v", err) } diff --git a/test/e2e/i18n_notify_language_e2e_test.go b/test/e2e/i18n_notify_language_e2e_test.go index d6c767a63..b6b90e87c 100644 --- a/test/e2e/i18n_notify_language_e2e_test.go +++ b/test/e2e/i18n_notify_language_e2e_test.go @@ -63,6 +63,9 @@ func i18nAuthLocale(t *testing.T, port int) string { func i18nConfigSection(t *testing.T, admin *APIClient, section string) (json.RawMessage, bool) { t.Helper() resp, body, err := admin.Get("/admin/config/") + if resp != nil { + defer func() { _ = resp.Body.Close() }() + } if err != nil { t.Fatalf("GET /admin/config/: %v", err) } @@ -203,6 +206,10 @@ func TestAPI_I18n_NotifyLanguage(t *testing.T) { url := fmt.Sprintf("http://e2e-notify-sink.gameplane-system.svc.cluster.local:8080/cgi-bin/hook?%s", s.query) resp, body, err := admin.Do(http.MethodPut, "/admin/notifications/sinks/"+s.name+"/secret", map[string]string{"kind": "webhook", "url": url, "authorization": ""}) + if resp != nil { + r := resp + defer func() { _ = r.Body.Close() }() + } if err != nil { t.Fatalf("PUT secret %s: %v", s.name, err) } @@ -230,6 +237,9 @@ func TestAPI_I18n_NotifyLanguage(t *testing.T) { "configRef": "gameplane-notify-e2e-notify-default"}, }, }) + if resp != nil { + defer func() { _ = resp.Body.Close() }() + } if err != nil { t.Fatalf("PUT notifications config: %v", err) } @@ -248,6 +258,10 @@ func TestAPI_I18n_NotifyLanguage(t *testing.T) { for _, s := range sinks { resp, body, err := admin.Post("/admin/notifications/sinks/"+s.name+"/test", nil) + if resp != nil { + r := resp + defer func() { _ = r.Body.Close() }() + } if err != nil { t.Fatalf("POST test %s: %v", s.name, err) } From 0c623a7c3b36d650390e8a7795dc2212aa1692f1 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 20 Oct 2026 22:36:23 +0000 Subject: [PATCH 09/24] chore(modules): bump to module French translations; tick 019 T098 Points modules at GameplanePanel/module main after PR 52 (fr.json for minecraft-java, terraria and valheim). Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: Claude --- modules | 2 +- specs/019-localization-language-packs/tasks.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules b/modules index 10fbd51b7..283598736 160000 --- a/modules +++ b/modules @@ -1 +1 @@ -Subproject commit 10fbd51b786cb02ccbc519f9d384b4391640ca6b +Subproject commit 2835987368627a0cb8e40e4d21165447d08d469b diff --git a/specs/019-localization-language-packs/tasks.md b/specs/019-localization-language-packs/tasks.md index f67e98b52..66b5fa934 100644 --- a/specs/019-localization-language-packs/tasks.md +++ b/specs/019-localization-language-packs/tasks.md @@ -239,7 +239,7 @@ description: "Task list for 019 App Localization & Community Language Packs" - [ ] T096 Create `i18n/locales/fr/meta.json` (`fr`, `Français`, `French`, `ltr`, credits) - [ ] T097 Translate `i18n/locales/fr/app.json` to 100% (web, server, condition, notify, audit); drafts may be machine-assisted but every entry is approved by a native French reviewer on Weblate or in PR review; `i18n-check` shows fr 100% and listed -- [ ] T098 [module repo] Translate `modules/minecraft-java/locales/fr.json`, `modules/terraria/locales/fr.json`, `modules/valheim/locales/fr.json` to 100%; module-repo PR; root pointer bump after merge +- [X] T098 [module repo] Translate `modules/minecraft-java/locales/fr.json`, `modules/terraria/locales/fr.json`, `modules/valheim/locales/fr.json` to 100%; module-repo PR; root pointer bump after merge - [X] T099 Implement pseudo-language `en-XA` generation (accented, ~40% longer, bracketed) behind `VITE_I18N_PSEUDO=1` in the `web/vite.config.ts` build step; never present in release `languages.json` (FR-038) - [X] T100 Add an `en-XA` project to `web/playwright.config.ts` running the screenshot suite (`web/e2e/screenshots/`), and fix clipping/overlap found (truncation must reveal full text on hover/focus, FR-010); any visual change goes through `design.pen` first (local machine) *Done note: `npm run screenshots:pseudo` runs the screenshot suite as the Playwright `en-XA` project (`VITE_I18N_PSEUDO=1`). The specs keep driving the UI in English; `e2e/screenshots/capture.ts` switches the dashboard to `en-XA` for each capture through `window.gameplaneI18nTest` (installed by `I18nProvider` only when `en-XA` is listed), then soft-fails on text cut off without a `title` (OD-018, OD-019). The CI job "en-XA screenshots" in `visual-diff.yaml` uploads the captures and clipping reports. The first run found only data text already truncated in English (mobile Servers names and addresses, Overview endpoint hosts, module summaries, file names); each now has a `title` with the full text and nothing changes visually, so `design.pen` is unchanged (OD-020). The run is green (122 passed). Screenshots that specs take with `page.screenshot` directly stay English.* From ff1ec6f9a2c6b14b97cfadd66b00afea4bc866cf Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 20 Oct 2026 22:43:02 +0000 Subject: [PATCH 10/24] feat(i18n): credit valgul as French reviewer; tick 019 T096, T097 Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: Claude --- i18n/locales/fr/meta.json | 8 +++++++- specs/019-localization-language-packs/tasks.md | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/i18n/locales/fr/meta.json b/i18n/locales/fr/meta.json index 99c6bba65..28dddcbad 100644 --- a/i18n/locales/fr/meta.json +++ b/i18n/locales/fr/meta.json @@ -2,5 +2,11 @@ "tag": "fr", "nativeName": "Français", "englishName": "French", - "direction": "ltr" + "direction": "ltr", + "credits": [ + { + "name": "valgul", + "url": "https://github.com/ValgulNecron" + } + ] } diff --git a/specs/019-localization-language-packs/tasks.md b/specs/019-localization-language-packs/tasks.md index 66b5fa934..230dc9245 100644 --- a/specs/019-localization-language-packs/tasks.md +++ b/specs/019-localization-language-packs/tasks.md @@ -237,8 +237,8 @@ description: "Task list for 019 App Localization & Community Language Packs" **Purpose**: French at 100% and the pseudo-language pass. Depends on US4, US5, US6 English references being complete. -- [ ] T096 Create `i18n/locales/fr/meta.json` (`fr`, `Français`, `French`, `ltr`, credits) -- [ ] T097 Translate `i18n/locales/fr/app.json` to 100% (web, server, condition, notify, audit); drafts may be machine-assisted but every entry is approved by a native French reviewer on Weblate or in PR review; `i18n-check` shows fr 100% and listed +- [X] T096 Create `i18n/locales/fr/meta.json` (`fr`, `Français`, `French`, `ltr`, credits) +- [X] T097 Translate `i18n/locales/fr/app.json` to 100% (web, server, condition, notify, audit); drafts may be machine-assisted but every entry is approved by a native French reviewer on Weblate or in PR review; `i18n-check` shows fr 100% and listed — reviewed and approved by valgul in PR 745 review (2026-10-20) - [X] T098 [module repo] Translate `modules/minecraft-java/locales/fr.json`, `modules/terraria/locales/fr.json`, `modules/valheim/locales/fr.json` to 100%; module-repo PR; root pointer bump after merge - [X] T099 Implement pseudo-language `en-XA` generation (accented, ~40% longer, bracketed) behind `VITE_I18N_PSEUDO=1` in the `web/vite.config.ts` build step; never present in release `languages.json` (FR-038) - [X] T100 Add an `en-XA` project to `web/playwright.config.ts` running the screenshot suite (`web/e2e/screenshots/`), and fix clipping/overlap found (truncation must reveal full text on hover/focus, FR-010); any visual change goes through `design.pen` first (local machine) *Done note: `npm run screenshots:pseudo` runs the screenshot suite as the Playwright `en-XA` project (`VITE_I18N_PSEUDO=1`). The specs keep driving the UI in English; `e2e/screenshots/capture.ts` switches the dashboard to `en-XA` for each capture through `window.gameplaneI18nTest` (installed by `I18nProvider` only when `en-XA` is listed), then soft-fails on text cut off without a `title` (OD-018, OD-019). The CI job "en-XA screenshots" in `visual-diff.yaml` uploads the captures and clipping reports. The first run found only data text already truncated in English (mobile Servers names and addresses, Overview endpoint hosts, module summaries, file names); each now has a `title` with the full text and nothing changes visually, so `design.pen` is unchanged (OD-020). The run is green (122 passed). Screenshots that specs take with `page.screenshot` directly stay English.* From db316884845be39e20682a470e09c02130063dc7 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 20 Oct 2026 23:02:29 +0000 Subject: [PATCH 11/24] test(e2e): run the 0% fixture check and switch language in one tab (spec 019 T037) Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: Claude --- .github/workflows/ci.yaml | 1 + .../019-localization-language-packs/tasks.md | 2 +- web/e2e/specs/live/i18n.spec.ts | 93 +++++++------------ 3 files changed, 36 insertions(+), 60 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index d85c5d84c..b8a1afcb6 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -2033,6 +2033,7 @@ jobs: env: GAMEPLANE_E2E_TARGET: live GAMEPLANE_E2E_CLUSTER: ${{ env.CLUSTER }} + VITE_I18N_FIXTURES: "1" run: | cd web && npm run test:e2e:live - name: dump cluster state on failure (e2e-web-live) diff --git a/specs/019-localization-language-packs/tasks.md b/specs/019-localization-language-packs/tasks.md index 230dc9245..3c6a12bcd 100644 --- a/specs/019-localization-language-packs/tasks.md +++ b/specs/019-localization-language-packs/tasks.md @@ -89,7 +89,7 @@ description: "Task list for 019 App Localization & Community Language Packs" - [X] T035 [P] [US1] Go API E2E `test/e2e/i18n_language_pref_e2e_test.go`: `PUT /api/v1/users/me/language` `fr` → `GET /users/me/preferences` returns `language: "fr"`; `null` clears; unknown tag → 400 with `server.i18n.unknown_language` problem body; register in `test/e2e/buckets.sh` (bucket `api-auth`, respect its login budget) - [X] T036 [P] [US1] Go API E2E `test/e2e/i18n_auth_locale_e2e_test.go`: unauthenticated `GET /auth/locale` returns exactly `{"defaultLanguage": …}` and no other keys (rule 3); register in `test/e2e/buckets.sh` -- [ ] T037 [P] [US1] Playwright `web/e2e/specs/live/i18n.spec.ts`: switch to French in personal settings with create-server dialog open and a typed name (kept, no reload, ``), reload and second browser context keep French with no English flash, login and share pages offer a picker remembered per browser, 0%-fixture language shows English everywhere (SC-008) +- [X] T037 [P] [US1] Playwright `web/e2e/specs/live/i18n.spec.ts`: switch to French in personal settings with create-server dialog open and a typed name (kept, no reload, ``), reload and second browser context keep French with no English flash, login and share pages offer a picker remembered per browser, 0%-fixture language shows English everywhere (SC-008) — same-tab switch per valgul (2026-10-20); kept input covered by I18nProvider.test.tsx; fixture via VITE_I18N_FIXTURES=1 in the live job ### Implementation for User Story 1 diff --git a/web/e2e/specs/live/i18n.spec.ts b/web/e2e/specs/live/i18n.spec.ts index c46fca054..209de2442 100644 --- a/web/e2e/specs/live/i18n.spec.ts +++ b/web/e2e/specs/live/i18n.spec.ts @@ -1,11 +1,13 @@ import { test, expect } from "@playwright/test"; import type { APIRequestContext, Page } from "@playwright/test"; -import { apiSend, loginIfNeeded, seedTemplate } from "./_seed"; +import { apiSend, loginIfNeeded } from "./_seed"; // Live: spec 019 T037, language switching end to end on the real backend. // Covers: -// A. Switching language keeps the create-server wizard's typed input and -// does not reload the page. +// A. Switching language applies in place on the same page, without a reload. +// Keeping typed input across a switch is covered by the unit test +// web/src/lib/i18n/I18nProvider.test.tsx ("switches language without +// unmounting a stateful child"). // B. A reload and a new browser context keep French, with no English flash. // C. The logged-out login and share pages offer a picker that is remembered // per browser, and a fresh logged-out context starts in English. @@ -62,61 +64,33 @@ test.describe("live: i18n", () => { } }); - test("switching language keeps the wizard input and does not reload", async ({ page, request }) => { - const stamp = Date.now(); - const templateName = `e2e-pw-i18n-tpl-${stamp}`; - const serverName = `e2e-pw-i18n-${stamp}`; - const template = await seedTemplate(request, templateName); - try { - // The template is preselected by the query string. The Configure step, - // which holds the server name, is the next step. - await page.goto(`/servers/new?template=${templateName}`); - await page.waitForLoadState("domcontentloaded"); - await loginIfNeeded(page); - - await page.getByRole("button", { name: /^Continue to/ }).click(); - const nameInput = page.getByPlaceholder("mc-hardcore"); - await expect(nameInput).toBeVisible({ timeout: 15_000 }); - await nameInput.fill(serverName); - await page.evaluate(() => { - (window as I18nWindow).__i18nMark = 1; - }); - - // Switch the account language from a second page of the same browser. - const second = await page.context().newPage(); - try { - await second.goto("/settings/theme"); - await second.waitForLoadState("domcontentloaded"); - await second.getByRole("radio", { name: /Français/ }).click(); - - const secondHtml = second.locator("html"); - await expect(secondHtml).toHaveAttribute("lang", "fr", { timeout: 15_000 }); - await expect(secondHtml).toHaveAttribute("dir", "ltr"); - await expect(secondHtml).toHaveAttribute("data-i18n-loaded", "fr"); - } finally { - await second.close(); - } + test("switching language applies in place without a reload", async ({ page }) => { + // One tab, no second page and no reconnect replay: the switch is made on + // /settings/theme and the marker shows the page was never reloaded. + await page.goto("/settings/theme"); + await page.waitForLoadState("domcontentloaded"); + await loginIfNeeded(page); - // Back on the wizard page: the typed input and the marker survive, and the - // French label replaces the English one without a reload. - await page.bringToFront(); - // The dashboard does not refetch the account on focus and has no cross-tab - // channel, so replay a network reconnect (offline then online) until the - // stale "me" query refetches and the new language applies in place. - await expect(async () => { - await page.evaluate(() => { - window.dispatchEvent(new Event("offline")); - window.dispatchEvent(new Event("online")); - }); - await expect(page.locator("html")).toHaveAttribute("lang", "fr", { timeout: 2_000 }); - }).toPass({ timeout: 30_000 }); - await expect(page.getByText("Nom du serveur").first()).toBeVisible(); - await expect(nameInput).toHaveValue(serverName); - const mark = await page.evaluate(() => (window as I18nWindow).__i18nMark); - expect(mark).toBe(1); - } finally { - await template.cleanup(request); - } + const french = page.getByRole("radio", { name: /Français/ }); + await expect(french).toBeVisible({ timeout: 15_000 }); + await page.evaluate(() => { + (window as I18nWindow).__i18nMark = 1; + }); + await french.click(); + + const html = page.locator("html"); + await expect(html).toHaveAttribute("lang", "fr", { timeout: 15_000 }); + await expect(html).toHaveAttribute("dir", "ltr"); + await expect(html).toHaveAttribute("data-i18n-loaded", "fr"); + // The personal Language card title is "Language" in English and "Langue" in French. + await expect(page.getByText("Langue", { exact: true }).first()).toBeVisible(); + expect(await page.evaluate(() => (window as I18nWindow).__i18nMark)).toBe(1); + + // Client-side navigation to Servers keeps the marker and shows French there. + await page.getByRole("link", { name: "Serveurs" }).first().click(); + await expect(page).toHaveURL(/\/servers$/); + await expect(page.getByText("Créer un serveur").first()).toBeVisible({ timeout: 15_000 }); + expect(await page.evaluate(() => (window as I18nWindow).__i18nMark)).toBe(1); }); test("reload and a new browser context keep French without an English flash", async ({ page, browser }) => { @@ -218,8 +192,9 @@ test.describe("live: i18n", () => { await expect(page.getByRole("radio", { name: /Français/ })).toBeVisible({ timeout: 15_000 }); // The fixture language exists only when the web server was started with - // VITE_I18N_FIXTURES=1 (vite.config.ts). The CI live job does not set it, - // so this check reports the subtest as skipped rather than passed. + // VITE_I18N_FIXTURES=1 (vite.config.ts). The CI live job sets it on the + // Playwright step, so the subtest runs there. Local runs without it report + // the subtest as skipped rather than passed. const fixture = page.getByRole("radio", { name: /Fixture/ }); if ((await fixture.count()) === 0) { test.skip(true, "VITE_I18N_FIXTURES is not set for the live web server, so the zz fixture language is absent"); From 8c75ddf3f30abb55c43927d419e863f4580071b9 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 20 Oct 2026 23:54:28 +0000 Subject: [PATCH 12/24] fix(e2e): restore i18n state from defers and move T062/T092 to operator (spec 019) The API client's port-forward is bound to t.Context(), which is canceled before t.Cleanup runs, so the instance default restore never reached the API and TestAPI_I18n_NotifyLanguage saw "fr". Restores and the mutex unlock now run from defers. An empty defaultLanguage returns 422 like every config validation error, so the T092 check expects 422. T062 and T092 move from multicluster to operator: their two extra admin logins made TestAPI_I18n_ProblemJSON's failed-login pair hit 429. Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: Claude --- test/e2e/buckets.sh | 15 ++++++------ test/e2e/i18n_instance_default_e2e_test.go | 27 +++++++++++++--------- test/e2e/i18n_language_pref_e2e_test.go | 9 ++++---- test/e2e/i18n_notify_language_e2e_test.go | 15 +++++++----- 4 files changed, 38 insertions(+), 28 deletions(-) diff --git a/test/e2e/buckets.sh b/test/e2e/buckets.sh index a72843855..46ee4cfcc 100755 --- a/test/e2e/buckets.sh +++ b/test/e2e/buckets.sh @@ -106,6 +106,8 @@ TestAddressPool_AddressInUseConflict TestAddressPool_PoolExhausted TestAPI_EventStreamAndRoleEdits_FollowCallerPermissions TestAPI_I18n_LanguagePref +TestAPI_I18n_NotifyLanguage +TestAPI_I18n_InstanceDefault TestE2E_ResticRepoReady EOF } @@ -339,11 +341,12 @@ EOF # e2e-admin login (account removal also uses fake-OIDC sign-ins, which every # e2e.sh cluster deploys), and TestAPI_I18n_ProblemJSON costs one admin login, # one viewer login and one failed-login pair, bringing this job to 5 admin -# logins (ceiling ~7). TestAPI_I18n_NotifyLanguage (T062) and -# TestAPI_I18n_InstanceDefault (T092) add one e2e-admin login each, bringing -# this job to 7 (ceiling ~7); TestAPI_I18n_InstanceDefault also takes one -# viewer login. TestAPI_I18n_LanguagePref (T035) sits in operator for the same -# login-budget reason. All run against the primary cluster. api-auth, +# logins (ceiling ~7). TestAPI_I18n_LanguagePref (T035), +# TestAPI_I18n_NotifyLanguage (T062) and TestAPI_I18n_InstanceDefault (T092) +# sit in operator for the same login-budget reason: two more admin logins here +# made TestAPI_I18n_ProblemJSON's failed-login pair hit 429. They cost one +# e2e-admin login each, plus one viewer login for T092 (operator: 4 admin +# logins with the EventStream test). All run against the primary cluster. api-auth, # api-roles, api-mods, api-rbac and api-agent are at their ceilings (see their # comments); do not add to them. bucket_multicluster() { cat <<'EOF' @@ -351,8 +354,6 @@ TestAPI_AccountRemoval_RevokesSharesAndAllowsSSOReprovision TestAPI_ModuleUpload_ExtractionStaysWithinBudget TestAPI_I18n_ProblemJSON TestAPI_I18n_ModuleLocales -TestAPI_I18n_NotifyLanguage -TestAPI_I18n_InstanceDefault TestMultiCluster_ClusterDispatchAndScopedRBAC TestMultiCluster_GatewayParity EOF diff --git a/test/e2e/i18n_instance_default_e2e_test.go b/test/e2e/i18n_instance_default_e2e_test.go index 914d92b42..0687dc5a0 100644 --- a/test/e2e/i18n_instance_default_e2e_test.go +++ b/test/e2e/i18n_instance_default_e2e_test.go @@ -11,12 +11,12 @@ import ( // TestAPI_I18n_InstanceDefault proves the instance default language (spec 019, // T092): an admin can set it through PUT /admin/config/i18n, the unauthenticated -// GET /auth/locale reflects it, an unlisted or empty value is rejected without +// GET /auth/locale reflects it, an unlisted (400) or empty (422) value is rejected without // writing, and a viewer cannot change it. The default is process-wide, so the // test holds i18nInstanceDefaultMu for its whole body and restores the prior -// value on cleanup. +// value in a defer before releasing it. // -// Budget: one e2e-admin login plus one viewer login, in the multicluster +// Budget: one e2e-admin login plus one viewer login, in the operator // bucket for the login budget (see buckets.sh). func TestAPI_I18n_InstanceDefault(t *testing.T) { t.Parallel() @@ -26,22 +26,26 @@ func TestAPI_I18n_InstanceDefault(t *testing.T) { // Close is registered first so it runs last: every cleanup below uses admin. t.Cleanup(admin.Close) + // Defers, not t.Cleanup, for everything that calls the API: the client's + // port-forward is bound to t.Context(), which is canceled before cleanups + // run, so a request made from a cleanup never reaches the API. Defers run + // last-in first-out, so the lock is released after the default is restored. i18nInstanceDefaultMu.Lock() - t.Cleanup(i18nInstanceDefaultMu.Unlock) + defer i18nInstanceDefaultMu.Unlock() username, password, id := envInstance.CreateUser(t, admin, "viewer", "e2e-i18n-default") - t.Cleanup(func() { + defer func() { r, _, _ := admin.Delete("/users/" + id) if r != nil { _ = r.Body.Close() } - }) + }() viewer := envInstance.APIClient(t, username, password) t.Cleanup(viewer.Close) // Restore the prior i18n section, or en when it was absent. prior, hadPrior := i18nConfigSection(t, admin, "i18n") - t.Cleanup(func() { + defer func() { body := prior if !hadPrior { body = json.RawMessage(`{"defaultLanguage":"en"}`) @@ -50,7 +54,7 @@ func TestAPI_I18n_InstanceDefault(t *testing.T) { if r != nil { _ = r.Body.Close() } - }) + }() port, stop := envInstance.PortForward(t, "gameplane-system", "svc/gameplane-api", 80) defer stop() @@ -83,11 +87,12 @@ func TestAPI_I18n_InstanceDefault(t *testing.T) { t.Errorf("PUT /admin/config/i18n tlh: args=%v, want language=tlh", doc["args"]) } - // An empty default is rejected as plain text. + // An empty default fails validation as plain text with 422, the status + // every config section returns for a validation error (config.go put). status, _, raw = rawDo(t, admin, http.MethodPut, "/admin/config/i18n", map[string]string{"defaultLanguage": ""}, "") - if status != http.StatusBadRequest || strings.TrimSpace(string(raw)) != "defaultLanguage is required" { - t.Errorf("PUT /admin/config/i18n empty: status=%d body=%q, want 400 \"defaultLanguage is required\"", status, string(raw)) + if status != http.StatusUnprocessableEntity || strings.TrimSpace(string(raw)) != "defaultLanguage is required" { + t.Errorf("PUT /admin/config/i18n empty: status=%d body=%q, want 422 \"defaultLanguage is required\"", status, string(raw)) } // Neither rejected write changed the default. diff --git a/test/e2e/i18n_language_pref_e2e_test.go b/test/e2e/i18n_language_pref_e2e_test.go index e7185c224..903489e6f 100644 --- a/test/e2e/i18n_language_pref_e2e_test.go +++ b/test/e2e/i18n_language_pref_e2e_test.go @@ -48,15 +48,16 @@ func TestAPI_I18n_LanguagePref(t *testing.T) { envInstance.BootstrapAdmin(t, adminUsername, adminPassword) admin := envInstance.APIClient(t, adminUsername, adminPassword) - // Registered before the reset so the reset runs while the client is open - // (cleanups run last-in first-out). t.Cleanup(admin.Close) - t.Cleanup(func() { + // A defer, not t.Cleanup: the client's port-forward is bound to + // t.Context(), which is canceled before cleanups run, so the reset would + // never reach the API from a cleanup. + defer func() { r, _, _ := admin.Do(http.MethodPut, "/users/me/language", map[string]any{"language": nil}) if r != nil { _ = r.Body.Close() } - }) + }() // A listed tag is saved and echoed back by the preferences payload. resp, body, err := admin.Do(http.MethodPut, "/users/me/language", map[string]any{"language": "fr"}) diff --git a/test/e2e/i18n_notify_language_e2e_test.go b/test/e2e/i18n_notify_language_e2e_test.go index b6b90e87c..fcf3749c5 100644 --- a/test/e2e/i18n_notify_language_e2e_test.go +++ b/test/e2e/i18n_notify_language_e2e_test.go @@ -145,7 +145,7 @@ func i18nCheckNotifyPayload(t *testing.T, want i18nNotifyExpect, raw string) { // // The test-send delivers to an in-cluster webhook receiver (fixtures/ // notify-sink.yaml) that logs each request's query and body. Budget: one -// e2e-admin login, in the multicluster bucket for the login budget. +// e2e-admin login, in the operator bucket for the login budget. func TestAPI_I18n_NotifyLanguage(t *testing.T) { t.Parallel() @@ -154,8 +154,11 @@ func TestAPI_I18n_NotifyLanguage(t *testing.T) { // Close is registered first so it runs last: every cleanup below uses admin. t.Cleanup(admin.Close) + // Defers, not t.Cleanup, for everything that calls the API: the client's + // port-forward is bound to t.Context(), which is canceled before cleanups + // run, so a request made from a cleanup never reaches the API. i18nInstanceDefaultMu.Lock() - t.Cleanup(i18nInstanceDefaultMu.Unlock) + defer i18nInstanceDefaultMu.Unlock() // Receiver: a hard gate. The rollout waits for the TCP readiness probe, so // the test-send cannot race the receiver's startup. @@ -176,7 +179,7 @@ func TestAPI_I18n_NotifyLanguage(t *testing.T) { // Restore the notifications section (or an empty one if it was absent). prior, hadPrior := i18nConfigSection(t, admin, "notifications") - t.Cleanup(func() { + defer func() { body := prior if !hadPrior { body = json.RawMessage(`{"sinks":[]}`) @@ -185,7 +188,7 @@ func TestAPI_I18n_NotifyLanguage(t *testing.T) { if r != nil { _ = r.Body.Close() } - }) + }() sinks := []struct{ name, query string }{ {"e2e-notify-fr", "sink=fr"}, @@ -193,12 +196,12 @@ func TestAPI_I18n_NotifyLanguage(t *testing.T) { } for _, s := range sinks { name := s.name - t.Cleanup(func() { + defer func() { r, _, _ := admin.Delete("/admin/notifications/sinks/" + name + "/secret") if r != nil { _ = r.Body.Close() } - }) + }() } // Secrets hold each sink's URL; configRef must name the returned Secret. From bd6d261542c80334334284738790698cc37ed69f Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 21 Oct 2026 00:03:54 +0000 Subject: [PATCH 13/24] test(e2e): address review on the spec 019 French tests - T035's task record names the operator bucket it actually runs in. - TestAPI_I18n_LanguagePref restores the admin's prior language instead of clearing it. - The live reload test also watches the new browser context for an English flash. - The 0% fixture subtest fails in CI when the fixture is missing instead of skipping. Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: Claude --- .../019-localization-language-packs/tasks.md | 2 +- test/e2e/i18n_language_pref_e2e_test.go | 12 +++-- web/e2e/specs/live/i18n.spec.ts | 48 ++++++++++++------- 3 files changed, 40 insertions(+), 22 deletions(-) diff --git a/specs/019-localization-language-packs/tasks.md b/specs/019-localization-language-packs/tasks.md index 3c6a12bcd..c9d5ef07f 100644 --- a/specs/019-localization-language-packs/tasks.md +++ b/specs/019-localization-language-packs/tasks.md @@ -87,7 +87,7 @@ description: "Task list for 019 App Localization & Community Language Packs" ### Tests for User Story 1 -- [X] T035 [P] [US1] Go API E2E `test/e2e/i18n_language_pref_e2e_test.go`: `PUT /api/v1/users/me/language` `fr` → `GET /users/me/preferences` returns `language: "fr"`; `null` clears; unknown tag → 400 with `server.i18n.unknown_language` problem body; register in `test/e2e/buckets.sh` (bucket `api-auth`, respect its login budget) +- [X] T035 [P] [US1] Go API E2E `test/e2e/i18n_language_pref_e2e_test.go`: `PUT /api/v1/users/me/language` `fr` → `GET /users/me/preferences` returns `language: "fr"`; `null` clears; unknown tag → 400 with `server.i18n.unknown_language` problem body; register in `test/e2e/buckets.sh` (bucket `operator` for the login budget; see the comment above `bucket_multicluster`) - [X] T036 [P] [US1] Go API E2E `test/e2e/i18n_auth_locale_e2e_test.go`: unauthenticated `GET /auth/locale` returns exactly `{"defaultLanguage": …}` and no other keys (rule 3); register in `test/e2e/buckets.sh` - [X] T037 [P] [US1] Playwright `web/e2e/specs/live/i18n.spec.ts`: switch to French in personal settings with create-server dialog open and a typed name (kept, no reload, ``), reload and second browser context keep French with no English flash, login and share pages offer a picker remembered per browser, 0%-fixture language shows English everywhere (SC-008) — same-tab switch per valgul (2026-10-20); kept input covered by I18nProvider.test.tsx; fixture via VITE_I18N_FIXTURES=1 in the live job diff --git a/test/e2e/i18n_language_pref_e2e_test.go b/test/e2e/i18n_language_pref_e2e_test.go index 903489e6f..3cf1797b0 100644 --- a/test/e2e/i18n_language_pref_e2e_test.go +++ b/test/e2e/i18n_language_pref_e2e_test.go @@ -38,8 +38,8 @@ func i18nGetLanguage(t *testing.T, cli *APIClient) *string { // TestAPI_I18n_LanguagePref proves PUT /users/me/language (spec 019, T035): // a listed tag is saved and reads back through the preferences payload, null // clears it, and a rejected tag returns the registry problem document without -// writing anything. It acts only on the admin's own account and resets the -// language to null on cleanup, so later live specs run in English again. +// writing anything. It acts only on the admin's own account and restores the +// admin's prior language when it finishes. // // Budget: one e2e-admin login. It lives in the operator bucket for the login // budget, not by subject, the same precedent as the EventStream test there. @@ -49,11 +49,17 @@ func TestAPI_I18n_LanguagePref(t *testing.T) { envInstance.BootstrapAdmin(t, adminUsername, adminPassword) admin := envInstance.APIClient(t, adminUsername, adminPassword) t.Cleanup(admin.Close) + // The shared admin may already have a saved language; put it back after. + prior := i18nGetLanguage(t, admin) // A defer, not t.Cleanup: the client's port-forward is bound to // t.Context(), which is canceled before cleanups run, so the reset would // never reach the API from a cleanup. defer func() { - r, _, _ := admin.Do(http.MethodPut, "/users/me/language", map[string]any{"language": nil}) + var restore any + if prior != nil { + restore = *prior + } + r, _, _ := admin.Do(http.MethodPut, "/users/me/language", map[string]any{"language": restore}) if r != nil { _ = r.Body.Close() } diff --git a/web/e2e/specs/live/i18n.spec.ts b/web/e2e/specs/live/i18n.spec.ts index 209de2442..8162f86fa 100644 --- a/web/e2e/specs/live/i18n.spec.ts +++ b/web/e2e/specs/live/i18n.spec.ts @@ -43,6 +43,28 @@ async function pickFrenchFromCompactPicker(page: Page): Promise { await page.getByRole("menu", { name: /^(Language options|Options de langue)$/ }).getByText("Français").click(); } +// watchForEnglish records whether the English "Create server" label ever shows +// on the page, from before React renders. Install it before the first goto. +async function watchForEnglish(page: Page): Promise { + await page.addInitScript(() => { + const w = window as I18nWindow; + w.__englishSeen = false; + const watch = () => { + const observer = new MutationObserver(() => { + if (/Create server/.test(document.body?.textContent ?? "")) { + w.__englishSeen = true; + } + }); + observer.observe(document.documentElement, { childList: true, subtree: true, characterData: true }); + }; + if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", watch); + } else { + watch(); + } + }); +} + test.describe("live: i18n", () => { test.skip( process.env.GAMEPLANE_E2E_TARGET !== "live", @@ -96,23 +118,7 @@ test.describe("live: i18n", () => { test("reload and a new browser context keep French without an English flash", async ({ page, browser }) => { // Watch the DOM from before React renders. A later "Create server" text is // the English label leaking into the French page. - await page.addInitScript(() => { - const w = window as I18nWindow; - w.__englishSeen = false; - const watch = () => { - const observer = new MutationObserver(() => { - if (/Create server/.test(document.body?.textContent ?? "")) { - w.__englishSeen = true; - } - }); - observer.observe(document.documentElement, { childList: true, subtree: true, characterData: true }); - }; - if (document.readyState === "loading") { - document.addEventListener("DOMContentLoaded", watch); - } else { - watch(); - } - }); + await watchForEnglish(page); await page.goto("/servers"); await page.waitForLoadState("domcontentloaded"); @@ -127,10 +133,12 @@ test.describe("live: i18n", () => { const ctx2 = await browser.newContext({ storageState: ADMIN_STATE }); try { const p2 = await ctx2.newPage(); + await watchForEnglish(p2); await p2.goto("/servers"); await p2.waitForLoadState("domcontentloaded"); await expect(p2.locator("html")).toHaveAttribute("lang", "fr", { timeout: 15_000 }); await expect(p2.getByText("Créer un serveur").first()).toBeVisible({ timeout: 15_000 }); + expect(await p2.evaluate(() => (window as I18nWindow).__englishSeen)).toBe(false); } finally { await ctx2.close(); } @@ -194,9 +202,13 @@ test.describe("live: i18n", () => { // The fixture language exists only when the web server was started with // VITE_I18N_FIXTURES=1 (vite.config.ts). The CI live job sets it on the // Playwright step, so the subtest runs there. Local runs without it report - // the subtest as skipped rather than passed. + // the subtest as skipped; in CI it fails instead. const fixture = page.getByRole("radio", { name: /Fixture/ }); if ((await fixture.count()) === 0) { + // CI sets VITE_I18N_FIXTURES=1, so a missing fixture there is a failure. + if (process.env.CI) { + throw new Error("zz fixture language missing; CI sets VITE_I18N_FIXTURES=1 on the live web server"); + } test.skip(true, "VITE_I18N_FIXTURES is not set for the live web server, so the zz fixture language is absent"); } From 313010b561bf3a0ee5e33dc52e5bb9808d9e34b4 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 21 Oct 2026 17:08:24 +0000 Subject: [PATCH 14/24] ci: build the i18n catalogs in the live web e2e job (spec 019 T037) The live job serves the dashboard with plain vite, so the gitignored languages.json and compiled fr catalog never existed and the settings page listed English only. Run i18n:compile and i18n:manifest first, as the web job and the web image already do. Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: Claude --- .github/workflows/ci.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index b8a1afcb6..e2f31fec5 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -2029,6 +2029,11 @@ jobs: - name: install playwright browser run: | cd web && npm ci && npx playwright install --with-deps chromium + - name: i18n catalogs for the live dashboard + # The live job serves the dashboard with plain vite, which never builds + # the gitignored language list and catalogs (spec 019 T037). + run: | + cd web && npm run i18n:compile && npm run i18n:manifest - name: run playwright (live) env: GAMEPLANE_E2E_TARGET: live From 4c59659859bea45eedf1c835fb3a5400474f7121 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 21 Oct 2026 17:15:39 +0000 Subject: [PATCH 15/24] test(e2e): watch for the English flash from the first DOM node (spec 019 T037) Module scripts can render before DOMContentLoaded, so the observer now starts in the init script and checks the current text right away. Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: Claude --- web/e2e/specs/live/i18n.spec.ts | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/web/e2e/specs/live/i18n.spec.ts b/web/e2e/specs/live/i18n.spec.ts index 8162f86fa..6fb562359 100644 --- a/web/e2e/specs/live/i18n.spec.ts +++ b/web/e2e/specs/live/i18n.spec.ts @@ -49,19 +49,15 @@ async function watchForEnglish(page: Page): Promise { await page.addInitScript(() => { const w = window as I18nWindow; w.__englishSeen = false; - const watch = () => { - const observer = new MutationObserver(() => { - if (/Create server/.test(document.body?.textContent ?? "")) { - w.__englishSeen = true; - } - }); - observer.observe(document.documentElement, { childList: true, subtree: true, characterData: true }); + // Observe from the init script itself: module scripts can render before + // DOMContentLoaded, so waiting for it could miss a short English flash. + const check = () => { + if (/Create server/.test(document.documentElement?.textContent ?? "")) { + w.__englishSeen = true; + } }; - if (document.readyState === "loading") { - document.addEventListener("DOMContentLoaded", watch); - } else { - watch(); - } + new MutationObserver(check).observe(document, { childList: true, subtree: true, characterData: true }); + check(); }); } From 8816dfd9a9457fa17ab904b90a94cbc21c72baf3 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 21 Oct 2026 22:03:58 +0000 Subject: [PATCH 16/24] =?UTF-8?q?test(e2e):=20click=20the=20Fran=C3=A7ais?= =?UTF-8?q?=20label,=20not=20the=20hidden=20radio=20input=20(spec=20019=20?= =?UTF-8?q?T037)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit HeroUI's radio control intercepts pointer clicks on the input. Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: Claude --- web/e2e/specs/live/i18n.spec.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/web/e2e/specs/live/i18n.spec.ts b/web/e2e/specs/live/i18n.spec.ts index 6fb562359..43810018e 100644 --- a/web/e2e/specs/live/i18n.spec.ts +++ b/web/e2e/specs/live/i18n.spec.ts @@ -94,7 +94,9 @@ test.describe("live: i18n", () => { await page.evaluate(() => { (window as I18nWindow).__i18nMark = 1; }); - await french.click(); + // HeroUI's Radio hides the input under its decorative control, which + // intercepts a pointer click; click the visible label text instead. + await page.getByText("Français", { exact: true }).click(); const html = page.locator("html"); await expect(html).toHaveAttribute("lang", "fr", { timeout: 15_000 }); From a52673b06cf103ab47880078298fcbd55d4a9e56 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 21 Oct 2026 23:05:13 +0000 Subject: [PATCH 17/24] fix(web): keep edge spaces outside i18n messages (spec 019) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit formatjs extract trims edge whitespace, so the language packs store "· Secret: {ref}" while the code said " · Secret: {ref}". Builds that compile catalogs rendered "webhook· Secret", "Name*" and similar. Move the 21 edge spaces into the markup, as i18n-migrate.ts already does; the packs and their sourceHash stay unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: Claude --- web/src/components/modules/ModuleCard.tsx | 2 +- web/src/components/modules/UploadModuleDialog.tsx | 2 +- web/src/components/server/ConfigFields.tsx | 2 +- web/src/components/server/ServerActionsCard.tsx | 6 +++--- web/src/routes/AdminSettings.tsx | 4 ++-- web/src/routes/AuditLog.tsx | 2 +- web/src/routes/Dashboard.tsx | 2 +- web/src/routes/tabs/Backups.tsx | 2 +- web/src/routes/tabs/Logs.tsx | 2 +- web/src/routes/tabs/Mods.tsx | 10 +++++----- web/src/routes/tabs/Players.tsx | 4 ++-- 11 files changed, 19 insertions(+), 19 deletions(-) diff --git a/web/src/components/modules/ModuleCard.tsx b/web/src/components/modules/ModuleCard.tsx index 9bf43ddc1..61f27525e 100644 --- a/web/src/components/modules/ModuleCard.tsx +++ b/web/src/components/modules/ModuleCard.tsx @@ -93,7 +93,7 @@ export function ModuleCard({ const versionLabel = entry.installed ? `${intl.formatMessage({ id: "web.components.modules.module_card.installed_version", defaultMessage: "v{version} installed", description: "Module card: installed version text; {version} is the installed module version" }, { version: entry.installedVersion ?? "?" })}${ - upgradeAvailable ? intl.formatMessage({ id: "web.components.modules.module_card.update_available_suffix", defaultMessage: " · v{version} available", description: "Module card: suffix naming the newer version that is available; {version} is that version" }, { version: entry.latestVersion }) : "" + upgradeAvailable ? " " + intl.formatMessage({ id: "web.components.modules.module_card.update_available_suffix", defaultMessage: "· v{version} available", description: "Module card: suffix naming the newer version that is available; {version} is that version" }, { version: entry.latestVersion }) : "" }` : entry.latestVersion ? intl.formatMessage({ id: "web.components.modules.module_card.latest_version", defaultMessage: "v{version}", description: "Module card: latest published version; {version} is the version number" }, { version: entry.latestVersion }) diff --git a/web/src/components/modules/UploadModuleDialog.tsx b/web/src/components/modules/UploadModuleDialog.tsx index 04cccfbd0..b19dd94e5 100644 --- a/web/src/components/modules/UploadModuleDialog.tsx +++ b/web/src/components/modules/UploadModuleDialog.tsx @@ -151,7 +151,7 @@ export function UploadModuleDialog({ open, onOpenChange, sources, onUploaded, cl {preview.module.displayName} {intl.formatMessage({ id: "web.components.modules.upload_module_dialog.vversion", defaultMessage: "v{version}", description: "Upload module dialog: sentence with version in span" }, { version: preview.module.version })}
{intl.formatMessage({ id: "web.components.modules.upload_module_dialog.game", defaultMessage: "game:", description: "Upload module dialog: label before the game identifier in the preview card of the chosen bundle; the value after it is the game name" })} {preview.module.game} - {preview.module.summary && intl.formatMessage({ id: "web.components.modules.upload_module_dialog.summary_line", defaultMessage: " — {summary}", description: "Upload module dialog: module summary after an em dash; {summary} is the module summary" }, { summary: preview.module.summary })} + {preview.module.summary && " " + intl.formatMessage({ id: "web.components.modules.upload_module_dialog.summary_line", defaultMessage: "— {summary}", description: "Upload module dialog: module summary after an em dash; {summary} is the module summary" }, { summary: preview.module.summary })}
)} diff --git a/web/src/components/server/ConfigFields.tsx b/web/src/components/server/ConfigFields.tsx index d275fafc1..57bbc22d9 100644 --- a/web/src/components/server/ConfigFields.tsx +++ b/web/src/components/server/ConfigFields.tsx @@ -183,7 +183,7 @@ export function ConfigFieldInput({ {label} {field.required && ( )} diff --git a/web/src/components/server/ServerActionsCard.tsx b/web/src/components/server/ServerActionsCard.tsx index f4caeee73..f33fab238 100644 --- a/web/src/components/server/ServerActionsCard.tsx +++ b/web/src/components/server/ServerActionsCard.tsx @@ -484,7 +484,7 @@ function ParamField({ > @@ -517,7 +517,7 @@ function ParamField({ {label} - {param.required && {intl.formatMessage({ id: "web.common.required_marker", defaultMessage: " *", description: "Asterisk shown after the label of a required field" })}} + {param.required && {" " + intl.formatMessage({ id: "web.common.required_marker", defaultMessage: "*", description: "Asterisk shown after the label of a required field" })}} @@ -525,7 +525,7 @@ function ParamField({
{p.displayName || p.name}
{p.kind} - {p.issuer ? intl.formatMessage({ id: "web.routes.admin_settings.provider_issuer_suffix", defaultMessage: " · {issuer}", description: "Admin settings: issuer shown after the provider type; {issuer} is the issuer URL" }, { issuer: p.issuer }) : p.configRef ? intl.formatMessage({ id: "web.routes.admin_settings.provider_secret_suffix", defaultMessage: " · {ref}", description: "Admin settings: Secret reference shown after the provider type when no issuer is set; {ref} is the Secret name" }, { ref: p.configRef }) : ""} + {p.issuer ? " " + intl.formatMessage({ id: "web.routes.admin_settings.provider_issuer_suffix", defaultMessage: "· {issuer}", description: "Admin settings: issuer shown after the provider type; {issuer} is the issuer URL" }, { issuer: p.issuer }) : p.configRef ? " " + intl.formatMessage({ id: "web.routes.admin_settings.provider_secret_suffix", defaultMessage: "· {ref}", description: "Admin settings: Secret reference shown after the provider type when no issuer is set; {ref} is the Secret name" }, { ref: p.configRef }) : ""}
+ {errMsg(listError, intl)}{" " + intl.formatMessage({ id: "web.routes.tabs.mods.error_retry_separator", defaultMessage: "·", description: "Mods: separator between the list error and the retry button" }) + " "} )} @@ -347,7 +347,7 @@ function FileModsTab({ name, tmpl, gs, ns }: { name: string; tmpl?: GameTemplate
{formatBytes(m.size)} - {m.modTime ? intl.formatMessage({ id: "web.routes.tabs.mods.modified_relative", defaultMessage: " · {time}", description: "Mods: suffix after a mod's version showing when it was modified; {time} is how long ago" }, { time: formatRelative(m.modTime, intl) }) : ""} + {m.modTime ? " " + intl.formatMessage({ id: "web.routes.tabs.mods.modified_relative", defaultMessage: "· {time}", description: "Mods: suffix after a mod's version showing when it was modified; {time} is how long ago" }, { time: formatRelative(m.modTime, intl) }) : ""}
@@ -636,7 +636,7 @@ function ModsByIdTab({ {isError && (
- {errMsg(listError, intl)}{intl.formatMessage({ id: "web.routes.tabs.mods.error_retry_separator", defaultMessage: " · ", description: "Mods: separator between the list error and the retry button" })} + {errMsg(listError, intl)}{" " + intl.formatMessage({ id: "web.routes.tabs.mods.error_retry_separator", defaultMessage: "·", description: "Mods: separator between the list error and the retry button" }) + " "}
)} @@ -1224,7 +1224,7 @@ function ModCard({ const f = v.files.find((x) => x.primary) ?? v.files[0]; return ( ); })} diff --git a/web/src/routes/tabs/Players.tsx b/web/src/routes/tabs/Players.tsx index a569aa2ba..3710fd98c 100644 --- a/web/src/routes/tabs/Players.tsx +++ b/web/src/routes/tabs/Players.tsx @@ -333,7 +333,7 @@ export function PlayersTab({ name, ns }: { name: string; ns?: string }) { {(b.reason || b.source) && (
{b.source && {intl.formatMessage({ id: "web.routes.tabs.players.by_source", defaultMessage: "by {source}", description: "Players: source of a ban shown under the player; {source} is who issued the ban" }, { source: b.source })}} - {b.source && b.reason && {intl.formatMessage({ id: "web.routes.tabs.players.source_reason_separator", defaultMessage: " · ", description: "Players: separator between the ban source and the ban reason" })}} + {b.source && b.reason && {" " + intl.formatMessage({ id: "web.routes.tabs.players.source_reason_separator", defaultMessage: "·", description: "Players: separator between the ban source and the ban reason" }) + " "}} {b.reason && {b.reason}}
)} @@ -389,7 +389,7 @@ function ConfirmAction({

{verb} {player} - {playerId !== player && intl.formatMessage({ id: "web.routes.tabs.players.player_id_in_parens", defaultMessage: " ({playerId})", description: "Players: player ID shown in parentheses after the player name; {playerId} is the ID element" }, { playerId: {playerId} })} + {playerId !== player && <>{" "}{intl.formatMessage({ id: "web.routes.tabs.players.player_id_in_parens", defaultMessage: "({playerId})", description: "Players: player ID shown in parentheses after the player name; {playerId} is the ID element" }, { playerId: {playerId} })}} {intl.formatMessage({ id: "web.routes.tabs.players.question_mark", defaultMessage: "?", description: "Players: question mark that ends the confirm sentence" })}

Date: Wed, 21 Oct 2026 23:13:46 +0000 Subject: [PATCH 18/24] test(e2e): click the fixture language label, not its hidden input (spec 019 T037) Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: Claude --- web/e2e/specs/live/i18n.spec.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/e2e/specs/live/i18n.spec.ts b/web/e2e/specs/live/i18n.spec.ts index 43810018e..866639111 100644 --- a/web/e2e/specs/live/i18n.spec.ts +++ b/web/e2e/specs/live/i18n.spec.ts @@ -210,7 +210,8 @@ test.describe("live: i18n", () => { test.skip(true, "VITE_I18N_FIXTURES is not set for the live web server, so the zz fixture language is absent"); } - await fixture.click(); + // Click the visible name, as above: the radio control intercepts the input. + await page.getByText("Fixture 0%", { exact: true }).click(); await expect(page.locator("html")).toHaveAttribute("lang", "zz", { timeout: 15_000 }); // Every string falls back to English, and no raw message id is shown. From 15c7fc5a79e4270ef490807de3cfea94886f041e Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Oct 2026 01:03:30 +0000 Subject: [PATCH 19/24] fix(web): write the moved edge spaces as JSX text (spec 019) formatjs/no-literal-string-in-jsx rejects " " string literals in JSX, so the spaces are now plain JSX text next to each message; the version dropdown builds its label outside JSX. Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: Claude --- web/src/components/modules/UploadModuleDialog.tsx | 2 +- web/src/components/server/ConfigFields.tsx | 2 +- web/src/components/server/ServerActionsCard.tsx | 6 +++--- web/src/routes/AdminSettings.tsx | 4 ++-- web/src/routes/AuditLog.tsx | 2 +- web/src/routes/tabs/Backups.tsx | 2 +- web/src/routes/tabs/Logs.tsx | 2 +- web/src/routes/tabs/Mods.tsx | 11 ++++++----- web/src/routes/tabs/Players.tsx | 4 ++-- 9 files changed, 18 insertions(+), 17 deletions(-) diff --git a/web/src/components/modules/UploadModuleDialog.tsx b/web/src/components/modules/UploadModuleDialog.tsx index b19dd94e5..4ecdc16a2 100644 --- a/web/src/components/modules/UploadModuleDialog.tsx +++ b/web/src/components/modules/UploadModuleDialog.tsx @@ -151,7 +151,7 @@ export function UploadModuleDialog({ open, onOpenChange, sources, onUploaded, cl {preview.module.displayName} {intl.formatMessage({ id: "web.components.modules.upload_module_dialog.vversion", defaultMessage: "v{version}", description: "Upload module dialog: sentence with version in span" }, { version: preview.module.version })}
{intl.formatMessage({ id: "web.components.modules.upload_module_dialog.game", defaultMessage: "game:", description: "Upload module dialog: label before the game identifier in the preview card of the chosen bundle; the value after it is the game name" })} {preview.module.game} - {preview.module.summary && " " + intl.formatMessage({ id: "web.components.modules.upload_module_dialog.summary_line", defaultMessage: "— {summary}", description: "Upload module dialog: module summary after an em dash; {summary} is the module summary" }, { summary: preview.module.summary })} + {preview.module.summary && <> {intl.formatMessage({ id: "web.components.modules.upload_module_dialog.summary_line", defaultMessage: "— {summary}", description: "Upload module dialog: module summary after an em dash; {summary} is the module summary" }, { summary: preview.module.summary })}}
)} diff --git a/web/src/components/server/ConfigFields.tsx b/web/src/components/server/ConfigFields.tsx index 57bbc22d9..6858ea144 100644 --- a/web/src/components/server/ConfigFields.tsx +++ b/web/src/components/server/ConfigFields.tsx @@ -183,7 +183,7 @@ export function ConfigFieldInput({ {label} {field.required && ( )} diff --git a/web/src/components/server/ServerActionsCard.tsx b/web/src/components/server/ServerActionsCard.tsx index f33fab238..7a04be753 100644 --- a/web/src/components/server/ServerActionsCard.tsx +++ b/web/src/components/server/ServerActionsCard.tsx @@ -484,7 +484,7 @@ function ParamField({ > @@ -517,7 +517,7 @@ function ParamField({ {label} - {param.required && {" " + intl.formatMessage({ id: "web.common.required_marker", defaultMessage: "*", description: "Asterisk shown after the label of a required field" })}} + {param.required && {intl.formatMessage({ id: "web.common.required_marker", defaultMessage: "*", description: "Asterisk shown after the label of a required field" })}} @@ -525,7 +525,7 @@ function ParamField({
{p.displayName || p.name}
{p.kind} - {p.issuer ? " " + intl.formatMessage({ id: "web.routes.admin_settings.provider_issuer_suffix", defaultMessage: "· {issuer}", description: "Admin settings: issuer shown after the provider type; {issuer} is the issuer URL" }, { issuer: p.issuer }) : p.configRef ? " " + intl.formatMessage({ id: "web.routes.admin_settings.provider_secret_suffix", defaultMessage: "· {ref}", description: "Admin settings: Secret reference shown after the provider type when no issuer is set; {ref} is the Secret name" }, { ref: p.configRef }) : ""} + {p.issuer ? <> {intl.formatMessage({ id: "web.routes.admin_settings.provider_issuer_suffix", defaultMessage: "· {issuer}", description: "Admin settings: issuer shown after the provider type; {issuer} is the issuer URL" }, { issuer: p.issuer })} : p.configRef ? <> {intl.formatMessage({ id: "web.routes.admin_settings.provider_secret_suffix", defaultMessage: "· {ref}", description: "Admin settings: Secret reference shown after the provider type when no issuer is set; {ref} is the Secret name" }, { ref: p.configRef })} : ""}
+ {errMsg(listError, intl)} {intl.formatMessage({ id: "web.routes.tabs.mods.error_retry_separator", defaultMessage: "·", description: "Mods: separator between the list error and the retry button" })} )} @@ -347,7 +347,7 @@ function FileModsTab({ name, tmpl, gs, ns }: { name: string; tmpl?: GameTemplate
{formatBytes(m.size)} - {m.modTime ? " " + intl.formatMessage({ id: "web.routes.tabs.mods.modified_relative", defaultMessage: "· {time}", description: "Mods: suffix after a mod's version showing when it was modified; {time} is how long ago" }, { time: formatRelative(m.modTime, intl) }) : ""} + {m.modTime ? <> {intl.formatMessage({ id: "web.routes.tabs.mods.modified_relative", defaultMessage: "· {time}", description: "Mods: suffix after a mod's version showing when it was modified; {time} is how long ago" }, { time: formatRelative(m.modTime, intl) })} : ""}
@@ -636,7 +636,7 @@ function ModsByIdTab({ {isError && (
- {errMsg(listError, intl)}{" " + intl.formatMessage({ id: "web.routes.tabs.mods.error_retry_separator", defaultMessage: "·", description: "Mods: separator between the list error and the retry button" }) + " "} + {errMsg(listError, intl)} {intl.formatMessage({ id: "web.routes.tabs.mods.error_retry_separator", defaultMessage: "·", description: "Mods: separator between the list error and the retry button" })}
)} @@ -1222,9 +1222,10 @@ function ModCard({ > {list.map((v) => { const f = v.files.find((x) => x.primary) ?? v.files[0]; + const fileSuffix = f ? " " + intl.formatMessage({ id: "web.routes.tabs.mods.version_file_suffix", defaultMessage: "· {filename}", description: "Mods: suffix on a version option naming its file; {filename} is the file name" }, { filename: f.filename }) : ""; return ( ); })} diff --git a/web/src/routes/tabs/Players.tsx b/web/src/routes/tabs/Players.tsx index 3710fd98c..7b8670f18 100644 --- a/web/src/routes/tabs/Players.tsx +++ b/web/src/routes/tabs/Players.tsx @@ -333,7 +333,7 @@ export function PlayersTab({ name, ns }: { name: string; ns?: string }) { {(b.reason || b.source) && (
{b.source && {intl.formatMessage({ id: "web.routes.tabs.players.by_source", defaultMessage: "by {source}", description: "Players: source of a ban shown under the player; {source} is who issued the ban" }, { source: b.source })}} - {b.source && b.reason && {" " + intl.formatMessage({ id: "web.routes.tabs.players.source_reason_separator", defaultMessage: "·", description: "Players: separator between the ban source and the ban reason" }) + " "}} + {b.source && b.reason && {intl.formatMessage({ id: "web.routes.tabs.players.source_reason_separator", defaultMessage: "·", description: "Players: separator between the ban source and the ban reason" })} } {b.reason && {b.reason}}
)} @@ -389,7 +389,7 @@ function ConfirmAction({

{verb} {player} - {playerId !== player && <>{" "}{intl.formatMessage({ id: "web.routes.tabs.players.player_id_in_parens", defaultMessage: "({playerId})", description: "Players: player ID shown in parentheses after the player name; {playerId} is the ID element" }, { playerId: {playerId} })}} + {playerId !== player && <> {intl.formatMessage({ id: "web.routes.tabs.players.player_id_in_parens", defaultMessage: "({playerId})", description: "Players: player ID shown in parentheses after the player name; {playerId} is the ID element" }, { playerId: {playerId} })}} {intl.formatMessage({ id: "web.routes.tabs.players.question_mark", defaultMessage: "?", description: "Players: question mark that ends the confirm sentence" })}

Date: Thu, 22 Oct 2026 02:35:48 +0000 Subject: [PATCH 20/24] test(e2e): anchor the French checks on the servers subtitle (spec 019 T037) The "Create server" button only renders when the user has a placement, so on a cluster without one the French check never found it. The page subtitle is always shown. Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: Claude --- web/e2e/specs/live/i18n.spec.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/web/e2e/specs/live/i18n.spec.ts b/web/e2e/specs/live/i18n.spec.ts index 866639111..fbf509c9a 100644 --- a/web/e2e/specs/live/i18n.spec.ts +++ b/web/e2e/specs/live/i18n.spec.ts @@ -43,8 +43,10 @@ async function pickFrenchFromCompactPicker(page: Page): Promise { await page.getByRole("menu", { name: /^(Language options|Options de langue)$/ }).getByText("Français").click(); } -// watchForEnglish records whether the English "Create server" label ever shows +// watchForEnglish records whether the English servers-page subtitle ever shows // on the page, from before React renders. Install it before the first goto. +// The subtitle is the anchor, not the "Create server" button, because that +// button only renders when the user has a placement to create in. async function watchForEnglish(page: Page): Promise { await page.addInitScript(() => { const w = window as I18nWindow; @@ -52,7 +54,7 @@ async function watchForEnglish(page: Page): Promise { // Observe from the init script itself: module scripts can render before // DOMContentLoaded, so waiting for it could miss a short English flash. const check = () => { - if (/Create server/.test(document.documentElement?.textContent ?? "")) { + if (/Manage your game servers/.test(document.documentElement?.textContent ?? "")) { w.__englishSeen = true; } }; @@ -109,13 +111,13 @@ test.describe("live: i18n", () => { // Client-side navigation to Servers keeps the marker and shows French there. await page.getByRole("link", { name: "Serveurs" }).first().click(); await expect(page).toHaveURL(/\/servers$/); - await expect(page.getByText("Créer un serveur").first()).toBeVisible({ timeout: 15_000 }); + await expect(page.getByText("Gérez vos serveurs de jeu").first()).toBeVisible({ timeout: 15_000 }); expect(await page.evaluate(() => (window as I18nWindow).__i18nMark)).toBe(1); }); test("reload and a new browser context keep French without an English flash", async ({ page, browser }) => { - // Watch the DOM from before React renders. A later "Create server" text is - // the English label leaking into the French page. + // Watch the DOM from before React renders. A later English subtitle is + // English leaking into the French page. await watchForEnglish(page); await page.goto("/servers"); @@ -124,7 +126,7 @@ test.describe("live: i18n", () => { await page.reload({ waitUntil: "domcontentloaded" }); await expect(page.locator("html")).toHaveAttribute("lang", "fr"); - await expect(page.getByText("Créer un serveur").first()).toBeVisible({ timeout: 15_000 }); + await expect(page.getByText("Gérez vos serveurs de jeu").first()).toBeVisible({ timeout: 15_000 }); expect(await page.evaluate(() => (window as I18nWindow).__englishSeen)).toBe(false); // A new browser context has no local storage, so the account choice decides. @@ -135,7 +137,7 @@ test.describe("live: i18n", () => { await p2.goto("/servers"); await p2.waitForLoadState("domcontentloaded"); await expect(p2.locator("html")).toHaveAttribute("lang", "fr", { timeout: 15_000 }); - await expect(p2.getByText("Créer un serveur").first()).toBeVisible({ timeout: 15_000 }); + await expect(p2.getByText("Gérez vos serveurs de jeu").first()).toBeVisible({ timeout: 15_000 }); expect(await p2.evaluate(() => (window as I18nWindow).__englishSeen)).toBe(false); } finally { await ctx2.close(); @@ -217,7 +219,7 @@ test.describe("live: i18n", () => { // Every string falls back to English, and no raw message id is shown. await page.goto("/servers"); await page.waitForLoadState("domcontentloaded"); - await expect(page.getByText("Create server").first()).toBeVisible({ timeout: 15_000 }); + await expect(page.getByText("Manage your game servers").first()).toBeVisible({ timeout: 15_000 }); const visible = await page.locator("body").innerText(); expect(visible.split("\n").some((line) => /^web\.[a-z_.]+$/.test(line.trim()))).toBe(false); } finally { From fc86c80fbde9042946603af120a5dce67f23893b Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Oct 2026 03:12:07 +0000 Subject: [PATCH 21/24] =?UTF-8?q?test(e2e):=20pick=20Fran=C3=A7ais=20by=20?= =?UTF-8?q?its=20menu=20item=20name=20(spec=20019=20T037)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit React Aria names the menu after its trigger, so the "Language options" menu lookup never matched. Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: Claude --- web/e2e/specs/live/i18n.spec.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/web/e2e/specs/live/i18n.spec.ts b/web/e2e/specs/live/i18n.spec.ts index fbf509c9a..bb8455563 100644 --- a/web/e2e/specs/live/i18n.spec.ts +++ b/web/e2e/specs/live/i18n.spec.ts @@ -39,8 +39,9 @@ async function resetAccountLanguage(request: APIRequestContext): Promise { // Français. The trigger is named "Language" in English and "Langue" in French. async function pickFrenchFromCompactPicker(page: Page): Promise { await page.getByRole("button", { name: /^(Langue|Language)$/ }).click(); - // The menu is named in the page's current language, English on a first visit. - await page.getByRole("menu", { name: /^(Language options|Options de langue)$/ }).getByText("Français").click(); + // React Aria labels the menu by its trigger (aria-labelledby wins over + // aria-label), so pick the item by its own name. + await page.getByRole("menuitem", { name: "Français" }).click(); } // watchForEnglish records whether the English servers-page subtitle ever shows From 33b3a7160d8b005abac40ccf557612cc3bd91a0d Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Oct 2026 07:29:47 +0000 Subject: [PATCH 22/24] test(e2e): check the login heading, not the submit button (spec 019 T037) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The submit button reads "Sign in →" / "Se connecter →"; the heading is the plain "Sign in" / "Connexion" the checks expected. Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: Claude --- web/e2e/specs/live/i18n.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/e2e/specs/live/i18n.spec.ts b/web/e2e/specs/live/i18n.spec.ts index bb8455563..01fda63de 100644 --- a/web/e2e/specs/live/i18n.spec.ts +++ b/web/e2e/specs/live/i18n.spec.ts @@ -157,7 +157,7 @@ test.describe("live: i18n", () => { await expect(page.locator("html")).toHaveAttribute("lang", "fr"); await expect(page.getByRole("textbox", { name: /E-mail ou nom d'utilisateur/ })).toBeVisible(); - await expect(page.getByRole("button", { name: /^Connexion$/ })).toBeVisible(); + await expect(page.getByRole("heading", { name: "Connexion", exact: true })).toBeVisible(); try { // The browser choice persists across a reload. @@ -171,7 +171,7 @@ test.describe("live: i18n", () => { try { const freshPage = await fresh.newPage(); await freshPage.goto("/login"); - await expect(freshPage.getByRole("button", { name: /^Sign in$/ })).toBeVisible(); + await expect(freshPage.getByRole("heading", { name: "Sign in", exact: true })).toBeVisible(); await expect(freshPage.locator("html")).toHaveAttribute("lang", "en"); } finally { await fresh.close(); From bc4dc749736c6708b7786ae80662170162c487f0 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Oct 2026 07:55:51 +0000 Subject: [PATCH 23/24] test(e2e): retry the notify test-send while the receiver Service warms up (spec 019 T062) A ready receiver pod can still refuse connections on its Service IP until kube-proxy programs the endpoint (seen on arm64: 502 connection refused). Only that refusal is retried, for up to 30s. Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: Claude --- test/e2e/i18n_notify_language_e2e_test.go | 33 +++++++++++++++-------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/test/e2e/i18n_notify_language_e2e_test.go b/test/e2e/i18n_notify_language_e2e_test.go index fcf3749c5..22a569503 100644 --- a/test/e2e/i18n_notify_language_e2e_test.go +++ b/test/e2e/i18n_notify_language_e2e_test.go @@ -260,17 +260,28 @@ func TestAPI_I18n_NotifyLanguage(t *testing.T) { } for _, s := range sinks { - resp, body, err := admin.Post("/admin/notifications/sinks/"+s.name+"/test", nil) - if resp != nil { - r := resp - defer func() { _ = r.Body.Close() }() - } - if err != nil { - t.Fatalf("POST test %s: %v", s.name, err) - } - if resp.StatusCode != http.StatusOK { - t.Fatalf("POST test %s: status=%d body=%s", s.name, resp.StatusCode, string(body)) - } + // A ready pod can still refuse connections on its Service IP for a + // moment while kube-proxy programs the new endpoint, so only that + // refusal is retried. A refused send reaches no receiver, so it adds + // no log line. + var body []byte + envInstance.Eventually(t, 30*time.Second, func() (bool, string) { + resp, b, err := admin.Post("/admin/notifications/sinks/"+s.name+"/test", nil) + if resp != nil { + _ = resp.Body.Close() + } + if err != nil { + t.Fatalf("POST test %s: %v", s.name, err) + } + if resp.StatusCode == http.StatusBadGateway && strings.Contains(string(b), "connection refused") { + return false, fmt.Sprintf("POST test %s: receiver not reachable yet: %s", s.name, string(b)) + } + if resp.StatusCode != http.StatusOK { + t.Fatalf("POST test %s: status=%d body=%s", s.name, resp.StatusCode, string(b)) + } + body = b + return true, "" + }) var delivered struct { Delivered bool `json:"delivered"` } From b8c6f19a284ab85397bb2d3ac88fe327a2051085 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Oct 2026 08:38:32 +0000 Subject: [PATCH 24/24] test(web): set the 0% fixture language per browser in the live test (spec 019 T037) The API saves only the languages this release ships, so PUT /users/me/language refuses the web-only fixture "zz" ("unknown language: zz") and the account picker kept French. The test now clears the account language and seeds the browser choice "zz", which decides when no account language is set. Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: Claude --- web/e2e/specs/live/i18n.spec.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/web/e2e/specs/live/i18n.spec.ts b/web/e2e/specs/live/i18n.spec.ts index 01fda63de..2ea96255c 100644 --- a/web/e2e/specs/live/i18n.spec.ts +++ b/web/e2e/specs/live/i18n.spec.ts @@ -195,6 +195,18 @@ test.describe("live: i18n", () => { }); test("a 0% fixture language shows English everywhere (SC-008)", async ({ page, request }) => { + // The API saves only the languages this release ships, so it refuses the + // web-only fixture language "zz" as an account language ("unknown language: + // zz"). Use the per-browser choice instead: with no account language, the + // browser choice decides (FR-004). + await resetAccountLanguage(request); + await page.addInitScript(() => { + try { + window.localStorage.setItem("gameplane-language", JSON.stringify({ choice: "zz", applied: "zz" })); + } catch { + // Storage can be blocked; the lang check below then fails with a clear message. + } + }); try { await page.goto("/settings/theme"); await page.waitForLoadState("domcontentloaded"); @@ -213,8 +225,6 @@ test.describe("live: i18n", () => { test.skip(true, "VITE_I18N_FIXTURES is not set for the live web server, so the zz fixture language is absent"); } - // Click the visible name, as above: the radio control intercepts the input. - await page.getByText("Fixture 0%", { exact: true }).click(); await expect(page.locator("html")).toHaveAttribute("lang", "zz", { timeout: 15_000 }); // Every string falls back to English, and no raw message id is shown.