From a3bd7c59ad1165eee76aaa962b11b437a41ef1ef Mon Sep 17 00:00:00 2001 From: "karitham.dev" Date: Wed, 20 May 2026 22:20:20 +0300 Subject: [PATCH] feat(locales): add french locale, fix missing error keys, remove dead keys Adds french locale registration and translation file. Fixes 4 missing error keys that were referenced in code but absent from all locale files. Removes 2 unreferenced keys from `register.validation` that duplicated `registerPasskey.errors`. I *think* that's all that's needed for french locale? Couldn't find anything else required to make it work! --- crates/tranquil-comms/src/locale.rs | 38 +- frontend/src/lib/i18n.ts | 4 +- frontend/src/locales/en.json | 6 +- frontend/src/locales/fr.json | 1179 +++++++++++++++++++++++++++ 4 files changed, 1224 insertions(+), 3 deletions(-) create mode 100644 frontend/src/locales/fr.json diff --git a/crates/tranquil-comms/src/locale.rs b/crates/tranquil-comms/src/locale.rs index 0f64049..d7e342e 100644 --- a/crates/tranquil-comms/src/locale.rs +++ b/crates/tranquil-comms/src/locale.rs @@ -1,5 +1,5 @@ pub const DEFAULT_LOCALE: &str = "en"; -pub const VALID_LOCALES: &[&str] = &["en", "zh", "ja", "ko", "sv", "fi"]; +pub const VALID_LOCALES: &[&str] = &["en", "zh", "ja", "ko", "sv", "fi", "fr"]; pub fn validate_locale(locale: &str) -> &str { if VALID_LOCALES.contains(&locale) { @@ -44,6 +44,7 @@ pub fn get_strings(locale: &str) -> &'static NotificationStrings { "ko" => &STRINGS_KO, "sv" => &STRINGS_SV, "fi" => &STRINGS_FI, + "fr" => &STRINGS_FR, _ => &STRINGS_EN, } } @@ -216,6 +217,34 @@ static STRINGS_FI: NotificationStrings = NotificationStrings { channel_verification_body: "Vahvistuskoodisi on:\n{code}\n\nTai vahvista suoraan:\n{verify_link}", }; +static STRINGS_FR: NotificationStrings = NotificationStrings { + welcome_subject: "Bienvenue sur {hostname}", + welcome_body: "Bienvenue sur {hostname} !\n\nVotre identifiant est : @{handle}\n\nMerci de nous avoir rejoint.", + password_reset_subject: "Réinitialisation du mot de passe - {hostname}", + password_reset_body: "Bonjour @{handle},\n\nVotre code de réinitialisation du mot de passe est : {code}\n\nCe code expirera dans 10 minutes.\n\nSi vous n'avez pas demandé cela, veuillez ignorer ce message.", + email_update_subject: "Confirmer votre nouvelle adresse e-mail - {hostname}", + email_update_body: "Bonjour @{handle},\n\nVotre code de vérification est :\n{code}\n\nCopiez le code ci-dessus et saisissez-le ici :\n{verify_page}\n\nCe code expirera dans 10 minutes.\n\nOu si vous aimez vivre dangereusement :\n{verify_link}\n\nSi vous n'avez pas demandé cela, veuillez ignorer cet e-mail.", + short_token_body: "Bonjour @{handle},\n\nVotre code de vérification est :\n{code}\n\nCe code expirera dans 15 minutes.\n\nSi vous n'avez pas demandé cela, veuillez ignorer cet e-mail.", + account_deletion_subject: "Demande de suppression de compte - {hostname}", + account_deletion_body: "Bonjour @{handle},\n\nVotre code de confirmation de suppression de compte est : {code}\n\nCe code expirera dans 10 minutes.\n\nSi vous n'avez pas demandé cela, sécurisez votre compte immédiatement.", + plc_operation_subject: "{hostname} - Jeton d'opération PLC", + plc_operation_body: "Bonjour @{handle},\n\nVous avez demandé à signer une opération PLC pour votre compte.\n\nVotre jeton de vérification est : {token}\n\nCe jeton expirera dans 10 minutes.\n\nSi vous n'avez pas demandé cela, vous pouvez ignorer ce message en toute sécurité.", + two_factor_code_subject: "Vérification de connexion - {hostname}", + two_factor_code_body: "Bonjour @{handle},\n\nVotre code de vérification de connexion est : {code}\n\nCe code expirera dans 10 minutes.\n\nSi vous n'avez pas demandé cela, sécurisez votre compte immédiatement.", + passkey_recovery_subject: "Récupération de compte - {hostname}", + passkey_recovery_body: "Bonjour @{handle},\n\nVous avez demandé la récupération de votre compte à clé d'accès uniquement.\n\nCliquez sur le lien ci-dessous pour définir un mot de passe temporaire et retrouver l'accès :\n{url}\n\nCe lien expirera dans 1 heure.\n\nSi vous n'avez pas demandé cela, veuillez ignorer ce message. Votre compte reste sécurisé.", + signup_verification_subject: "Vérifier votre compte - {hostname}", + signup_verification_body: "Bienvenue ! Votre code de vérification est :\n{code}\n\nCopiez le code ci-dessus et saisissez-le ici :\n{verify_page}\n\nCe code expirera dans 30 minutes.\n\nOu si vous aimez vivre dangereusement :\n{verify_link}\n\nSi vous n'avez pas créé de compte sur {hostname}, veuillez ignorer ce message.", + legacy_login_subject: "Alerte de sécurité : Connexion classique détectée - {hostname}", + legacy_login_body: "Bonjour @{handle},\n\nUne connexion à votre compte a été détectée via une application classique (comme Bluesky) qui ne prend pas en charge la vérification TOTP.\n\nDétails :\n- Date : {timestamp}\n- Adresse IP : {ip}\n\nVotre protection TOTP a été contournée pour cette connexion. La session dispose de permissions limitées pour les opérations sensibles.\n\nSi ce n'était pas vous :\n1. Changez votre mot de passe immédiatement\n2. Vérifiez vos sessions actives\n3. Envisagez de désactiver les connexions d'applications classiques dans vos paramètres de sécurité\n\nRestez vigilant,\n{hostname}", + migration_verification_subject: "Vérifier votre adresse e-mail - {hostname}", + migration_verification_body: "Bienvenue sur {hostname} !\n\nVotre compte a été migré avec succès. Pour finaliser la configuration, veuillez vérifier votre adresse e-mail.\n\nVotre code de vérification est :\n{code}\n\nCopiez le code ci-dessus et saisissez-le ici :\n{verify_page}\n\nCe code expirera dans 48 heures.\n\nOu si vous aimez vivre dangereusement :\n{verify_link}\n\nSi vous n'avez pas migré votre compte, veuillez ignorer cet e-mail.", + channel_verified_subject: "Canal de notification vérifié - {hostname}", + channel_verified_body: "Bonjour {handle},\n\n{channel} a été vérifié comme canal de notification pour votre compte sur {hostname}.", + channel_verification_subject: "Vérifier votre canal - {hostname}", + channel_verification_body: "Votre code de vérification est :\n{code}\n\nOu vérifiez directement :\n{verify_link}", +}; + pub fn format_message(template: &str, vars: &[(&str, &str)]) -> String { vars.iter() .fold(template.to_string(), |result, (key, value)| { @@ -233,6 +262,9 @@ mod tests { assert_eq!(validate_locale("zh"), "zh"); assert_eq!(validate_locale("ja"), "ja"); assert_eq!(validate_locale("ko"), "ko"); + assert_eq!(validate_locale("sv"), "sv"); + assert_eq!(validate_locale("fi"), "fi"); + assert_eq!(validate_locale("fr"), "fr"); assert_eq!(validate_locale("invalid"), DEFAULT_LOCALE); assert_eq!(validate_locale(""), DEFAULT_LOCALE); } @@ -252,5 +284,9 @@ mod tests { let zh = get_strings("zh"); assert!(zh.welcome_subject.contains("{hostname}")); assert!(zh.welcome_body.contains("欢迎")); + + let fr = get_strings("fr"); + assert!(fr.welcome_subject.contains("{hostname}")); + assert!(fr.welcome_body.contains("Bienvenue")); } } diff --git a/frontend/src/lib/i18n.ts b/frontend/src/lib/i18n.ts index 238f95b..1195bce 100644 --- a/frontend/src/lib/i18n.ts +++ b/frontend/src/lib/i18n.ts @@ -2,7 +2,7 @@ import { _, getLocaleFromNavigator, init, locale, register } from "svelte-i18n"; const LOCALE_STORAGE_KEY = "tranquil-pds-locale"; -const SUPPORTED_LOCALES = ["en", "zh", "ja", "ko", "sv", "fi"] as const; +const SUPPORTED_LOCALES = ["en", "zh", "ja", "ko", "sv", "fi", "fr"] as const; export type SupportedLocale = typeof SUPPORTED_LOCALES[number]; export const localeNames: Record = { @@ -12,6 +12,7 @@ export const localeNames: Record = { ko: "한국어", sv: "Svenska", fi: "Suomi", + fr: "Français", }; register("en", () => import("../locales/en.json")); @@ -20,6 +21,7 @@ register("ja", () => import("../locales/ja.json")); register("ko", () => import("../locales/ko.json")); register("sv", () => import("../locales/sv.json")); register("fi", () => import("../locales/fi.json")); +register("fr", () => import("../locales/fr.json")); function getInitialLocale(): string { const stored = localStorage.getItem(LOCALE_STORAGE_KEY); diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index ca1dd57..d33ff64 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -359,6 +359,7 @@ "passwordRemoved": "Password removed. Your account is now passkey-only.", "failedToRemovePassword": "Failed to remove password", "failedToLoadTotpStatus": "Failed to load TOTP status", + "failedToRegenerateBackupCodes": "Failed to regenerate backup codes", "totpEnabledSuccess": "Two-factor authentication enabled successfully", "totpDisabledSuccess": "Two-factor authentication disabled", "backupCodesCopied": "Backup codes copied to clipboard", @@ -490,6 +491,8 @@ "invitesEnabled": "User invites enabled", "invitesDisabled": "User invites disabled", "userDeleted": "User account deleted", + "failedToDeleteAccount": "Failed to delete account", + "failedToSaveConfig": "Failed to save server configuration", "failedToLoadConfig": "Failed to load server configuration", "signalIntegration": "Signal", "signalLinked": "Linked", @@ -597,7 +600,8 @@ }, "error": { "title": "Authorization failed", - "tryAgain": "Retry" + "tryAgain": "Retry", + "genericError": "An unexpected error occurred" } }, "sso_register": { diff --git a/frontend/src/locales/fr.json b/frontend/src/locales/fr.json new file mode 100644 index 0000000..0ec54ae --- /dev/null +++ b/frontend/src/locales/fr.json @@ -0,0 +1,1179 @@ +{ + "common": { + "loading": "Chargement", + "error": "Erreur", + "save": "Enregistrer", + "cancel": "Annuler", + "back": "Retour", + "done": "Terminé", + "continue": "Continuer", + "refresh": "Actualiser", + "delete": "Supprimer", + "created": "Créé", + "name": "Nom", + "backToDashboard": "← Tableau de bord", + "copied": "Copié", + "copyToClipboard": "Copier", + "verifying": "Vérification", + "saving": "Enregistrement", + "creating": "Création", + "sending": "Envoi", + "checking": "Vérification", + "signIn": "Se connecter", + "verify": "Vérifier", + "revoke": "Révoquer", + "resendCode": "Renvoyer le code", + "backToLogin": "Retour à la connexion", + "backToSettings": "Retour aux paramètres" + }, + "login": { + "title": "Connexion", + "button": "Se connecter", + "redirecting": "Redirection", + "chooseAccount": "Choisir un compte", + "signInToAnother": "Ou se connecter avec un autre compte", + "forgotPassword": "Mot de passe oublié ?", + "lostPasskey": "Clé d'accès perdue ?", + "noAccount": "Pas de compte ?", + "createAccount": "Créer un compte", + "removeAccount": "Supprimer" + }, + "verification": { + "title": "Vérifier le compte", + "subtitle": "Entrez le code envoyé à votre méthode de contact", + "codeLabel": "Code", + "codePlaceholder": "Code à 6 chiffres", + "resent": "Code renvoyé" + }, + "register": { + "title": "Créer un compte", + "redirecting": "Redirection", + "migrateTitle": "Vous avez déjà un compte ?", + "migrateDescription": "Migrez au lieu de créer un nouveau compte", + "migrateLink": "Migrer votre compte", + "handle": "Identifiant", + "handlePlaceholder": "votrenom", + "handleHint": "Votre identifiant complet sera : @{handle}", + "handleTaken": "Cet identifiant est déjà pris", + "handleDotWarning": "Les domaines personnalisés peuvent être configurés dans les Paramètres", + "password": "Mot de passe", + "passwordPlaceholder": "Au moins 8 caractères", + "confirmPassword": "Confirmer le mot de passe", + "confirmPasswordPlaceholder": "Confirmez votre mot de passe", + "identityType": "Type d'identité", + "didPlc": "did:plc", + "didPlcHint": "Identité portable via le répertoire PLC", + "didWeb": "did:web", + "didWebHint": "Identité hébergée sur ce PDS", + "didWebDisabledHint": "Non disponible sur ce PDS", + "didWebBYOD": "did:web (BYOD)", + "didWebBYODHint": "Apportez votre propre domaine", + "didWebWarningTitle": "Compromis de did:web", + "didWebWarning1": "Lien permanent :", + "didWebWarning1Detail": "Votre identité sera {did}", + "didWebWarning2": "Pas de récupération :", + "didWebWarning2Detail": "Pas de clés de rotation comme did:plc", + "didWebWarning3": "Notre engagement :", + "didWebWarning3Detail": "Nous continuerons à héberger votre document DID si vous migrez", + "externalDid": "Votre did:web", + "externalDidPlaceholder": "did:web:votredomaine.com", + "externalDidHint": "Servez le document DID à", + "contactMethod": "Méthode de contact", + "verificationMethod": "Méthode de vérification", + "email": "E-mail", + "emailAddress": "Adresse e-mail", + "emailPlaceholder": "vous@exemple.com", + "discord": "Discord", + "discordUsername": "Nom d'utilisateur Discord", + "discordUsernamePlaceholder": "votrenomutilisateur", + "telegram": "Telegram", + "telegramUsername": "Nom d'utilisateur Telegram", + "telegramUsernamePlaceholder": "@votrenomutilisateur", + "signal": "Signal", + "signalUsername": "Nom d'utilisateur Signal", + "signalUsernamePlaceholder": "nomutilisateur.01", + "notConfigured": "non configuré", + "inviteCode": "Code d'invitation", + "inviteCodePlaceholder": "Entrez votre code d'invitation", + "inviteCodeRequired": "requis", + "alreadyHaveAccount": "Vous avez déjà un compte ?", + "signIn": "Se connecter", + "passkeyAccount": "Clé d'accès", + "passwordAccount": "Mot de passe", + "ssoAccount": "SSO", + "noSsoProviders": "Aucun fournisseur SSO configuré", + "continueWith": "Continuer avec {provider}", + "validation": { + "handleRequired": "L'identifiant est requis", + "handleNoDots": "L'identifiant ne peut pas contenir de points. Vous pourrez configurer un domaine personnalisé après la création de votre compte.", + "passwordRequired": "Le mot de passe est requis", + "passwordLength": "Le mot de passe doit contenir au moins 8 caractères", + "passwordsMismatch": "Les mots de passe ne correspondent pas", + "inviteCodeRequired": "Le code d'invitation est requis", + "externalDidRequired": "Le did:web externe est requis", + "externalDidFormat": "Le DID externe doit commencer par did:web:", + "emailRequired": "L'e-mail est requis pour la vérification par e-mail", + "discordUsernameRequired": "Le nom d'utilisateur Discord est requis pour la vérification Discord" + } + }, + "dashboard": { + "title": "Tableau de bord", + "accountManager": "Gestionnaire de comptes", + "switchAccount": "Changer de compte", + "addAnotherAccount": "Ajouter un autre compte", + "signOut": "Déconnexion @{handle}", + "deactivatedTitle": "Compte désactivé", + "deactivatedMessage": "Votre compte est actuellement désactivé. Cela se produit généralement lors de la migration de compte. Certaines fonctionnalités peuvent être limitées jusqu'à la réactivation de votre compte.", + "handle": "Identifiant", + "did": "DID", + "primaryContact": "Contact principal", + "admin": "Administrateur", + "deactivated": "Désactivé", + "verified": "Vérifié", + "unverified": "Non vérifié", + "navAppPasswords": "Mots de passe d'application", + "navSessions": "Sessions actives", + "navInviteCodes": "Codes d'invitation", + "navSettings": "Général", + "navSecurity": "Sécurité", + "navComms": "Préférences de communication", + "navRepo": "Explorateur de dépôt", + "navDelegation": "Délégation", + "navAdmin": "Panneau d'administration", + "navDidDocument": "Document DID", + "migrated": "Migré", + "migratedTitle": "Compte migré", + "migratedMessage": "Votre compte a été migré vers {pds}. Votre document DID est toujours hébergé ici, et vous pouvez le mettre à jour pour de futures migrations." + }, + "didEditor": { + "preview": "Document DID actuel", + "verificationMethods": "Méthodes de vérification", + "verificationMethodsDesc": "Clés de signature pouvant agir au nom de votre DID. Lorsque vous migrez vers un nouveau PDS, ajoutez leur clé de signature ici.", + "addKey": "Ajouter une clé", + "removeKey": "Supprimer", + "keyId": "ID de clé", + "keyIdPlaceholder": "#atproto", + "publicKey": "Clé publique (Multibase)", + "publicKeyPlaceholder": "zQ3sh...", + "noKeys": "Aucune méthode de vérification configurée. Utilisation de la clé PDS locale.", + "alsoKnownAs": "Aussi connu comme", + "alsoKnownAsDesc": "Identifiants pointant vers votre DID. Mettez à jour lorsque votre identifiant change sur un nouveau PDS.", + "addHandle": "Ajouter un identifiant", + "removeHandle": "Supprimer", + "handle": "Identifiant", + "handlePlaceholder": "at://identifiant.nouveaupds.com", + "noHandles": "Aucun identifiant configuré. Utilisation de l'identifiant local.", + "serviceEndpoint": "Point de terminaison de service", + "serviceEndpointDesc": "Le PDS qui héberge actuellement les données de votre compte. Mettez à jour lors de la migration.", + "currentPds": "URL du PDS actuel", + "save": "Enregistrer les modifications", + "success": "Document DID mis à jour avec succès", + "saveFailed": "Échec de l'enregistrement du document DID", + "loadFailed": "Échec du chargement du document DID", + "invalidMultibase": "La clé publique doit être une chaîne multibase valide commençant par 'z'", + "helpTitle": "Qu'est-ce que c'est ?", + "helpText": "Lorsque vous migrez vers un autre PDS, ce PDS génère de nouvelles clés de signature. Mettez à jour votre document DID ici pour qu'il pointe vers vos nouvelles clés et votre nouvel emplacement. Cela permet les migrations multi-sauts (PDS 1 → PDS 2 → PDS 3)." + }, + "settings": { + "language": "Langue", + "changeEmail": "Changer l'e-mail", + "currentEmail": "Actuel : {email}", + "newEmail": "Nouvel e-mail", + "newEmailPlaceholder": "nouveau@exemple.com", + "emailInUseWarning": "Cet e-mail est déjà utilisé par un autre compte. Vous pouvez quand même l'utiliser, mais la récupération de compte peut nécessiter votre identifiant.", + "changeEmailButton": "Changer l'e-mail", + "requesting": "Demande en cours...", + "verificationCode": "Code de vérification", + "verificationCodePlaceholder": "Entrez le code de vérification", + "confirmEmailChange": "Confirmer le changement d'e-mail", + "emailTokenHint": "Entrez le code de l'e-mail, ou cliquez sur le lien dans l'e-mail depuis n'importe quel appareil.", + "emailUpdateAuthorized": "Changement d'e-mail autorisé ! Cliquez sur confirmer pour terminer.", + "updating": "Mise à jour...", + "changeHandle": "Changer l'identifiant", + "currentHandle": "Actuel : @{handle}", + "pdsHandle": "Identifiant PDS", + "customDomain": "Domaine personnalisé", + "setupInstructions": "Instructions de configuration", + "setupMethodsIntro": "Choisissez l'une de ces méthodes de vérification :", + "dnsMethod": "Option 1 : Enregistrement DNS TXT (Recommandé)", + "dnsMethodDesc": "Ajoutez cet enregistrement TXT à votre domaine :", + "httpMethod": "Option 2 : Fichier HTTP Well-Known", + "httpMethodDesc": "Servez votre DID à cette URL :", + "httpMethodContent": "Le fichier doit contenir uniquement :", + "yourDomain": "Votre domaine", + "yourDomainPlaceholder": "exemple.com", + "verifyAndUpdate": "Vérifier et mettre à jour l'identifiant", + "newHandle": "Nouvel identifiant", + "newHandlePlaceholder": "votreidentifiant", + "changeHandleButton": "Changer l'identifiant", + "exportData": "Exporter les données", + "downloadRepo": "Télécharger le dépôt", + "exporting": "Exportation...", + "deleteAccount": "Supprimer le compte", + "deleteWarning": "Cette action est irréversible. Toutes vos données seront définitivement supprimées.", + "requestDeletion": "Demander la suppression du compte", + "confirmationCode": "Code de confirmation", + "confirmationCodePlaceholder": "Entrez le code de confirmation", + "yourPassword": "Votre mot de passe", + "yourPasswordPlaceholder": "Entrez votre mot de passe", + "permanentlyDelete": "Supprimer définitivement le compte", + "deleting": "Suppression...", + "messages": { + "emailCodeSentToCurrent": "Code de vérification envoyé à votre adresse e-mail actuelle", + "emailUpdated": "E-mail mis à jour avec succès", + "emailUpdateFailed": "Échec de la mise à jour de l'e-mail", + "handleUpdated": "Identifiant mis à jour avec succès", + "handleUpdateFailed": "Échec de la mise à jour de l'identifiant", + "deletionConfirmationSent": "Confirmation de suppression envoyée à votre e-mail", + "deletionRequestFailed": "Échec de la demande de suppression du compte", + "deleteConfirmation": "Êtes-vous absolument sûr de vouloir supprimer votre compte ? Cette action est irréversible.", + "deletionFailed": "Échec de la suppression du compte", + "repoExported": "Dépôt exporté avec succès", + "exportFailed": "Échec de l'exportation" + } + }, + "appPasswords": { + "create": "Créer", + "name": "Nom", + "namePlaceholder": "Nom de l'application (ex. Witchsky)", + "created": "Mot de passe d'application créé", + "createdMessage": "Copiez ce mot de passe maintenant. Vous ne pourrez plus le voir.", + "noPasswords": "Aucun mot de passe d'application", + "deleteConfirm": "Révoquer le mot de passe d'application « {name} » ?", + "deleted": "Mot de passe d'application révoqué", + "loadFailed": "Échec du chargement des mots de passe d'application", + "createFailed": "Échec de la création du mot de passe d'application", + "deleteFailed": "Échec de la révocation du mot de passe d'application", + "saveWarning": "Enregistrez ce mot de passe maintenant — vous ne le verrez plus", + "saveWarningTitle": "Important : enregistrez ce mot de passe d'application !", + "saveWarningMessage": "Ce mot de passe est nécessaire pour se connecter aux applications qui ne supportent pas les clés d'accès ou OAuth. Vous ne le verrez qu'une seule fois.", + "acknowledgeLabel": "J'ai enregistré mon mot de passe d'application dans un endroit sécurisé", + "permissions": "Permissions", + "scopeFull": "Accès complet", + "scopeReadOnly": "Lecture seule", + "scopePostOnly": "Publication uniquement", + "scopeCustom": "Personnalisé", + "byController": "Par contrôleur" + }, + "sessions": { + "noSessions": "Aucune session active trouvée.", + "current": "Actuelle", + "oauth": "OAuth", + "session": "Session", + "signOut": "Déconnexion", + "revoke": "Révoquer", + "revokeAll": "Révoquer toutes les autres sessions", + "revokeCurrentConfirm": "Cela vous déconnectera de cette session. Continuer ?", + "revokeConfirm": "Révoquer cette session ?", + "revokeAllConfirm": "Cela révoquera {count} autre(s) session(s). Continuer ?", + "noOtherSessions": "Aucune autre session à révoquer", + "sessionRevoked": "Session révoquée", + "allSessionsRevoked": "Toutes les autres sessions ont été révoquées", + "failedToLoad": "Échec du chargement des sessions", + "failedToRevoke": "Échec de la révocation de la session", + "failedToRevokeAll": "Échec de la révocation des sessions", + "created": "Créée :", + "expires": "Expire :", + "daysAgo": "Il y a {count} jour(s)", + "hoursAgo": "Il y a {count} heure(s)", + "minutesAgo": "Il y a {count} minute(s)", + "justNow": "À l'instant" + }, + "inviteCodes": { + "loadFailed": "Échec du chargement des codes d'invitation", + "createFailed": "Échec de la création du code d'invitation", + "createNew": "Créer un nouveau code d'invitation", + "yourCodes": "Vos codes d'invitation", + "noCodes": "Aucun code d'invitation", + "available": "Disponible", + "used": "Utilisé par @{handle}", + "spent": "Utilisé", + "disabled": "Désactivé", + "created": "Code d'invitation créé", + "copy": "Copier", + "disable": "Désactiver", + "disableConfirm": "Désactiver le code d'invitation {code} ?", + "disableSuccess": "Code d'invitation désactivé", + "disableFailed": "Échec de la désactivation du code d'invitation", + "createdOn": "Créé le {date}" + }, + "security": { + "passkeys": "Clés d'accès", + "addPasskey": "Ajouter une clé d'accès", + "adding": "Ajout...", + "noPasskeys": "Aucune clé d'accès enregistrée", + "passkeyName": "Nom de la clé d'accès", + "passkeyNamePlaceholder": "ex. yubikey, téléphone", + "rename": "Renommer", + "deletePasskey": "Supprimer", + "deletePasskeyConfirm": "Supprimer la clé d'accès « {name} » ? Vous ne pourrez plus l'utiliser pour vous connecter.", + "totp": "Application d'authentification (TOTP)", + "totpEnabled": "TOTP est activé", + "totpDisabled": "TOTP n'est pas activé", + "enableTotp": "Activer TOTP", + "disableTotp": "Désactiver TOTP", + "disabling": "Désactivation...", + "totpSetupInstructions": "Scannez ce code QR avec votre application d'authentification, puis entrez le code à 6 chiffres pour vérifier.", + "totpCode": "Code TOTP", + "totpCodePlaceholder": "6 chiffres", + "verifyAndEnable": "Vérifier et activer", + "backupCodes": "Codes de secours", + "backupCodesDescription": "Utilisez ces codes pour vous connecter si vous perdez l'accès à votre application d'authentification. Chaque code ne peut être utilisé qu'une seule fois.", + "regenerateBackupCodes": "Régénérer les codes de secours", + "regenerating": "Régénération...", + "regenerateConfirm": "Régénérer les codes de secours ? Vos codes actuels ne fonctionneront plus.", + "legacyLogin": "Connexion classique", + "legacyLoginDescription": "Autoriser la connexion avec identifiant/mot de passe directement (mode classique). Lorsque désactivé, vous devez utiliser OAuth avec MFA.", + "legacyLoginOn": "La connexion classique est activée", + "legacyLoginOff": "La connexion classique est désactivée", + "enableLegacyLogin": "Activer la connexion classique", + "disableLegacyLogin": "Désactiver la connexion classique", + "legacyLoginWarning": "Attention : Activer la connexion classique contourne la MFA pour les connexions directes par mot de passe. N'activez que si nécessaire pour la compatibilité des applications.", + "legacyAppsTitle": "Que sont les applications classiques ?", + "legacyAppsDescription": "Certaines applications (comme l'application officielle Bluesky) utilisent une authentification ancienne qui ne nécessite que votre mot de passe. Lorsque la MFA est activée, ces applications contourne votre second facteur. Désactiver la connexion classique force toutes les applications à utiliser OAuth, qui applique correctement la MFA.", + "password": "Mot de passe", + "passwordStatus": "Vous avez un mot de passe défini", + "noPassword": "Aucun mot de passe défini (compte à clé d'accès uniquement)", + "setPassword": "Définir un mot de passe", + "removePassword": "Supprimer le mot de passe", + "removing": "Suppression...", + "next": "Suivant : Vérifier le code", + "copyToClipboard": "Copier dans le presse-papiers", + "savedMyCodes": "J'ai enregistré mes codes", + "cantScan": "Impossible de scanner ? Entrez manuellement", + "unnamedPasskey": "Clé d'accès sans nom", + "added": "Ajoutée", + "lastUsed": "Dernière utilisation", + "disableTotpWarning": "Cela rendra votre compte moins sécurisé.", + "removePasswordWarning": "Cela rendra votre compte accessible uniquement par clé d'accès. Vous ne pourrez vous connecter qu'avec vos clés d'accès enregistrées. Si vous perdez l'accès à toutes vos clés d'accès, vous pouvez récupérer votre compte via votre canal de notification.", + "trustedDevices": "Appareils de confiance", + "trustedDevicesDescription": "Appareils pouvant ignorer l'authentification à deux facteurs lors de la connexion. La confiance est accordée pour 30 jours et se prolonge automatiquement lors de l'utilisation de l'appareil.", + "appCompatibility": "Compatibilité des applications", + "enterPassword": "Entrez votre mot de passe", + "sessionExpired": "Session expirée. Veuillez vous reconnecter.", + "legacyLoginEnabled": "Connexion d'application classique activée", + "legacyLoginDisabled": "Connexion d'application classique désactivée — seules les applications OAuth peuvent se connecter", + "failedToUpdatePreference": "Échec de la mise à jour de la préférence", + "passwordRemoved": "Mot de passe supprimé. Votre compte est maintenant accessible uniquement par clé d'accès.", + "failedToRemovePassword": "Échec de la suppression du mot de passe", + "failedToLoadTotpStatus": "Échec du chargement du statut TOTP", + "failedToRegenerateBackupCodes": "Échec de la régénération des codes de secours", + "totpEnabledSuccess": "Authentification à deux facteurs activée avec succès", + "totpDisabledSuccess": "Authentification à deux facteurs désactivée", + "backupCodesCopied": "Codes de secours copiés dans le presse-papiers", + "failedToLoadPasskeys": "Échec du chargement des clés d'accès", + "passkeysNotSupported": "Les clés d'accès ne sont pas supportées dans ce navigateur", + "passkeyCreationCancelled": "La création de la clé d'accès a été annulée", + "passkeyAddedSuccess": "Clé d'accès ajoutée avec succès", + "passkeyDeleted": "Clé d'accès supprimée", + "passkeyRenamed": "Clé d'accès renommée", + "changePassword": "Changer le mot de passe", + "currentPassword": "Mot de passe actuel", + "currentPasswordPlaceholder": "Entrez le mot de passe actuel", + "newPassword": "Nouveau mot de passe", + "newPasswordPlaceholder": "Entrez le nouveau mot de passe", + "confirmPassword": "Confirmer le nouveau mot de passe", + "confirmPasswordPlaceholder": "Confirmez le nouveau mot de passe", + "passwordsDoNotMatch": "Les mots de passe ne correspondent pas", + "passwordTooShort": "Le mot de passe doit contenir au moins 8 caractères", + "passwordChanged": "Mot de passe modifié avec succès", + "failedToChangePassword": "Échec du changement de mot de passe", + "changing": "Modification...", + "setting": "Définition...", + "passwordSet": "Mot de passe défini avec succès", + "failedToSetPassword": "Échec de la définition du mot de passe", + "failedToDisableTotp": "Échec de la désactivation de TOTP" + }, + "comms": { + "failedToLoad": "Échec du chargement des préférences", + "failedToSave": "Échec de l'enregistrement des préférences", + "failedToVerify": "Échec de la vérification", + "failedToLoadHistory": "Échec du chargement de l'historique des messages", + "preferredChannel": "Canal préféré", + "channelConfiguration": "Configuration des canaux", + "emailVia": "Recevoir les messages par e-mail", + "discordVia": "Recevoir les messages par DM Discord", + "telegramVia": "Recevoir les messages par Telegram", + "signalVia": "Recevoir les messages par Signal", + "configureToEnable": "Configurez ci-dessous pour activer", + "notConfiguredOnServer": "Non configuré sur ce serveur", + "primary": "Principal", + "verified": "Vérifié", + "notVerified": "Non vérifié", + "verifyButton": "Vérifier", + "verifyCodePlaceholder": "Entrez le code de vérification", + "submit": "Soumettre", + "savePreferences": "Enregistrer les préférences", + "preferencesSaved": "Préférences de communication enregistrées", + "verifiedSuccess": "{channel} vérifié avec succès", + "messageHistory": "Historique des messages", + "noMessages": "Aucun message trouvé.", + "telegramStartBot": "Ou envoyez /start {handle} à @{botUsername} manuellement", + "telegramOpenLink": "Ouvrir Telegram pour vérifier", + "discordStartBot": "Envoyez un DM à @{botUsername} sur Discord avec /start {handle}", + "discordOpenLink": "Ouvrir Discord pour vérifier" + }, + "repoExplorer": { + "collections": "Collections", + "noRecords": "Aucun enregistrement dans cette collection", + "uri": "URI", + "cid": "CID", + "deleteConfirm": "Supprimer l'enregistrement {rkey} ? Cette action est irréversible.", + "unknownError": "Une erreur inconnue est survenue", + "invalidJson": "JSON invalide", + "collectionRequired": "La collection est requise", + "recordCreated": "Enregistrement créé : {uri}", + "recordUpdated": "Enregistrement mis à jour", + "recordDeleted": "Enregistrement supprimé", + "newRecord": "Nouvel enregistrement", + "createRecord": "Créer un enregistrement", + "filterCollections": "Filtrer les collections...", + "filterRecords": "Filtrer les enregistrements...", + "noCollectionsYet": "Aucune collection. Créez votre premier enregistrement pour commencer.", + "recordJson": "JSON de l'enregistrement", + "updateRecord": "Mettre à jour l'enregistrement", + "collectionNsid": "Collection (NSID)", + "recordKeyOptional": "Clé d'enregistrement (optionnel)", + "autoGenerated": "Auto-généré si vide (TID)", + "autoGeneratedHint": "Laissez vide pour auto-générer une clé basée sur TID", + "demoPostText": "Bonjour depuis mon PDS ! C'est mon premier message.", + "demoDisplayName": "Votre nom d'affichage", + "demoBio": "Une courte biographie vous concernant." + }, + "admin": { + "failedToLoadStats": "Échec du chargement des statistiques du serveur", + "failedToLoadUsers": "Échec du chargement des utilisateurs", + "searchToSeeUsers": "Recherchez pour voir les utilisateurs", + "search": "Rechercher", + "serverConfig": "Configuration du serveur", + "serverName": "Nom du serveur", + "serverNamePlaceholder": "Mon PDS", + "serverNameHelp": "Affiché dans l'onglet du navigateur et d'autres endroits", + "serverLogo": "Logo du serveur", + "logoPreview": "Aperçu du logo", + "removeLogo": "Supprimer", + "logoHelp": "Utilisé comme favicon et affiché dans la barre de navigation", + "themeColors": "Couleurs du thème", + "themeColorsHint": "Laissez vide pour utiliser les couleurs par défaut.", + "primaryLight": "Primaire (Mode clair)", + "primaryDark": "Primaire (Mode sombre)", + "secondaryLight": "Secondaire (Mode clair)", + "secondaryDark": "Secondaire (Mode sombre)", + "configSaved": "Configuration du serveur enregistrée", + "saveConfig": "Enregistrer la configuration", + "serverStats": "Statistiques du serveur", + "users": "Utilisateurs", + "repos": "Dépôts", + "records": "Enregistrements", + "blobStorage": "Stockage des blobs", + "refreshStats": "Actualiser les statistiques", + "userManagement": "Gestion des utilisateurs", + "searchPlaceholder": "Rechercher par identifiant (optionnel)", + "handle": "Identifiant", + "email": "E-mail", + "status": "Statut", + "created": "Créé", + "loadMore": "Charger plus", + "disabled": "Désactivé", + "userDetails": "Détails de l'utilisateur", + "did": "DID", + "invites": "Invitations", + "enabled": "Activé", + "enableInvites": "Activer les invitations", + "disableInvites": "Désactiver les invitations", + "deleteAccount": "Supprimer le compte", + "deleteConfirm": "Supprimer le compte @{handle} ? Cette action est irréversible.", + "verified": "Vérifié", + "unverified": "Non vérifié", + "deactivated": "Désactivé", + "invitesEnabled": "Invitations utilisateur activées", + "invitesDisabled": "Invitations utilisateur désactivées", + "userDeleted": "Compte utilisateur supprimé", + "failedToDeleteAccount": "Échec de la suppression du compte", + "failedToSaveConfig": "Échec de l'enregistrement de la configuration du serveur", + "failedToLoadConfig": "Échec du chargement de la configuration du serveur", + "signalIntegration": "Signal", + "signalLinked": "Lié", + "signalNotLinked": "Non lié", + "signalLinkDevice": "Lier l'appareil", + "signalUnlinkDevice": "Dissocier", + "signalLinking": "Scannez avec Signal", + "signalLinkSuccess": "Appareil Signal lié", + "signalUnlinkSuccess": "Appareil Signal dissocié", + "signalUnlinkConfirm": "Dissocier l'appareil Signal ? Les notifications s'arrêteront.", + "signalLinkFailed": "Échec de la liaison de l'appareil Signal", + "signalLinkTimedOut": "Délai de liaison Signal expiré", + "signalUnlinkFailed": "Échec de la dissociation de l'appareil Signal", + "signalFailedToLoad": "Échec du chargement du statut Signal" + }, + "oauth": { + "login": { + "title": "Se connecter", + "subtitle": "Connexion à", + "signingIn": "Connexion en cours", + "authenticating": "Authentification", + "checkingPasskey": "Vérification", + "signInWithPasskey": "Clé d'accès", + "passkeyNotSetUp": "Aucune clé d'accès", + "orUsePassword": "ou", + "password": "Mot de passe", + "rememberDevice": "Se souvenir de cet appareil", + "passkeyHintChecking": "Vérification", + "passkeyHintAvailable": "Utiliser la clé d'accès", + "passkeyHintNotAvailable": "Aucune clé d'accès enregistrée", + "passwordPlaceholder": "Mot de passe", + "usePasskey": "Utiliser la clé d'accès", + "passkeyNotAllowed": "Aucune clé d'accès disponible pour ce site", + "orUseCredentials": "ou", + "verificationResent": "Code de vérification envoyé" + }, + "sso": { + "linkedAccounts": "Comptes liés", + "noLinkedAccounts": "Aucun compte lié", + "unlink": "Dissocier", + "unlinkConfirm": "Êtes-vous sûr de vouloir dissocier ce compte ?", + "unlinked": "{provider} dissocié", + "linkedAt": "Lié", + "linkNewAccount": "Lier un compte", + "linked": "Lié", + "linkSuccess": "Compte lié avec succès", + "linkFailed": "Échec de la liaison du compte", + "unlinkFailed": "Échec de la dissociation du compte" + }, + "consent": { + "title": "Autoriser l'application", + "appWantsAccess": "{app} veut accéder à votre compte", + "authorize": "Autoriser", + "deny": "Refuser", + "authorizing": "Autorisation...", + "signingInAs": "Connexion en tant que :", + "permissionsRequested": "Permissions demandées", + "required": "Requis", + "rememberChoiceLabel": "Se souvenir de mon choix pour cette application", + "scopes": { + "atproto": { + "name": "Accès AT Protocol", + "description": "Vérification d'identité et établissement de session" + }, + "atprotoWithGranular": { + "name": "Accès AT Protocol", + "description": "Base AT Protocol (permissions déterminées par les options sélectionnées ci-dessous)" + } + }, + "unexpectedState": { + "title": "État inattendu", + "description": "La page de consentement est dans un état inattendu. Veuillez vérifier la console du navigateur pour les erreurs.", + "reload": "Recharger la page" + } + }, + "accounts": { + "title": "Choisir un compte", + "useAnother": "Utiliser un autre compte" + }, + "register": { + "title": "Créer un compte", + "subtitle": "pour", + "subtitleGeneric": "Créer un compte", + "haveAccount": "Vous avez un compte ? Se connecter" + }, + "twoFactorCode": { + "title": "Vérification", + "subtitle": "Code envoyé à {channel}", + "codeLabel": "Code", + "codePlaceholder": "Code à 6 chiffres", + "errors": { + "missingRequestUri": "URI de requête manquante", + "verificationFailed": "Échec de la vérification", + "connectionFailed": "Échec de la connexion", + "unexpectedResponse": "Réponse inattendue" + } + }, + "totp": { + "title": "Code d'authentification", + "codePlaceholder": "Code à 6 chiffres", + "backupCodePlaceholder": "Code de secours", + "trustDevice": "Faire confiance à cet appareil pour 30 jours", + "hintBackupCode": "Code de secours", + "hintTotpCode": "Code d'authentification" + }, + "error": { + "title": "Échec de l'autorisation", + "tryAgain": "Réessayer", + "genericError": "Une erreur inattendue est survenue" + } + }, + "sso_register": { + "title": "Terminer l'inscription", + "subtitle": "Création du compte avec {provider}", + "handle_label": "Choisissez votre identifiant", + "handle_available": "Disponible", + "handle_taken": "Déjà pris", + "submit": "Créer le compte", + "error_expired": "Session d'inscription expirée. Veuillez réessayer.", + "error_handle_required": "Veuillez choisir un identifiant", + "emailVerifiedByProvider": "Cet e-mail est vérifié par {provider}. Aucune vérification supplémentaire nécessaire.", + "emailChangedNeedsVerification": "Si vous utilisez un e-mail différent, vous devrez le vérifier.", + "infoAfterTitle": "Après la création de votre compte", + "infoAddPassword": "Ajouter un mot de passe pour la connexion classique", + "infoAddPasskey": "Configurer une clé d'accès pour la connexion sans mot de passe", + "infoLinkProviders": "Lier des fournisseurs SSO supplémentaires", + "infoChangeHandle": "Changer votre identifiant ou utiliser un domaine personnalisé", + "tryAgain": "Réessayer" + }, + "verify": { + "title": "Vérifiez votre compte", + "subtitle": "Nous avons envoyé un code de vérification à votre {channel}. Entrez-le ci-dessous pour terminer l'inscription.", + "tokenSubtitle": "Entrez le code de vérification et l'identifiant auquel il a été envoyé.", + "tokenTitle": "Vérifier", + "codePlaceholder": "Collez le code de vérification", + "codeLabel": "Code de vérification", + "codeHelp": "Copiez le code entier depuis votre message", + "pleaseWait": "Veuillez patienter...", + "codeResent": "Code de vérification renvoyé !", + "codeResentDetail": "Code de vérification envoyé ! Vérifiez votre boîte de réception.", + "verifyingAccount": "Vérification du compte : @{handle}", + "startOver": "Recommencer avec un autre compte", + "noPending": "Aucune vérification en attente trouvée.", + "noPendingInfo": "Si vous avez récemment créé un compte et devez le vérifier, vous devrez peut-être créer un nouveau compte. Si vous avez déjà vérifié votre compte, vous pouvez vous connecter.", + "createAccount": "Créer un compte", + "signIn": "Se connecter", + "verified": "Vérifié !", + "channelVerified": "Votre {channel} a été vérifié avec succès.", + "canNowSignIn": "Vous pouvez maintenant vous connecter à votre compte.", + "migrationContinue": "Vous pouvez fermer cet onglet et continuer votre migration dans la fenêtre d'origine.", + "continue": "Continuer", + "identifierLabel": "E-mail ou identifiant", + "identifierPlaceholder": "vous@exemple.com", + "identifierHelp": "L'adresse e-mail ou l'identifiant auquel le code a été envoyé", + "emailUpdateTitle": "Mettre à jour l'adresse e-mail", + "emailUpdateSubtitle": "Entrez votre nouvelle adresse e-mail et le code de vérification envoyé à votre e-mail actuel.", + "emailUpdateRequiresAuth": "Vous devez être connecté pour mettre à jour votre adresse e-mail.", + "emailUpdateFailed": "Échec de la mise à jour de l'adresse e-mail", + "emailUpdateCodeHelp": "Le code a été envoyé à votre adresse e-mail actuelle", + "newEmailLabel": "Nouvelle adresse e-mail", + "newEmailPlaceholder": "nouveau@exemple.com", + "updateEmail": "Mettre à jour l'e-mail", + "updating": "Mise à jour...", + "emailUpdated": "Votre e-mail a été mis à jour avec succès.", + "emailUpdatedInfo": "Vous devrez peut-être vérifier votre nouvelle adresse e-mail.", + "emailAuthorizeSuccess": "Votre mise à jour d'e-mail a été autorisée.", + "emailAuthorizeInfo": "Vous pouvez maintenant terminer le changement sur votre appareil d'origine." + }, + "resetPassword": { + "title": "Réinitialiser le mot de passe", + "forgotTitle": "Mot de passe oublié", + "subtitle": "Entrez le code que vous avez reçu et choisissez un nouveau mot de passe.", + "forgotSubtitle": "Entrez votre identifiant ou e-mail et nous vous enverrons un code pour réinitialiser votre mot de passe.", + "handleOrEmail": "Identifiant ou e-mail", + "emailPlaceholder": "identifiant ou vous@exemple.com", + "sendCode": "Envoyer le code de réinitialisation", + "sending": "Envoi...", + "codeSent": "Code de réinitialisation envoyé ! Vérifiez votre canal de notification préféré.", + "code": "Code de réinitialisation", + "codePlaceholder": "Entrez le code de réinitialisation", + "newPassword": "Nouveau mot de passe", + "newPasswordPlaceholder": "Au moins 8 caractères", + "confirmPassword": "Confirmer le mot de passe", + "confirmPasswordPlaceholder": "Confirmez le nouveau mot de passe", + "resetButton": "Réinitialiser le mot de passe", + "resetting": "Réinitialisation...", + "success": "Mot de passe réinitialisé avec succès !", + "requestNewCode": "Demander un nouveau code", + "passwordsMismatch": "Les mots de passe ne correspondent pas", + "passwordLength": "Le mot de passe doit contenir au moins 8 caractères" + }, + "recoverPasskey": { + "title": "Récupérer votre compte", + "invalidLinkTitle": "Lien de récupération invalide", + "invalidLinkMessage": "Ce lien de récupération est invalide ou a été corrompu. Veuillez demander un nouvel e-mail de récupération.", + "goToLogin": "Aller à la connexion", + "successTitle": "Mot de passe défini !", + "successMessage": "Votre mot de passe temporaire a été défini. Vous pouvez maintenant vous connecter avec ce mot de passe.", + "successNextSteps": "Après vous être connecté, nous vous recommandons d'ajouter une nouvelle clé d'accès dans vos paramètres de sécurité pour restaurer l'authentification par clé d'accès uniquement.", + "signIn": "Se connecter", + "subtitle": "Définissez un mot de passe temporaire pour retrouver l'accès à votre compte à clé d'accès uniquement.", + "newPassword": "Nouveau mot de passe", + "newPasswordPlaceholder": "Au moins 8 caractères", + "confirmPassword": "Confirmer le mot de passe", + "confirmPasswordPlaceholder": "Confirmez votre mot de passe", + "whatHappensNext": "Que se passe-t-il ensuite ?", + "whatHappensNextDetail": "Après avoir défini ce mot de passe, vous pouvez vous connecter et ajouter une nouvelle clé d'accès dans vos paramètres de sécurité. Une fois que vous avez une nouvelle clé d'accès, vous pouvez facultativement supprimer le mot de passe temporaire.", + "setPassword": "Définir le mot de passe", + "settingPassword": "Définition du mot de passe...", + "validation": { + "passwordRequired": "Le nouveau mot de passe est requis", + "passwordLength": "Le mot de passe doit contenir au moins 8 caractères", + "passwordsMismatch": "Les mots de passe ne correspondent pas" + }, + "errors": { + "invalidLink": "Lien de récupération invalide. Veuillez en demander un nouveau.", + "expired": "Ce lien de récupération a expiré. Veuillez en demander un nouveau." + } + }, + "requestPasskeyRecovery": { + "title": "Récupérer le compte à clé d'accès", + "subtitle": "Perdu l'accès à votre clé d'accès ? Entrez votre identifiant ou e-mail et nous vous enverrons un lien de récupération.", + "successTitle": "Lien de récupération envoyé", + "successMessage": "Si votre compte existe et est un compte à clé d'accès uniquement, vous recevrez un lien de récupération sur votre canal de notification préféré.", + "successInfo": "Le lien expirera dans 1 heure. Vérifiez votre e-mail, Discord, Telegram ou Signal selon les paramètres de votre compte.", + "handleOrEmail": "Identifiant ou e-mail", + "emailPlaceholder": "identifiant ou vous@exemple.com", + "howItWorks": "Comment ça marche", + "howItWorksDetail": "Nous enverrons un lien sécurisé à votre canal de notification enregistré. Cliquez sur le lien pour définir un mot de passe temporaire. Vous pourrez ensuite vous connecter et ajouter une nouvelle clé d'accès.", + "sendRecoveryLink": "Envoyer le lien de récupération", + "sending": "Envoi..." + }, + "registerPasskey": { + "identityType": "Type d'identité", + "identityTypeHint": "Comment votre identité décentralisée est gérée", + "didPlcRecommended": "did:plc (Recommandé)", + "didPlcHint": "Identité portable via le répertoire PLC", + "didWeb": "did:web", + "didWebHint": "Identité hébergée sur ce PDS", + "didWebDisabledHint": "Non disponible sur ce PDS", + "didWebBYOD": "did:web (BYOD)", + "didWebBYODHint": "Apportez votre propre domaine", + "didWebWarningTitle": "Compromis de did:web", + "didWebWarning1": "Lien permanent :", + "didWebWarning1Detail": "Votre identité sera {did}", + "didWebWarning2": "Pas de récupération :", + "didWebWarning2Detail": "Pas de clés de rotation comme did:plc", + "didWebWarning3": "Notre engagement :", + "didWebWarning3Detail": "Nous continuerons à héberger votre document DID si vous migrez", + "didWebWarning4": "Recommandation :", + "didWebWarning4Detail": "Choisissez did:plc sauf si vous avez une raison spécifique de préférer did:web.", + "externalDid": "Votre did:web", + "externalDidPlaceholder": "did:web:votredomaine.com", + "externalDidHint": "Servez le document DID à", + "passkeyName": "Nom de la clé d'accès", + "passkeyNamePlaceholder": "ex. yubikey, téléphone", + "passkeyNameHint": "Identifiant optionnel", + "setupPasskey": "Créer une clé d'accès", + "passkeyDescription": "Enregistrer une clé d'accès pour ce compte", + "createPasskey": "Créer une clé d'accès", + "creatingAccount": "Création du compte", + "activatingAccount": "Activation", + "errors": { + "handleRequired": "L'identifiant est requis", + "handleNoDots": "L'identifiant ne peut pas contenir de points. Vous pourrez configurer un domaine personnalisé après la création de votre compte.", + "inviteRequired": "Le code d'invitation est requis", + "externalDidRequired": "Le did:web externe est requis", + "externalDidFormat": "Le DID externe doit commencer par did:web:", + "emailRequired": "L'e-mail est requis pour la vérification par e-mail", + "discordRequired": "Le nom d'utilisateur Discord est requis pour la vérification Discord", + "telegramRequired": "Le nom d'utilisateur Telegram est requis pour la vérification Telegram", + "signalRequired": "Le nom d'utilisateur Signal est requis", + "passkeysNotSupported": "Clés d'accès non supportées dans ce navigateur", + "passkeyCancelled": "Annulé", + "passkeyFailed": "Échec de l'enregistrement de la clé d'accès" + } + }, + "trustedDevices": { + "noDevices": "Aucun appareil de confiance", + "lastSeen": "Dernière activité :", + "trustedSince": "Approuvé depuis :", + "expired": "Expiré", + "tomorrow": "Demain", + "inDays": "Dans {days} jours", + "revoke": "Révoquer", + "revokeConfirm": "Révoquer la confiance pour cet appareil ?", + "deviceRevoked": "Confiance révoquée", + "deviceRenamed": "Appareil renommé", + "deviceNamePlaceholder": "Nom de l'appareil", + "unknownDevice": "Appareil inconnu", + "noDevicesHint": "Lorsque vous vous connectez avec l'authentification à deux facteurs activée, vous pouvez choisir de faire confiance à l'appareil pendant 30 jours." + }, + "reauth": { + "title": "Se réauthentifier", + "password": "Mot de passe", + "totp": "TOTP", + "passkey": "Clé d'accès", + "authenticatorCode": "Code d'authentification", + "usePasskey": "Clé d'accès", + "authenticating": "Authentification", + "cancel": "Annuler" + }, + "delegation": { + "controllers": "Contrôleurs", + "controllersDesc": "Comptes pouvant agir en votre nom", + "noControllers": "Aucun contrôleur", + "inactive": "Inactif", + "did": "DID", + "granted": "Accordé", + "remove": "Supprimer", + "removeConfirm": "Êtes-vous sûr de vouloir supprimer ce contrôleur ?", + "cannotAddControllers": "Vous ne pouvez pas ajouter de contrôleurs car ce compte contrôle d'autres comptes. Un compte peut soit avoir des contrôleurs, soit contrôler d'autres comptes, mais pas les deux.", + "addController": "Ajouter un contrôleur", + "controllerDid": "DID du contrôleur", + "controllerIdentifier": "Identifiant ou DID du contrôleur", + "accessLevel": "Niveau d'accès", + "adding": "Ajout...", + "addControllerButton": "+ Ajouter un contrôleur", + "addControllerWarningTitle": "Important : Cela changera votre façon de vous connecter", + "addControllerWarningText": "Ajouter un contrôleur signifie que seul le compte contrôleur pourra se connecter à ce compte via OAuth. Vous ne pourrez plus vous connecter directement avec vos propres identifiants via des applications tierces ou l'interface web.", + "addControllerWarningBullet1": "Le contrôleur pourra agir en votre nom avec les permissions que vous accordez", + "addControllerWarningBullet2": "Vous devrez vous connecter en tant que contrôleur d'abord, puis basculer vers ce compte", + "addControllerWarningBullet3": "Vous pouvez supprimer le contrôleur ultérieurement pour retrouver l'accès direct", + "addControllerConfirm": "Je comprends que je ne pourrai plus me connecter directement", + "controllerAdded": "Contrôleur ajouté avec succès", + "controllerRemoved": "Contrôleur supprimé avec succès", + "controlledAccounts": "Comptes contrôlés", + "controlledAccountsDesc": "Comptes que vous pouvez gérer", + "controlledAccountsLocalOnly": "Seuls les comptes sur ce PDS sont affichés. Un autre PDS peut vous avoir accordé l'accès contrôleur séparément", + "noControlledAccounts": "Vous n'avez accès à aucun compte délégué.", + "actAs": "Agir comme", + "cannotControlAccounts": "Vous ne pouvez pas contrôler d'autres comptes car ce compte a des contrôleurs. Un compte peut soit avoir des contrôleurs, soit contrôler d'autres comptes, mais pas les deux.", + "createDelegatedAccount": "Créer un compte délégué", + "handle": "Identifiant", + "emailOptional": "E-mail (optionnel)", + "yourAccessLevel": "Votre niveau d'accès", + "createAccount": "Créer le compte", + "createDelegatedAccountButton": "+ Créer un compte délégué", + "accountCreated": "Compte délégué créé : {handle}", + "auditLog": "Journal d'audit", + "auditLogDesc": "Voir toute l'activité de délégation", + "scopeOwner": "Propriétaire", + "scopeViewer": "Lecteur", + "scopeCustom": "Personnalisé", + "actor": "Acteur", + "details": "Détails", + "previous": "Précédent", + "next": "Suivant", + "actionGrantCreated": "Autorisation créée", + "actionGrantRevoked": "Autorisation révoquée", + "actionScopesModified": "Périmètres modifiés", + "actionTokenIssued": "Jeton émis", + "actionRepoWrite": "Écriture dans le dépôt", + "actionBlobUpload": "Téléversement de blob", + "actionAccountAction": "Action sur le compte", + "noAuditEntries": "Aucune entrée d'audit", + "target": "Cible", + "failedToLoadAudit": "Échec du chargement du journal d'audit" + }, + "actAs": { + "noAccountSpecified": "Aucun DID de compte spécifié", + "noAccess": "Vous n'avez pas accès à ce compte", + "failedToInitiate": "Échec de l'initialisation du flux OAuth", + "invalidResponse": "Réponse OAuth invalide", + "preparing": "Préparation du changement de compte...", + "title": "Agir comme", + "backToControllers": "Retour aux contrôleurs" + }, + "oauthDelegation": { + "loading": "Chargement", + "title": "Compte délégué", + "isDelegated": "{handle} est délégué", + "enterControllerHandle": "Se connecter en tant que contrôleur", + "controllerHandle": "Identifiant du contrôleur", + "handlePlaceholder": "identifiant.exemple.com", + "checking": "Vérification", + "controllerNotFound": "Contrôleur non trouvé", + "missingParams": "Paramètres de délégation manquants", + "missingInfo": "Informations manquantes", + "passkeyCancelled": "Annulé", + "passkeyFailed": "Échec de la clé d'accès", + "failedPasskeyStart": "Échec du démarrage de la clé d'accès", + "authFailed": "Échec de l'authentification", + "unexpectedResponse": "Réponse inattendue", + "signInAsController": "Connexion contrôleur", + "authenticateAs": "Se connecter en tant que {controller} pour {delegated}", + "useDifferentController": "Autre contrôleur", + "signInWithPasskey": "Clé d'accès", + "authenticating": "Authentification", + "usePasskey": "Clé d'accès", + "or": "ou", + "password": "Mot de passe", + "enterPassword": "Entrez le mot de passe", + "rememberDevice": "Se souvenir de cet appareil", + "signingIn": "Connexion en cours", + "signIn": "Se connecter", + "goBack": "Retour", + "unableToLoad": "Impossible de charger les informations de délégation" + }, + "oauthConsent": { + "delegatedAccess": "Accès délégué", + "actingAs": "Agissant comme", + "controller": "Contrôleur", + "accessLevel": "Niveau d'accès", + "readOnlyAccess": "Accès en lecture seule", + "readOnlyDesc": "Voir les informations publiques uniquement. Aucun accès en écriture à ce compte.", + "permissionsLimited": "Permissions limitées", + "permissionsLimitedDesc": "Vos permissions réelles seront limitées à votre niveau d'accès {level}, indépendamment de ce que l'application demande.", + "viewerLimitedDesc": "En tant que Lecteur, vous avez un accès en lecture seule. Cette application ne pourra pas créer, modifier ou supprimer du contenu sur ce compte.", + "editorLimitedDesc": "En tant qu'Éditeur, vous pouvez créer et modifier du contenu mais ne pouvez pas gérer les paramètres du compte ou la sécurité." + }, + "migration": { + "title": "Migration de compte", + "subtitle": "Déplacez votre identité AT Protocol entre serveurs", + "migrateHere": "Migrer ici", + "migrateHereDesc": "Déplacez votre compte AT Protocol existant vers ce PDS depuis un autre serveur.", + "bringDid": "Apportez votre DID et votre identité", + "transferData": "Transférez toutes vos données", + "keepFollowers": "Gardez vos abonnés", + "whatIsMigration": "Qu'est-ce que la migration de compte ?", + "whatIsMigrationDesc": "La migration de compte vous permet de déplacer votre identité AT Protocol entre serveurs de données personnels (PDS). Votre DID (identifiant décentralisé) reste le même, donc vos abonnés et connexions sociales sont préservés.", + "beforeMigrate": "Avant de migrer", + "beforeMigrate1": "Vous aurez besoin des identifiants de votre compte actuel", + "beforeMigrate2": "La migration nécessite une vérification par e-mail pour la sécurité", + "beforeMigrate3": "Les gros comptes avec beaucoup d'images peuvent prendre plusieurs minutes", + "beforeMigrate4": "Votre ancien PDS sera notifié pour désactiver votre compte", + "importantWarning": "La migration de compte est une action importante. Assurez-vous de faire confiance au PDS de destination et comprenez que vos données seront déplacées. En cas de problème, la récupération peut nécessiter une intervention manuelle.", + "learnMore": "En savoir plus sur les risques de migration", + "offlineRestore": "Restauration hors ligne", + "offlineRestoreDesc": "Restaurez depuis une sauvegarde lorsque votre ancien PDS est indisponible.", + "offlineFeature1": "Utilisez un fichier de sauvegarde CAR", + "offlineFeature2": "Prouvez la propriété avec la clé de rotation", + "offlineFeature3": "Récupération pour serveurs arrêtés", + "oauthCompleting": "Finalisation de l'authentification...", + "oauthFailed": "Échec de l'authentification", + "tryAgain": "Réessayer", + "resume": { + "title": "Reprendre la migration ?", + "incomplete": "Vous avez une migration incomplète en cours :", + "direction": "Direction", + "migratingHere": "Migration ici", + "from": "De", + "to": "Vers", + "progress": "Progression", + "reenterCredentials": "Vous devrez saisir à nouveau vos identifiants pour continuer.", + "startOver": "Recommencer", + "resumeButton": "Reprendre" + }, + "inbound": { + "welcome": { + "title": "Migrer vers ce PDS", + "desc": "Déplacez votre compte AT Protocol existant vers ce serveur.", + "understand": "Je comprends les risques et souhaite continuer" + }, + "sourceAuth": { + "title": "Entrez votre identifiant actuel", + "titleResume": "Reprendre la migration", + "desc": "Entrez l'identifiant du compte que vous souhaitez migrer.", + "descResume": "Reconnectez-vous à votre PDS source pour continuer la migration.", + "handle": "Identifiant", + "handlePlaceholder": "alice.bsky.social", + "handleHint": "Votre identifiant actuel sur votre PDS existant", + "continue": "Continuer", + "connecting": "Connexion...", + "reauthenticate": "Se réauthentifier", + "resumeTitle": "Migration en cours", + "resumeFrom": "De", + "resumeTo": "Vers", + "resumeProgress": "Progression", + "resumeOAuthNote": "Vous devez vous réauthentifier via OAuth pour continuer." + }, + "chooseHandle": { + "title": "Choisissez votre nouvel identifiant", + "desc": "Sélectionnez un identifiant pour votre compte sur ce PDS.", + "migratingFrom": "Migration depuis", + "handleChoice": "Identifiant", + "keepExisting": "Conserver l'identifiant existant", + "createNew": "Créer un nouvel identifiant", + "existingHandle": "Votre identifiant", + "verifyInstructions": "Vérifiez la propriété via DNS ou HTTP", + "or": "ou", + "returning": "retour", + "verifyOwnership": "Vérifier la propriété", + "verifying": "Vérification", + "verified": "Vérifié", + "checkAgain": "Vérifier à nouveau", + "newHandle": "Nouvel identifiant", + "checkingAvailability": "Vérification de la disponibilité...", + "handleAvailable": "L'identifiant est disponible !", + "handleTaken": "L'identifiant est déjà pris", + "handleTooShort": "L'identifiant doit contenir au moins 3 caractères", + "handleHint": "Vous pouvez aussi utiliser votre propre domaine en entrant l'identifiant complet (ex. : alice.mondomaine.com)", + "email": "Adresse e-mail", + "authMethod": "Méthode d'authentification", + "authPassword": "Mot de passe", + "authPasswordDesc": "Connexion classique par mot de passe", + "authPasskey": "Clé d'accès", + "authPasskeyDesc": "Connexion sans mot de passe via biométrie ou clé de sécurité", + "password": "Mot de passe", + "passwordHint": "Au moins 8 caractères", + "passkeyInfo": "Vous configurerez une clé d'accès après la création de votre compte. Votre appareil vous invitera à utiliser la biométrie (empreinte digitale, Face ID) ou une clé de sécurité.", + "inviteCode": "Code d'invitation" + }, + "review": { + "title": "Vérifier la migration", + "desc": "Veuillez confirmer les détails de votre migration.", + "currentHandle": "Identifiant actuel", + "newHandle": "Nouvel identifiant", + "did": "DID", + "sourcePds": "Du PDS", + "targetPds": "Vers le PDS", + "email": "E-mail", + "authentication": "Authentification", + "authPasskey": "Clé d'accès (sans mot de passe)", + "authPassword": "Mot de passe", + "warning": "Après avoir cliqué sur « Démarrer la migration », votre dépôt et vos données commenceront à être transférés. Ce processus ne peut pas être facilement annulé.", + "startMigration": "Démarrer la migration", + "starting": "Démarrage..." + }, + "migrating": { + "title": "Migration en cours", + "desc": "Veuillez patienter pendant le transfert de votre compte...", + "exportRepo": "Exportation du dépôt", + "importRepo": "Importation du dépôt", + "migrateBlobs": "Migration des blobs", + "migratePrefs": "Migration des préférences" + }, + "passkeySetup": { + "title": "Configurez votre clé d'accès", + "desc": "Votre e-mail a été vérifié. Configurez maintenant votre clé d'accès pour une connexion sécurisée sans mot de passe.", + "nameLabel": "Nom de la clé d'accès (optionnel)", + "namePlaceholder": "ex. yubikey, téléphone", + "nameHint": "Un nom convivial pour identifier cette clé d'accès", + "instructions": "Cliquez sur le bouton ci-dessous pour enregistrer votre clé d'accès. Votre appareil vous invitera à utiliser la biométrie (empreinte digitale, Face ID) ou une clé de sécurité.", + "register": "Enregistrer la clé d'accès", + "registering": "Enregistrement..." + }, + "appPassword": { + "title": "Enregistrez votre mot de passe d'application", + "desc": "Votre clé d'accès a été créée. Un mot de passe d'application a été généré pour vous permettre de l'utiliser avec les applications qui ne supportent pas encore les clés d'accès.", + "warning": "Ce mot de passe d'application est nécessaire pour se connecter aux applications qui ne supportent pas encore les clés d'accès (comme bsky.app). Vous ne verrez ce mot de passe qu'une seule fois.", + "label": "Mot de passe d'application pour", + "saved": "J'ai enregistré mon mot de passe d'application dans un endroit sécurisé", + "continue": "Continuer" + }, + "emailVerify": { + "title": "Vérifiez votre compte", + "desc": "Un code de vérification a été envoyé à {email}.", + "hint": "Entrez le code ci-dessous, ou cliquez sur le lien dans le message pour continuer automatiquement.", + "tokenLabel": "Code de vérification", + "tokenPlaceholder": "Entrez le code de vérification", + "resend": "Renvoyer le code", + "telegramInstructions": "Envoyez un message au bot Telegram pour vérifier votre compte.", + "discordInstructions": "Envoyez un message au bot Discord pour vérifier votre compte.", + "openTelegram": "Ouvrir Telegram pour vérifier", + "openDiscord": "Ouvrir Discord pour vérifier", + "waitingForVerification": "En attente de vérification..." + }, + "plcToken": { + "title": "Vérifier la migration", + "desc": "Un code de vérification a été envoyé à l'e-mail enregistré avec votre ancien compte.", + "info": "Ce code confirme que vous avez accès au compte et autorise la mise à jour de votre identité pour pointer vers ce PDS.", + "tokenLabel": "Code de vérification", + "tokenPlaceholder": "Entrez le code de l'e-mail", + "resend": "Renvoyer le code", + "complete": "Terminer la migration", + "completing": "Vérification..." + }, + "didWebUpdate": { + "title": "Mettez à jour votre document DID", + "desc": "Puisque vous utilisez une identité did:web, vous devez mettre à jour votre document DID pour pointer vers ce PDS.", + "yourDid": "Votre DID est :", + "updateInstructions": "Mettez à jour le fichier did.json sur votre domaine pour que le point de terminaison de service atproto_pds pointe vers ce PDS :", + "important": "Important :", + "verifyFirst": "Assurez-vous que votre document DID est mis à jour et accessible publiquement avant de terminer la migration.", + "fileLocation": "Le fichier doit se trouver à :", + "complete": "Terminer la migration", + "completing": "Finalisation..." + }, + "finalizing": { + "title": "Finalisation de la migration", + "desc": "Veuillez patienter pendant que nous terminons la migration...", + "signingPlc": "Signature de la mise à jour d'identité", + "activating": "Activation du compte sur le nouveau PDS", + "deactivating": "Désactivation du compte sur l'ancien PDS" + }, + "success": { + "title": "Migration terminée !", + "desc": "Votre compte a été migré avec succès vers ce PDS.", + "yourNewHandle": "Votre nouvel identifiant", + "did": "DID", + "blobsWarning": "{count} blobs n'ont pas pu être migrés. Il peut s'agir d'images ou d'autres médias qui ne sont plus disponibles.", + "redirecting": "Redirection vers le tableau de bord..." + }, + "error": { + "title": "Erreur de migration", + "desc": "Une erreur est survenue pendant la migration.", + "startOver": "Recommencer", + "unknown": "Une erreur inconnue est survenue." + }, + "common": { + "back": "Retour", + "cancel": "Annuler", + "continue": "Continuer", + "whatWillHappen": "Ce qui va se passer :", + "step1": "Connectez-vous à votre PDS actuel", + "step2": "Choisissez votre nouvel identifiant sur ce serveur", + "step3": "Votre dépôt et vos blobs seront transférés", + "step4": "Vérifiez la migration par e-mail", + "step5": "Votre identité sera mise à jour pour pointer ici", + "beforeProceed": "Avant de continuer :", + "warning1": "Vous devez avoir accès à l'e-mail enregistré avec votre compte actuel", + "warning2": "Les gros comptes peuvent prendre plusieurs minutes à transférer", + "warning3": "Votre ancien compte sera désactivé après la migration" + } + }, + "offline": { + "welcome": { + "title": "Restauration hors ligne", + "desc": "Restaurez votre compte lorsque votre ancien PDS est indisponible. C'est pour la récupération d'urgence lorsque vous ne pouvez pas contacter votre serveur précédent.", + "warningTitle": "Méthode de récupération avancée", + "warningDesc": "Cette méthode nécessite votre clé privée de rotation. Utilisez-la uniquement si votre PDS précédent a fermé ou si vous n'y avez pas accès.", + "requirementsTitle": "Vous aurez besoin de :", + "requirement1": "Votre DID (did:plc:...)", + "requirement2": "Un fichier de sauvegarde CAR de votre dépôt", + "requirement3": "Votre clé de rotation (clé privée en hex, base58 ou format JWK)", + "understand": "Je comprends que c'est pour la récupération hors ligne uniquement" + }, + "provideDid": { + "title": "Entrez votre DID", + "desc": "Entrez le DID du compte que vous souhaitez restaurer.", + "label": "Votre DID", + "hint": "Votre identifiant décentralisé (ex. : did:plc:abc123...)" + }, + "uploadCar": { + "title": "Téléversez la sauvegarde du dépôt", + "desc": "Téléversez le fichier CAR contenant les données de votre dépôt.", + "label": "Fichier CAR", + "hint": "Il doit s'agir d'un fichier .car provenant d'une sauvegarde précédente de votre dépôt", + "reuploadWarningTitle": "Fichier CAR requis", + "reuploadWarning": "Votre session a été restaurée, mais vous devez téléverser à nouveau votre fichier CAR. Pour des raisons de sécurité, le contenu des fichiers n'est pas conservé entre les sessions." + }, + "rotationKey": { + "title": "Fournir la clé de rotation", + "desc": "Entrez votre clé de rotation pour prouver la propriété de ce DID.", + "securityWarningTitle": "Avertissement de sécurité", + "securityWarning1": "Votre clé de rotation est extrêmement sensible — toute personne la possédant peut prendre le contrôle de votre identité", + "securityWarning2": "Ne l'entrez que sur des appareils et connexions de confiance", + "securityWarning3": "La clé ne sera pas stockée après la migration", + "label": "Clé de rotation", + "placeholder": "Collez votre clé de rotation (hex, base58 ou JWK)...", + "hint": "Supporte le format hex 64 caractères, base58 ou JWK", + "valid": "Clé de rotation vérifiée ! Vous avez le contrôle de ce DID.", + "invalid": "Cette clé n'est pas une clé de rotation valide pour ce DID.", + "validating": "Validation...", + "validate": "Valider la clé" + }, + "chooseHandle": { + "migratingDid": "Restauration du DID" + }, + "review": { + "desc": "Veuillez confirmer les détails de votre restauration hors ligne.", + "carFile": "Fichier CAR", + "rotationKey": "Clé de rotation", + "plcWarningTitle": "Point de non-retour", + "plcWarning": "Une fois démarré, votre document DID sera mis à jour pour pointer vers ce PDS. En cas de problème, vous pouvez utiliser votre clé de rotation pour récupérer, mais vous devriez terminer la migration pour éviter un état d'identité cassé." + }, + "migrating": { + "title": "Restauration du compte", + "desc": "Veuillez patienter pendant la restauration de votre compte...", + "creating": "Création du compte", + "importing": "Importation du dépôt" + }, + "blobs": { + "title": "Migration des blobs", + "desc": "Tentative de récupération des images et médias depuis votre ancien PDS...", + "migrating": "Migration des blobs", + "failedTitle": "Certains blobs n'ont pas pu être migrés", + "failedDesc": "{count} blobs n'ont pas pu être récupérés depuis votre ancien PDS. Cela peut arriver si le serveur est inaccessible ou si les fichiers ont été supprimés." + }, + "success": { + "desc": "Votre compte a été restauré avec succès sur ce PDS." + } + } + } +}