diff --git a/frontend/src/lib/locales/fr/actions.js b/frontend/src/lib/locales/fr/actions.js new file mode 100644 index 00000000..9c7279bf --- /dev/null +++ b/frontend/src/lib/locales/fr/actions.js @@ -0,0 +1,304 @@ +/** + * Actions automation translations (French) + */ +export default { + actions: { + title: 'Actions', + description: 'Automatisez les flux de travail avec des actions basées sur des règles', + create: 'Créer une action', + createFirst: 'Créer votre première action', + noActions: 'Aucune action pour le moment', + noActionsDescription: 'Créez des actions pour automatiser vos flux de travail en fonction des événements d’éléments', + enabled: 'Activé', + disabled: 'Désactivé', + enable: 'Activer', + disable: 'Désactiver', + viewLogs: 'Afficher les journaux', + confirmDelete: 'Voulez-vous vraiment supprimer l’action « {name} » ?', + failedToSave: 'Échec de l’enregistrement de l’action', + newAction: 'Nouvelle action', + + // Action templates (shipped automation blueprints) + templates: { + pickTitle: 'Choisir un modèle d’action', + fromTemplate: 'À partir d’un modèle', + empty: 'Aucun modèle disponible.', + help: 'Appliquez un modèle d’automatisation préconfiguré à cet espace de travail. Chaque application crée une nouvelle action que vous pouvez modifier ultérieurement.', + apply: 'Appliquer', + }, + + // Trigger types + trigger: { + statusTransition: 'Changement de statut', + itemCreated: 'Élément créé', + itemUpdated: 'Élément mis à jour', + itemLinked: 'Élément lié', + manual: 'Manuel', + respondToCascades: 'Réagir aux modifications déclenchées par d’autres actions', + respondToCascadesHint: + 'Lorsqu’elle est activée, cette action s’exécutera également si elle est déclenchée par d’autres actions, et pas seulement par des modifications d’utilisateurs.', + }, + + manualAccess: { + label: 'Qui peut exécuter cette action manuelle ?', + allEditors: 'Tous les éditeurs de l’espace de travail', + unrestrictedHint: + 'Aucune restriction de rôle. Toute personne disposant des droits de modification peut voir et exécuter cette action.', + restrictedHint: + 'Seuls les membres possédant au moins un rôle sélectionné peuvent voir et exécuter cette action. Les administrateurs de l’espace de travail conservent toujours l’accès.', + }, + + // Node types + nodes: { + trigger: 'Déclencheur', + setField: 'Définir un champ', + setStatus: 'Définir le statut', + addComment: 'Ajouter un commentaire', + notifyUser: 'Notifier un utilisateur', + condition: 'Condition', + updateAsset: 'Mettre à jour l’actif', + createAsset: 'Créer un actif', + httpRequest: 'Requête HTTP', + containerRun: 'Exécuter le conteneur', + aiExtract: 'Extraction par IA', + aiAgent: 'Agent IA', + relatedItems: 'Pour chaque élément lié', + transitionItem: 'Faire évoluer l’élément', + roundRobinAssign: 'Assignation à tour de rôle', + createMilestone: 'Créer un jalon', + }, + + // Toast shown when the AI chat updates the open action via update_action. + aiUpdated: 'Action mise à jour par l’IA', + + // Actor override (run-as) + runAs: 'Exécuter en tant que', + runAsTriggerUser: 'Exécuter en tant qu’utilisateur déclencheur', + runAsHint: + 'L’action s’exécute avec les autorisations de cet utilisateur. Laissez vide pour exécuter en tant que déclencheur initial.', + runAsReadonlyHint: 'Nécessite la permission « Définir l’acteur de l’action » pour être modifié.', + + // Node palette and tips + addNodes: 'Ajouter des nœuds', + tips: 'Conseils', + tipDragToConnect: 'Faites glisser à partir des poignées pour connecter les nœuds', + tipClickToEdit: 'Cliquez sur un nœud pour le configurer', + tipConditionBranches: 'Les conditions ont des branches vrai/faux', + + // Config panel + nodeConfig: 'Configuration du nœud', + config: { + from: 'De', + to: 'Vers', + selectField: 'Sélectionner un champ...', + selectStatus: 'Sélectionner un statut...', + config: 'Configuration', + configure: 'Configurer', + selectConfig: 'Sélectionner la configuration', + enterComment: 'Saisir un commentaire...', + selectRecipient: 'Sélectionner un destinataire...', + setCondition: 'Définir une condition...', + targetStatus: 'Statut cible', + fieldName: 'Nom du champ', + value: 'Valeur', + commentContent: 'Contenu du commentaire', + commentPlaceholder: 'Saisissez le texte du commentaire. Utilisez {{item.title}} pour les variables.', + privateComment: 'Commentaire privé (interne uniquement)', + fieldToCheck: 'Champ à vérifier', + operator: 'Opérateur', + compareValue: 'Valeur de comparaison', + private: 'Privé', + triggerType: 'Type de déclencheur', + fromStatus: 'Depuis le statut', + toStatus: 'Vers le statut', + anyStatus: 'N’importe quel statut', + triggerField: 'Champ modifié', + anyField: 'N’importe quel champ (toutes les modifications)', + recipientType: 'Destinataire', + notifyMessage: 'Message', + notifyPlaceholder: 'Saisissez le message. Utilisez {{item.title}} pour les variables.', + includeLink: 'Inclure le lien vers l’élément', + // Update Asset config + sourceAssetField: 'Champ d’actif sur l’élément', + selectAssetField: 'Sélectionner le champ d’actif...', + sourceAssetFieldHint: 'Sélectionnez le champ d’élément qui contient l’actif lié', + targetAssetType: 'Type d’actif cible', + selectAssetType: 'Sélectionner le type d’actif...', + fieldMappingsLabel: 'Mappages de champs', + fieldMappings: '{count} mappages de champs', + fieldMappings_one: '{count} mappage de champ', + fieldMappings_other: '{count} mappages de champs', + configureAssetUpdate: 'Configurer la mise à jour de l’actif...', + fromField: 'Depuis le champ', + sourceTypeVariable: 'Variable/Modèle', + sourceTypeItemField: 'Champ d’élément', + sourceTypeLiteral: 'Valeur littérale', + selectTargetField: 'Sélectionner le champ cible...', + addMapping: 'Ajouter un mappage', + milestonePickerHint: 'Stocke les identifiants de jalons pour l’action ; les noms sont affichés uniquement pour la modification.', + userPickerHint: 'Choisissez un utilisateur spécifique, ou saisissez un ID utilisateur/modèle ci-dessous.', + // Create Asset config + assetSet: 'Ensemble d’actifs', + selectAssetSet: 'Sélectionner l’ensemble d’actifs...', + assetTitle: 'Titre de l’actif', + assetTitleHint: 'Utilisez {{item.title}} ou d’autres variables', + assetDescription: 'Description', + assetTagLabel: 'Étiquette de l’actif', + assetCategory: 'Catégorie', + selectCategory: 'Sélectionner une catégorie (facultatif)...', + assetStatus: 'Statut', + selectStatusOptional: 'Sélectionner un statut (facultatif)...', + requiredField: 'Requis', + configureAssetCreation: 'Configurer la création de l’actif...', + // Capability picker (HTTP, Docker, LLM nodes) + capability: 'Capacité', + selectCapability: 'Sélectionner une capacité...', + noCapabilitiesForWorkspace: + 'Aucune capacité disponible dans cet espace de travail. Demandez à un administrateur d’en approvisionner une.', + configureRequest: 'Configurer la requête HTTP...', + configureExtract: 'Configurer l’extraction IA...', + selectModelAndTools: 'Sélectionner le modèle et les outils...', + // HTTP request node + httpCapability: 'Capacité de client HTTP', + httpMethod: 'Méthode', + urlTemplate: 'Modèle d’URL', + requestBody: 'Corps de la requête', + requestBodyPlaceholder: 'Facultatif. Corps JSON, peut utiliser {{variables}}.', + httpHeaders: 'En-têtes', + addHeader: 'Ajouter un en-tête', + headerName: 'Nom de l’en-tête', + headerValue: 'Valeur', + // Container run node + dockerCapability: 'Environnement Docker', + timeoutSecs: 'Délai d’expiration (secondes)', + // AI nodes + llmCapability: 'Connexion LLM', + model: 'Modèle', + tools: 'Outils', + aiPrompt: 'Invite (Prompt)', + aiExtractPromptPlaceholder: + 'Extrayez des données structurées à partir de l’entrée. Soyez précis sur ce qu’il faut extraire.', + systemPrompt: 'Invite système', + systemPromptPlaceholder: 'Vous êtes un assistant utile. Utilisez les outils pour...', + inputField: 'Champ d’entrée', + inputFieldPlaceholder: 'nom de la variable dans laquelle lire l’entrée', + inputFields: 'Champs d’entrée', + inputFieldsPlaceholder: 'noms de variables séparés par des virgules', + outputField: 'Champ de sortie', + outputFieldPlaceholder: 'nom de la variable dans laquelle écrire la sortie', + outputSchema: 'Schéma JSON de sortie', + agentTools: 'Outils', + agentToolsHint: + 'Capacités du client HTTP que l’agent peut appeler. Seules les capacités restreintes à cet espace de travail sont répertoriées.', + noToolsAvailable: 'Aucune capacité de client HTTP disponible pour cet espace de travail.', + maxSteps: 'Iterations max', + }, + + // Recipients + recipients: { + assignee: 'Assigné', + creator: 'Créateur', + specific: 'Utilisateurs spécifiques', + }, + + // Condition + condition: { + true: 'Oui', + false: 'Non', + }, + + // Operators + operators: { + equals: 'Égal à', + notEquals: 'Différent de', + contains: 'Contient', + greaterThan: 'Supérieur à', + lessThan: 'Inférieur à', + isEmpty: 'Est vide', + isNotEmpty: 'N’est pas vide', + }, + + // Execution logs + logs: { + title: 'Journaux d’exécution', + noLogs: 'Aucun journal d’exécution', + status: 'Statut', + running: 'En cours d’exécution', + completed: 'Terminé', + failed: 'Échoué', + skipped: 'Ignoré', + startedAt: 'Démarre à', + completedAt: 'Terminé à', + error: 'Erreur', + details: 'Détails', + viewDetails: 'Afficher les détails', + }, + + // Execution trace + trace: { + title: 'Détails de l’exécution', + noSteps: 'Aucune étape d’exécution enregistrée', + setStatus: 'Statut modifié de « {from} » à « {to} »', + setField: 'Champ {field} modifié de « {from} » à « {to} »', + addComment: 'Commentaire {prefix}ajouté : « {content} »', + notifyUser: 'Notification envoyée à {count} utilisateurs', + notifyUser_one: 'Notification envoyée à {count} utilisateur', + notifyUser_other: 'Notification envoyée à {count} utilisateurs', + notifySkipped: 'Notification ignorée : {reason}', + conditionResult: 'La condition a été évaluée à {result}', + updateAsset: 'Actif #{asset_id} mis à jour', + updateAssetSkipped: 'Mise à jour de l’actif ignorée : {reason}', + createAsset: 'Actif #{asset_id} créé : {title}', + createAssetFailed: 'Échec de la création de l’actif : {reason}', + }, + + // Test/manual execution + test: { + title: 'Tester l’action', + description: + 'Sélectionnez un élément sur lequel exécuter cette action. Cela exécutera l’action immédiatement, en contournant le déclencheur habituel.', + selectItem: 'Sélectionner un élément', + itemPlaceholder: 'Rechercher un élément...', + execute: 'Exécuter l’action', + run: 'Exécution de test', + executionFailed: 'Échec de l’exécution de l’action', + executionQueued: 'Action mise en file d’attente pour exécution', + }, + + // Placeholder reference + placeholders: { + title: 'Espaces réservés disponibles', + description: + 'Utilisez ces espaces réservés dans votre modèle. Ils seront remplacés par des valeurs réelles lorsque l’action s’exécutera.', + showReference: 'Afficher la référence des espaces réservés', + categories: { + item: 'Champs de l’élément', + user: 'Utilisateur actuel', + old: 'Anciennes valeurs', + trigger: 'Contexte du déclencheur', + }, + item: { + title: 'Titre de l’élément', + id: 'ID de l’élément', + statusId: 'ID du statut', + assigneeId: 'ID de l’utilisateur assigné', + any: 'N’importe quel champ de l’élément', + }, + user: { + name: 'Nom complet de l’utilisateur', + email: 'E-mail de l’utilisateur', + id: 'ID de l’utilisateur', + }, + old: { + description: 'Valeur précédente avant la modification', + example: 'Valeur précédente de n’importe quel champ', + }, + trigger: { + itemId: 'ID de l’élément déclencheur', + workspaceId: 'ID de l’espace de travail', + }, + }, + switchToVertical: 'Passer à la disposition verticale', + switchToHorizontal: 'Passer à la disposition horizontale', + }, +}; \ No newline at end of file diff --git a/frontend/src/lib/locales/fr/admin.js b/frontend/src/lib/locales/fr/admin.js new file mode 100644 index 00000000..954ced52 --- /dev/null +++ b/frontend/src/lib/locales/fr/admin.js @@ -0,0 +1,1112 @@ +/** + * Admin locale strings (French) + * Contains settings, roles, and permissions translations + */ + +export default { + // Settings (merged from general and detailed settings sections) + settings: { + title: 'Paramètres', + subtitle: 'Configurez les paramètres de votre application', + general: 'Général', + appearance: 'Apparence', + privacy: 'Confidentialité', + language: 'Langue', + timezone: 'Fuseau horaire', + dateFormat: 'Format de date', + timeFormat: 'Format d\'heure', + theme: 'Thème', + lightMode: 'Mode clair', + darkMode: 'Mode sombre', + systemDefault: 'Par défaut du système', + themeManager: { + background: 'Arrière-plan', text: 'Texte', + failedToLoad: 'Échec du chargement des thèmes.', failedToCreate: 'Échec de la création du thème.', + failedToUpdate: 'Échec de la mise à jour du thème.', failedToDelete: 'Échec de la suppression du thème.', + failedToActivate: 'Échec de l\'activation du thème.', + defaults: { + default: { name: 'Par défaut', description: 'Thème épuré avec couleurs de navigation standard' }, + ocean: { name: 'Océan', description: 'Thème professionnel avec nuances de bleu' }, + forest: { name: 'Forêt', description: 'Thème de navigation vert inspiré de la nature' }, + }, + }, + + // Admin settings + admin: 'Administration', + systemSettings: 'Paramètres système', + organizationSettings: 'Paramètres de l\'organisation', + workspaceSettings: 'Paramètres de l\'espace de travail', + + // Admin panel group labels (for Admin.svelte) + adminGroups: { + contentStructure: 'Contenu et structure', + workflowProcess: 'Flux de travail et processus', + integrationLinks: 'Intégrations et liens', + usersAccess: 'Utilisateurs et accès', + communication: 'Communication', + assetManagement: 'Gestion des actifs', + system: 'Système', + }, + + // Admin panel item labels and descriptions (for Admin.svelte) + adminItems: { + customFields: { title: 'Champs personnalisés', description: 'Gérer les définitions des champs personnalisés' }, + screens: { title: 'Écrans', description: 'Configurer les écrans de formulaires et les dispositions' }, + hierarchyLevels: { + title: 'Niveaux hiérarchiques', + description: 'Configurer les niveaux hiérarchiques des éléments de travail', + }, + itemTypes: { + title: 'Types d\'éléments', + description: 'Gérer les types d\'éléments avec icônes et couleurs', + }, + priorities: { + title: 'Priorités', + description: 'Configurer les niveaux de priorité avec icônes et couleurs', + }, + configurationSets: { title: 'Ensembles de configuration', description: 'Gérer les ensembles de configuration' }, + statuses: { + title: 'Statuts', + description: 'Gérer les catégories de statuts et les statuts individuels', + }, + workflows: { title: 'Flux de travail', description: 'Concevoir et gérer les transitions de flux de travail' }, + conditionSets: { title: 'Ensembles de conditions', description: 'Définir les conditions qui restreignent les transitions' }, + approvalSets: { title: 'Ensembles d\'approbations', description: 'Approbations multi-étapes qui conditionnent le changement de statut' }, + scmProviders: { + title: 'Fournisseurs SCM', + description: 'Configurer les intégrations GitHub, GitLab, Gitea et Bitbucket', + }, + integrationProviders: { + title: 'Intégrations', + description: 'Gérer les intégrations sortantes de Windshift et les applications entrantes via OAuth.', + }, + systemImport: { title: 'Importation système', description: 'Importer des données à partir de systèmes externes' }, + linkTypes: { title: 'Types de liens', description: 'Gérer les types de liens entre les éléments' }, + attachments: { title: 'Pièces jointes', description: 'Gérer les paramètres des pièces jointes' }, + modules: { title: 'Modules', description: 'Activer ou désactiver les modules système' }, + themes: { title: 'Thèmes', description: 'Gérer les thèmes et l\'apparence de l\'application' }, + users: { title: 'Utilisateurs', description: 'Gérer les comptes d\'utilisateurs et les profils' }, + groups: { title: 'Groupes', description: 'Gérer les groupes d\'utilisateurs et les adhésions' }, + permissions: { + title: 'Permissions globales', + description: 'Gérer les définitions de permissions globales', + }, + workspaceRoles: { + title: 'Rôles de l\'espace de travail', + description: 'Consulter les rôles d\'espace de travail et leurs permissions', + }, + sso: { title: 'Authentification unique (SSO)', description: 'Configurer les fournisseurs d\'identité OIDC pour le SSO' }, + security: { title: 'Sécurité', description: 'Gérer les paramètres de sécurité et le contrôle des fonctionnalités' }, + workspaces: { title: 'Espaces de travail', description: 'Gérer les espaces de travail et leurs paramètres' }, + notificationSettings: { + title: 'Paramètres de notification', + description: 'Gérer les configurations de notification', + }, + emailTemplates: { + title: 'Modèles de courriel', + description: 'Personnaliser l\'objet et le corps des courriels transactionnels', + }, + channels: { + title: 'Canaux', + description: 'Configurer les canaux entrants/sortants, portails et webhooks', + }, + assets: { title: 'Actifs', description: 'Gérer les ensembles, types, catégories et actifs' }, + aiFeatures: { + title: 'Fonctionnalités IA', + description: 'Configurer les fonctionnalités IA activées et leurs connexions LLM', + }, + llmConnections: { + title: 'Connexions IA', + description: 'Configurer les fournisseurs de modèles IA pour les fonctionnalités intelligentes', + }, + workItemStaleness: { + title: 'Métriques de qualité', + description: 'Définir quand les éléments non modifiés sont considérés comme inactifs', + }, + agentTemplates: { + title: 'Modèles d\'agent', + description: 'Personnaliser le catalogue Agent Studio : noms des modèles, profils et instructions', + }, + actionCapabilities: { + title: 'Capacités d\'action', + description: 'Gérer les environnements Docker, clients HTTP et connexions LLM pour les actions', + }, + diagnostics: { + title: 'Diagnostics', + description: 'Santé du système : horloge serveur, échecs d\'action et autres signaux opérationnels', + }, + }, + + // Attachment Settings + attachments: { + title: 'Paramètres des pièces jointes', + subtitle: 'Configurer les restrictions de téléversement et le stockage des pièces jointes', + systemStatus: 'État du système', + attachmentsEnabled: 'Pièces jointes activées', + attachmentsDisabled: 'Pièces jointes désactivées', + storagePath: 'Chemin de stockage', + notConfigured: 'Non configuré', + pathWritable: 'Chemin accessible en écriture', + pathStatusUnknown: 'État du chemin inconnu', + enableNote: + 'Pour activer les pièces jointes, redémarrez le serveur windshift avec l\'option --attachment-path.', + enableExample: 'Exemple : ./windshift --attachment-path /chemin/vers/pieces_jointes', + generalSettings: 'Paramètres généraux', + enableAttachments: 'Activer les pièces jointes', + enableAttachmentsDesc: 'Autoriser les utilisateurs à ajouter des pièces jointes aux éléments', + maxFileSize: 'Taille maximale des fichiers (Mo)', + current: 'Actuel', + fileTypeRestrictions: 'Restrictions de types de fichiers', + fileTypeRestrictionsDesc: + 'Laissez vide pour autoriser tous les types. Ajoutez des types MIME spécifiques pour restreindre.', + quickAddCommonTypes: 'Ajout rapide de types courants', + images: 'Images', + documents: 'Documents', + spreadsheets: 'Feuilles de calcul', + archives: 'Archives', + text: 'Texte', + clearAll: 'Tout effacer', + addMimeType: 'Ajouter un type MIME', + mimeTypePlaceholder: 'ex. : image/jpeg', + allowedMimeTypes: 'Types MIME autorisés', + allFileTypesAllowed: 'Tous les types de fichiers sont autorisés', + settingsSavedSuccess: 'Paramètres enregistrés avec succès', + notAvailable: + 'La fonctionnalité de pièce jointe n\'est pas disponible. Configurez un répertoire de stockage.', + failedToLoad: 'Échec du chargement des paramètres de pièces jointes', + failedToSave: 'Échec de l\'enregistrement des paramètres', + }, + + // Group Manager + groups: { + title: 'Gestion des groupes', + subtitle: 'Gérer les groupes d\'utilisateurs et leurs membres pour le contrôle d\'accès', + addGroup: 'Ajouter un groupe', + editGroup: 'Modifier le groupe', + createGroup: 'Créer un nouveau groupe', + groupName: 'Nom du groupe', + groupNamePlaceholder: 'ex. : Développeurs, Gestionnaires, Équipe de support', + descriptionOptional: 'Description (optionnel)', + descriptionPlaceholder: 'Décrivez l\'objectif et les responsabilités de ce groupe', + updateGroup: 'Mettre à jour le groupe', + loadingGroups: 'Chargement des groupes...', + noGroupsFound: 'Aucun groupe trouvé', + description: 'Description', + members: 'Membres', + noMembers: 'Aucun membre', + memberCount: '{count} membre', + membersCount: '{count} membres', + ldapSync: 'Synchronisation LDAP', + enabled: 'Activé', + manual: 'Manuel', + status: 'Statut', + active: 'Actif', + inactive: 'Inactif', + actions: 'Actions', + edit: 'Modifier', + manageMembers: 'Gérer les membres', + delete: 'Supprimer', + confirmDelete: 'Voulez-vous vraiment supprimer ce groupe ? Tous les membres seront retirés.', + failedToLoad: 'Échec du chargement des groupes', + failedToSave: 'Échec de l\'enregistrement du groupe', + failedToDelete: 'Échec de la suppression du groupe', + currentMembers: 'Membres actuels', + unknownUser: 'Utilisateur inconnu', + added: 'Ajouté', + ldapManaged: 'Géré par LDAP', + remove: 'Retirer', + addMembers: 'Ajouter des membres', + searchAndSelectUser: 'Rechercher et sélectionner un utilisateur à ajouter...', + usersToAdd: 'Utilisateurs à ajouter', + confirmRemoveMember: 'Voulez-vous vraiment retirer ce membre du groupe ?', + failedToAddMembers: 'Échec de l\'ajout des membres', + failedToRemoveMember: 'Échec du retrait du membre', + failedToLoadDetails: 'Échec du chargement des détails du groupe', + }, + + // Notification Settings + notifications: { + title: 'Paramètres de notification', + subtitle: + 'Créer et gérer les configurations de notification attribuables aux ensembles de configuration', + createSetting: 'Créer un paramètre de notification', + editSetting: 'Modifier le paramètre de notification', + setting: 'Paramètre', + eventRules: 'Règles d\'événement', + rulesConfigured: '{count} règles configurées ({enabled} activées)', + noRules: '0 règle configurée', + createdBy: 'Créé par', + noSettingsFound: + 'Aucun paramètre de notification trouvé. Créez-en un pour commencer.', + name: 'Nom', + namePlaceholder: 'ex. : Notifications de l\'équipe de développement', + nameRequired: 'Le nom est requis', + description: 'Description', + descriptionPlaceholder: 'Description de ce paramètre de notification...', + activeCanBeAssigned: 'Actif (peut être attribué à un ensemble de configuration)', + addRule: 'Ajouter une règle', + noEventRulesConfigured: 'Aucune règle d\'événement configurée', + noEventRulesDesc: 'Ajoutez des règles pour définir quand envoyer les notifications', + rule: 'Règle', + eventType: 'Type d\'événement', + selectEventType: 'Sélectionner le type d\'événement...', + notifyRecipients: 'Notifier les destinataires', + assignee: 'Assigné', + creator: 'Créateur', + watchers: 'Observateurs', + workspaceAdmins: 'Administrateurs de l\'espace', + customMessageTemplate: 'Modèle de message personnalisé (optionnel)', + messageTemplatePlaceholder: 'Laissez vide pour le message par défaut, ou personnalisez avec des variables', + availableVariables: 'Variables disponibles', + example: 'Exemple', + selectRecipients: 'Sélectionner les destinataires...', + customizeMessage: 'Personnaliser le message', + updateSetting: 'Mettre à jour le paramètre', + failedToLoad: 'Échec du chargement des paramètres de notification', + failedToSave: 'Échec de l\'enregistrement du paramètre de notification', + failedToDelete: 'Échec de la suppression du paramètre de notification', + failedToUpdate: 'Échec de la mise à jour du paramètre de notification', + confirmDelete: 'Voulez-vous vraiment supprimer "{name}" ? Cette action est irréversible.', + }, + + // Configuration Sets + configSets: { + title: 'Ensembles de configuration', + subtitle: + 'Gérer les ensembles qui combinent les flux de travail et les écrans pour différents contextes d\'espace de travail.', + addConfigSet: 'Ajouter un ensemble de configuration', + createConfigSet: 'Créer un ensemble de configuration', + editConfigSet: 'Modifier l\'ensemble de configuration', + searchPlaceholder: 'Rechercher des ensembles de configuration...', + loading: 'Chargement des ensembles de configuration...', + noConfigSets: 'Aucun ensemble de configuration pour le moment', + previewUnavailable: 'Aucun ensemble de configuration disponible', + previewUnavailableDescription: 'Créez un ensemble de configuration par défaut pour configurer cet espace de travail.', + assignerDescription: 'Sélectionnez l\'ensemble de configuration qui définit le flux de travail et les écrans pour cet espace de travail. Un seul ensemble peut être attribué.', + select: 'Sélectionner un ensemble de configuration', + updating: 'Mise à jour de la configuration…', + configured: 'Configuré', + customScreenOverrides: '{count} personnalisé(s)', + importSuccess: 'Ensemble de configuration importé avec succès', + import: 'Importer', + importing: 'Importation en cours…', + importWarning: 'L\'ensemble de configuration a été importé avec des avertissements.', + importFailed: 'Échec de l\'importation de l\'ensemble de configuration.', + unresolvedReferencesHeading: 'L\'importation fait référence à des entités qui n\'existent pas sur cette instance.', + defaultConflictHeading: 'L\'importation entre en conflit avec une entité par défaut sur cette instance.', + unresolvedImportTitle: 'Importation : références non résolues', + unresolvedImportDescription: 'Rien n\'a été écrit. Supprimez ces références du fichier source ou créez les entités manquantes ici, puis réessayez.', + referenceLocation: 'À {path}', + unresolvedKinds: { + user: 'Utilisateur {name}', role: 'Rôle « {name} »', group: 'Groupe « {name} »', + status_category: 'Catégorie de statut « {name} »', entity: 'Entité « {name} »', + }, + export: 'Exporter', + exportTitle: 'Exporter cet ensemble de configuration sous forme de modèle JSON portable', + exportDefaultDisabled: 'L\'ensemble de configuration par défaut ne peut pas être exporté. Dupliquez-le d\'abord si vous avez besoin d\'une copie portable.', + defaults: { + configuration: { name: 'Configuration par défaut', description: 'Ensemble de configuration par défaut avec flux de travail et écran de base' }, + notifications: { name: 'Notifications par défaut', description: 'Règles de notification standard pour les mises à jour d\'éléments' }, + }, + getStarted: 'Commencez par créer votre premier ensemble de configuration.', + createFirst: 'Créer le premier ensemble de configuration', + noWorkspacesAvailable: 'Aucun espace de travail disponible', + workspaces: 'Espaces de travail', + noWorkspacesAssigned: 'Aucun espace de travail attribué', + itemTypes: 'Types d\'éléments', + noItemTypesAssigned: 'Aucun type d\'élément attribué', + workflow: 'Flux de travail', + noneAssigned: 'Aucun attribué', + notifications: 'Notifications', + screens: 'Écrans', + createScreen: 'Créer :', + editScreen: 'Modifier :', + viewScreen: 'Afficher :', + none: 'Aucun', + created: 'Créé', + newConfigSet: 'Nouvel ensemble de configuration', + configureDesc: 'Configurer les flux de travail, les écrans et les attributions d\'espaces de travail', + unsavedChanges: 'Modifications non enregistrées', + basicInfo: 'Informations de base', + name: 'Nom', + namePlaceholder: 'Configuration de développement', + description: 'Description', + setAsDefault: 'Définir comme ensemble de configuration par défaut', + defaultSettings: 'Paramètres par défaut', + noWorkflow: 'Aucun flux de travail', + notificationSettings: 'Paramètres de notification', + defaultItemType: 'Type d\'élément par défaut', + firstAvailable: 'Premier disponible', + configurePerItemType: 'Configurer par type d\'élément', + preselectedItemType: 'Type d\'élément pré-sélectionné lors de la création', + selectItemTypes: + 'Sélectionnez les types d\'éléments disponibles dans les espaces de travail utilisant cet ensemble.', + workflowScreenOverrides: 'Surcharges de flux de travail et d\'écrans', + overridesDesc: + "Configurez des flux et écrans personnalisés par type d'élément. Utilisez 'Par défaut' pour hériter de l'onglet Général.", + itemType: 'Type d\'élément', + default: 'Par défaut', + selectPriorities: + 'Sélectionnez les priorités disponibles dans les espaces de travail utilisant cet ensemble.', + selectWorkspaces: + 'Sélectionnez les espaces utilisant cet ensemble. Un espace ne peut appartenir qu\'à un seul ensemble.', + }, + + // Permissions + permissions: { + title: 'Gestion des permissions', + subtitle: 'Gérer les permissions globales pour les utilisateurs et les groupes', + workspacePermissions: 'Permissions d\'espace de travail', + itemPermissions: 'Permissions d\'élément', + addPermission: 'Ajouter une permission', + allWorkspaceUsers: 'Tous les utilisateurs de l\'espace', + allGroups: 'Tous les groupes', + systemAdmin: 'Administrateur système', + user: 'Utilisateur', + group: 'Groupe', + remove: 'Retirer', + failedToLoadData: 'Échec du chargement des données : ', + loadingPermissions: 'Chargement des permissions...', + globalPermissions: 'Permissions globales', + permission: 'Permission', + assignedUsers: 'Utilisateurs attribués', + assignedGroups: 'Groupes attribués', + systemPermission: 'Permission système', + revokePermission: 'Révoker la permission', + noUsersAssigned: 'Aucun utilisateur attribué', + noGroupsAssigned: 'Aucun groupe attribué', + revokePermissionFromGroup: 'Révoker la permission du groupe', + assign: 'Attribuer', + assignPermission: 'Attribuer la permission "{permission}"', + grantPermission: 'Accorder la permission', + permissionGrantedToUser: 'Permission accordée à l\'utilisateur avec succès', + permissionGrantedToGroup: 'Permission accordée au groupe avec succès', + failedToGrantPermission: 'Échec de l\'octroi de la permission : ', + cannotRevokeLastAdmin: 'Impossible de retirer les droits administrateur au dernier administrateur système', + permissionRevokedFromUser: 'Permission révoquée de l\'utilisateur avec succès', + permissionRevokedFromGroup: 'Permission révoquée du groupe avec succès', + failedToRevokePermission: 'Échec de la révocation de la permission : ', + failedToRevokePermissionFromGroup: 'Échec de la révocation de la permission du groupe : ', + }, + + // Permission Sets + permissionSets: { + title: 'Ensembles de permissions', + subtitle: 'Gérer les regroupements de permissions attribuables aux ensembles de configuration', + createPermissionSet: 'Créer un ensemble de permissions', + name: 'Nom', + namePlaceholder: 'ex. : Permissions de l\'équipe de développement', + description: 'Description', + descriptionPlaceholder: 'Description optionnelle de cet ensemble de permissions', + deletePermissionSet: 'Supprimer l\'ensemble de permissions', + confirmDelete: 'Voulez-vous vraiment supprimer cet ensemble de permissions', + system: 'Système', + custom: 'Personnalisé', + noPermissionSets: 'Aucun ensemble de permissions trouvé. Créez-en un pour commencer.', + backToPermissionSets: 'Retour aux ensembles de permissions', + manageSubtitle: 'Gérer les détails et les attributions de l\'ensemble de permissions', + loadingPermissionSet: 'Chargement de l\'ensemble de permissions...', + basicInfo: 'Informations de base', + saveChanges: 'Enregistrer les modifications', + permissionAssignments: 'Attributions de permissions', + addAssignment: 'Ajouter une attribution', + addAssignmentDesc: + 'Sélectionnez un rôle, un groupe ou un utilisateur pour l\'ajouter à cette permission. L\'attribution sera appliquée immédiatement.', + addByRole: 'Ajouter par rôle', + addByGroup: 'Ajouter par groupe', + addByUser: 'Ajouter par utilisateur', + failedToCreate: 'Échec de la création de l\'ensemble de permissions : ', + failedToDelete: 'Échec de la suppression de l\'ensemble de permissions : ', + failedToLoad: 'Échec du chargement de l\'ensemble de permissions : ', + failedToUpdate: 'Échec de la mise à jour de l\'ensemble de permissions : ', + failedToAddAssignment: 'Échec de l\'ajout de l\'attribution : ', + failedToRemoveAssignment: 'Échec du retrait de l\'attribution : ', + removeAssignment: 'Retirer l\'attribution', + confirmRemoveAssignment: 'Voulez-vous vraiment retirer cette attribution ?', + role: 'Rôle', + group: 'Groupe', + user: 'Utilisateur', + unknown: 'Inconnu', + searchAndSelectRole: 'Rechercher et sélectionner un rôle...', + searchAndSelectGroup: 'Rechercher et sélectionner un groupe...', + searchAndSelectUser: 'Rechercher et sélectionner un utilisateur...', + }, + + // SCM Providers + scmProviders: { + title: 'Fournisseurs SCM', + subtitle: 'Configurer les intégrations GitHub et Gitea pour la liaison de dépôts.', + addProvider: 'Ajouter un fournisseur', + connectionSuccessful: 'Connexion réussie', + connectionFailed: 'Échec de la connexion', + noProviders: 'Aucun fournisseur SCM', + getStarted: 'Commencez par ajouter un fournisseur GitHub ou Gitea.', + enabled: 'Activé', + disabled: 'Désactivé', + default: 'Par défaut', + restricted: 'Restreint', + patConfigured: 'JETON (PAT) configuré', + patNotConfigured: 'JETON (PAT) non configuré', + addSCMProvider: 'Ajouter un fournisseur SCM', + editSCMProvider: 'Modifier le fournisseur SCM', + failedToLoad: 'Échec du chargement des fournisseurs SCM', + failedToSave: 'Échec de l\'enregistrement du fournisseur', + failedToDelete: 'Échec de la suppression du fournisseur', + slug: 'Identifiant (Slug)', + slugHelp: 'Identifiant compatible URL (minuscules, tirets autorisés)', + name: 'Nom', + namePlaceholder: 'GitHub - Organisation principale', + providerType: 'Type de fournisseur', + authMethod: 'Méthode d\'authentification', + baseUrl: 'URL de base', + baseUrlPlaceholder: 'Entrez l\'URL de votre instance Gitea/Forgejo hébergée', + callbackUrl: 'URL de rappel (Callback)', + copy: 'Copier', + copied: 'Copié !', + enterSlugForCallback: 'Saisissez un identifiant ci-dessus pour générer l\'URL de rappel', + useThisUrl: 'Utilisez cette URL lors de la configuration de votre application OAuth dans', + oauthClientId: 'ID client OAuth', + oauthClientSecret: 'Secret client OAuth', + leaveEmptyToKeep: 'Laissez vide pour conserver le secret actuel', + personalAccessToken: 'Jeton d\'accès personnel (PAT)', + githubAppId: 'ID de l\'application GitHub', + privateKeyPem: 'Clé privée (PEM)', + orgInstallation: 'Installation d\'organisation', + discoverInstallations: 'Découvrir où votre application GitHub est installée', + discoverButton: 'Découvrir les installations', + orgInstallationId: 'Installation d\'organisation (ID :', + scopes: 'Portées (Scopes)', + scopesHelp: 'Liste des portées OAuth séparées par des espaces', + workspaceAccess: 'Accès aux espaces de travail', + allWorkspaces: 'Tous les espaces de travail peuvent utiliser ce fournisseur', + restrictToWorkspaces: 'Restreindre à des espaces de travail spécifiques', + allowedWorkspaces: 'Espaces de travail autorisés', + noWorkspacesWarning: + 'Attention : Aucun espace sélectionné. Ce fournisseur ne sera utilisable par aucun espace.', + }, + + // Security Settings + security: { + title: 'Sécurité', + subtitle: 'Gérer les paramètres de sécurité et le contrôle des fonctionnalités', + calendarFeeds: 'Abonnements aux flux de calendrier', + calendarFeedsDesc: 'Autoriser les utilisateurs à générer des URL de flux ICS pour des applications externes', + calendarFeedsWarning: + 'Attention : Une fois désactivés, les flux existants cesseront de fonctionner immédiatement. Les utilisateurs ne pourront plus en générer de nouveaux.', + pluginExecution: 'Exécution de commandes CLI par les plugins', + pluginExecutionDesc: 'Autoriser les extensions à exécuter des commandes système sur le serveur', + pluginExecutionWarning: + "Attention : Activer ceci permet aux extensions d'exécuter des commandes console. Les commandes sont restreintes au répertoire de chaque extension par sécurité. N'activez que si vous faites confiance à toutes les extensions.", + externalImages: 'Images Markdown externes', + externalImagesDesc: 'Autoriser le contenu Markdown à charger des images depuis n\'importe quel hôte HTTP ou HTTPS', + externalImagesRefresh: + 'Les changements s\'appliquent au prochain chargement de page. Unsplash, Atlassian, les hôtes Jira configurés, les pièces jointes et images intégrées restent disponibles si désactivé.', + externalImagesWarning: + "Les images externes peuvent révéler l'adresse IP et les détails du navigateur, et faire des requêtes vers le réseau local. N'activez que si vous faites confiance aux auteurs du contenu.", + failedToLoad: 'Échec du chargement des paramètres de sécurité', + failedToSave: 'Échec de l\'enregistrement des paramètres', + }, + + // SSO Settings + sso: { + title: 'Authentification unique (SSO)', + subtitle: 'Configurer les fournisseurs d\'identité OIDC pour la connexion SSO', + oidcTab: 'OIDC', + addProvider: 'Ajouter un fournisseur', + addSsoProvider: 'Ajouter un fournisseur SSO', + editSsoProvider: 'Modifier le fournisseur SSO', + deleteSsoProvider: 'Supprimer le fournisseur SSO', + createProvider: 'Créer le fournisseur', + deleteProvider: 'Supprimer le fournisseur', + noProviderConfigured: 'Aucun fournisseur SSO configuré', + noProviderDescription: + 'Ajoutez un fournisseur d\'identité OIDC pour activer le SSO. Prend en charge Keycloak, Authentik, Pocket ID et d\'autres fournisseurs compatibles OIDC.', + slug: 'Identifiant (Slug)', + slugPlaceholder: 'ex. : authentik', + slugHelp: 'Utilisé dans l\'URL de connexion SSO', + slugRequired: 'L\'identifiant est requis', + slugInvalid: 'L\'identifiant ne doit contenir que des lettres minuscules, des chiffres et des tirets', + name: 'Nom', + displayName: 'Nom d\'affichage', + displayNamePlaceholder: 'ex. : Authentik, Keycloak', + nameRequired: 'Le nom est requis', + providerType: 'Type de fournisseur', + issuerUrl: 'URL d\'émetteur (Issuer)', + issuerUrlPlaceholder: 'https://auth.example.com/realms/monrealm', + issuerUrlHelp: 'L\'URL d\'émetteur OIDC. Pour Keycloak : https://hote/realms/nomrealm', + issuerUrlRequired: 'L\'URL d\'émetteur est requise', + issuerUrlInvalid: 'L\'URL d\'émetteur doit commencer par https:// ou http://', + clientId: 'ID Client', + clientIdPlaceholder: 'windshift', + clientIdRequired: 'L\'ID client est requis', + clientSecret: 'Secret Client', + clientSecretRequired: 'Le secret client est requis', + enterClientSecret: 'Saisir le secret client', + leaveEmptyToKeepCurrent: '(laisser vide pour conserver l\'actuel)', + notConfigured: 'Non configuré', + callbackUrl: 'URL de rappel (Callback)', + callbackUrlHelp: 'Configurez cette URL comme URL de redirection dans votre fournisseur d\'identité', + scopes: 'Portées (Scopes)', + scopesHelp: 'Liste des portées OIDC séparées par des espaces', + enabled: 'Activé', + disabled: 'Désactivé', + default: 'Par défaut', + oidc: 'OIDC', + enableThisProvider: 'Activer ce fournisseur', + enableThisProviderDesc: 'Les utilisateurs peuvent se connecter via ce fournisseur SSO', + autoProvisionUsers: 'Création automatique de comptes', + autoProvisionUsersDesc: 'Créer automatiquement les comptes lors de la première connexion SSO', + manualUserCreationOnly: 'Création manuelle d\'utilisateurs uniquement', + trustIdpEmailVerification: 'Faire confiance aux courriels vérifiés du fournisseur', + trustIdpEmailVerificationDesc: + 'Ignorer la vérification de courriel Windshift si le fournisseur omet le statut. Les adresses explicitement non vérifiées nécessiteront toujours une confirmation.', + idpVerificationNotEnforced: 'Windshift vérifie les courriels non confirmés', + unsafeProvisioningWarningTitle: 'Confiance aux courriels du fournisseur activée', + unsafeProvisioningWarningBody: + "Les utilisateurs dont le fournisseur omet la vérification de courriel peuvent être liés à des comptes existants par leur courriel. N'activez ceci que si le fournisseur gère strictement ces adresses.", + testConnection: 'Tester la connexion', + testing: 'Test en cours...', + connectionSuccessful: 'Connexion réussie', + connectionFailed: 'Échec de la connexion', + connectionTestFailed: 'Échec du test de connexion', + confirmDeleteProvider: 'Voulez-vous vraiment supprimer le fournisseur SSO', + deleteWarning: + 'Ceci dissociera tous les comptes externes associés. Les utilisateurs qui utilisent uniquement ce fournisseur devront se connecter par mot de passe.', + failedToLoad: 'Échec du chargement des fournisseurs SSO', + failedToCreate: 'Échec de la création du fournisseur SSO', + failedToUpdate: 'Échec de la mise à jour du fournisseur SSO', + failedToDelete: 'Échec de la suppression du fournisseur SSO', + }, + + // Channel Managers + channelManagers: { + title: 'Gestionnaires de canal', + description: 'Utilisateurs et groupes pouvant configurer et gérer ce canal', + addManager: 'Ajouter un gestionnaire', + addChannelManager: 'Ajouter un gestionnaire de canal', + systemChannelNote: 'Ceci est un canal système. Seuls les administrateurs peuvent le gérer.', + systemAdminsCanManage: 'Les administrateurs système peuvent gérer ce canal', + noManagersAssigned: 'Aucun gestionnaire attribué', + defaultChannelsManaged: 'Les canaux par défaut sont automatiquement gérés par les administrateurs', + addUsersOrGroups: 'Ajoutez des utilisateurs ou des groupes pour gérer ce canal', + user: 'Utilisateur', + group: 'Groupe', + addedBy: 'Ajouté par', + on: 'le', + removeManager: 'Retirer le gestionnaire', + confirmRemoveMessage: + 'Voulez-vous vraiment retirer {name} des gestionnaires du canal ? Cette personne ne pourra plus le configurer ni le gérer.', + note: 'Remarque :', + noteText: + 'Les gestionnaires de canaux peuvent configurer les paramètres, tester les connexions et modifier les paramètres spécifiques au canal. Les administrateurs globaux ont toujours accès à tous les canaux.', + }, + + // Connected Accounts + connectedAccounts: { + failedToLoad: 'Échec du chargement des comptes connectés', + failedToDisconnect: 'Échec de la déconnexion du compte', + failedToStartConnection: 'Échec du démarrage de la procédure de connexion', + noProvidersTitle: 'Aucun fournisseur SCM disponible', + noProvidersDesc: 'Votre administrateur n\'a configuré aucun fournisseur SCM avec OAuth.', + connectDesc: 'Connectez votre compte pour créer des branches et des demandes d\'extraction (pull requests)', + connected: 'Connecté', + notConnected: 'Non connecté', + connectedOn: 'Connecté le', + disconnect: 'Déconnecter', + disconnecting: 'Déconnexion en cours...', + connect: 'Connecter', + account: 'Compte', + footerNote: + 'Les comptes connectés permettent de créer des branches et demandes d\'extraction en votre nom. Chaque utilisateur doit connecter son propre compte pour une attribution correcte.', + }, + + // Email Templates + emailTemplates: { + title: 'Modèles de courriels', + subtitle: 'Personnaliser l\'objet et le corps des courriels transactionnels envoyés par Windshift.', + loading: 'Chargement…', + empty: 'Aucun modèle de courriel trouvé.', + template: 'Modèle', + description: 'Description', + status: 'Statut', + actions: 'Actions', + active: 'Actif', + inactive: 'Inactif', + edit: 'Modifier', + editTitle: 'Modifier : {name}', + editFallbackTitle: 'Modifier le modèle', + subject: 'Objet', + htmlBody: 'Corps HTML', + textBody: 'Corps texte brut', + adminNotes: 'Description (notes d\'administration)', + activeHint: 'Actif (décocher pour réutiliser la valeur par défaut)', + availableVariables: 'Variables disponibles', + syntaxHint: + "Les modèles utilisent la syntaxe html/template de Go. Utilisez {open} .Field {close} pour les valeurs et {open} range .Items {close}…{open} end {close} pour boucler.", + preview: 'Aperçu', + previewLoading: 'Rendu en cours…', + previewTitle: 'Aperçu', + previewSubject: 'Objet :', + plainTextVersion: 'Version texte brut', + save: 'Enregistrer', + cancel: 'Annuler', + saved: 'Modèle de courriel enregistré', + loadFailed: 'Échec du chargement des modèles de courriels : {error}', + saveFailed: 'Échec de l\'enregistrement : {error}', + previewFailed: 'Échec de l\'aperçu : {error}', + labels: { + magic_link: 'Connexion portail par lien magique', + email_verification: 'Vérification de l\'adresse courriel', + invitation: 'Invitation de nouvel utilisateur', + notification_batch: 'Notifications regroupées', + portal_reply: 'Réponse client du portail', + }, + }, + + // Link Types + linkTypes: { + addLinkType: 'Ajouter un type de lien', + editLinkType: 'Modifier le type de lien', + name: 'Nom', + description: 'Description', + forwardLabel: 'Libellé direct', + reverseLabel: 'Libellé inverse', + color: 'Couleur', + active: 'Actif', + inactive: 'Inactif', + system: 'Système', + cannotDeleteSystem: 'Impossible de supprimer les types de liens système', + nameRequired: 'Le nom est requis', + failedToSave: 'Échec de l\'enregistrement du type de lien.', + namePlaceholder: 'ex. : Implémente', + forwardPlaceholder: 'ex. : implémente', + reversePlaceholder: 'ex. : est implémenté par', + forwardHint: 'Quand A lie B, affiché comme « A implémente B »', + reverseHint: 'Quand B est lié depuis A, affiché comme « B est implémenté par A »', + descriptionPlaceholder: 'Description optionnelle de ce type de relation', + empty: 'Aucun type de lien trouvé. Créez votre premier type de lien pour lier des éléments.', + defaults: { + tests: { name: 'Teste', description: 'Le cas de test teste l\'élément de travail' }, + implements: { name: 'Implémente', description: 'L\'élément de travail en implémente un autre' }, + dependsOn: { name: 'Dépend de', description: 'L\'élément de travail dépend d\'un autre' }, + relatesTo: { name: 'Est lié à', description: 'Relation bidirectionnelle générale' }, + linksTo: { name: 'Pointe vers', description: 'Lien directionnel général' }, + duplicates: { name: 'Est un doublon de', description: 'L\'élément est un doublon d\'un autre' }, + childOf: { name: 'Enfant de', description: 'Relation hiérarchique alternative' }, + page: { name: 'Page', description: 'L\'élément fait référence à une page de connaissances' }, + }, + }, + + // Status Categories + statusCategories: { + title: 'Catégories de statuts', + subtitle: 'Créer de nouvelles catégories pour organiser les statuts de flux de travail', + addStatusCategory: 'Ajouter une catégorie de statut', + name: 'Nom', + color: 'Couleur', + description: 'Description', + default: 'Par défaut', + completed: 'Terminé', + nameRequired: 'Le nom est requis', + failedToSave: 'Échec de l\'enregistrement de la catégorie de statut.', + failedToDelete: 'Échec de la suppression de la catégorie de statut.', + confirmDelete: 'Supprimer la catégorie de statut « {name} » ? Cette action est irréversible.', + count: 'Catégories : {count}', + statusCount: 'Statuts : {count}', + empty: 'Aucune catégorie de statut trouvée. Créez-en une pour commencer.', + inUseByStatuses: 'Impossible de supprimer « {name} » car elle est utilisée par un ou plusieurs statuts. Retirez ou réattribuez ces statuts d\'abord.', + defaults: { + toDo: { name: 'À faire', description: "Travail qui n'a pas encore commencé" }, + inProgress: { name: 'En cours', description: 'Travail en cours de réalisation' }, + done: { name: 'Terminé', description: 'Travail accompli' }, + }, + }, + + // Item Types + itemTypes: { + title: 'Types d\'éléments', + subtitle: 'Gérer les types d\'éléments et les attribuer à des niveaux hiérarchiques', + addItemType: 'Ajouter un type d\'élément', + name: 'Nom', + description: 'Description', + icon: 'Icône', + color: 'Couleur', + hierarchyLevel: 'Niveau hiérarchique', + genericSubtaskLevel: 'Sous-tâche générique (tout niveau parent)', + nameRequired: 'Le nom est requis', + failedToLoad: 'Échec du chargement des types d\'éléments.', + failedToSave: 'Échec de l\'enregistrement du type d\'élément.', + deleteFailed: 'Échec de la suppression du type d\'élément.', + confirmDelete: 'Supprimer « {name} » ? Cette action est irréversible.', + noItemTypes: 'Aucun type d\'élément configuré pour le moment.', + noConfigurationSets: 'Aucun ensemble de configuration', + defaultConfiguration: 'Configuration par défaut', + namePlaceholder: 'ex. : Épique, Histoire, Tâche, Bogue', + descriptionPlaceholder: 'Brève description de ce type d\'élément', + levelLabel: 'Niveau {level} — {name}', + levelOnly: 'Niveau {level}', + levelOption: '{name} (Niveau {level})', + defaults: { + initiative: 'Initiative', + epic: 'Épique', + story: 'Histoire', + task: 'Tâche', + bug: 'Bogue', + subtask: 'Sous-tâche', + }, + hierarchyChangeWarningTitle: 'Les éléments existants conserveront leurs liens', + hierarchyChangeWarningDescription: + 'Passer du niveau {fromLevel} au niveau {toLevel} ne déplace pas les éléments existants. Les futurs choix parents/enfants seront calculés à partir du niveau {toLevel} ; les utilisateurs devront peut-être ajuster le type avant de réassocier d\'anciens éléments.', + }, + + // Hierarchy Levels + hierarchyLevels: { + title: 'Niveaux hiérarchiques', + subtitle: 'Gérer les niveaux hiérarchiques de votre système', + addHierarchyLevel: 'Ajouter un niveau hiérarchique', + level: 'Niveau', + name: 'Nom', + description: 'Description', + nameRequired: 'Le nom du niveau hiérarchique est requis', + failedToLoad: 'Échec du chargement des niveaux hiérarchiques.', + failedToSave: 'Échec de l\'enregistrement du niveau hiérarchique.', + deleteFailed: 'Échec de la suppression du niveau hiérarchique.', + cannotDeleteTitle: 'Impossible de supprimer le niveau hiérarchique', + levelOccupied: 'Le niveau {level} est déjà occupé par « {name} ».', + negativeLevel: 'Un niveau hiérarchique ne peut pas être inférieur à 0.', + empty: 'Aucun niveau hiérarchique configuré pour le moment.', + numericHint: 'Niveau hiérarchique numérique (0 = le plus élevé)', + namePlaceholder: 'ex. : Initiative, Épique, Histoire, Tâche', + descriptionPlaceholder: 'Brève description de ce niveau hiérarchique', + createTitle: 'Créer un niveau hiérarchique', + editTitle: 'Modifier le niveau hiérarchique', + confirmDelete: 'Supprimer « {name} » ?', + defaults: { + initiative: { + name: 'Initiative', + description: 'Travail stratégique de haut niveau englobant plusieurs épiques', + }, + epic: { + name: 'Épique', + description: 'Ensemble de travaux volumineux pouvant être décomposé en histoires', + }, + story: { + name: 'Histoire', + description: 'Récit utilisateur ou fonctionnalité apportant de la valeur', + }, + task: { + name: 'Tâche', + description: 'Élément de travail individuel ou tâche technique', + }, + activity: { + name: 'Activité', + description: 'Activité distincte au sein d\'une tâche', + }, + }, + }, + + // Module Settings (Plugins) + modules: { + title: 'Modules', + subtitle: 'Activer ou désactiver les modules système et gérer les extensions', + plugins: 'Extensions', + pluginsDisabledMessage: + 'Le système d\'extensions a été désactivé dans la configuration de démarrage du serveur.', + uploadPlugin: 'Téléverser une extension', + dropOrSelect: 'Glissez-déposez les fichiers d\'extension ici ou cliquez pour sélectionner', + supportedFormats: 'Formats pris en charge : .zip ou .wasm', + installedPlugins: 'Extensions installées', + noPluginsInstalled: 'Aucune extension installée pour le moment', + pluginUploadedSuccess: 'Extension téléversée avec succès !', + pluginReloadedSuccess: 'L\'extension {name} a été rechargée avec succès', + pluginDeletedSuccess: 'L\'extension {name} a été supprimée avec succès', + confirmDeletePlugin: 'Voulez-vous vraiment supprimer l\'extension "{name}" ?', + wasmManifestRequired: 'Manifeste requis pour les fichiers WASM', + wasmManifestRequiredDesc: + 'Les fichiers WASM doivent être accompagnés d\'un fichier manifest.json décrivant l\'extension.', + chooseManifest: 'Choisir un fichier manifest.json', + changeManifest: 'Changer le fichier manifest.json', + manifestSelected: 'Manifeste sélectionné : {name}', + registeredRoutes: 'Routes enregistrées', + moduleSettingsNote: + 'Les modifications des paramètres de module prennent effet immédiatement. Désactiver un module le masquera de la navigation.', + failedToUpload: 'Échec du téléversement : {error}', + failedToReload: 'Échec du rechargement de l\'extension : {error}', + failedToDelete: 'Échec de la suppression de l\'extension : {error}', + pleaseSelectPlugin: 'Veuillez sélectionner un fichier d\'extension', + wasmNeedsManifest: + 'Les fichiers WASM nécessitent un fichier manifest.json. Veuillez en sélectionner un ou téléverser un fichier .zip contenant les deux.', + by: 'Par', + settingsSavedSuccess: 'Paramètres des modules enregistrés avec succès !', + failedToSave: 'Échec de l\'enregistrement des paramètres des modules. Veuillez réessayer.', + }, + + // AI Features Settings + aiFeatures: { + title: 'Fonctionnalités IA', + subtitle: 'Configurer les fonctionnalités IA activées et leur attribuer des connexions LLM spécifiques', + loading: 'Chargement de la configuration des fonctionnalités IA...', + saveSuccess: 'Configuration des fonctionnalités IA enregistrée', + saveFailed: 'Échec de l\'enregistrement de la configuration IA', + loadFailed: 'Échec du chargement de la configuration IA', + noConnections: 'Aucune connexion LLM configurée. Ajoutez d\'abord des connexions dans Connexions IA.', + modeDefault: 'Utiliser la valeur par défaut', + modeSpecific: 'Utiliser une connexion spécifique', + modeDisabled: 'Désactivé', + selectConnection: 'Sélectionner une connexion...', + scheduleLabel: 'Fréquence de génération', + scheduleDaily: 'Une fois par jour', + scheduleEvery6h: 'Toutes les 6 heures', + features: { + ai_chat: { + name: 'Clavardage IA', + description: 'Discussion interactive avec l\'assistant IA au fait du contexte de l\'espace', + }, + daily_briefing: { + name: 'Briefing quotidien', + description: 'Résumés quotidiens automatisés avec fréquence de génération configurable', + }, + plan_my_day: { + name: 'Planifier ma journée', + description: 'Planification et priorisation quotidiennes des tâches assistées par IA', + }, + catch_me_up: { + name: 'Mettre à jour', + description: 'Obtenir un résumé de l\'activité récente sur un élément de travail', + }, + find_similar: { + name: 'Trouver des éléments similaires', + description: 'Rechercher des éléments similaires par analyse IA', + }, + decompose: { + name: 'Décomposer', + description: 'Subdiviser les éléments de travail en sous-tâches plus petites', + }, + release_notes: { + name: 'Notes de version', + description: 'Générer des notes de version à partir de l\'avancement du jalon', + }, + dependency_analysis: { + name: 'Analyse des dépendances', + description: 'Analyser et suggérer des dépendances entre éléments', + }, + logbook_articles: { + name: 'Articles de journal de bord', + description: + 'Classification, nettoyage et génération d\'articles automatisés pour le journal de bord', + }, + }, + }, + + workItemStaleness: { + title: 'Inactivité des éléments', + subtitle: 'Appliquer un seul seuil d\'activité aux détails des éléments et aux analyses', + thresholdTitle: 'Seuil d\'inactivité des éléments', + thresholdDescription: + 'Un élément non terminé devient inactif après ce nombre de jours complets sans commentaire, modification ou autre activité.', + daysLabel: 'Jours sans activité', + daysHelper: 'Choisissez un nombre entier entre 1 et 365.', + impact: 'Ce changement s\'applique à l\'ensemble des espaces de travail et prend effet immédiatement.', + save: 'Enregistrer le seuil', + saveSuccess: 'Seuil d\'inactivité enregistré avec succès', + saveFailed: 'Échec de l\'enregistrement du seuil d\'inactivité', + loadFailed: 'Échec du chargement des paramètres d\'inactivité', + validation: 'Saisissez un nombre entier compris entre 1 et 365 jours.', + }, + + // Action Capabilities + actionCapabilities: { + title: 'Capacités d\'action', + subtitle: 'Gérer les capacités allouées aux nœuds d\'action d\'automatisation', + addCapability: 'Ajouter une capacité', + editCapability: 'Modifier la capacité', + deleteCapability: 'Supprimer la capacité', + name: 'Nom', + namePlaceholder: 'ex. : Bac à sable Python', + capabilityType: 'Type de capacité', + selectType: 'Sélectionner un type...', + enabled: 'Activé', + noCapabilities: 'Aucune capacité d\'action configurée pour le moment.', + addFirst: 'Ajoutez votre première capacité', + createSuccess: 'Capacité d\'action créée', + updateSuccess: 'Capacité d\'action mise à jour', + deleteSuccess: 'Capacité d\'action supprimée', + createFailed: 'Échec de la création de la capacité', + updateFailed: 'Échec de la mise à jour de la capacité', + deleteFailed: 'Échec de la suppression de la capacité', + loadFailed: 'Échec du chargement des capacités d\'action', + confirmDelete: 'Voulez-vous vraiment supprimer', + typeDocker: 'Environnement Docker', + typeHTTP: 'Client HTTP', + typeLLM: 'Connexion LLM', + docker: { + image: 'Image Docker', + imagePlaceholder: 'ex. : python:3.12-slim', + memory: 'Limite de mémoire', + memoryPlaceholder: 'ex. : 512m', + cpus: 'Limite de processeur (CPU)', + cpusPlaceholder: 'ex. : 1', + networkMode: 'Mode réseau', + envVars: 'Variables d\'environnement', + addEnvVar: 'Ajouter une variable', + key: 'Clé', + value: 'Valeur', + healthCheck: 'Bilan de santé (Health Check)', + endpoint: 'Point d\'accès (Endpoint)', + endpointPlaceholder: 'ex. : /health', + intervalSecs: 'Intervalle (s)', + timeoutSecs: 'Délai d\'expiration (s)', + }, + http: { + allowedPatterns: 'Motifs d\'URL autorisés', + addPattern: 'Ajouter un motif', + patternPlaceholder: 'ex. : https://api.example.com/*', + defaultHeaders: 'En-têtes par défaut', + addHeader: 'Ajouter un en-tête', + key: 'Clé', + value: 'Valeur', + timeout: 'Délai d\'expiration (secondes)', + timeoutPlaceholder: 'ex. : 30', + }, + llm: { + connection: 'Connexion LLM', + selectConnection: 'Sélectionner une connexion...', + noConnections: 'Aucune connexion LLM disponible. Configurez-en une dans Connexions IA.', + }, + }, + + localizedObjects: { + displayLabelFor: 'Libellé d\'affichage pour {locale}', + primaryHelp: 'Il s\'agit du libellé visible par les administrateurs et utilisateurs dans la langue active.', + localeOverrides: 'Surcharges de langue', + canonicalFallback: 'Avancé : valeur canonique de secours', + canonicalHelp: + 'La valeur canonique est utilisée uniquement si aucune traduction intégrée ne s\'applique. Les identifiants, clés système et automatisations restent inchangés.', + baseName: 'Nom canonique de secours', + baseDescription: 'Description canonique de secours', + chooseCanonicalEffect: 'Choisir l\'effet sur les surcharges de langue…', + keepOverrides: 'Modifier uniquement la valeur de secours et conserver les surcharges de langue', + removeInstanceOverrides: 'Modifier la valeur de secours et supprimer les surcharges personnalisées', + canonicalWarning: + 'Ces traductions locales continueront de prévaloir sur la valeur canonique. Choisissez explicitement la gestion des surcharges administrateur.', + canonicalChoiceRequired: 'Choisissez l\'impact du changement canonique sur les surcharges de langue.', + localizedNameRequired: 'Le libellé d\'affichage de la langue active est requis.', + shippedFallback: 'Valeur intégrée de secours : {value}', + permissionRequired: 'Seul un administrateur d\'instance peut gérer les libellés traduits.', + loadFailed: 'Échec du chargement des surcharges de langue.', + }, + + // Workspace Roles + workspaceRoles: { + readOnlyNote: + 'Les rôles d\'espace de travail sont définis par le système et ne peuvent pas être modifiés. Ils servent de référence pour comprendre les permissions associées.', + addCustom: 'Ajouter un rôle personnalisé', + addCustomTitle: 'Nouveau rôle personnalisé', + labelOnlyBadge: 'Libellé uniquement', + permissionBearingBadge: 'Porteur de permissions', + labelOnlyNotice: + 'Les rôles personnalisés servent actuellement de libellés uniquement. Ils permettent d\'acheminer les approbations mais ne peuvent pas encore accorder de permissions.', + cannotDeleteSystem: 'Les rôles système ne peuvent pas être supprimés.', + deleteConfirm: 'Supprimer ce rôle personnalisé ? Les membres perdront ce libellé.', + nameRequired: 'Un nom de rôle est requis.', + nameTaken: 'Un rôle portant ce nom existe déjà.', + namePlaceholder: 'ex. : Réviseurs AQ', + descriptionPlaceholder: 'Ce que représente ce rôle (optionnel)', + }, + + // Board Configuration + boardConfig: { + configureBoard: 'Configurer le tableau', + columns: 'Colonnes', + backlog: 'Carnet de commandes (Backlog)', + dragToReorder: 'Glisser pour réordonner', + addColumn: 'Ajouter une colonne', + saveConfiguration: 'Enregistrer la configuration', + unsavedChanges: 'Modifications non enregistrées', + columnNameRequired: 'Le nom de la colonne est requis', + resetToDefault: 'Réinitialiser par défaut', + noStatusesMapped: 'Aucun statut associé', + wipLimit: 'Limite TEC (WIP)', + mappedStatuses: 'Statuts associés', + noStatusesSelected: 'Aucun statut sélectionné', + backlogStatuses: 'Statuts du carnet de commandes', + backlogStatusesHelp: 'Les éléments ayant ces statuts apparaîtront dans le carnet de commandes', + availableStatuses: 'Statuts disponibles', + dragStatusesToColumns: 'Glissez des statuts dans les colonnes pour les assigner', + dropStatusesHere: 'Déposez les statuts ici', + allStatusesAssigned: 'Tous les statuts ont été assignés à des colonnes', + noStatusesMatchSearch: 'Aucun statut ne correspond à votre recherche', + boardColumns: 'Colonnes du tableau', + searchStatuses: 'Rechercher des statuts...', + cardFields: 'Champs de carte', + cardFieldsTitle: 'Champs de carte', + cardFieldsDescription: 'Configurer les champs affichés sur les cartes du tableau.', + noCardFields: 'Aucun champ de carte configuré.', + addField: 'Ajouter un champ', + systemFields: 'Champs système', + customFields: 'Champs personnalisés', + manageCustomFields: 'Gérer les champs personnalisés', + createCustomField: 'Créer un champ personnalisé', + customFieldsGlobalNote: 'Les champs personnalisés sont gérés globalement par un administrateur système.', + latestFiftyRightmost: 'Afficher seulement les 50 derniers éléments dans la colonne la plus à droite', + latestFiftyRightmostHint: 'Utile pour les colonnes "Terminé" très chargées sans limiter le reste du tableau.', + hideCompletedByAge: 'Masquer les éléments terminés anciens', + hideCompletedByAgeHint: 'Le travail non terminé reste visible ; le travail terminé est filtré par activité récente.', + recentActivityWindow: 'Fenêtre d\'activité récente', + days: 'jours', + retentionWindowHint: 'Les éléments terminés sans activité pendant cette période sont masqués.', + retentionRangeError: 'Saisissez un nombre entier entre 1 et 3650.', + backlogSelected: '{count} statuts sélectionnés pour le carnet de commandes', + backlogSelected_one: '{count} statut sélectionné pour le carnet de commandes', + backlogSelected_other: '{count} statuts sélectionnés pour le carnet de commandes', + systemFieldNames: { + status: 'Statut', priority: 'Priorité', milestone: 'Jalon', iteration: 'Itération', + due_date: 'Date d\'échéance', start_date: 'Date de début', end_date: 'Date de fin', labels: 'Étiquettes', + created_at: 'Créé le', project: 'Projet', parent: 'Parent', time_in_status: 'Temps dans le statut', + story_points: 'Points d\'histoire', estimate: 'Estimation', + }, + }, + }, + + // Roles and Permissions + roles: { + title: 'Rôles', + subtitle: 'Gérer les rôles et les niveaux d\'accès', + role: 'Rôle', + roles_one: '{count} rôle', + roles_other: '{count} rôles', + createRole: 'Créer un rôle', + editRole: 'Modifier le rôle', + deleteRole: 'Supprimer le rôle', + roleName: 'Nom du rôle', + roleDescription: 'Description', + permissions: 'Permissions', + members: 'Membres', + assignRole: 'Attribuer le rôle', + removeRole: 'Retirer le rôle', + noRoles: 'Aucun rôle trouvé', + roleCreated: 'Rôle créé avec succès', + roleUpdated: 'Rôle mis à jour avec succès', + roleDeleted: 'Rôle supprimé avec succès', + cannotDeleteSystemRole: 'Les rôles système ne peuvent pas être supprimés', + }, + + // Permissions + permissions: { + title: 'Permissions', + subtitle: 'Configurer les ensembles de permissions', + permission: 'Permission', + permissionSet: 'Ensemble de permissions', + grantPermission: 'Accorder la permission', + revokePermission: 'Révoquer la permission', + read: 'Lecture', + write: 'Écriture', + delete: 'Supprimer', + admin: 'Admin', + manage: 'Gérer', + view: 'Afficher', + edit: 'Modifier', + create: 'Créer', + noAccessMessage: 'Vous n\'avez pas la permission d\'accéder à cette page.', + }, +}; \ No newline at end of file diff --git a/frontend/src/lib/locales/fr/adminOperations.js b/frontend/src/lib/locales/fr/adminOperations.js new file mode 100644 index 00000000..a60214b3 --- /dev/null +++ b/frontend/src/lib/locales/fr/adminOperations.js @@ -0,0 +1,625 @@ +export default { + settings: { + adminOperations: { + tabs: { + capabilities: 'Capacités', + credentials: 'Identifiants', + runnerPools: 'Pools d’exécutants', + }, + agentTemplates: { + title: 'Modèles d’agent', + subtitle: 'Surcharger le catalogue de création d’Agent Studio : noms de modèles, types de profil et instructions', + addTemplate: 'Ajouter un modèle', + empty: 'Aucune surcharge configurée pour le moment. Agent Studio utilise les valeurs par défaut intégrées.', + standard: 'Standard', + coding: 'Développement', + startFromNew: 'Partir de… (nouveau modèle personnalisé)', + loadFailed: 'Échec du chargement des modèles d’agent : {error}', + templateKeyRequired: 'La clé de modèle est requise', + nameRequired: 'Le nom est requis', + saveFailed: 'Échec de l’enregistrement : {error}', + deleteMessage: 'Voulez-vous vraiment supprimer la surcharge pour « {name} » ? Cela restaure le modèle par défaut.', + key: 'Clé', + profileType: 'Type de profil', + editOverride: 'Modifier la surcharge de modèle', + addOverride: 'Ajouter une surcharge de modèle', + startFromDefault: 'Partir du modèle par défaut', + startFromPlaceholder: 'Remplacer un modèle intégré ou en créer un nouveau', + startFromHelp: 'Choisir un modèle par défaut le remplace par vos modifications ; « Partir de… » crée un tout nouveau modèle.', + templateKey: 'Clé de modèle', + templateKeyPlaceholder: 'ex. software_engineer', + builtInOverride: 'Cette surcharge remplace le modèle intégré « {name} ».', + namePlaceholder: 'ex. Ingénieur logiciel', + instructions: 'Instructions', + instructionsPlaceholder: 'L’invite système (prompt) ou le rôle pour ce modèle', + }, + llmConnections: { + title: 'Connexions IA', + subtitle: 'Configurer les fournisseurs de modèles IA pour les fonctionnalités intelligentes', + addConnection: 'Ajouter une connexion', + empty: 'Aucune connexion IA configurée pour le moment.', + addFirst: 'Ajouter votre première connexion', + provider: 'Fournisseur', + model: 'Modèle', + default: 'Par défaut', + referenced: 'Référencé par des capacités activées', + vision: 'Vision', + noVision: 'Sans vision', + visionTitle: 'Ce modèle peut analyser les images sur les éléments de travail', + noVisionTitle: 'Ce modèle est uniquement textuel et ne peut pas traiter les images', + testConnection: 'Tester la connexion', + addTitle: 'Ajouter une connexion IA', + editTitle: 'Modifier la connexion IA', + disableWarning: 'Désactiver cette connexion désactivera au moment de l’exécution les capacités d’action LLM qui en dépendent.', + disableWarningHelp: 'Référencé par {capabilities}. Les capacités apparaîtront toujours comme activées, mais les actions les utilisant échoueront tant que la connexion n’est pas réactivée ou que la capacité n’est pas réorientée.', + namePlaceholder: 'ex. OpenRouter Claude Sonnet', + selectProvider: 'Sélectionner un fournisseur…', + baseUrl: 'URL de base', + apiKey: 'Clé API', + keyConfigured: 'Clé configurée (laisser vide pour conserver)', + enterApiKey: 'Saisir la clé API', + searchModels: 'Rechercher des modèles ou saisir un identifiant…', + typeModelId: 'Saisir un identifiant de modèle (ou cliquer sur Rafraîchir)', + supportsImages: 'Prend en charge l’entrée d’images', + textOnly: 'Texte uniquement', + lastAttemptFailed: 'La dernière tentative a échoué : {error}', + cachedCount: '{count} en cache', + refreshing: 'Rafraîchissement…', + enterBaseUrlHelp: 'Saisissez une URL de base pour récupérer les modèles, ou saisissez un identifiant de modèle ci-dessus pour l’utiliser sans parcourir la liste.', + noCachedModels: 'Aucun modèle en cache. Cliquez sur Rafraîchir pour récupérer depuis {provider}, ou saisissez un identifiant de modèle ci-dessus pour l’utiliser sans parcourir la liste.', + selectModel: 'Sélectionner un modèle…', + providerConfig: 'JSON de configuration du fournisseur', + providerConfigHelp: 'Champs de requête optionnels de niveau supérieur fusionnés dans les appels au fournisseur. Les champs Windshift existants prévalent.', + visionSupport: 'Prise en charge de la vision', + visionAuto: 'Auto (détecter depuis le modèle)', + visionOn: 'Activé (forcer la vision)', + visionOff: 'Désactivé (forcer le texte uniquement)', + visionHelp: 'Contrôle si l’agent de développement peut envoyer des images à ce modèle. « Auto » utilise la capacité connue du modèle ; surchargez ce paramètre pour les modèles locaux ou personnalisés.', + defaultConnection: 'Connexion par défaut', + rateHint: '{prompt}/{completion} par million de jetons', + neverRefreshed: 'Jamais rafraîchi', + refreshedNow: 'Rafraîchi à l’instant', + refreshedMinutes: 'Rafraîchi il y a {count} min', + refreshedHours: 'Rafraîchi il y a {count} h', + refreshedDays: 'Rafraîchi il y a {count} j', + modelsFetched: '{count} modèles récupérés depuis {provider}', + refreshFailed: 'Échec du rafraîchissement des modèles', + loadFailed: 'Échec du chargement des connexions IA', + oneCapability: '1 capacité d’action activée : {name}', + manyCapabilities: '{count} capacités d’action activées : {names}', + deleteTitle: 'Supprimer la connexion IA', + deleteMessage: 'Voulez-vous vraiment supprimer {name} ? Cette action est irréversible.', + deleteImpact: 'Cette connexion est référencée par {capabilities}. Ces capacités cesseront de fonctionner.', + deleted: 'Connexion IA supprimée', + deleteFailed: 'Échec de la suppression de la connexion', + configObject: 'La configuration du fournisseur doit être un objet JSON', + configJson: 'La configuration du fournisseur doit être un JSON valide', + created: 'Connexion IA créée', + createFailed: 'Échec de la création de la connexion', + updated: 'Connexion IA mise à jour', + updateFailed: 'Échec de la mise à jour de la connexion', + connectionSuccessful: 'Connexion réussie', + testPassed: 'Test de connexion réussi', + testFailed: 'Échec du test de connexion', + }, + actionCapabilities: { + workspaceScope: 'Portée de l’espace de travail', + availableAll: 'Disponible dans tous les espaces de travail', + availableAllHelp: 'Les actions de n’importe quel espace de travail peuvent référer à cette capacité.', + restrict: 'Restreindre à des espaces de travail spécifiques', + restrictHelp: 'Seuls les espaces de travail sélectionnés ci-dessous peuvent référer à cette capacité.', + noWorkspaces: 'Aucun espace de travail disponible.', + useCredential: 'Utiliser un identifiant d’authentification', + credential: 'Identifiant', + globalCredentialHelp: 'Les capacités globales ne peuvent référer qu’à des identifiants globaux.', + workspaceCredentialHelp: 'Les capacités restreintes aux espaces de travail peuvent référer à des identifiants globaux ou à des identifiants autorisés dans leur espace de travail.', + headerName: 'Nom de l’en-tête', + scheme: 'Schéma (jetons porteurs / bearer uniquement)', + additionalHeaders: 'En-têtes secrets supplémentaires', + addSecretHeader: 'Ajouter un en-tête secret', + additionalHeadersHelp: 'Pour les API nécessitant plusieurs en-têtes secrets. Chaque ligne résout un identifiant lors de la requête.', + defaultHeadersHelp: 'En-têtes non sensibles uniquement. Les jetons d’authentification appartiennent à la section ci-dessous.', + authorizationPlaceholder: 'Authorization', + bearerPlaceholder: 'Bearer', + apiKeyHeaderPlaceholder: 'X-API-Key', + }, + actionCredentials: { + title: 'Identifiants d’action', + subtitle: 'Jetons d’API chiffrés référencés par les capacités HTTP au lieu de stocker les jetons en clair. Les valeurs secrètes sont en écriture seule et ne peuvent pas être relues.', + add: 'Ajouter un identifiant', + addFirst: 'Ajouter le premier identifiant', + empty: 'Aucun identifiant pour le moment.', + bearerToken: 'Jeton porteur (Bearer token)', + apiKey: 'Clé API', + basicAuth: 'Authentification de base (utilisateur:mot_de_passe)', + customHeader: 'Valeur d’en-tête personnalisée', + loadFailed: 'Échec du chargement des identifiants d’action', + nameSecretRequired: 'Le nom et le secret sont requis', + workspaceRequired: 'Sélectionnez au moins un espace de travail, ou rendez l’identifiant disponible dans tous les espaces de travail.', + masked: 'masqué', + created: 'Identifiant créé ({prefix})', + createFailed: 'Échec de la création de l’identifiant', + updated: 'Identifiant mis à jour', + updateFailed: 'Échec de la mise à jour de l’identifiant', + rotated: 'Secret renouvelé ({prefix})', + rotateFailed: 'Échec du renouvellement du secret', + deleteTitle: 'Supprimer l’identifiant', + deleteMessage: 'Supprimer {name} ? Toute capacité y faisant référence échouera lors de l’exécution.', + deleted: 'Identifiant supprimé', + deleteFailed: 'Échec de la suppression de l’identifiant', + secret: 'Secret', + scope: 'Portée', + storedSecret: 'Stocké et jamais affiché en totalité', + noSecret: 'aucun secret', + allWorkspaces: 'Tous les espaces de travail', + workspaceCount: 'Espaces de travail : {count}', + rotateSecret: 'Renouveler le secret', + workspaceScope: 'Portée de l’espace de travail', + availableAll: 'Disponible dans tous les espaces de travail', + availableAllHelp: 'N’importe quel espace de travail peut résoudre cet identifiant.', + restrict: 'Restreindre à des espaces de travail spécifiques', + restrictHelp: 'Seuls les espaces de travail sélectionnés ci-dessous peuvent résoudre cet identifiant.', + noWorkspaces: 'Aucun espace de travail disponible.', + secretPlaceholder: 'Saisissez le jeton, la clé API ou utilisateur:mot_de_passe', + secretHelp: 'Stocké de manière chiffrée. Cette valeur ne sera plus réaffichée ; renouvelez-la ultérieurement si nécessaire.', + metadata: 'Métadonnées (JSON, optionnel)', + metadataHelp: 'Métadonnées non sensibles uniquement. Les clés telles que token, secret et password sont refusées.', + edit: 'Modifier l’identifiant', + storedRotate: 'Stocké ({prefix}) — utilisez Renouveler pour le remplacer.', + rotateTitle: 'Renouveler le secret — {name}', + rotateHelp: 'Saisissez le nouveau secret. L’ancienne valeur sera immédiatement remplacée et ne pourra pas être récupérée.', + newSecret: 'Nouveau secret', + rotate: 'Renouveler', + namePlaceholder: 'GitHub PAT', + }, + runnerPools: { + title: 'Pools d’exécutants', + subtitle: 'Pools d’exécutants distants qui exécutent les tâches de l’agent de développement. Générez un jeton d’enregistrement pour ajouter un hôte exécutant ; consultez la documentation de configuration RUNNER.', + newPool: 'Nouveau pool', + empty: 'Aucun pool d’exécutants', + emptyDescription: 'Créez un pool, puis générez un jeton d’enregistrement pour connecter un hôte exécutant.', + pool: 'Pool', + maxConcurrent: 'Max simultané', + token: 'Jeton', + expires: 'Expire le', + runner: 'Exécutant', + registered: 'Enregistré', + lastHeartbeat: 'Dernier signal de vie', + unlimited: 'Illimité', + registrationTokens: 'Jetons d’enregistrement — {name}', + mintToken: 'Générer un jeton', + noTokens: 'Aucun jeton pour le moment', + never: 'Jamais', + revokeToken: 'Révoquer le jeton', + runnersForPool: 'Exécutants — {name}', + noRunners: 'Aucun exécutant enregistré', + revokeRunner: 'Révoquer l’exécutant', + newPoolTitle: 'Nouveau pool d’exécutants', + maxConcurrentRuns: 'Exécutions simultanées max', + zeroUnlimited: '0 = illimité', + availableAll: 'Disponible pour tous les espaces de travail', + createPool: 'Créer un pool', + mintTitle: 'Générer un jeton d’enregistrement', + descriptionOptional: 'Description (optionnel)', + expiresHours: 'Expire dans (heures)', + zeroNeverExpires: '0 = n’expire jamais (révoquer pour désactiver)', + addHostTitle: 'Ajouter l’hôte exécutant', + installHelp: 'Exécutez ceci sur l’hôte exécutant. Cela installe et démarre le conteneur exécutant avec cette URL de serveur, la balise d’image correspondante et le nouveau jeton.', + manualPrefix: 'Configuration manuelle à la place ? Définissez le jeton comme', + setTokenPrefix: 'Définissez le jeton comme', + tokenOnce: 'sur l’hôte exécutant. Il s’affiche une seule fois et ne pourra plus être récupéré.', + loadFailed: 'Échec du chargement des pools d’exécutants', + tokensLoadFailed: 'Échec du chargement des jetons', + runnersLoadFailed: 'Échec du chargement des instances d’exécutants', + created: 'Pool d’exécutants créé', + createFailed: 'Échec de la création du pool d’exécutants', + mintFailed: 'Échec de la génération du jeton', + revokeTokenTitle: 'Révoquer le jeton d’enregistrement', + revokeTokenMessage: 'Révoquer le jeton {prefix}… ? Les exécutants ne pourront plus s’enregistrer avec celui-ci.', + revoke: 'Révoquer', + tokenRevoked: 'Jeton révoqué', + revokeTokenFailed: 'Échec de la révocations du jeton', + revokeRunnerTitle: 'Révoquer l’exécutant', + revokeRunnerMessage: 'Expulser l’exécutant {name} ? Il devra s’enregistrer à nouveau pour rejoindre le pool.', + runnerRevoked: 'Exécutant révoqué', + revokeRunnerFailed: 'Échec de la révocations de l’exécutant', + copied: 'Copié dans le presse-papiers', + copyFailed: 'Échec de la copie — sélectionnez et copiez manuellement', + revoked: 'Révoqué', + expired: 'Expiré', + active: 'Actif', + online: 'En ligne', + stale: 'Inactif', + unknown: 'Inconnu', + }, + }, + }, + workspaceMembers: { + invalidWorkspace: 'Espace de travail non valide.', + loadFailed: 'Échec du chargement des membres de l’espace de travail.', + addMemberFailed: 'Échec de l’ajout du membre : {error}', + addGroupFailed: 'Échec de l’ajout du groupe : {error}', + removeRoleTitle: 'Retirer le rôle {role} de {name} ?', + removeRoleMessage: 'Cela supprime l’attribution du rôle et ses autorisations.', + removeRoleFailed: 'Échec du retrait du rôle : {error}', + summaryTitle: 'Résumé des accès', + summaryDescription: + 'Affiche comment les rôles sont attribués dans cet espace de travail. Un rôle sans membres ni groupes attribués est accessible à tous. Hiérarchie des permissions : {viewer} → {editor} → {tester}.', + role: 'Rôle', + effectiveAccess: 'Accès', + assignments: 'Attributions', + memberOrGroup: 'Membre ou groupe', + rolesLabel: 'Rôles', + actions: 'Actions', + roles: { + viewer: { + name: 'Lecteur', + description: 'Voir le contenu de l’espace de travail et participer aux discussions', + }, + editor: { + name: 'Éditeur', + description: 'Créer et modifier des éléments de travail dans cet espace de travail', + }, + tester: { + name: 'Testeur', + description: 'Voir les éléments, exécuter les tests, gérer les cas de test et créer des anomalies', + }, + administrator: { + name: 'Administrateur', + description: 'Gérer l’ensemble de l’espace de travail, y compris les autorisations d’accès', + }, + }, + memberCount: '{count} membres', + memberCount_one: '{count} membre', + memberCount_other: '{count} membres', + everyone: 'Tout le monde', + noDirectAssignments: 'Aucune attribution directe', + addMember: 'Ajouter un membre', + addGroup: 'Ajouter un groupe', + searchPlaceholder: 'Rechercher des membres ou des groupes par nom ou e-mail…', + loading: 'Chargement des membres de l’espace de travail…', + empty: 'Aucun membre ou groupe pour le moment. Ajoutez-en pour configurer l’accès à cet espace de travail.', + noSearchResults: 'Aucun membre ou groupe trouvé pour « {query} ».', + unknownUser: 'Utilisateur inconnu', + group: 'Groupe', + removeRoleAction: 'Retirer le rôle : {role}', + addMemberTitle: 'Ajouter un membre à l’espace de travail', + user: 'Utilisateur', + selectUser: 'Sélectionner un utilisateur…', + selectRole: 'Sélectionner un rôle…', + addingMember: 'Ajout du membre…', + cancel: 'Annuler', + addGroupTitle: 'Ajouter un groupe à l’espace de travail', + selectGroup: 'Sélectionner un groupe…', + addingGroup: 'Ajout du groupe…', + }, + boardControls: { + groupBy: 'Regrouper par', + groupByValue: 'Regrouper par : {name}', + sortBy: 'Trier par', + sortBubble: 'Tri : Remontée automatique', + rankMode: 'Mode rang', + rankModeDescription: 'Ordre glisser-déposer manuel', + bubbleMode: 'Mode remontée', + bubbleModeDescription: 'Les cartes récemment actives remontent au sommet', + rightmostColumn: 'colonne la plus à droite', + noSwimlanes: 'Sans couloirs', + noSwimlanesDescription: 'Afficher chaque élément sous forme de carte standard', + hideColumnSwimlanes: 'Masquer les couloirs {name}', + groupOutsideColumnWithType: 'Regrouper les éléments {type} uniquement en dehors de {column}', + groupOutsideColumn: 'Regrouper les éléments uniquement en dehors de {column}', + hiddenCount: '{count} masqués', + useAsSwimlanes: 'Utiliser ces éléments comme couloirs', + }, + systemImport: { + title: 'Importation système', + subtitle: 'Importer des données depuis Jira Cloud et d’autres systèmes externes', + newImport: 'Nouvelle importation', + savedConnections: 'Connexions enregistrées', + savedConnectionsDescription: 'Gérer les connexions Jira Cloud utilisées pour importer des données.', + noConnections: 'Aucune connexion enregistrée pour le moment. Démarrez une importation pour connecter Jira Cloud.', + lastUsed: 'Dernière utilisation', + deleteConnection: 'Supprimer la connexion', + deleteConnectionMessage: 'Supprimer cette connexion ? Cette action est irréversible.', + connectionDeleted: 'Connexion supprimée', + importHistory: 'Historique des importations', + importHistoryDescription: 'Statut et résultats des importations précédentes', + noImports: 'Aucune importation pour le moment. Démarrez une importation pour voir son historique ici.', + instance: 'Instance', + scope: 'Portée', + started: 'Démarré', + completed: 'Terminé', + projects: 'Projets', + workspaceCount: '{count} espaces de travail', + workspaceCount_one: '{count} espace de travail', + workspaceCount_other: '{count} espaces de travail', + itemCount: '{count} éléments', + itemCount_one: '{count} élément', + itemCount_other: '{count} éléments', + statuses: { completed: 'Terminé', running: 'En cours', failed: 'Échoué', data_deleted: 'Données supprimées', queued: 'En attente' }, + phases: { initializing: 'Initialisation', importing_project: 'Importation du projet', completed: 'Terminé', running: 'En cours' }, + scopes: { workItems: 'Éléments de travail' }, + deleteData: 'Supprimer les données', + unavailable: 'Indisponible', + dataDeleted: 'Données Jira importées supprimées. Vous pouvez réexécuter l’importation.', + deleteImportedTitle: 'Supprimer les données Jira importées', + deleteWarningTitle: 'Cela peut supprimer plusieurs espaces de travail Windshift.', + deleteWarningDescription: 'Windshift supprimera tout ce qui a été enregistré par cette importation Jira, y compris les espaces de travail associés, les éléments, les commentaires, les pièces jointes, les liens, les jalons et les métadonnées d’importation. Cette action est irréversible.', + jobId: 'ID de la tâche', + importedScope: 'Données importées', + workspacesToRemove: 'Espaces de travail susceptibles d’être supprimés', + typeJobId: 'Saisissez l’ID de la tâche pour confirmer la suppression', + deleteImportedData: 'Supprimer les données importées', + }, + scmSettings: { + title: 'Gestion de code source (SCM)', + subtitle: 'Connecter les fournisseurs SCM et lier des dépôts à cet espace de travail', + availableProviders: 'Fournisseurs disponibles', + connectedProviders: 'Fournisseurs connectés', + connected: 'Connecté', + connect: 'Connecter', + reconnect: 'Reconnecter', + noProviders: 'Aucun fournisseur SCM configuré', + noProvidersDescription: 'Demandez à un administrateur système de configurer les fournisseurs SCM dans le panneau d’administration.', + noRepositories: 'Aucun dépôt lié pour le moment', + linkRepositories: 'Lier des dépôts', + repositoryCount: '{count} dépôts', + repositoryCount_one: '{count} dépôt', + repositoryCount_other: '{count} dépôts', + repositorySettings: 'Paramètres du dépôt', + tagPattern: 'Motif d’étiquette (tag)', + branchPattern: 'Motif de branche', + tags: 'étiquettes', + branches: 'branches', + patternsHelp: 'Ces motifs glob pilotent l’automatisation des jalons. Les étiquettes correspondantes promeuvent un jalon ; les branches correspondantes créent un jalon de planification.', + repoSettingsSaved: 'Paramètres du dépôt enregistrés', + repoSettingsSaveFailed: 'Échec de l’enregistrement des paramètres du dépôt', + loadFailed: 'Échec du chargement des paramètres SCM', + oauthFailed: 'Échec du démarrage de la reconnexion OAuth', + connectedTo: 'Connecté à {name}', + connectFailed: 'Échec de la connexion au fournisseur', + disconnectMessage: 'Déconnecter {name} ? Cela déliara également tous les dépôts.', + disconnectedFrom: 'Déconnecté de {name}', + disconnectFailed: 'Échec de la déconnexion du fournisseur', + linkedRepositories: '{count} dépôts liés', + linkedRepositories_one: '{count} dépôt lié', + linkedRepositories_other: '{count} dépôts liés', + unlinkMessage: 'Délier {name} ?', + unlinkedRepository: '{name} délié', + unlinkFailed: 'Échec de la dissociation du dépôt', + }, + workspaceTemplates: { + title: 'Modèles d’éléments de travail', + subtitle: 'Descriptions réutilisables pour les nouveaux éléments. Un modèle peut être sélectionné lors de la création d’un élément ou attribué à un type d’élément.', + newTemplate: 'Nouveau modèle', + editTemplate: 'Modifier le modèle', + empty: 'Aucun modèle pour le moment', + emptyDescription: 'Créez un modèle pour donner une structure cohérente aux nouveaux éléments.', + modeLabel: 'Mode', + mode: { + selectable: 'Sélectionnable — disponible lors de la création d’un élément', + mandatory: 'Obligatoire — appliqué à un type d’élément spécifique', + }, + modeValue: { selectable: 'Sélectionnable', mandatory: 'Obligatoire' }, + itemTypes: 'Types d’éléments', + allTypes: 'Tous les types', + allItemTypes: 'Tous les types d’éléments', + selectItemType: 'Sélectionner un type d’élément', + targetTypesOptional: 'Types d’éléments (optionnel ; laisser vide pour tous)', + targetTypeRequired: 'Type d’élément (sélectionner exactement un)', + descriptionBody: 'Description (Markdown)', + bodyPlaceholder: '## Étapes pour reproduire\n\n1. …', + mandatoryTypeError: 'Un modèle obligatoire doit cibler exactement un type d’élément.', + createTemplate: 'Créer le modèle', + deleteTemplate: 'Supprimer le modèle', + deleteTitle: 'Supprimer le modèle ?', + deleteMessage: 'Supprimer « {name} » ? Le modèle ne sera plus proposé pour les nouveaux éléments. Les éléments existants ne seront pas modifiés.', + loadFailed: 'Échec du chargement des modèles', + saveFailed: 'Échec de l’enregistrement du modèle', + deleteFailed: 'Échec de la suppression du modèle', + created: 'Modèle créé', + updated: 'Modèle mis à jour', + deleted: 'Modèle supprimé', + }, + systemCatalog: { + statuses: { + open: 'Ouvert', + inProgress: 'En cours', + done: 'Terminé', + }, + statusCategories: { + toDo: 'À faire', + inProgress: 'En cours', + done: 'Terminé', + }, + permissions: { + system_admin: { name: 'Administrateur système', description: 'Accès complet à l’administration du système' }, + workspace_create: { name: 'Créer des espaces de travail', description: 'Peut créer de nouveaux espaces de travail' }, + milestone_create: { name: 'Gérer les jalons globaux', description: 'Peut créer et gérer des jalons globaux visibles dans tous les espaces de travail' }, + iteration_manage: { name: 'Gérer les itérations globales', description: 'Peut créer et gérer des itérations globales visibles dans tous les espaces de travail' }, + user_list: { name: 'Lister les utilisateurs', description: 'Peut consulter l’annuaire des utilisateurs' }, + asset_manage: { name: 'Gérer les ensembles de ressources', description: 'Peut créer et gérer des ensembles de ressources' }, + customers_manage: { name: 'Gérer les clients', description: 'Peut gérer les organisations clientes et les clients du portail' }, + project_manage: { name: 'Gérer les projets temporels', description: 'Peut gérer tous les projets temporels et les journaux de travail' }, + workspace_admin: { name: 'Gérer l’espace de travail', description: 'Accès administratif complet à un espace de travail, y compris les paramètres et la configuration' }, + item_view: { name: 'Afficher l’espace de travail et les éléments', description: 'Peut consulter l’espace de travail et les éléments de travail' }, + item_create: { name: 'Créer des éléments', description: 'Peut créer des éléments de travail dans un espace de travail' }, + item_edit: { name: 'Modifier des éléments', description: 'Peut modifier des éléments de travail dans un espace de travail' }, + item_delete: { name: 'Supprimer des éléments', description: 'Peut supprimer des éléments de travail dans un espace de travail' }, + item_comment: { name: 'Ajouter des commentaires et modifier les siens', description: 'Peut ajouter des commentaires et modifier ses propres commentaires' }, + comment_edit_others: { name: 'Modifier les autres commentaires', description: 'Peut modifier les commentaires créés par d’autres utilisateurs' }, + test_view: { name: 'Afficher les tests', description: 'Peut consulter les cas de test, les exécutions de test et les résultats de test' }, + test_execute: { name: 'Exécuter des tests', description: 'Peut exécuter des séries de tests et enregistrer les résultats' }, + test_manage: { name: 'Gérer les tests', description: 'Peut créer, modifier et supprimer des cas, ensembles et dossiers de test' }, + action_manage: { name: 'Gérer les actions', description: 'Peut créer, modifier, supprimer et exécuter des actions d’espace de travail' }, + action_credential_manage: { name: 'Gérer les identifiants d’action', description: 'Peut créer, renouveler et supprimer des identifiants d’action restreints à l’espace de travail' }, + action_set_actor: { name: 'Définir l’acteur de l’action', description: 'Peut exécuter une action en tant qu’un autre utilisateur, y compris entre espaces de travail' }, + teams_manage: { name: 'Gérer les équipes', description: 'Peut créer, modifier et supprimer des équipes' }, + public_board_manage: { name: 'Gérer les tableaux publics', description: 'Peut rendre des collections publiques et configurer le partage de tableaux publics' }, + }, + }, + securitySettings: { + agentUpdateFailed: 'Échec de la mise à jour des paramètres de sécurité des agents', + policySaveFailed: 'Échec de l’enregistrement de la politique d’authentification', + workspaceAgents: 'Agents gérés par l’espace de travail', + workspaceAgentsDescription: 'Autoriser les administrateurs d’espace de travail à créer des comptes de service d’agent détenus par leur espace de travail. Les comptes existants restent disponibles lorsque cela est désactivé.', + userAgents: 'Agents gérés par l’utilisateur', + userAgentsDescription: 'Autoriser les utilisateurs à créer des agents dans leur profil et à leur émettre des jetons d’API. Les agents héritent toujours des autorisations de leur propriétaire.', + maxAgentsPerUser: 'Limite d’agents par utilisateur', + maxAgentsDescription: 'Régler sur 0 pour empêcher les utilisateurs de créer des agents personnels. Maximum : 1000.', + centralizedUsers: 'Comptes de service d’agent centralisés', + centralizedUsersDescription: 'Autoriser les administrateurs d’espace de travail à lier les exécutions d’agents de développement à des comptes de service centralisés approuvés.', + centralizedUsersWarning: 'Chaque liaison est enregistrée dans le journal d’audit. N’approuvez que les comptes destinés à l’automatisation partagée.', + authenticationPolicy: 'Politique de connexion', + authenticationPolicyDescription: 'Choisir comment les utilisateurs s’authentifient sur Windshift.', + authenticationMethod: 'Méthode de connexion', + previewMode: 'Mode aperçu', + previewModeDescription: 'Afficher les avertissements de politique sans bloquer la connexion.', + userStatistics: 'Statistiques utilisateurs', + totalUsers: 'Total des utilisateurs', + withPasskey: 'Avec une clé d’accès (passkey)', + withoutPasskey: 'Sans clé d’accès', + ssoUsers: 'Utilisateurs SSO', + systemAdmins: 'Administrateurs système', + affectedUsers: 'Utilisateurs affectés : {count}', + admin: 'Administrateur', + policyActive: 'Politique appliquée', + policyActiveDescription: 'Les utilisateurs ne répondant pas aux exigences sélectionnées pourraient ne pas pouvoir se connecter.', + adminFallbackShort: 'Le recours d’urgence pour administrateur est actuellement activé.', + fallbackEnabled: 'Recours d’urgence administrateur activé', + fallbackEnabledDescription: 'Les administrateurs système peuvent toujours se connecter avec un mot de passe si la méthode principale est indisponible.', + disableFallback: 'Redémarrer le serveur sans', + fallbackDisabled: 'Recours d’urgence administrateur désactivé', + fallbackDisabledDescription: 'La politique sélectionnée s’applique également aux administrateurs système. Pour activer l’accès d’urgence, démarrez le serveur avec', + enableFallback: 'Démarrer le serveur avec', + requiresSso: 'configurer le SSO d’abord', + notRecommendedWithSso: 'non disponible lorsque le SSO est configuré', + enableCentralizedTitle: 'Activer les comptes de service centralisés ?', + enableCentralizedMessage: 'Les administrateurs d’espace de travail pourront lier les exécutions d’agents de développement aux comptes de service approuvés ci-dessous.', + disableCentralizedTitle: 'Désactiver les comptes de service centralisés ?', + disableCentralizedMessage: 'Les nouvelles liaisons seront bloquées. Les approbations existantes restent enregistrées mais inactives.', + reasonLabel: 'Raison (enregistrée dans le journal d’audit)', + reasonPlaceholder: 'Pourquoi modifiez-vous ce paramètre ?', + policy: { + password: 'Mot de passe uniquement', + passwordDescription: 'Authentification standard par mot de passe.', + passwordOrSso: 'Mot de passe ou SSO', + passwordOrSsoDescription: 'Les utilisateurs peuvent se connecter avec un mot de passe ou via le fournisseur SSO configuré.', + passwordPasskey: 'Mot de passe + clé d’accès en second facteur', + passwordPasskeyDescription: 'Exiger un mot de passe suivi d’une clé d’accès (passkey) enregistrée.', + passkeyOnly: 'Clé d’accès uniquement', + passkeyOnlyDescription: 'Exiger une clé d’accès enregistrée ; les mots de passe ne peuvent pas être utilisés pour une connexion normale.', + ssoRequired: 'SSO uniquement', + ssoRequiredDescription: 'Exiger des utilisateurs qu’ils se connectent via le fournisseur SSO configuré.', + }, + allowlist: { + title: 'Comptes de service centralisés approuvés', + disabledHint: 'Les approbations sont enregistrées mais inactives tant que le paramètre ci-dessus n’est pas activé. Vous pouvez les préparer à l’avance.', + empty: 'Aucun compte n’est encore approuvé. Ajoutez-en un ci-dessous pour le rendre disponible aux administrateurs d’espace de travail.', + user: 'Compte de service', + workspace: 'Espace de travail', + scope: 'Disponibilité', + reason: 'Raison', + reasonLogged: 'Raison (enregistrée dans le journal d’audit)', + reasonExample: 'Par exemple, déploiement pilote pour acme-agent', + pickUser: 'Sélectionner un compte de service', + anyWorkspace: 'N’importe quel espace de travail', + userNumber: 'Utilisateur n°{id}', + workspaceNumber: 'Espace de travail n°{id}', + add: 'Approuver le compte', + remove: 'Révoquer l’approbation', + removeFor: 'Révoquer l’approbation pour {name}', + removeTitle: 'Révoquer cette approbation ?', + removeMessage: 'Cela révoque l’approbation pour {name}. Les administrateurs d’espace de travail ne pourront plus utiliser ce compte dans la portée sélectionnée.', + removeReason: 'Pourquoi révoquez-vous cette approbation ?', + loadFailed: 'Échec du chargement des comptes de service approuvés', + addFailed: 'Échec de l’approbation du compte de service', + removeFailed: 'Échec de la révocations de l’approbation', + }, + }, + + diagnostics: { + title: 'Diagnostics', + subtitle: 'Signaux opérationnels du système en cours d’exécution. Les seuils d’avertissement peuvent être ajustés sans modifier les limites strictes de sécurité.', + groups: { overview: 'Vue d’ensemble', automation: 'Automatisation', data: 'Données', infrastructure: 'Infrastructure' }, + tabs: { + clock: 'Horloge serveur', actions: 'Exécutions d’actions', webhooks: 'Livraisons de webhooks', schedulers: 'Tâches en arrière-plan', + recurrence: 'Récurrence', domainEvents: 'Événements du domaine', fracIndex: 'Index fractionnaire', runnerPools: 'Pools d’exécutants', + databasePools: 'Pools de bases de données', cacheMemory: 'Mémoire cache', scmConnections: 'Connexions SCM', + }, + clock: { + title: 'Horloge serveur', + subtitle: 'Compare l’en-tête HTTP Date des réponses API avec l’horloge du navigateur. La médiane glissante des cinq derniers échantillons corrige les horodatages affichés et déclenche un avertissement si le décalage dépasse le seuil.', + currentOffset: 'Décalage actuel', driftStatus: 'État de dérive', samplesCollected: 'Échantillons collectés', driftThreshold: 'Seuil de dérive', + recentSamples: 'Échantillons récents', autoRefresh: 'Les plus récents en premier · se rafraîchit toutes les 2 secondes', when: 'Quand', clientTime: 'Heure client (UTC)', serverTime: 'Heure serveur (UTC)', offset: 'Décalage', + noSamples: 'Aucun échantillon pour le moment. Les échantillons sont enregistrés automatiquement lorsque les requêtes API se terminent.', noSamplesShort: 'Aucun échantillon', overThreshold: 'Au-dessus du seuil', withinThreshold: 'Dans le seuil', inSync: 'Synchronisé', + ahead: '{value} en avance', behind: '{value} en retard', minutesSeconds: '{minutes}m {seconds}s', seconds: '{seconds}s', minutesShort: '{count}m', secondsShort: '{count}s', zeroSeconds: '0s', signedSeconds: '{value}s', justNow: 'à l’instant', secondsAgo: 'il y a {count}s', minutesSecondsAgo: 'il y a {minutes}m {seconds}s', + }, + }, + + integrations: { + directions: { + outbound: 'Sortant', + inbound: 'Entrant', + outboundDescription: 'Applications auxquelles Windshift se connecte. Ajoutez les identifiants OAuth utilisés par Windshift pour lire des données depuis des services externes tels que Notion ou Confluence.', + inboundDescription: 'Applications qui se connectent à Windshift au nom d’un utilisateur. Enregistrez une application tierce une fois ; les utilisateurs l’autorisent ensuite avec OAuth 2.0 et PKCE.', + }, + providers: { + loadFailed: 'Échec du chargement des fournisseurs d’intégration', + updated: 'Fournisseur mis à jour', + created: 'Fournisseur créé', + saveFailed: 'Échec de l’enregistrement du fournisseur', + deleteTitle: 'Supprimer « {name} » ?', + deleteMessage: 'Cela supprimera le fournisseur d’intégration et déconnectera tous les utilisateurs.', + deleted: 'Fournisseur supprimé', + deleteFailed: 'Échec de la suppression du fournisseur', + oauthConfigured: 'OAuth configuré', + namePlaceholder: 'Mon intégration Notion', + slug: 'Slug', + }, + oauthClients: { + title: 'Clients OAuth', + subtitle: 'Enregistrer des applications tierces pouvant autoriser les utilisateurs Windshift avec OAuth 2.0 et PKCE et générer des jetons d’API par utilisateur.', + template: 'Modèle', + oauthCallback: 'Rappel OAuth (callback)', + registerClient: 'Enregistrer le client', + empty: 'Aucun client OAuth enregistré', + emptyDescription: 'Cliquez sur Enregistrer le client pour ajouter une application tierce.', + loadFailed: 'Échec du chargement des clients OAuth', + updated: 'Client OAuth mis à jour', + publicCreated: 'Client OAuth public créé', + saveFailed: 'Échec de l’enregistrement du client OAuth', + rotateTitle: 'Renouveler le secret pour « {name} » ?', + rotateMessage: 'L’ancien secret cesse de fonctionner immédiatement. Les jetons existants restent valides jusqu’à leur expiration ; les futurs échanges de jetons avec l’ancien secret échoueront.', + rotateFailed: 'Échec du renouvellement du secret', + deleteTitle: 'Supprimer « {name} » ?', + deleteMessage: 'Tous les jetons d’accès et de rafraîchissement émis pour ce client seront immédiatement révoqués. Les utilisateurs connectés devront se reconnecter.', + deleted: 'Client OAuth supprimé', + deleteFailed: 'Échec de la suppression du client OAuth', + scopeHelp: 'Les jetons d’accès émis sont des jetons d’API Windshift par utilisateur. Ils héritent des autorisations de l’utilisateur demandeur, limitées aux portées sélectionnées ici.', + scopeCount: 'Portées : {count}', + redirectCount: 'URI de redirection : {count}', + clientId: 'ID Client', + clientSecret: 'Secret Client', + secretSet: 'secret défini', + secretMissing: 'secret manquant', + rotateSecret: 'Renouveler le secret client', + deleteClient: 'Supprimer le client', + editTitle: 'Modifier le client OAuth', + registerTitle: 'Enregistrer un client OAuth', + displayName: 'Nom d’affichage', + displayNamePlaceholder: 'ex. Docmost', + slug: 'Slug', + clientType: 'Type de client', + confidential: 'Confidentiel (serveur à serveur, possède un secret)', + public: 'Public (PKCE uniquement, sans secret)', + clientTypeHelp: 'Les clients confidentiels stockent un client_secret sur le serveur. Les clients publics doivent utiliser PKCE pour chaque échange de jeton.', + redirectUris: 'URI de redirection', + redirectHelp: 'Saisissez une URI par ligne. Le redirect_uri de la demande d’autorisation doit correspondre exactement à l’une d’entre elles.', + allowedScopes: 'Portées autorisées', + register: 'Enregistrer', + copySecretNow: 'Copier ce secret maintenant', + secretWarning: 'Le secret client ne s’affiche qu’une seule fois. Windshift ne stocke que son empreinte bcrypt, veillez donc à le copier avant de fermer cette boîte de dialogue. Renouvelez le secret s’il est perdu.', + }, + }, +}; \ No newline at end of file diff --git a/frontend/src/lib/locales/fr/agents.js b/frontend/src/lib/locales/fr/agents.js new file mode 100644 index 00000000..9fe9471c --- /dev/null +++ b/frontend/src/lib/locales/fr/agents.js @@ -0,0 +1,60 @@ +/** Workspace agents and Agent Studio. */ + +export default { + workspaceAgents: { + availability: { + ready: 'Prêt', + offline: 'Hors ligne · travaux en attente', + needsSetup: 'Configuration requise', + draft: 'Brouillon', + paused: 'En pause', + invalid: 'Non valide', + archived: 'Archivé', + unavailable: 'Indisponible', + }, + types: { + standard: 'Standard', + coding: 'Développement', + legacy: 'Hérité', + }, + runtimes: { + windshift: 'Exécuteur Windshift intégré', + authorizedRunner: 'Exécutant de développement autorisé', + legacyLocal: 'Exécuteur local hérité', + }, + identities: { + workspaceManaged: 'Géré par l’espace de travail', + centralizedService: 'Identité de service centralisée', + userOwned: 'Identité détenue par l’utilisateur', + }, + runStatuses: { + queued: 'en attente', + running: 'en cours', + succeeded: 'terminé', + completed: 'terminé', + failed: 'échoué', + cancelled: 'annulé', + }, + catalog: { + title: 'Agents', + subtitle: 'Spécialistes disponibles dans cet espace de travail', + createAgent: 'Créer un agent', + searchPlaceholder: 'Rechercher des agents', + loading: 'Chargement des agents de l’espace de travail…', + loadErrorTitle: 'Impossible de charger les agents', + loadErrorMessage: 'Impossible de charger les agents.', + emptyTitle: 'Aucun agent pour le moment', + emptyAdminDescription: 'Créez un spécialiste Standard ou Développement pour cet espace de travail.', + emptyMemberDescription: 'Un administrateur d’espace de travail peut ajouter des spécialistes ici.', + noMatchesTitle: 'Aucun agent correspondant', + noMatchesDescription: 'Essayez un autre nom, identifiant, objectif ou type de profil.', + defaultPurpose: 'Un spécialiste d’espace de travail configuré par vos administrateurs.', + ownedBy: 'Détenu par {name}', + modelUnavailable: 'Modèle indisponible', + standardInteraction: 'Attribution, mentions et discussion d’espace de travail', + codingInteraction: 'Attribution directe et mentions', + lastRun: 'Dernière exécution : {status}', + viewProfileVersion: 'Voir le profil · Version {version}', + }, + }, +}; \ No newline at end of file diff --git a/frontend/src/lib/locales/fr/analytics.js b/frontend/src/lib/locales/fr/analytics.js new file mode 100644 index 00000000..2d79991b --- /dev/null +++ b/frontend/src/lib/locales/fr/analytics.js @@ -0,0 +1,131 @@ +/** + * Analytics translations for French locale. + */ +export default { + analytics: { + title: 'Analytique', + subtitle: 'Santé et flux de livraison, sans nécessiter d’itérations', + loading: 'Chargement de l’analytique…', + noData: 'Aucune donnée disponible', + errorTitle: 'Impossible de charger l’analytique', + unsupportedVersion: + 'Le serveur a retourné un format d’analytique non pris en charge. Rafraîchissez la page une fois le déploiement terminé.', + collectionLoadError: + 'Impossible de charger les collections. L’analytique affiche tous les éléments de l’espace de travail.', + retry: 'Réessayer', + dateRange: 'Plage de dates', + collection: 'Collection', + allItems: 'Tous les éléments de l’espace de travail', + from: 'Du', + to: 'Au', + daysValue: '{value} j', + items_one: '{count} élément', + items_other: '{count} éléments', + range: { + last30Days: '30 derniers jours', + last12Weeks: '12 dernières semaines', + last6Months: '6 derniers mois', + lastYear: 'L’année dernière', + custom: 'Personnalisée', + }, + validation: { + invalid: 'Saisissez des dates de début et de fin valides.', + reversed: 'La date de début doit être antérieure ou égale à la date de fin.', + too_long: 'Choisissez une plage de dates inférieure ou égale à 366 jours.', + }, + scope: { + summary: '{items} éléments actuels · {from}–{to}', + currentWorkspace: 'Cohorte de l’espace de travail actuel', + currentWorkspaceNote: + 'La plage de dates s’applique aux graphiques de flux et de livraison ; la santé et la maturité sont des instantanés actuels. Les graphiques historiques utilisent les éléments présents aujourd’hui dans cet espace de travail. Les éléments déplacés ou supprimés ne sont pas inclus.', + currentCollection: 'Cohorte de la collection actuelle', + currentCollectionNote: + 'La plage de dates s’applique aux graphiques de flux et de livraison ; la santé et la maturité sont des instantanés actuels. Les graphiques historiques utilisent les éléments correspondant aujourd’hui à cette collection. Modifier la collection peut changer la cohorte.', + }, + health: { + title: 'Attention requise', + description: 'Travaux en cours non terminés présentant des signaux méritant un examen plus approfondi.', + unfinished: 'Non terminé', + overdue: 'En retard', + stale: 'Inactif', + staleHint: 'Aucune activité depuis {days}+ jours', + unassigned: 'Non attribué', + withoutPriority: 'Sans priorité', + withoutEstimate: 'Sans estimation', + attentionItems: 'Éléments à examiner', + item: 'Élément', + status: 'Statut', + age: 'Âge', + signals: 'Signaux', + flags: { + overdue: 'En retard', + stale: 'Inactif', + unassigned: 'Non attribué', + without_priority: 'Sans priorité', + without_estimate: 'Sans estimation', + }, + allClear: 'Aucun élément non terminé ne correspond actuellement à un signal d’attention.', + }, + throughput: { + title: 'Créés vs terminés', + description: + 'Arrivées hebdomadaires et premières finalisations. La réouverture d’un élément ne réécrit pas sa finalisation d’origine.', + created: 'Créés', + completed: 'Terminés', + net: 'Variation nette', + average: 'Moyenne terminés / semaine', + period: 'Période', + definition: 'La finalisation correspond au premier passage à un statut terminé.', + }, + aging: { + title: 'Maturité des travaux en cours', + description: 'Durée depuis laquelle les éléments actuellement non terminés sont ouverts.', + total: 'Éléments actifs', + median: 'Âge médian', + p85: '85e percentile', + ageBand: 'Tranche d’âge', + itemCount: 'Éléments', + byStatus: 'Âge par statut', + oldest: 'Éléments non terminés les plus anciens', + status: 'Statut', + noActive: 'Il n’y a aucun travail non terminé dans cette portée.', + buckets: { + '0_7': '0–7 jours', + '8_14': '8–14 jours', + '15_30': '15–30 jours', + '31_60': '31–60 jours', + '61_plus': '61+ jours', + }, + }, + deliveryTime: { + title: 'Délai de livraison', + description: 'De la création à la première finalisation, regroupé par semaine de finalisation.', + analyzed: 'Éléments terminés', + average: 'Moyenne', + median: 'Médiane', + p85: '85e percentile', + period: 'Période de finalisation', + completed: 'Terminés', + slowest: 'Délais de livraison les plus longs', + completedDate: 'Premier terminé', + duration: 'Délai de livraison', + missingHistory: '{count} éléments actuellement terminés ont été exclus car leur historique de finalisation est manquant.', + missingHistory_one: + '1 élément actuellement terminé a été exclu car son historique de finalisation est manquant.', + missingHistory_other: + '{count} éléments actuellement terminés ont été exclus car leur historique de finalisation est manquant.', + definition: + 'Mesuré de la création de l’élément à son premier passage à un statut terminé. Les réouvertures ultérieures ne modifient pas cette valeur.', + }, + dataTable: { + show: 'Afficher le tableau de données', + }, + insufficientData: { + no_items: 'Cette portée ne contient encore aucun élément.', + no_active_items: 'Il n’y a aucun travail non terminé dans cette portée.', + no_completed_items: 'Aucune première finalisation n’a été enregistrée dans la plage de dates sélectionnée.', + few_completed_items: + 'Seuls quelques éléments ont été terminés dans cette plage. Traitez les percentiles comme une tendance indicative.', + }, + }, +}; \ No newline at end of file diff --git a/frontend/src/lib/locales/fr/auth.js b/frontend/src/lib/locales/fr/auth.js new file mode 100644 index 00000000..65974bbf --- /dev/null +++ b/frontend/src/lib/locales/fr/auth.js @@ -0,0 +1,253 @@ +/** + * Authentication and User-related translations + * + * This module contains translations for: + * - auth: Login, logout, password reset, and authentication flows + * - users: User management, profiles, and account settings + * - security: Security settings, API tokens, and credentials + * - portalLogin: Portal-specific login interface + */ + +export default { + auth: { + login: 'Se connecter', + logout: 'Se déconnecter', + signIn: 'Se connecter', + signOut: 'Se déconnecter', + signUp: 'S’inscrire', + register: 'S’enregistrer', + forgotPassword: 'Mot de passe oublié ?', + resetPassword: 'Réinitialiser le mot de passe', + changePassword: 'Changer le mot de passe', + currentPassword: 'Mot de passe actuel', + newPassword: 'Nouveau mot de passe', + confirmPassword: 'Confirmer le mot de passe', + passwordRequired: 'Le mot de passe est requis', + rememberMe: 'Se souvenir de moi', + staySignedIn: 'Rester connecté à Windshift pendant 30 jours', + emailOrUsername: 'E-mail ou nom d’utilisateur', + welcomeBack: 'Bon retour', + loginTitle: 'Connectez-vous à votre compte', + loginSubtitle: 'Saisissez vos identifiants pour continuer', + loggingIn: 'Connexion en cours…', + logoutConfirm: 'Voulez-vous vraiment vous déconnecter ?', + sessionExpired: 'Votre session a expiré. Veuillez vous reconnecter.', + accountLocked: 'Votre compte a été verrouillé. Veuillez contacter le support.', + twoFactor: 'Authentification à deux facteurs', + enterCode: 'Saisissez le code de vérification', + resendCode: 'Renvoyer le code', + verificationSent: 'Code de vérification envoyé à votre adresse e-mail', + sso: 'Authentification unique (SSO)', + continueWith: 'Continuer avec {provider}', + orSignInWithPassword: 'ou se connecter avec un mot de passe', + touchSecurityKey: 'Touchez votre clé de sécurité…', + signInWithSecurityKey: 'Se connecter avec une clé de sécurité', + inviteUser: 'Inviter un utilisateur', + inviteSent: 'Invitation envoyée', + invitationLink: 'Lien d’invitation', + }, + + users: { + title: 'Utilisateurs', + subtitle: 'Gérer les comptes utilisateurs et les autorisations', + user: 'Utilisateur', + users_one: '{count} utilisateur', + users_other: '{count} utilisateurs', + addUser: 'Ajouter un utilisateur', + createUser: 'Créer un utilisateur', + editUser: 'Modifier l’utilisateur', + deleteUser: 'Supprimer l’utilisateur', + inviteUser: 'Inviter un utilisateur', + profile: 'Profil', + myProfile: 'Mon profil', + account: 'Compte', + firstName: 'Prénom', + lastName: 'Nom', + fullName: 'Nom complet', + displayName: 'Nom d’affichage', + email: 'E-mail', + phone: 'Téléphone', + avatar: 'Avatar', + role: 'Rôle', + roles: 'Rôles', + permissions: 'Autorisations', + groups: 'Groupes', + lastLogin: 'Dernière connexion', + lastActive: 'Dernière activité', + createdAt: 'Membre depuis', + status: 'Statut', + active: 'Actif', + inactive: 'Inactif', + pending: 'En attente', + suspended: 'Suspendu', + searchUsers: 'Rechercher des utilisateurs…', + noUsers: 'Aucun utilisateur trouvé', + userCreated: 'Utilisateur créé avec succès', + userUpdated: 'Utilisateur mis à jour avec succès', + userDeleted: 'Utilisateur supprimé avec succès', + inviteSent: 'Invitation envoyée avec succès', + cannotDeleteSelf: 'Vous ne pouvez pas supprimer votre propre compte', + activateUser: 'Activer l’utilisateur', + deactivateUser: 'Désactiver l’utilisateur', + activeLabel: 'Actif', + activeHint: 'Les utilisateurs inactifs ne peuvent pas se connecter. Vous pourrez réactiver ce compte ultérieurement depuis le menu contextuel.', + confirmDelete: 'Voulez-vous vraiment supprimer {name} ? Cette action est irréversible.', + confirmActivate: + 'Voulez-vous vraiment activer {name} ? Cet utilisateur pourra accéder au système.', + confirmDeactivate: + 'Voulez-vous vraiment désactiver {name} ? Cet utilisateur ne pourra plus accéder au système.', + failedToLoad: 'Échec du chargement des utilisateurs', + failedToSave: 'Échec de l’enregistrement de l’utilisateur', + failedToDelete: 'Échec de la suppression de l’utilisateur', + failedToActivate: 'Échec de l’activation de l’utilisateur', + failedToDeactivate: 'Échec de la désactivation de l’utilisateur', + failedToResetPassword: 'Échec de la réinitialisation du mot de passe', + profileSubtitle: 'Gérer vos informations de profil, votre avatar et vos paramètres régionaux', + profileInformation: 'Informations du profil', + passwordResetRequired: 'Réinitialisation du mot de passe requise', + profilePicture: 'Photo de profil', + uploadAndManageAvatar: 'Téléverser et gérer votre image d’avatar', + currentProfilePicture: 'Photo de profil actuelle', + customAvatarActive: 'Votre avatar personnalisé est actif', + usingDefaultAvatar: 'Utilisation de l’avatar par défaut', + avatarRecommendation: 'Recommandé : image carrée d’au moins 200x200 pixels', + uploadNewAvatar: 'Téléverser un nouvel avatar', + uploadAvatar: 'Téléverser un avatar', + changeAvatar: 'Changer d’avatar', + uploadingAvatar: 'Téléversement de l’avatar…', + avatarFileHint: 'Sélectionnez un fichier image (JPEG, PNG, GIF, WebP). Maximum 50 Mo.', + removeProfilePicture: 'Voulez-vous vraiment supprimer votre photo de profil ?', + regionalSettings: 'Paramètres régionaux', + regionalSettingsDesc: 'Configurer vos préférences de fuseau horaire et de langue', + timezone: 'Fuseau horaire', + timezoneHint: 'Utilisé pour afficher les dates et heures dans votre fuseau horaire local', + language: 'Langue', + languageHint: 'Votre langue préférée pour l’interface de l’application', + saveSettings: 'Enregistrer les paramètres', + settingsSaved: 'Paramètres enregistrés avec succès', + connectedAccounts: 'Comptes connectés', + connectedAccountsTab: 'Comptes', + connectedAccountsDesc: + 'Connectez vos comptes de gestion de code source pour créer des branches et des demandes de tirage (pull requests)', + labels: { + tabLabel: 'Étiquettes personnelles', + tabDescription: 'Gérer vos étiquettes personnelles.', + searchPlaceholder: 'Rechercher des étiquettes', + new: 'Nouvelle étiquette', + createTitle: 'Créer une étiquette', + editTitle: 'Modifier l’étiquette', + namePlaceholder: 'ex. urgent', + mine: 'Mes étiquettes', + empty: 'Aucune étiquette pour le moment', + emptyHint: 'Créez votre première étiquette pour organiser vos éléments.', + created: 'Étiquette créée', + updated: 'Étiquette mise à jour', + deleted: 'Étiquette supprimée', + deleteTitle: 'Supprimer l’étiquette ?', + deleteMessage: + 'Supprimer « {name} » ? Cela la retirera de tous les éléments auxquels elle est associée.', + }, + agents: { + title: 'Agents', + description: + 'Les agents sont des utilisateurs non humains qui héritent de vos autorisations et s’authentifient uniquement avec des jetons d’API. Ils ne peuvent pas se connecter de manière interactive.', + createTitle: 'Créer un agent', + create: 'Créer un agent', + creating: 'Création…', + creationUnavailable: + 'La création d’agents n’est pas disponible pour ce compte. Votre administrateur doit peut-être activer les agents gérés par l’utilisateur, ou votre limite d’agents a été atteinte.', + requiredFields: 'Le nom d’utilisateur, le prénom et le nom sont requis.', + createFailed: 'Échec de la création de l’agent.', + yourAgents: 'Vos agents', + empty: 'Vous n’avez aucun agent pour le moment.', + actionsFor: 'Actions pour {name}', + deleteTitle: 'Supprimer l’agent', + deleteMessage: 'Supprimer cet agent ? Tous ses jetons d’API cesseront de fonctionner.', + rename: 'Renommer l’agent', + nameRequired: 'Le nom de l’agent est requis.', + renameFailed: 'Échec du changement de nom de l’agent.', + renameHint: 'Le nom d’utilisateur stable @{username} ne changera pas.', + manageTokens: 'Gérer les jetons', + hideTokens: 'Masquer les jetons', + tokenExpirationHint: + 'Le jeton reste valide jusqu’à cette date dans votre fuseau horaire configuré. Laissez vide pour aucune expiration.', + existingTokens: 'Jetons existants', + tokenDates: 'Créé le {created} • Expire le {expires}', + neverExpires: 'N’expire jamais', + noTokens: 'Aucun jeton pour le moment.', + tokenCreateFailed: 'Échec de la création du jeton.', + confirmRevokeUnnamedToken: + 'Révoquer ce jeton ? Tout service l’utilisant cessera de fonctionner immédiatement.', + }, + calendarIntegration: 'Intégration du calendrier', + calendarIntegrationDesc: 'Abonnez-vous à vos éléments planifiés dans des applications de calendrier externes', + loadCalendarFeedSettings: 'Charger les paramètres du flux de calendrier', + enableCalendarSubscription: 'Activer l’abonnement au calendrier', + calendarSubscriptionDesc: + 'Générer une URL d’abonnement pour synchroniser vos éléments de travail planifiés avec des applications de calendrier externes.', + generateCalendarFeedUrl: 'Générer l’URL du flux de calendrier', + yourCalendarFeedUrl: 'Votre URL de flux de calendrier', + showFullUrl: 'Afficher l’URL complète', + calendarFeedWarning: 'Ne partagez pas cette URL car elle donne accès à vos éléments planifiés.', + lastSynced: 'Dernière synchronisation', + howToSubscribe: 'Comment s’abonner', + copyFeedUrlStep: 'Copiez l’URL du flux ci-dessus', + regenerateUrl: 'Régénérer l’URL', + revokeFeed: 'Révoquer le flux', + regenerateUrlNote: + 'La régénération de l’URL invalidera votre URL actuelle. Tous les calendriers utilisant l’ancienne URL devront être mis à jour.', + calendarFeedsDisabled: 'Les flux de calendrier sont désactivés par votre administrateur.', + googleCalendar: 'Google Calendar', + googleCalendarInstructions: 'Paramètres > Ajouter un calendrier > À partir de l’URL > Coller l’URL', + outlook: 'Outlook', + outlookInstructions: 'Ajouter un calendrier > S’abonner à partir du Web > Coller l’URL', + appleCalendar: 'Apple Calendar', + appleCalendarInstructions: 'Fichier > Nouvel abonnement à un calendrier > Coller l’URL', + }, + + security: { + title: 'Sécurité', + subtitle: 'Gérer vos paramètres de sécurité', + credentials: 'Identifiants de sécurité', + credentialsSubtitle: 'Gérer vos méthodes d’authentification', + apiTokens: 'Jetons d’API', + apiTokensSubtitle: 'Créer des jetons pour accéder à votre compte de manière programmatique', + createToken: 'Créer un jeton', + revokeToken: 'Révoquer le jeton', + tokenName: 'Nom du jeton', + tokenCreated: 'Jeton créé avec succès', + tokenRevoked: 'Jeton révoqué avec succès', + copyToken: 'Copier le jeton', + tokenWarning: 'Veillez à copier votre jeton dès maintenant. Vous ne pourrez plus le revoir.', + enterSecurityKeyName: 'Veuillez saisir un nom pour cette clé de sécurité', + webAuthnNotSupported: 'WebAuthn n’est pas pris en charge par ce navigateur', + invalidRegistrationChallenge: 'Défis d’inscription non valide reçu du serveur', + noActiveFidoCredentials: + 'Aucun identifiant FIDO actif trouvé. Veuillez d’abord enregistrer une clé de sécurité.', + authenticationFailed: 'Échec de l’authentification', + authenticationCancelled: 'L’authentification a été annulée ou a échoué. Veuillez réessayer.', + failedToTestFidoLogin: 'Échec du test de connexion FIDO', + enterTokenName: 'Veuillez saisir un nom pour ce jeton', + confirmRevokeToken: 'Voulez-vous vraiment révoquer « {name} » ? Cette action est irréversible.', + securityKeyFido: 'Clé de sécurité (FIDO2)', + authenticatorAppTotp: 'Application d’authentification (TOTP)', + }, + + portalLogin: { + welcomeBack: 'Bon retour', + signInToCustomize: 'Connectez-vous pour personnaliser ce portail', + emailOrUsername: 'E-mail ou nom d’utilisateur', + enterEmailOrUsername: 'Saisissez votre e-mail ou nom d’utilisateur', + password: 'Mot de passe', + enterPassword: 'Saisissez votre mot de passe', + keepMeSignedIn: 'Rester connecté pendant 30 jours', + or: 'ou', + touchSecurityKey: 'Touchez votre clé de sécurité…', + signInWithSecurityKey: 'Se connecter avec une clé de sécurité', + signingIn: 'Connexion en cours…', + signIn: 'Se connecter', + emailRequired: 'L’e-mail ou le nom d’utilisateur est requis', + passwordRequired: 'Le mot de passe est requis', + webAuthnNotSupported: 'WebAuthn n’est pas pris en charge par ce navigateur', + }, +}; \ No newline at end of file diff --git a/frontend/src/lib/locales/fr/channels.js b/frontend/src/lib/locales/fr/channels.js new file mode 100644 index 00000000..cd9c0a20 --- /dev/null +++ b/frontend/src/lib/locales/fr/channels.js @@ -0,0 +1,749 @@ +export default { + notifications: { + title: 'Notifications', + subtitle: 'Gérer les paramètres de notification', + notification: 'Notification', + notifications_one: '{count} notification', + notifications_other: '{count} notifications', + markAsRead: 'Marquer comme lue', + markAllAsRead: 'Tout marquer comme lu', + markAllRead: 'Tout marquer comme lu', + clearAll: 'Tout effacer', + noNotifications: 'Aucune notification', + allCaughtUp: 'Vous êtes à jour !', + notificationSettings: 'Paramètres de notification', + emailNotifications: 'Notifications par e-mail', + pushNotifications: 'Notifications push', + channels: 'Canaux', + searchNotifications: 'Rechercher des notifications…', + closeToast: 'Fermer le message instantané', + closeNotifications: 'Fermer les notifications', + viewAll: 'Voir toutes les notifications', + dismiss: 'Ignorer', + // Email verification + verifyEmail: 'Veuillez vérifier votre adresse e-mail', + verifyEmailDescription: + 'Un lien de vérification a été envoyé à votre adresse e-mail. Cliquez sur le lien pour finaliser la configuration de votre compte.', + verificationEmailSent: 'E-mail de vérification envoyé !', + sending: 'Envoi en cours…', + resendEmail: 'Renvoyer l’e-mail', + failedToSendVerification: 'Échec de l’envoi de l’e-mail de vérification', + error: 'Erreur', + // Filter labels for NotificationsPage + filters: { + allTypes: 'Tous les types', + assignments: 'Attributions', + comments: 'Commentaires', + statusChanges: 'Changements de statut', + reminders: 'Rappels', + milestones: 'Jalons', + all: 'Tous', + unreadOnly: 'Non lus uniquement', + readOnly: 'Lus uniquement', + }, + }, + + channels: { + title: 'Canaux', + subtitle: 'Gérer les intégrations', + channel: 'Canal', + channels_one: '{count} canal', + channels_other: '{count} canaux', + createChannel: 'Créer un canal', + editChannel: 'Modifier le canal', + deleteChannel: 'Supprimer le canal', + channelName: 'Nom du canal', + channelType: 'Type de canal', + noChannels: 'Aucun canal trouvé', + channelCreated: 'Canal créé avec succès', + channelUpdated: 'Canal mis à jour avec succès', + channelDeleted: 'Canal supprimé avec succès', + cannotDeleteDefault: 'Impossible de supprimer le canal de notification par défaut', + searchChannels: 'Rechercher des canaux…', + manager: { + subtitle: 'Gérer les canaux dont vous êtes responsable', + purpose: 'Objectif', + publishedAt: 'Publié sur {path}', + deliversTo: 'Livre les demandes à {workspace}', + outboundNotifications: 'Envoie les notifications sortantes', + outboundEvents: 'Livre les événements sortants', + operational: 'Canal opérationnel', + noChannels: 'Aucun canal géré', + noChannelsDescription: 'Les canaux qui vous sont attribués apparaîtront ici.', + loadFailed: 'Échec du chargement des canaux', + }, + testWebhook: 'Tester le webhook', + webhookSent: 'Webhook de test envoyé avec succès !', + webhookFailed: 'Échec du test du webhook', + types: 'Types', + allTypes: 'Tous les types', + portal: 'Portail', + webhook: 'Webhook', + email: 'E-mail', + smtp: 'SMTP', + form: 'Formulaire', + categories: 'Catégories', + category: 'Catégorie', + allChannels: 'Tous les canaux', + allCategories: 'Toutes les catégories', + manageCategories: 'Gérer les catégories', + status: { + enabled: 'Activé', + disabled: 'Désactivé', + active: 'Actif', + configured: 'Configuré', + pending: 'En attente', + inactive: 'Inactif', + }, + }, + + channel: { + configuration: 'Configuration', + managers: 'Responsables', + basicInformation: 'Informations de base', + name: 'Nom', + channelName: 'Nom du canal', + category: 'Catégorie', + noCategory: 'Sans catégorie', + description: 'Description', + briefDescription: 'Brève description…', + openPortal: 'Ouvrir le portail', + saveChanges: 'Enregistrer les modifications', + channelSavedSuccess: 'Canal enregistré avec succès !', + failedToSave: 'Échec de l’enregistrement', + channelNameRequired: 'Le nom du canal est requis', + channelConfigCorrupted: 'La configuration du canal est corrompue et ne peut pas être modifiée en toute sécurité. Veuillez contacter un administrateur.', + + // Portal Configuration + portalConfiguration: 'Configuration du portail', + portalSlug: 'Slug du portail', + portalSlugHelp: 'Identifiant utilisable dans une URL', + portalSlugRequired: 'Le slug du portail est requis', + portalSlugInvalid: 'Le slug doit contenir de 1 à 40 lettres minuscules, chiffres ou trait d’union, et ne peut ni commencer ni se terminer par un trait d’union, ni contenir des traits d’union consécutifs.', + portalAllowedDomainsInvalid: 'Un ou plusieurs domaines autorisés ne sont pas valides (ex. « acme.com »).', + portalUrl: 'URL du portail', + targetWorkspaces: 'Espaces de travail cibles', + selectAtLeastOneWorkspace: 'Veuillez sélectionner au moins un espace de travail cible', + searchWorkspaces: 'Rechercher des espaces de travail…', + portalTitle: 'Titre du portail', + enablePortal: 'Activer le portail (autoriser les soumissions publiques)', + portalIsActive: 'Le portail est actif et accepte les soumissions', + portalIsInactive: 'Le portail est actuellement désactivé', + portalRegistration: 'Accès client', + portalRegistrationMode: 'Mode d’inscription', + portalRegistrationOpen: 'Ouvert — toute personne possédant un e-mail autorisé peut se connecter', + portalRegistrationManual: 'Manuel — seuls les clients gérés par un administrateur peuvent se connecter', + portalRegistrationOpenHelp: 'Tout visiteur dont l’e-mail correspond à la liste des domaines autorisés (ou tout visiteur si la liste est vide) peut s’inscrire lui-même.', + portalRegistrationManualHelp: 'Seuls les clients du portail que vous avez ajoutés et auxquels vous avez accordé l’accès au canal peuvent demander un lien d’accès instantané (magic link).', + portalAllowedDomains: 'Domaines e-mail autorisés', + portalAllowedDomainsHelp: 'Liste séparée par des virgules. Laissez vide pour autoriser n’importe quel domaine. S’applique aux inscriptions ouvertes et manuelles.', + + // Email toggle + enableEmail: 'Activer le canal e-mail', + emailIsActive: 'Le canal e-mail est actif et traite les e-mails', + emailIsInactive: 'Le canal e-mail est actuellement désactivé', + enableWebhook: 'Activer le webhook', + webhookIsActive: 'Le webhook est actif', + webhookIsInactive: 'Le webhook est actuellement désactivé', + enableSmtp: 'Activer le SMTP', + smtpIsActive: 'Le SMTP est actif', + smtpIsInactive: 'Le SMTP est actuellement désactivé', + failedToLoad: 'Échec du chargement du canal', + + // Webhook Configuration + webhookConfiguration: 'Configuration du webhook', + webhookUrl: 'URL du webhook', + webhookUrlRequired: 'L’URL du webhook est requise', + secretOptional: 'Secret (optionnel)', + secretPlaceholder: 'Saisissez le secret à mettre à jour, ou laissez vide pour conserver l’existant', + secretHelp: 'Utilisé pour signer les requêtes avec HMAC-SHA256.', + customHeaders: 'En-têtes personnalisés', + addHeader: '+ Ajouter un en-tête', + headerName: 'Nom de l’en-tête', + headerValue: 'Valeur de l’en-tête', + scope: 'Portée', + allItems: 'Tous les éléments (à l’échelle de l’instance)', + specificWorkspaces: 'Espaces de travail spécifiques', + selectWorkspaces: 'Sélectionner des espaces de travail…', + specificCollections: 'Collections spécifiques', + selectCollections: 'Sélectionner des collections…', + automaticTriggers: 'Déclencheurs automatiques', + enableAutoTriggers: 'Activer les déclencheurs automatiques', + autoTriggersHelp: 'Envoyer automatiquement des webhooks lorsque les événements sélectionnés se produisent', + testWebhook: 'Tester le webhook', + sendTestWebhook: 'Envoyer un webhook de test', + testWebhookSent: 'Webhook de test envoyé avec succès ! La configuration a été enregistrée.', + testWebhookFailed: 'Échec du test du webhook', + pleaseEnterUrl: 'Veuillez saisir une URL de webhook.', + pleaseEnterValidUrl: 'Veuillez saisir une URL valide.', + sendingTestWebhook: 'Envoi du webhook de test…', + managedByPlugin: 'Ce webhook est géré par le plugin {pluginName}.', + + // Email Configuration + emailConfiguration: 'Configuration e-mail', + authenticationMethod: 'Méthode d’authentification', + basicIMAP: 'De base (IMAP)', + usernameAndPassword: 'Nom d’utilisateur et mot de passe', + oauth: 'OAuth', + microsoftOrGoogle: 'Microsoft 365 ou Google', + provider: 'Fournisseur', + microsoft365: 'Microsoft 365', + google: 'Google', + clientId: 'ID client', + clientIdRequired: 'L’ID client OAuth est requis', + clientSecret: 'Secret client', + clientSecretRequired: 'Le secret client OAuth est requis', + leaveBlankToKeep: 'Laissez vide pour conserver l’existant', + tenantId: 'ID de locataire (Tenant ID)', + tenantIdHelp: 'Utilisez « common » pour autoriser tout compte Microsoft, ou saisissez un ID de locataire spécifique', + connected: 'Connecté', + reconnect: 'Se reconnecter', + notConnected: 'Non connecté', + saveAndConnect: 'Enregistrer les paramètres, puis connecter votre boîte aux lettres', + connectMailbox: 'Connecter la boîte aux lettres', + redirectUri: 'URI de redirection (pour Azure AD / Console Google)', + imapConnection: 'Connexion IMAP', + imapHost: 'Hôte IMAP', + imapHostRequired: 'L’hôte IMAP est requis', + port: 'Port', + encryption: 'Chiffrement', + username: 'Nom d’utilisateur', + usernameRequired: 'Le nom d’utilisateur IMAP est requis', + password: 'Mot de passe', + leaveBlankPassword: 'Laissez vide pour conserver le mot de passe existant', + itemCreation: 'Création d’élément', + targetWorkspace: 'Espace de travail cible', + targetWorkspaceRequired: 'L’espace de travail cible est requis', + selectWorkspace: 'Sélectionner un espace de travail…', + itemType: 'Type d’élément', + itemTypeRequired: 'Le type d’élément est requis', + selectItemType: 'Sélectionner un type d’élément…', + selectWorkspaceFirst: 'Sélectionnez d’abord un espace de travail', + workspaceFieldResolution: 'Résolution des champs de l’espace de travail', + processingOptions: 'Options de traitement', + mailbox: 'Boîte aux lettres', + mailboxHelp: 'Le dossier à interroger pour trouver de nouveaux e-mails', + markAsRead: 'Marquer comme lu après traitement', + markAsReadHelp: 'Marquer les e-mails comme lus une fois qu’ils ont été convertis en éléments', + deleteAfterProcess: 'Supprimer après traitement', + deleteAfterProcessHelp: + 'Supprimer les e-mails de la boîte aux lettres après la création des éléments (à utiliser avec précaution)', + // Form Channel Configuration + openForm: 'Ouvrir le formulaire', + formConfiguration: 'Page publique', + formSlug: 'URL publique', + formSlugHelp: 'Utilisé dans le lien de partage', + formSlugRequired: 'Le slug du formulaire est requis', + formSlugInvalid: 'Le slug doit contenir de 1 à 40 lettres minuscules, chiffres ou trait d’union, et ne peut ni commencer ni se terminer par un trait d’union, ni contenir des traits d’union consécutifs.', + formBrandColorInvalid: 'La couleur de la marque doit être un code hexadécimal à 6 chiffres (ex. #14b8a6).', + formLogoUrlInvalid: 'L’URL du logo doit être une URL http(s) valide.', + formRedirectUrlInvalid: 'L’URL de redirection doit être une URL http(s) valide.', + formUrl: 'URL du formulaire', + formTheme: 'Thème', + formBrandColor: 'Couleur de la marque', + formLogoUrl: 'URL du logo', + formSuccessMessage: 'Message de succès par défaut', + formSuccessMessagePlaceholder: 'Merci pour votre soumission !', + formRedirectUrl: 'URL de redirection', + formRedirectUrlHelp: 'Rediriger les utilisateurs vers cette URL après une soumission réussie (optionnel)', + enableForm: 'Activer le canal formulaire', + formIsActive: 'Le canal formulaire est actif et accepte les soumissions', + formIsInactive: 'Le canal formulaire est actuellement désactivé', + + comingSoon: 'La configuration pour les canaux de type {type} sera bientôt disponible', + lastActivity: 'Dernière activité', + + // Webhook Events + items: 'Éléments', + comments: 'Commentaires', + links: 'Liens', + itemCreated: 'Élément créé', + itemUpdated: 'Élément mis à jour', + itemDeleted: 'Élément supprimé', + itemAssigned: 'Élément attribué', + statusChanged: 'Statut modifié', + commentCreated: 'Commentaire créé', + commentUpdated: 'Commentaire mis à jour', + commentDeleted: 'Commentaire supprimé', + itemLinked: 'Élément lié', + itemUnlinked: 'Élément délié', + + // SMTP Configuration + smtpConfiguration: 'Configuration SMTP', + smtpHost: 'Hôte SMTP', + smtpHostRequired: 'L’hôte SMTP est requis', + smtpPort: 'Port', + smtpUsername: 'Nom d’utilisateur', + smtpUsernamePlaceholder: 'Nom d’utilisateur SMTP (si requis)', + smtpPassword: 'Mot de passe', + smtpFromEmail: 'E-mail de l’expéditeur', + smtpFromEmailRequired: 'L’adresse e-mail de l’expéditeur est requise', + smtpFromName: 'Nom de l’expéditeur', + smtpFromNamePlaceholder: 'Nom d’affichage pour les e-mails sortants', + smtpEncryption: 'Chiffrement', + smtpSkipTlsVerify: 'Ignorer la vérification du certificat', + smtpSkipTlsVerifyDescription: 'Autoriser les certificats auto-signés ou non approuvés uniquement pour ce serveur SMTP. Cela réduit la sécurité de la connexion.', + smtpNoEncryptionWarning: 'Le contenu des e-mails et les métadonnées de livraison seront envoyés sans chiffrement. N’utilisez cette option qu’avec un relais SMTP local de confiance.', + noEncryption: 'Aucun (non recommandé)', + authentication: 'Authentification', + senderSettings: 'Paramètres de l’expéditeur', + smtpHostAndFromRequired: 'L’hôte SMTP et l’e-mail de l’expéditeur sont requis', + testSmtp: 'Tester le SMTP', + testEmailAddress: 'Adresse e-mail de test', + testEmailPlaceholder: 'Saisissez l’e-mail auquel envoyer le test', + testEmailRequired: 'Veuillez saisir une adresse e-mail de test', + sendTestEmail: 'Envoyer un e-mail de test', + sendingTestEmail: 'Envoi de l’e-mail de test…', + testEmailSent: 'E-mail de test envoyé avec succès ! La configuration a été enregistrée.', + testEmailFailed: 'Échec de l’e-mail de test', + + // Processing Log + processingLog: 'Journal de traitement', + emailLog: { + syncStatus: 'Statut de synchronisation', + lastChecked: 'Dernière vérification', + never: 'Jamais', + errors: 'Erreurs', + noErrors: 'Aucune erreur', + from: 'De', + subject: 'Objet', + result: 'Résultat', + processedAt: 'Traité le', + newItem: 'Nouvel élément {key}', + commentOn: 'Commentaire sur {key}', + noEmails: 'Aucun e-mail traité pour le moment', + noResults: 'Aucune entrée trouvée dans le journal des e-mails', + previous: 'Précédent', + next: 'Suivant', + page: 'Page {page} sur {total}', + }, + }, + + portal: { + title: 'Portail', + subtitle: 'Paramètres du portail client', + portalTitle: 'Titre du portail', + selectWorkspace: 'Sélectionner un espace de travail', + workspaceFieldResolution: 'Résolution des champs de l’espace de travail', + + // Magic link authentication + signInTitle: 'Connectez-vous à votre compte', + signInDescription: 'Saisissez votre e-mail pour recevoir un lien de connexion', + enterEmail: 'Saisissez votre adresse e-mail', + sendMagicLink: 'Envoyer le lien de connexion', + noAccountNeeded: + 'Inutile de créer un compte. Saisissez simplement votre e-mail et nous vous enverrons un lien de connexion.', + checkYourEmail: 'Vérifiez vos e-mails', + magicLinkSent: + 'Nous avons envoyé un lien de connexion à votre adresse e-mail. Cliquez sur le lien pour accéder à votre compte portail.', + linkExpiresIn: 'Le lien expire dans 15 minutes.', + useAnotherEmail: 'Utiliser une autre adresse e-mail', + + // Magic link verification + verifying: 'Vérification de votre lien…', + pleaseWait: 'Veuillez patienter pendant que nous vous connectons.', + signInSuccess: 'Connexion réussie !', + redirecting: 'Redirection vers le portail…', + verificationFailed: 'Échec de la connexion', + invalidLink: 'Jeton non valide ou manquant', + + // Portal customer + portalCustomer: 'Client du portail', + + // Loading states + loading: 'Chargement du portail…', + notFound: 'Portail introuvable', + signInToAccess: 'Connectez-vous pour accéder au portail et soumettre des demandes', + checkingAuth: 'Vérification de l’authentification…', + + // Additional sending state + sending: 'Envoi en cours…', + + // Internal account login + internalSignIn: 'Connexion du personnel', + backToMagicLink: 'Retour à la connexion par e-mail', + password: 'Mot de passe', + enterPassword: 'Saisissez votre mot de passe', + signingIn: 'Connexion en cours…', + + // Keys used by hub and portal customize panels + theme: 'Thème', + gradient: 'Dégradé', + searchBox: 'Zone de recherche', + customizeButton: 'Personnaliser', + portalDescription: 'Description du portail (optionnelle)', + portalSlug: 'Slug du portail', + requestTypes: 'Types de demande', + createRequestType: 'Créer un type de demande', + editRequestType: 'Modifier le type de demande', + addRequestTypeSubtitle: 'Ajouter un nouveau type de demande pour votre portail', + editRequestTypeSubtitle: 'Mettre à jour les détails du type de demande', + requestTypeCreated: 'Type de demande créé avec succès !', + requestTypeUpdated: 'Type de demande mis à jour avec succès !', + createAssetReport: 'Créer un rapport d’actifs', + editAssetReport: 'Modifier le rapport d’actifs', + addAssetReportSubtitle: 'Ajouter un nouveau rapport d’actifs à votre portail', + editAssetReportSubtitle: 'Mettre à jour les détails du rapport d’actifs', + assetReportCreated: 'Rapport d’actifs créé avec succès !', + assetReportUpdated: 'Rapport d’actifs mis à jour avec succès !', + failedToSaveAssetReport: 'Échec de l’enregistrement du rapport d’actifs', + assetReportNamePlaceholder: 'ex. Ordinateurs portables, Serveurs actifs', + assetReportDescriptionPlaceholder: 'Brève description de ce rapport d’actifs', + assetSet: 'Ensemble d’actifs', + assetSetRequired: 'L’ensemble d’actifs est requis', + selectAssetSet: 'Sélectionner un ensemble d’actifs…', + assetSetHint: 'L’ensemble d’actifs interrogé par ce rapport.', + qlQuery: 'Requête QL', + qlQueryRequired: 'La requête QL est requise', + qlQueryTokenRequired: 'Les requêtes QL en mode formulaire doivent faire référence à au moins un champ du formulaire à l’aide de jetons ${field_identifier}', + qlQueryPlaceholder: 'status = "Active" AND category = "Hardware"', + qlQueryHint: 'Filtrer les actifs à l’aide du langage QL (Windshift Query Language).', + qlQueryFormPlaceholder: 'title = ${title} AND status = ${status}', + qlQueryFormHint: 'Utilisez les jetons ${field_identifier} pour faire référence aux valeurs soumises par le formulaire.', + runMode: 'Mode d’exécution', + runModeDirect: 'Tableau direct', + runModeDirectHint: 'Afficher les résultats directement sous forme de tableau plein écran dans une section.', + runModeForm: 'Demande par formulaire', + runModeFormHint: 'Lancer comme un type de demande : l’utilisateur remplit un formulaire, puis les résultats sont affichés.', + formConfiguration: 'Configuration du formulaire', + itemType: 'Type d’élément', + assetReportItemTypeHint: 'Utilisé pour déterminer quels champs personnalisés peuvent être collectés dans le formulaire.', + submitButtonLabel: 'Libellé du bouton de soumission (optionnel)', + submitButtonPlaceholder: 'Exécuter le rapport', + successMessage: 'Message de succès (optionnel)', + successMessagePlaceholder: 'Voici les actifs correspondant à vos critères.', + assetReportConfigureFieldsHint: 'Utilisez l’action « Configurer les champs » sur la carte du rapport pour choisir les champs du formulaire à afficher.', + assetReportFieldsAfterCreate: 'Après la création, utilisez « Configurer les champs » sur la carte du rapport pour choisir les champs à afficher dans le formulaire.', + runReport: 'Exécuter le rapport', + failedToRunReport: 'Échec de l’exécution du rapport', + fieldRequired: '{field} est requis', + noFieldsConfigured: 'Aucun champ de formulaire configuré pour le moment. Utilisez « Configurer les champs » sur la carte du rapport pour en ajouter.', + resultsCount: '{total} résultats', + editCriteria: 'Modifier les critères', + noAssetsFound: 'Aucun actif ne correspond aux critères.', + noFormFieldsConfigured: 'Ce rapport n’a encore aucun champ de formulaire configuré.', + exampleTitle: 'ex. Portail du support client', + exampleStatus: 'ex. Ouvert, En cours, Résolu', + describeRequest: 'Veuillez décrire votre demande', + // Request type modal + nameRequired: 'Le nom est requis', + itemTypeRequired: 'Le type d’élément est requis', + failedToSaveRequestType: 'Échec de l’enregistrement du type de demande', + requestTypeNamePlaceholder: 'ex. Rapport de bogue, Demande de fonctionnalité', + descriptionOptional: 'Description (optionnelle)', + requestTypeDescriptionPlaceholder: 'Brève description de ce type de demande', + iconAndColor: 'Icône & couleur', + createsItemType: 'Crée un type d’élément', + selectItemType: 'Sélectionner un type d’élément…', + submissionsCreateItemType: 'Les soumissions créeront ce type d’élément de travail', + creating: 'Création…', + removeFromSection: 'Retirer de la section', + removeLink: 'Retirer le lien', + backToApp: 'Retour à l’application', + lightMode: 'Mode clair', + darkMode: 'Mode sombre', + signIn: 'Se connecter', + signOut: 'Se déconnecter', + myRequests: 'Mes demandes', + myDrafts: 'Brouillons', + backToPortal: 'Retour au portail', + guestUser: 'Utilisateur invité', + notSignedIn: 'Non connecté', + draftsTitle: 'Vos brouillons', + draftsSubtitle: 'Reprenez là où vous vous étiez arrêté.', + draftsEmpty: 'Aucun brouillon pour le moment. Commencez une demande et votre progression sera automatiquement enregistrée.', + draftStepProgress: 'Étape {current}', + draftUpdatedAgo: 'Mis à jour {time}', + draftUntitled: 'Brouillon sans titre', + draftResume: 'Reprendre', + draftDelete: 'Supprimer le brouillon', + draftDeleteConfirm: 'Supprimer ce brouillon ? Cette action est irréversible.', + draftResumeBanner: 'Reprise du brouillon enregistré.', + draftStartFresh: 'Recommencer à zéro', + draftSaving: 'Enregistrement du brouillon…', + draftSaved: 'Brouillon enregistré', + + // Passkey enrolment + discoverable login + signInWithPasskey: 'Se connecter avec une clé d’accès (passkey)', + orSignInWithEmail: 'ou se connecter par e-mail', + profileAndSecurity: 'Profil & sécurité', + profileTitle: 'Profil', + profileSubtitle: 'Gérer vos options de connexion à ce portail.', + profileSection: 'Compte', + passkeysTitle: 'Clés d’accès (passkeys)', + passkeysSubtitle: 'Connectez-vous plus rapidement et en toute sécurité sans lien e-mail.', + passkeysUnsupported: + 'Votre navigateur ne prend pas en charge les clés d’accès. Utilisez une version récente de Chrome, Safari, Edge ou Firefox.', + addPasskey: 'Ajouter une clé d’accès', + addPasskeyTitle: 'Ajouter une clé d’accès', + addPasskeyHint: 'Donnez un nom à cet appareil pour pouvoir le reconnaître ultérieurement.', + passkeyName: 'Nom de la clé d’accès', + passkeyAdded: 'Ajoutée', + passkeyLastUsed: 'dernière utilisation', + noPasskeys: 'Aucune clé d’accès pour le moment.', + passkeyBannerTitle: 'Évitez l’e-mail la prochaine fois', + passkeyBannerBody: 'Configurez une clé d’accès pour une connexion plus rapide et résistante au hameçonnage.', + passkeyBannerCta: 'Configurer une clé d’accès', + addLink: 'Ajouter un lien', + addSection: 'Ajouter une section', + noContentSections: 'Aucune section de contenu configurée pour le moment.', + dropHereToAdd: 'Déposez ici pour ajouter un type de demande', + noRequestTypesInSection: + 'Aucun type de demande dans cette section pour le moment. Faites glisser des types de demande depuis la barre latérale.', + + // Customize panel + customize: { + heroGradient: 'Arrière-plan', + background: 'Arrière-plan', + navigation: 'Navigation', + requestTypes: 'Types de demande', + knowledgeBase: 'Base de connaissances', + gradientStyle: 'Style de dégradé', + gradients: 'Dégradés', + gradientDescription: 'Choisissez un dégradé pour la section d’en-tête (hero) du portail.', + backgroundDescription: + 'Choisissez un dégradé ou une image d’arrière-plan pour la section d’en-tête (hero) du portail. Les images d’arrière-plan remplaceront la sélection du dégradé.', + navigationComingSoon: 'La personnalisation de la navigation sera bientôt disponible.', + requestTypesDescription: + 'Glissez-déposez les types de demande pour les réordonner. Cliquez sur le menu pour modifier, renommer ou supprimer.', + creates: 'Crée', + fields: 'Champs', + addFields: 'Ajouter des champs', + rename: 'Renommer', + markAsInactive: 'Marquer comme inactif', + markAsActive: 'Marquer comme actif', + noRequestTypes: 'Aucun type de demande configuré pour le moment.', + addRequestType: 'Ajouter un type de demande', + confirmDeleteRequestType: + 'Voulez-vous vraiment supprimer ce type de demande ? Cette action est irréversible.', + confirmDeleteAssetReport: + 'Voulez-vous vraiment supprimer ce rapport d’actifs ? Cette action est irréversible.', + // Knowledge base section + docmostKnowledgeBase: 'Base de connaissances Docmost', + docmostDescription: + 'Connectez votre base de connaissances Docmost pour activer la fonctionnalité de recherche dans le portail', + docmostShareLink: 'Lien de partage Docmost', + docmostShareLinkPlaceholder: 'https://wiki.example.com/share/abc123xyz', + docmostShareLinkHelp: + 'Saisissez le lien de partage complet Docmost (ex. https://wiki.example.com/share/u1gkl0jk1u)', + parsedConfiguration: 'Configuration analysée :', + baseURL: 'URL de base :', + shareID: 'ID de partage :', + configurationValid: 'Configuration valide', + invalidShareLinkFormat: 'Format de lien de partage non valide', + expectedFormat: 'Format attendu : https://votre-domaine.com/share/id-de-partage', + howToGetShareLink: 'Comment obtenir un lien de partage Docmost :', + docmostStep1: 'Ouvrez votre espace Docmost', + docmostStep2: 'Cliquez sur le bouton Partager', + docmostStep3: 'Activez le partage public', + docmostStep4: 'Copiez le lien de partage', + docmostStep5: 'Collez-le ci-dessus', + // Logo section + logo: 'Logo du portail', + logoHelp: + 'Téléversez un logo à afficher dans l’en-tête du portail. S’il n’est pas défini, le logo du hub sera utilisé par défaut.', + // Asset reports section + assetReports: 'Rapports d’actifs', + assetReportsDescription: 'Configurer les rapports d’actifs que les utilisateurs du portail peuvent consulter.', + noAssetReports: 'Aucun rapport d’actifs configuré pour le moment.', + addAssetReport: 'Ajouter un rapport d’actifs', + }, + + // Visibility settings + visibility: { + title: 'Visibilité des types de demande', + configureAccess: 'Configurer qui peut voir ce type de demande', + configureVisibility: 'Configurer la visibilité', + everyone: 'Tout le monde', + everyoneDesc: 'Tous les utilisateurs authentifiés peuvent voir ce type de demande', + restricted: 'Groupes et/ou organisations spécifiques', + restrictedDesc: 'Seuls les groupes ou organisations sélectionnés peuvent voir ce type', + internalGroups: 'Groupes internes', + organizations: 'Organisations', + selectGroups: 'Sélectionner des groupes…', + selectOrganizations: 'Sélectionner des organisations…', + noRestrictions: 'Aucune restriction — visible par tout le monde', + hasRestrictions: 'Visibilité restreinte', + orLogicHint: 'L’utilisateur doit appartenir à au moins un groupe OU une organisation sélectionné(e)', + noGroupsAvailable: 'Aucun groupe disponible', + noOrganizationsAvailable: 'Aucune organisation disponible', + }, + }, + + requestForm: { + title: 'Titre', + description: 'Description', + enterTitle: 'Saisissez un titre pour votre demande', + describeRequest: 'Veuillez décrire votre demande', + yourName: 'Votre nom', + yourEmail: 'Votre e-mail', + emailPlaceholder: 'votre@email.com', + emailFollowUp: 'Nous utiliserons cette adresse pour assurer le suivi de votre demande', + submittingAs: 'Soumission en tant que {name} ({email})', + nameRequired: 'Le nom est requis', + emailRequired: 'L’e-mail est requis', + fieldRequired: '{field} est requis', + submitting: 'Soumission en cours…', + submitRequest: 'Soumettre la demande', + requestSubmittedSuccess: 'Demande soumise avec succès ! Nous vous répondrons sous peu.', + failedToLoadFields: 'Échec du chargement des champs du formulaire', + failedToSubmit: 'Échec de la soumission de la demande', + selectOption: 'Sélectionner une option…', + }, + + requestTypeFields: { + configureFields: 'Configurer les champs', + step: 'Étape', + fields: 'Champs', + addField: 'Ajouter un champ', + addVirtualField: 'Ajouter un champ virtuel', + addExistingField: 'Ajouter un champ existant', + field: 'Champ', + selectField: 'Sélectionner un champ…', + requiredField: 'Champ obligatoire', + required: 'Obligatoire', + noFieldsInStep: 'Aucun champ à l’étape {step}. Ajoutez des champs ci-dessous.', + stepHasNoFields: + 'Cette étape ne contient aucun champ. Faites glisser des champs depuis la liste disponible ou ajoutez un champ virtuel.', + virtualField: 'Virtuel', + virtual: 'Virtuel', + defaultField: 'Champ par défaut', + customField: 'Champ personnalisé', + system: 'Système', + custom: 'Personnalisé', + text: 'Texte', + multiLine: 'Multiligne', + select: 'Sélection', + checkbox: 'Case à cocher', + fieldName: 'Nom du champ', + fieldNamePlaceholder: 'ex. Niveau d’urgence', + fieldType: 'Type de champ', + options: 'Options', + value: 'Valeur', + label: 'Libellé', + addOption: 'Ajouter une option', + pleaseSelectField: 'Veuillez sélectionner un champ', + fieldAlreadyAdded: 'Ce champ est déjà ajouté à cette étape', + pleaseEnterFieldName: 'Veuillez saisir un nom de champ', + addAtLeastOneOption: 'Veuillez ajouter au moins une option pour le champ de sélection', + editFieldDisplay: 'Modifier l’affichage du champ', + displayName: 'Nom d’affichage', + overrideLabel: 'Remplacer le libellé affiché dans le formulaire du portail', + descriptionHelpText: 'Description / Texte d’aide', + helpTextPlaceholder: 'Saisissez le texte d’aide à afficher sous le champ…', + saving: 'Enregistrement…', + changesSavedAuto: 'Les modifications sont enregistrées automatiquement', + addNewStep: 'Ajouter une nouvelle étape', + removeCurrentStep: 'Supprimer l’étape actuelle', + removeField: 'Retirer le champ', + // Drag and drop + availableFields: 'Champs disponibles', + dragFieldsHint: 'Faites glisser des champs vers le panneau de droite pour les ajouter', + searchFields: 'Rechercher des champs…', + noFieldsMatch: 'Aucun champ ne correspond à votre recherche', + allFieldsAdded: 'Tous les champs disponibles ont été ajoutés', + dragToReorder: 'Faites glisser pour réordonner les champs', + dropFieldsHere: 'Déposez les champs ici', + failedToLoadFields: 'Échec du chargement des champs', + failedToSaveFields: 'Échec de l’enregistrement des champs', + }, + + // Form Builder translations + forms: { + title: 'Formulaires', + routing: { + title: 'Routage', + save: 'Enregistrer le routage', + }, + createForm: 'Créer un formulaire', + deleteForm: 'Supprimer le formulaire', + confirmDeleteForm: 'Voulez-vous vraiment supprimer « {name} » ? Cette action est irréversible.', + formCreated: 'Formulaire créé avec succès', + formDeleted: 'Formulaire supprimé avec succès', + formName: 'Nom du formulaire', + formNamePlaceholder: 'ex. Rapport de bogue', + formDescription: 'Description', + formDescriptionPlaceholder: 'Brève description de ce formulaire', + formNameRequired: 'Le nom du formulaire est requis', + createsItemType: 'Crée un type d’élément', + selectItemType: 'Sélectionner un type d’élément', + builder: { + title: 'Générateur de formulaires', + availableFields: 'Champs disponibles', + searchFields: 'Rechercher des champs…', + addField: 'Ajouter un champ', + noFieldsAvailable: 'Aucun champ disponible', + dropFieldsHere: 'Déposez les champs ici pour construire votre formulaire', + dragFromPalette: 'Faites glisser les champs depuis le panneau de droite ou cliquez sur + pour ajouter', + required: 'Obligatoire', + optional: 'Optionnel', + noForms: 'Aucun formulaire configuré pour le moment', + addFormHint: 'Créez le premier formulaire et choisissez la destination de ses réponses.', + editFields: 'Modifier les champs', + backToFields: 'Retour aux champs', + }, + settings: { + title: 'Paramètres du formulaire', + requireAuth: 'Exiger l’authentification', + allowAttachments: 'Autoriser les pièces jointes', + submitButton: 'Texte du bouton de soumission', + successMessage: 'Message de succès', + successMessagePlaceholder: 'Merci pour votre soumission !', + redirectUrl: 'URL de redirection', + }, + integration: { + title: 'Intégration', + hostedUrl: 'Lien de partage', + iframe: 'Intégration iframe', + jsWidget: 'Widget JS', + copyCode: 'Copier le code', + copied: 'Copié !', + openInNewTab: 'Ouvrir dans un nouvel onglet', + widthLabel: 'Largeur', + heightLabel: 'Hauteur', + }, + }, + + // Portal Hub translations + hub: { + loading: 'Chargement du hub…', + error: 'Erreur lors du chargement du hub', + section: 'Section', + inbox: 'Boîte de réception', + inboxDescription: 'Demandes provenant de tous les portails', + backToHub: 'Retour au hub', + customizeHub: 'Personnaliser le hub', + sections: 'Sections', + sectionsHelp: 'Organisez les portails en sections. Faites glisser les portails entre les sections sur la vue principale.', + addSection: 'Ajouter une section', + noPortals: 'Aucun portail disponible pour le moment', + noDescription: 'Aucune description', + requestTypes: 'types de demande', + unassignedPortals: 'Portails non attribués', + dragToSection: 'Faites glisser ces portails vers une section ci-dessus', + dropPortalsHere: 'Glissez les portails ici ou ajoutez-les depuis le panneau de personnalisation', + manageChannels: 'Gérer les canaux', + heroContent: 'Contenu d’en-tête (Hero)', + // Search + noSearchResults: 'Aucun résultat trouvé', + matchingPortals: 'Portails', + matchingRequestTypes: 'Types de demande', + // Inbox + noRequests: 'Aucune demande pour le moment', + noRequestsDescription: 'Les demandes soumises via vos portails apparaîtront ici', + request: 'Demande', + portal: 'Portail', + submitter: 'Auteur', + status: 'Statut', + date: 'Date', + allPortals: 'Tous les portails', + allStatuses: 'Tous les statuts', + anonymous: 'Anonyme', + showingResults: 'Affichage de', + of: 'sur', + // Logo + logo: 'Logo du hub', + logoHelp: + 'Ce logo sera affiché sur le hub et utilisé par défaut pour les portails n’ayant pas leur propre logo.', + }, +}; \ No newline at end of file diff --git a/frontend/src/lib/locales/fr/common.js b/frontend/src/lib/locales/fr/common.js new file mode 100644 index 00000000..aaf09ff2 --- /dev/null +++ b/frontend/src/lib/locales/fr/common.js @@ -0,0 +1,342 @@ +/** + * French (fr) - Common translations + * Includes: common, toast, errors, validation, placeholders, emptyStates + */ +export default { + // Common UI elements + common: { + save: 'Enregistrer', + saveChanges: 'Enregistrer les modifications', + cancel: 'Annuler', + close: 'Fermer', + delete: 'Supprimer', + remove: 'Retirer', + edit: 'Modifier', + create: 'Créer', + add: 'Ajouter', + update: 'Mettre à jour', + search: 'Rechercher', + filter: 'Filtrer', + addFilter: 'Ajouter un filtre', + clear: 'Effacer', + reset: 'Réinitialiser', + discard: 'Abandonner', + discardChanges: 'Abandonner les modifications', + disconnect: 'Déconnecter', + confirm: 'Confirmer', + done: 'Terminé', + ok: 'OK', + yes: 'Oui', + no: 'Non', + back: 'Retour', + next: 'Suivant', + previous: 'Précédent', + finish: 'Terminer', + continue: 'Continuer', + submit: 'Soumettre', + apply: 'Appliquer', + refresh: 'Actualiser', + reload: 'Recharger', + retry: 'Réessayer', + copy: 'Copier', + or: 'ou', + menu: 'Menu', + paste: 'Coller', + duplicate: 'Dupliquer', + move: 'Déplacer', + moveUp: 'Déplacer vers le haut', + moveDown: 'Déplacer vers le bas', + select: 'Sélectionner', + selectAll: 'Tout sélectionner', + deselectAll: 'Tout désélectionner', + all: 'Tous', + enable: 'Activer', + disable: 'Désactiver', + enabled: 'Activé', + disabled: 'Désactivé', + show: 'Afficher', + hide: 'Masquer', + expand: 'Développer', + collapse: 'Réduire', + view: 'Afficher', + preview: 'Aperçu', + download: 'Télécharger', + upload: 'Téléverser', + import: 'Importer', + export: 'Exporter', + print: 'Imprimer', + share: 'Partager', + open: 'Ouvrir', + + // Libellés communs + id: 'ID', + name: 'Nom', + title: 'Titre', + description: 'Description', + type: 'Type', + status: 'Statut', + priority: 'Priorité', + category: 'Catégorie', + date: 'Date', + time: 'Heure', + created: 'Créé', + createdAt: 'Créé le', + updated: 'Mis à jour', + updatedAt: 'Mis à jour le', + modified: 'Modifié', + owner: 'Propriétaire', + author: 'Auteur', + createdBy: 'Créé par', + assigned: 'Attribué', + assignedTo: 'Attribué à', + assignTo: 'Attribuer à', + assign: 'Attribuer', + assignee: 'Attribué à', + allAssignees: 'Tous les responsables', + unassigned: 'Non attribué', + members: 'Membres', + user: 'Utilisateur', + users: 'Utilisateurs', + group: 'Groupe', + groups: 'Groupes', + action: 'Action', + actions: 'Actions', + options: 'Options', + configure: 'Configurer', + settings: 'Paramètres', + profile: 'Profil', + details: 'Détails', + summary: 'Résumé', + notes: 'Notes', + comments: 'Commentaires', + attachments: 'Pièces jointes', + tags: 'Étiquettes', + labels: 'Libellés', + scheduling: 'Planification', + dueDate: 'Date d’échéance', + startDate: 'Date de début', + endDate: 'Date de fin', + email: 'E-mail', + username: 'Nom d’utilisateur', + password: 'Mot de passe', + url: 'URL', + link: 'Lien', + color: 'Couleur', + icon: 'Icône', + order: 'Ordre', + position: 'Position', + count: 'Nombre', + total: 'Total', + active: 'Actif', + inactive: 'Inactif', + activate: 'Activer', + deactivate: 'Désactiver', + default: 'Par défaut', + custom: 'Personnalisé', + + // Indicateurs de statut + loading: 'Chargement…', + loadingSlow: 'Cela peut prendre un moment sur une connexion lente.', + skipToMainContent: 'Passer au contenu principal', + saving: 'Enregistrement…', + deleting: 'Suppression…', + submitting: 'Soumission…', + processing: 'Traitement…', + uploading: 'Téléversement…', + downloading: 'Téléchargement…', + generating: 'Génération…', + regenerating: 'Régénération…', + revoking: 'Révocation…', + applying: 'Application…', + + // Libellés divers + note: 'Note', + unknown: 'Inconnu', + + // Pagination / charger plus + loadMore: 'Charger plus', + loadingMore: 'Chargement complémentaire…', + remaining: 'restant(s)', + showingOfTotal: 'Affichage de {count} sur {total}', + showingXofY: 'Affichage de {from} à {to} sur {total}', + + // Résultats + noResults: 'Aucun résultat trouvé', + noData: 'Aucune donnée', + noItems: 'Aucun élément', + item: 'élément', + items: 'éléments', + empty: 'Vide', + + // Exigences + required: 'Obligatoire', + optional: 'Optionnel', + + // Confirmations + areYouSure: 'Êtes-vous sûr ?', + confirmDelete: 'Voulez-vous vraiment supprimer ceci ?', + confirmAction: 'Voulez-vous vraiment continuer ?', + cannotUndo: 'Cette action est irréversible.', + + // Temps + today: 'Aujourd’hui', + yesterday: 'Hier', + tomorrow: 'Demain', + now: 'Maintenant', + never: 'Jamais', + always: 'Toujours', + deleted: 'Supprimé', + dismiss: 'Ignorer', + error: 'Erreur', + notFound: 'Introuvable', + workspace: 'Espace de travail', + + // Termes communs supplémentaires + none: 'Aucun', + unknownUser: 'Utilisateur inconnu', + selected: 'Sélectionné', + searching: 'Recherche…', + noItemsFound: 'Aucun élément trouvé', + typeToSearch: 'Saisissez pour rechercher…', + pressEscapeToCancel: 'Appuyez sur Échap pour annuler', + viewDetails: 'Afficher les détails', + openInNewTab: 'Ouvrir dans un nouvel onglet', + account: 'Compte', + key: 'Clé', + project: 'Projet', + noProject: 'Aucun projet', + milestone: 'Jalon', + iteration: 'Itération', + global: 'Global', + root: 'Racine', + saved: 'Enregistré avec succès', + overview: 'Aperçu', + files: 'Fichiers', + ticket: 'Élément de travail', + tickets: 'Éléments de travail', + noFiles: 'Aucun fichier', + filesComingSoon: 'La gestion des fichiers sera bientôt disponible', + noTickets: 'Aucun élément de travail', + ticketsComingSoon: 'La gestion des éléments de travail sera bientôt disponible', + }, + + // Notifications instantanées (Toast) + toast: { + success: 'Succès', + error: 'Erreur', + warning: 'Avertissement', + info: 'Information', + saved: 'Enregistré avec succès', + deleted: 'Supprimé avec succès', + created: 'Créé avec succès', + updated: 'Mis à jour avec succès', + copied: 'Copié dans le presse-papiers', + failed: 'L’opération a échoué', + layoutSaveFailed: 'Impossible d’enregistrer les modifications du tableau de bord. Vos modifications seront perdues lors du rechargement.', + }, + + // Codes d’erreur backend + errors: { + // Erreurs d’authentification + UNAUTHORIZED: 'Authentification requise. Veuillez vous connecter.', + AUTHENTICATION_REQUIRED: 'Authentification requise. Veuillez vous connecter.', + INVALID_TOKEN: 'Votre session n’est pas valide. Veuillez vous reconnecter.', + TOKEN_EXPIRED: 'Votre session a expiré. Veuillez vous reconnecter.', + INSUFFICIENT_PERMISSION: 'Vous n’avez pas la permission d’effectuer cette action.', + + // Erreurs de validation + VALIDATION_FAILED: 'Veuillez vérifier vos données et réessayer.', + INVALID_INPUT: 'Les données fournies ne sont pas valides.', + MISSING_FIELD: 'Champ obligatoire manquant : {field}', + + // Erreurs de ressources + NOT_FOUND: 'La ressource demandée est introuvable.', + ITEM_NOT_FOUND: 'L’élément est introuvable.', + WORKSPACE_NOT_FOUND: 'L’espace de travail est introuvable.', + USER_NOT_FOUND: 'L’utilisateur est introuvable.', + CONFLICT: 'Cette ressource existe déjà.', + ALREADY_EXISTS: 'Une ressource avec cet identifiant existe déjà.', + + // Limitation de débit + RATE_LIMITED: 'Trop de requêtes. Veuillez patienter et réessayer.', + + // Erreurs serveur + INTERNAL_ERROR: 'Une erreur inattendue s’est produite. Veuillez réessayer plus tard.', + SERVICE_UNAVAILABLE: 'Le service est temporairement indisponible.', + + // Erreurs réseau + NETWORK_ERROR: 'Erreur réseau. Veuillez vérifier votre connexion.', + TIMEOUT: 'La requête a expiré. Veuillez réessayer.', + + // Générique + UNKNOWN: 'Une erreur inconnue s’est produite.', + generic: 'Un problème est survenu. Veuillez réessayer.', + failedToCreate: 'Échec de la création', + failedToSave: 'Échec de l’enregistrement', + failedToDelete: 'Échec de la suppression', + failedToLoad: 'Échec du chargement', + failedToUpdate: 'Échec de la mise à jour', + }, + + // Messages de validation + validation: { + required: 'Ce champ est obligatoire', + requiredField: '{field} est obligatoire', + invalidEmail: 'Veuillez saisir une adresse e-mail valide', + invalidUrl: 'Veuillez saisir une URL valide', + minLength: 'Doit contenir au moins {min} caractères', + maxLength: 'Ne doit pas dépasser {max} caractères', + minValue: 'Doit être d’au moins {min}', + maxValue: 'Ne doit pas dépasser {max}', + pattern: 'Format non valide', + passwordMismatch: 'Les mots de passe ne correspondent pas', + passwordTooWeak: 'Le mot de passe est trop faible', + invalidCredentials: 'E-mail ou mot de passe non valide', + emailTaken: 'Cet e-mail est déjà utilisé', + usernameTaken: 'Ce nom d’utilisateur est déjà pris', + slugInvalid: 'Seuls les lettres minuscules, les chiffres et les traits d’union sont autorisés', + nameRequired: 'Le nom est obligatoire', + fillRequired: 'Veuillez remplir tous les champs obligatoires', + }, + + // Textes d’invite (Placeholders) + placeholders: { + search: 'Rechercher…', + searchUsers: 'Rechercher des utilisateurs…', + searchChannels: 'Rechercher des canaux…', + searchProjects: 'Rechercher des projets…', + searchFields: 'Rechercher des champs…', + searchIcons: 'Rechercher des icônes…', + enterEmailOrUsername: 'Saisissez un e-mail ou un nom d’utilisateur', + enterPassword: 'Saisissez le mot de passe actuel', + enterNewPassword: 'Saisissez le nouveau mot de passe', + confirmNewPassword: 'Confirmez le nouveau mot de passe', + optionalDescription: 'Description optionnelle…', + channelDescription: 'Brève description de l’objectif de ce canal', + columnName: 'Nom de la colonne', + typeName: 'Saisissez « {name} » ici', + searchWorkItems: 'Rechercher des éléments de travail…', + }, + + // États vides + emptyStates: { + noData: 'Aucune donnée', + noResults: 'Aucun résultat trouvé', + noItemsMatch: 'Aucun élément ne correspond au filtre', + noFieldChanges: 'Aucu changement de champ détecté', + noRequirements: 'Aucune exigence configurée', + configureFilter: 'Configurez un filtre pour afficher des éléments', + createToStart: 'Créez un(e) {item} pour commencer', + }, + dueDate: { + noDueDate: 'Pas de date d’échéance', + dueToday: 'À échéance aujourd’hui', + dueTomorrow: 'À échéance demain', + dueYesterday: 'Échu hier', + dueInDays: 'À échéance dans {days} jours', + overdueByDays: 'En retard de {days} jours', + overdueTooltip: 'En retard de {days} jours — était prévu pour le {date}', + dueSoonTooltip: 'À échéance dans {days} jours — {date}', + dueLaterTooltip: 'À échéance le {date}', + }, +}; \ No newline at end of file diff --git a/frontend/src/lib/locales/fr/dashboard.js b/frontend/src/lib/locales/fr/dashboard.js new file mode 100644 index 00000000..7de23827 --- /dev/null +++ b/frontend/src/lib/locales/fr/dashboard.js @@ -0,0 +1,118 @@ +export default { + dashboard: { + salutation: { + withName: '{salutation}, {name} !', + withoutName: '{salutation} !', + }, + sections: { + yourDay: { + title: 'Votre journée', + subtitle: 'Un aperçu rapide de ce qui requiert votre attention', + }, + work: { + title: 'Travail', + subtitle: 'Votre liste personnelle et les éléments qui vous sont attribués', + }, + workspaces: { + title: 'Espaces de travail', + subtitle: 'Reprenez où vous vous étiez arrêté', + }, + }, + widgetCatalog: { + dailyBriefing: { + name: 'Briefing quotidien', + description: 'Résumé généré par IA de ce qui importe pour vous aujourd’hui', + }, + yourActivity: { + name: 'Votre activité', + description: 'Éléments que vous avez récemment consultés, modifiés ou commentés', + }, + whatsNew: { + name: 'Nouveautés', + description: 'Dernières notifications et mises à jour non lues', + }, + personalTasks: { + name: 'Tâches personnelles', + description: 'Éléments de votre liste de tâches personnelles', + }, + savedSearch: { + name: 'Recherche enregistrée', + description: 'Afficher les éléments de travail d’une collection enregistrée', + }, + assignedToMe: { + name: 'Qui m’est attribué', + description: 'Éléments ouverts qui vous sont attribués dans tous les espaces de travail', + }, + watchedItems: { + name: 'Éléments suivis', + description: 'Éléments que vous suivez', + }, + upcomingMilestones: { + name: 'Jalons à venir', + description: 'Jalons dont la date cible approche', + }, + recentWorkspaces: { + name: 'Espaces de travail récents', + description: 'Espaces de travail que vous avez récemment visités', + }, + quickAccess: { + name: 'Accès rapide', + description: 'Liens rapides vers les espaces de travail auxquels vous avez accès', + }, + }, + customization: { + widgets: 'Widgets', + activity: { + name: 'Activité', + description: 'Briefings, flux d’activité et notifications', + }, + work: { + name: 'Travail', + description: 'Éléments, jalons et tâches qui vous sont attribués', + }, + navigation: { + name: 'Navigation', + description: 'Accès rapide aux espaces de travail', + }, + tipLabel: 'Conseil', + tip: 'Faites glisser des widgets d’ici vers n’importe quelle section de votre tableau de bord.', + }, + editor: { + newSection: 'Nouvelle section', + deleteSectionConfirm: 'Supprimer cette section ? Tous les widgets de cette section seront retirés.', + doneEditing: 'Terminer l’édition', + customize: 'Personnaliser', + editModeDescription: 'Mode édition : ajoutez, renommez, réordonnez ou supprimez des sections et des widgets', + addSection: 'Ajouter une section', + sectionLabel: 'Section du tableau de bord', + sectionTitlePlaceholder: 'Titre de la section', + sectionSubtitlePlaceholder: 'Sous-titre (optionnel)', + renameSection: 'Renommer la section', + deleteSection: 'Supprimer la section', + unknownWidgetType: 'Type de widget inconnu : {type}', + noWidgets: 'Aucun widget dans cette section pour le moment', + addWidgetsHint: 'Sélectionnez Personnaliser pour ajouter des widgets', + noSections: 'Aucune section configurée', + addSectionsHint: 'Sélectionnez Modifier pour ajouter des sections à votre tableau de bord', + }, + states: { + assignedLoadError: 'Impossible de charger les éléments qui vous sont attribués', + assignedEmpty: 'Rien ne vous est attribué pour le moment', + personalTasksLoadError: 'Impossible de charger vos tâches personnelles', + personalTasksEmpty: 'Votre liste de tâches personnelles est vide', + dailyBriefingUnavailable: 'Votre briefing quotidien n’est pas disponible pour le moment. Il repose sur une intégration IA. Si vous venez d’en configurer une, revenez dans quelques instants.', + updatedAt: 'Mis à jour {time}', + priorityLabel: 'Priorité : {priority}', + noWorkspaces: 'Aucun espace de travail pour le moment', + createWorkspace: 'En créer un', + workspaceAvatarAlt: 'Avatar de {name}', + visited: 'visité {time}', + noUpcomingMilestones: 'Aucun jalon à venir', + milestoneProgress: '{done} sur {total} terminés', + daysOverdue: '{days} jours de retard', + daysLeft: '{days} jours restants', + watchedItemsEmpty: 'Vous ne suivez aucun élément', + workspaceWithId: 'Espace de travail {id}', + }, + }, +}; \ No newline at end of file diff --git a/frontend/src/lib/locales/fr/index.js b/frontend/src/lib/locales/fr/index.js new file mode 100644 index 00000000..06d7da15 --- /dev/null +++ b/frontend/src/lib/locales/fr/index.js @@ -0,0 +1,46 @@ +/** + * French (fr) translations - Aggregated module + */ + +import { createLocale } from '../createLocale.js'; +import actions from './actions.js'; +import admin from './admin.js'; +import analytics from './analytics.js'; +import auth from './auth.js'; +import channels from './channels.js'; +import common from './common.js'; +import logbook from './logbook.js'; +import misc from './misc.js'; +import navigation from './navigation.js'; +import teams from './teams.js'; +import testing from './testing.js'; +import time from './time.js'; +import ui from './ui.js'; +import workflows from './workflows.js'; +import workspace from './workspace.js'; +import pages from './pages.js'; +import adminOperations from './adminOperations.js'; +import agents from './agents.js'; +import dashboard from './dashboard.js'; + +export default createLocale({ + common, + auth, + workspace, + admin, + testing, + time, + channels, + workflows, + ui, + navigation, + misc, + actions, + logbook, + analytics, + teams, + pages, + adminOperations, + agents, + dashboard, +}); diff --git a/frontend/src/lib/locales/fr/logbook.js b/frontend/src/lib/locales/fr/logbook.js new file mode 100644 index 00000000..f41a4bf8 --- /dev/null +++ b/frontend/src/lib/locales/fr/logbook.js @@ -0,0 +1,95 @@ +/** + * Logbook / Knowledge Base translations for French locale + */ + +export default { + logbook: { + title: 'Base de connaissances', + subtitle: 'Documents, notes et connaissances de l’équipe', + allDocuments: 'Tous les documents', + createBucket: 'Créer un dossier', + uploadDocument: 'Téléverser un document', + newNote: 'Nouvelle note', + noDocuments: 'Aucun document pour le moment', + noDocumentsDescription: 'Téléversez un fichier ou créez une note pour commencer', + noDocumentsAllDescription: 'Aucun document trouvé dans vos dossiers accessibles', + noBuckets: 'Aucun dossier pour le moment', + noBucketsDescription: 'Créez un dossier pour organiser vos connaissances', + search: 'Rechercher des documents…', + article: 'Article', + rawContent: 'Contenu brut', + info: 'Info', + back: 'Retour', + save: 'Enregistrer', + saving: 'Enregistrement…', + saved: 'Document enregistré', + uploadSuccess: 'Document téléversé avec succès', + noteCreated: 'Note créée avec succès', + bucketCreated: 'Dossier créé avec succès', + bucketUpdated: 'Dossier mis à jour', + bucketDeleted: 'Dossier supprimé', + confirmDeleteBucket: + 'Voulez-vous vraiment supprimer ce dossier ? Tous les documents qu’il contient seront archivés.', + documentArchived: 'Document archivé', + documentDeleted: 'Document supprimé', + confirmDelete: 'Voulez-vous vraiment supprimer ce document ?', + confirmArchiveDocument: 'Voulez-vous vraiment archiver ce document ?', + viewOriginal: 'Afficher l’original', + delete: 'Supprimer', + + // Bucket form + bucketName: 'Nom du dossier', + bucketNamePlaceholder: 'ex. Docs ingénierie', + bucketDescription: 'Description', + bucketDescriptionPlaceholder: 'Quel type de documents va ici ?', + + // Note form + noteTitle: 'Titre', + noteTitlePlaceholder: 'Titre de la note', + noteContent: 'Contenu', + noteContentPlaceholder: 'Rédigez votre note en markdown…', + + // Upload + dropzoneTitle: 'Déposez les fichiers ici', + dropzoneDescription: + 'ou cliquez pour parcourir. Prend en charge PDF, DOCX, PPTX, XLSX, TXT, MD, HTML et les images.', + uploading: 'Téléversement…', + documentTitle: 'Titre du document', + documentTitlePlaceholder: 'Optionnel - nom du fichier par défaut', + + // Status + status: { + pending: 'En attente', + processing: 'Traitement en cours', + ready: 'Prêt', + error: 'Erreur', + }, + + // Source type + sourceType: { + upload: 'Téléversement', + note: 'Note', + email: 'E-mail', + }, + + // Content type (classification) + contentType: { + knowledge: 'Connaissance', + record: 'Registre', + correspondence: 'Correspondance', + }, + + // Document info + mimeType: 'Type de fichier', + contentHash: 'Hachage du contenu', + retrievalCount: 'Nombre d’extractions', + chunkCount: 'Segments', + createdAt: 'Créé le', + updatedAt: 'Mis à jour le', + reviewedAt: 'Révisé le', + health: 'Santé', + author: 'Auteur', + processingMessage: 'Le document est en cours de traitement…', + runAction: 'Exécuter l’action', + }, +}; \ No newline at end of file diff --git a/frontend/src/lib/locales/fr/misc.js b/frontend/src/lib/locales/fr/misc.js new file mode 100644 index 00000000..96093378 --- /dev/null +++ b/frontend/src/lib/locales/fr/misc.js @@ -0,0 +1,916 @@ +/** + * Miscellaneous locale strings for Windshift + * Contains: iterations, milestones, assets, personal, connections, + * migration, migrationAssistant, setup, createModal, scm, organization, + * fields, itemTypes, categories, members, configuration, audit, auditLog, projects + */ + +export default { + jiraImport: { + title: { + cloud: 'Importation depuis Jira Cloud', + datacenter: 'Importation depuis Jira Data Center', + }, + subtitle: { + cloud: 'Importer des éléments de travail depuis Jira Cloud', + datacenter: 'Importer des éléments de travail depuis Jira Data Center', + }, + steps: { + connect: 'Connexion', + projects: 'Projets', + xray: 'Xray', + mapping: 'Mappage', + preview: 'Aperçu', + import: 'Importation', + }, + deploymentType: { + cloud: 'Jira Cloud', + cloudDesc: '*.atlassian.net', + datacenter: 'Data Center', + datacenterDesc: 'Auto-hébergé', + }, + form: { + urlCloud: 'URL Jira Cloud', + urlDatacenter: 'URL Jira Data Center', + email: 'Adresse e-mail', + username: 'Nom d’utilisateur', + apiToken: 'Jeton API', + personalAccessToken: 'Jeton d’accès personnel', + generateToken: 'Générer un jeton', + tokenHelpCloud: 'depuis les paramètres de votre compte Atlassian', + tokenHelpDatacenter: 'Créez un jeton d’accès personnel dans les paramètres de votre profil Jira', + }, + buttons: { + connect: 'Se connecter', + continue: 'Continuer', + analyzeAndConfigure: 'Analyser et configurer', + startImport: 'Démarrer l’importation', + done: 'Terminé', + back: 'Retour', + cancel: 'Annuler', + selectAll: 'Tout sélectionner', + deselectAll: 'Tout désélectionner', + addNewConnection: 'Ajouter une nouvelle connexion', + useExisting: 'Utiliser l’existant', + retryImport: 'Réessayer l’importation', + }, + messages: { + connected: 'Connecté à {name}', + selectConnection: 'Sélectionnez une connexion existante ou créez-en une nouvelle', + credentialsHelpCloud: + 'Saisissez vos identifiants Jira Cloud. Vous pouvez générer un jeton API depuis les paramètres de votre compte Atlassian.', + credentialsHelpDatacenter: + 'Saisissez un jeton d’accès personnel depuis les paramètres de votre profil Jira Data Center.', + reviewSummary: + 'Vérifiez le résumé de l’importation avant de continuer. Cette opération peut prendre plusieurs minutes pour les grands projets.', + noAttachments: 'Les pièces jointes ne seront pas importées', + noAttachmentsDesc: + 'Aucun stockage de pièces jointes n’est configuré. Définissez le paramètre --attachment-path lors du démarrage du serveur pour activer l’importation des pièces jointes.', + }, + projects: { + selected: 'Sélectionnez les projets à importer ({selected} sur {total} sélectionnés)', + openIssuesOnly: 'Importer uniquement les tickets ouverts', + openIssuesOnlyDesc: 'Exclut les tickets ayant la catégorie de statut « Terminé »', + teamManaged: 'Géré par l’équipe', + issues: '{count} tickets', + }, + mapping: { + workspaces: 'Espaces de travail', + workspacesDesc: 'Chaque projet Jira deviendra un espace de travail Windshift', + issueTypes: 'Types de tickets', + issueTypesDesc: 'Les types de tickets seront créés sous forme de types d’éléments dans Windshift', + statuses: 'Statuts', + statusesDesc: 'Les statuts seront créés et regroupés par catégorie', + customFields: 'Champs personnalisés', + customFieldsDesc: 'Les champs personnalisés pouvant être mappés seront créés dans Windshift', + versions: 'Versions / Jalons', + versionsDesc: 'Les versions Jira seront importées en tant que jalons de l’espace de travail.', + subtask: 'Sous-tâche', + create: 'Créer', + skip: 'Ignorer', + }, + preview: { + workspaces: 'Espaces de travail', + workItems: 'Éléments de travail', + statuses: 'Statuts', + itemTypes: 'Types d’éléments', + customFields: 'Champs personnalisés', + milestones: 'Jalons', + users: 'Utilisateurs', + usersNew: '({count} nouveaux)', + assets: 'Ressources', + projectsToImport: 'Projets à importer', + }, + import: { + importing: 'Importation en cours…', + starting: 'Démarrage de l’importation…', + complete: 'Importation terminée !', + completeWithErrors: 'Importation terminée avec des erreurs', + success: '{count} éléments ont été importés avec succès.', + failed: 'Échec de l’importation pour {count} éléments.', + ready: 'Prêt à importer', + readyDesc: 'Cliquez sur « Démarrer l’importation » pour commencer à importer {count} éléments.', + progress: 'Progression', + }, + errors: { + connectionFailed: 'Échec de la connexion à Jira', + loadProjectsFailed: 'Échec du chargement des projets', + analyzeFailed: 'Échec de l’analyse des projets', + importFailed: 'Échec du démarrage de l’importation', + loadConnectionsFailed: 'Échec du chargement des connexions', + deleteConnectionFailed: 'Échec de la suppression de la connexion', + }, + }, + + itemTypes: { + title: 'Types d’éléments', + subtitle: 'Configurer les types d’éléments et leurs propriétés', + itemType: 'Type d’élément', + itemTypes_one: '{count} type d’élément', + itemTypes_other: '{count} types d’éléments', + createItemType: 'Créer un type d’élément', + editItemType: 'Modifier le type d’élément', + deleteItemType: 'Supprimer le type d’élément', + typeName: 'Nom du type', + noItemTypes: 'Aucun type d’élément trouvé', + itemTypeCreated: 'Type d’élément créé avec succès', + itemTypeUpdated: 'Type d’élément mis à jour avec succès', + itemTypeDeleted: 'Type d’élément supprimé avec succès', + }, + + fields: { + title: 'Champs personnalisés', + requestFormFields: 'Champs du formulaire de demande', + subtitle: 'Définir des champs personnalisés pour vos éléments', + field: 'Champ', + fields_one: '{count} champ', + fields_other: '{count} champs', + createField: 'Créer un champ', + editField: 'Modifier le champ', + deleteField: 'Supprimer le champ', + returnToBoard: 'Retour aux champs du tableau', + fieldName: 'Nom du champ', + fieldType: 'Type de champ', + fieldDescription: 'Description', + defaultValue: 'Valeur par défaut', + placeholder: 'Espace réservé', + helpText: 'Texte d’aide', + noFields: 'Aucun champ trouvé', + fieldCreated: 'Champ créé avec succès', + fieldUpdated: 'Champ mis à jour avec succès', + fieldDeleted: 'Champ supprimé avec succès', + configureFields: 'Configurer les champs', + searchFields: 'Rechercher des champs…', + indexSettings: 'Paramètres d’indexation', + // Field types + text: 'Texte', + number: 'Nombre', + date: 'Date', + datetime: 'Date et heure', + select: 'Sélection', + multiSelect: 'Sélection multiple', + checkbox: 'Case à cocher', + user: 'Utilisateur', + url: 'URL', + // Field type hints + milestoneHint: + 'Les champs de type jalon font automatiquement référence aux jalons du système. Les utilisateurs pourront choisir parmi les jalons existants lors du renseignement de ce champ.', + dateHint: + 'Les champs de type date permettent aux utilisateurs de sélectionner des dates à l’aide d’un sélecteur. Les valeurs sont stockées au format AAAA-MM-JJ.', + assetHint: + 'Les champs de type ressource permettent aux utilisateurs de sélectionner des ressources au sein d’un ensemble défini. Vous pouvez optionnellement filtrer les ressources disponibles via une requête QL.', + portalCustomerHint: + 'Les champs Client du portail font référence aux clients du portail. À utiliser avec currentCustomer() dans les rapports de ressources pour filtrer selon le client connecté.', + customerOrganisationHint: + 'Les champs Organisation cliente font référence aux organisations clientes. À utiliser avec currentOrganisation() dans les rapports de ressources pour filtrer selon l’organisation du client.', + usedIn: 'Utilisé dans', + portalCustomers: 'Clients du portail', + customerOrganisations: 'Organisations clientes', + }, + + categories: { + title: 'Catégories', + subtitle: 'Gérer les catégories', + category: 'Catégorie', + categories_one: '{count} catégorie', + categories_other: '{count} catégories', + createCategory: 'Créer une catégorie', + editCategory: 'Modifier la catégorie', + deleteCategory: 'Supprimer la catégorie', + categoryName: 'Nom de la catégorie', + noCategories: 'Aucune catégorie trouvée', + noCategorizedWork: 'Aucun travail catégorisé pour le moment', + categoryCreated: 'Catégorie créée avec succès', + categoryUpdated: 'Catégorie mise à jour avec succès', + categoryDeleted: 'Catégorie supprimée avec succès', + deleteWarning: 'Les éléments de cette catégorie deviendront non catégorisés', + selectCategory: 'Sélectionner une catégorie', + uncategorized: 'Non catégorisé', + // Category modal + addNewCategory: 'Ajouter une nouvelle catégorie', + addCategory: 'Ajouter une catégorie', + categoryNamePlaceholder: 'Nom de la catégorie…', + existingCategories: 'Catégories existantes', + confirmDeleteCategory: + 'Supprimer la catégorie « {name} » ? Les éléments de cette catégorie deviendront non catégorisés.', + failedToDeleteCategory: 'Échec de la suppression de la catégorie. Elle est peut-être encore utilisée.', + noCategoriesYet: 'Aucune catégorie pour le moment', + addFirstCategoryHint: 'Ajoutez votre première catégorie ci-dessus.', + }, + + projects: { + title: 'Projets', + subtitle: 'Gérer vos projets', + project: 'Projet', + projects_one: '{count} projet', + projects_other: '{count} projets', + createProject: 'Créer un projet', + editProject: 'Modifier le projet', + deleteProject: 'Supprimer le projet', + projectName: 'Nom du projet', + projectKey: 'Clé du projet', + noProjects: 'Aucun projet trouvé', + searchProjects: 'Rechercher des projets…', + loadingProjects: 'Chargement des projets…', + projectCreated: 'Projet créé avec succès', + projectUpdated: 'Projet mis à jour avec succès', + projectDeleted: 'Projet supprimé avec succès', + }, + + iterations: { + title: 'Itérations', + subtitle: 'Gérer les itérations et les versions', + iteration: 'Itération', + iterations_one: '{count} itération', + iterations_other: '{count} itérations', + + // Actions + createIteration: 'Créer une itération', + editIteration: 'Modifier l’itération', + updateIteration: 'Mettre à jour l’itération', + deleteIteration: 'Supprimer l’itération', + startIteration: 'Démarrer l’itération', + completeIteration: 'Terminer l’itération', + start: 'Démarrer', + + // Listing + noIterations: 'Aucune itération trouvée', + allTypes: 'Tous les types', + manageTypes: 'Gérer les types', + backToIterations: 'Retour aux itérations', + filterByIteration: 'Filtrer par itération', + addGlobalIteration: 'Ajouter une itération', + + // Status + status: { + planned: 'Planifié', + active: 'Actif', + completed: 'Terminé', + cancelled: 'Annulé', + }, + statusPlanned: 'Planifié', + statusActive: 'Actif', + statusCompleted: 'Terminé', + statusCancelled: 'Annulé', + + // Scope + global: 'Global', + local: 'Local', + thisWorkspace: 'Cet espace de travail', + globalIteration: 'Itération globale', + globalIterations: 'Itérations globales', + globalIterationDescription: 'Visible dans tous les espaces de travail', + localIteration: 'Itération locale', + localIterations: 'Itérations locales', + localIterationDescription: 'Visible uniquement dans cet espace de travail', + switchTo: 'Passer à {scope}', + scope: 'Portée', + + // Time / progress + daysOverdue: '{count} jours de retard', + endsToday: 'Se termine aujourd’hui', + oneDayRemaining: '1 jour restant', + daysRemaining: '{count} jours restants', + overdue: 'En retard', + complete: 'terminé', + dateRange: 'Plage de dates', + startDate: 'Date de début', + endDate: 'Date de fin', + burndownChart: 'Graphique d’avancement (Burndown)', + idealProgress: 'Idéal', + ideal: 'Idéal', + noBurndownData: 'Aucune donnée d’avancement disponible', + + // Items in iteration + noItems: 'Aucun élément', + summary: 'Résumé', + totalItems: 'Total des éléments', + completed: 'Terminés', + remaining: 'Restants', + byStatusCategory: 'Par catégorie de statut', + noStatusData: 'Aucune donnée de statut', + workItems: 'Éléments de travail', + noItemsAssigned: 'Aucun élément attribué', + assignItemsHint: 'Attribuez des éléments de travail à cette itération pour suivre la progression', + + // Form fields + iterationName: 'Nom de l’itération', + iterationNamePlaceholder: 'ex. Sprint 1, T1 2025, Version 2.0', + iterationDescriptionPlaceholder: 'Description ou objectifs optionnels pour cette itération', + descriptionPlaceholder: 'Description optionnelle', + selectStatus: 'Sélectionner un statut…', + selectType: 'Sélectionner un type…', + noType: 'Aucun type', + + // Validation + iterationNameRequired: 'Le nom de l’itération est requis', + startDateRequired: 'La date de début est requise', + endDateRequired: 'La date de fin est requise', + endDateMustBeAfterStart: 'La date de fin doit être postérieure à la date de début', + typeRequired: 'Le type d’itération est requis', + failedToSaveIteration: 'Échec de l’enregistrement de l’itération', + + // Confirms / toasts + confirmDelete: 'Voulez-vous vraiment supprimer l’itération « {name} » ?', + completeIterationConfirm: 'Terminer l’itération « {name} » ?', + iterationStarted: 'Itération « {name} » démarrée', + iterationCompleted: 'Itération « {name} » terminée', + activeScopeWarning: 'Il n’est pas recommandé de modifier la portée d’une itération active', + + // Backlog complete-iteration dialog + itemsDone: '{count} éléments terminés', + itemsIncomplete: '{count} incomplets', + allItemsDone: 'Tous les éléments sont terminés !', + moveToBacklog: 'Déplacer vers le backlog', + moveToIteration: 'Déplacer vers une autre itération', + incompleteItemsAction: 'Éléments incomplets :', + }, + + milestones: { + title: 'Jalons', + milestone: 'Jalon', + subtitle: 'Suivre les versions et les échéances', + addMilestone: 'Ajouter un jalon', + noMilestones: 'Aucun jalon pour le moment', + noMilestonesDescription: 'Créez votre premier jalon pour suivre les versions et les échéances.', + noMilestonesInCategory: 'Aucun jalon dans cette catégorie', + allCategories: 'Toutes les catégories', + manageCategories: 'Gérer les catégories', + allMilestones: 'Tous les jalons', + workspaceMilestones: 'Jalons de l’espace de travail', + status: { + planning: 'Planification', + inProgress: 'En cours', + completed: 'Terminé', + cancelled: 'Annulé', + }, + daysOverdue: '{count} jours de retard', + dueToday: 'Échéance aujourd’hui', + oneDayRemaining: '1 jour restant', + daysRemaining: '{count} jours restants', + confirmDelete: 'Voulez-vous vraiment supprimer le jalon « {name} » ?', + complete: 'terminé', + noItems: 'Aucun élément', + byStatusCategory: 'Par catégorie de statut', + noStatusData: 'Aucune donnée de statut', + workItems: 'Éléments de travail', + noItemsAssigned: 'Aucun élément attribué', + assignItemsHint: 'Attribuez des éléments de travail à ce jalon pour suivre la progression', + milestoneName: 'Nom du jalon', + milestoneNamePlaceholder: 'ex. Version T1, Lancement Bêta', + targetDate: 'Date cible', + selectStatus: 'Sélectionner un statut…', + descriptionPlaceholder: 'Description optionnelle de ce jalon', + noCategory: 'Aucune catégorie', + overdue: 'En retard', + openEnded: 'Sans date de fin', + scope: 'Portée', + global: 'Global', + local: 'Local', + globalMilestone: 'Jalon global', + globalMilestones: 'Jalons globaux', + globalMilestoneDescription: 'Visible dans tous les espaces de travail', + localMilestone: 'Jalon local', + localMilestones: 'Jalons locaux', + localMilestoneDescription: 'Visible uniquement dans cet espace de travail', + switchTo: 'Passer à {scope}', + workspace: 'Espace de travail', + selectWorkspace: 'Sélectionner un espace de travail', + manageMilestoneCategories: 'Gérer les catégories de jalons', + hideCompleted: 'Masquer les terminés', + hideCompletedHelp: 'Masquer les jalons terminés de la liste', + noVisibleMilestones: 'Aucun jalon visible', + dragToReorder: 'Faire glisser pour réordonner', + columnStatus: 'Statut', + columnMilestone: 'Jalon', + columnTargetDate: 'Date cible', + columnTimeline: 'Chronologie', + columnTests: 'Tests', + visibleCount: '{count} jalons', + visibleCount_one: '{count} jalon', + visibleCount_other: '{count} jalons', + visibleInCategory: '{count} jalons dans cette catégorie', + visibleInCategory_one: '{count} jalon dans cette catégorie', + visibleInCategory_other: '{count} jalons dans cette catégorie', + }, + + assets: { + // Asset Management + title: 'Gestion des ressources', + subtitle: 'Gérer les ensembles de ressources, types, catégories et ressources', + selectAssetSet: 'Sélectionner un ensemble de ressources', + newSet: 'Nouvel ensemble', + createAssetSet: 'Créer un ensemble de ressources', + editSet: 'Modifier l’ensemble', + deleteSet: 'Supprimer l’ensemble', + noAssetSets: 'Aucun ensemble de ressources', + noAssetSetsDesc: 'Créez votre premier ensemble de ressources pour commencer la gestion.', + selectAnAssetSet: 'Sélectionner un ensemble de ressources', + selectAnAssetSetDesc: 'Choisissez un ensemble de ressources dans le menu déroulant ci-dessus pour afficher et gérer les ressources.', + default: 'Par défaut', + assetTag: 'Étiquette de ressource', + preview: 'Aperçu', + serialNumber: 'Numéro de série', + + // Tabs + types: 'Types', + categories: 'Catégories', + permissions: 'Autorisations', + automations: 'Automatisations', + + // Asset Types + newType: 'Nouveau type', + createType: 'Créer un type', + editType: 'Modifier le type', + noAssetTypes: 'Aucun type de ressource', + noAssetTypesDesc: 'Créez des types de ressources pour catégoriser vos ressources.', + assetType: 'Type de ressource', + + // Asset Categories + newCategory: 'Nouvelle catégorie', + createCategory: 'Créer une catégorie', + editCategory: 'Modifier la catégorie', + noCategories: 'Aucune catégorie', + noCategoriesDesc: 'Créez des catégories pour organiser vos ressources.', + parentCategory: 'Catégorie parente', + noParent: 'Aucun parent (Niveau racine)', + + // Permissions + assignRole: 'Attribuer un rôle', + everyoneRole: 'Rôle Tout le monde', + everyoneRoleDesc: 'Rôle par défaut pour tous les utilisateurs. Les attributions individuelles prévalent sur ce rôle.', + noRoleAssignments: 'Aucune attribution de rôle', + noRoleAssignmentsDesc: 'Ajoutez des attributions de rôle pour contrôler qui peut accéder à cet ensemble de ressources.', + assignee: 'Attribué à', + role: 'Rôle', + + // Image preview + failedToDownload: 'Échec du téléchargement du fichier', + editDiagram: 'Modifier le schéma', + untitledDiagram: 'Schéma sans titre', + diagram: 'schéma', + uploadedBy: 'Téléversé par {name}', + zoomOut: 'Zoom arrière (-)', + zoomIn: 'Zoom avant (+)', + rotate: 'Faire pivoter (R)', + fitToScreen: 'Ajuster à l’écran (F)', + shortcutScroll: 'Défiler : Zoom', + shortcutDrag: 'Faire glisser : Panoramique', + shortcutRotate: 'R : Pivoter', + shortcutFit: 'F : Ajuster', + shortcutReset: '0 : Réinitialiser', + shortcutClose: 'Échap : Fermer', + }, + + personal: { + // Navigation + myTasks: 'Mes tâches', + reviews: 'Revues', + weeklyCalendar: 'Calendrier hebdomadaire', + + // Tasks + personalTasks: 'Tâches personnelles', + addPersonalTask: 'Ajouter une tâche personnelle', + noPersonalWorkspace: 'Aucun espace de travail personnel trouvé', + taskTitlePlaceholder: 'Titre de la tâche…', + noTasksYet: 'Aucune tâche pour le moment', + noOpenTasks: 'Aucune tâche ouverte', + markIncomplete: 'Marquer comme incomplète', + markComplete: 'Marquer comme terminée', + openTask: 'Ouvrir la tâche', + unlinkTask: 'Dissocier la tâche', + setDueDate: 'Définir la date d’échéance', + comments: 'Commentaires', + + // Review + personalReview: 'Revue personnelle', + today: 'aujourd’hui', + daily: 'Quotidienne', + weekly: 'Hebdomadaire', + dailyReview: 'Revue quotidienne', + weeklyReview: 'Revue hebdomadaire', + recentReviews: 'Revues récentes', + noPreviousReviews: 'Aucune revue précédente trouvée', + exitFocusMode: 'Quitter le mode concentration', + enterFocusMode: 'Passer en mode concentration', + saveReview: 'Enregistrer la revue', + + // Completed section + completedToday: 'Terminé aujourd’hui', + completedThisWeek: 'Terminé cette semaine', + loadingCompletedItems: 'Chargement des éléments terminés…', + noCompletedItemsDay: 'Aucun élément terminé pour ce jour', + noCompletedItemsWeek: 'Aucun élément terminé pour cette semaine', + + // Reflection section - Daily prompts + reflection: 'Réflection', + whatAccomplished: 'Qu’ai-je accompli aujourd’hui ?', + whatWentWell: 'Qu’est-ce qui s’est bien passé aujourd’hui ?', + whatImprove: 'Que pourrais-je améliorer demain ?', + + // Reflection section - Weekly prompts + weeklyAccomplishments: 'Qu’avons-nous accompli cette semaine ?', + weeklyChallenges: 'À quels défis avons-nous fait face ?', + weeklyPriorities: 'Quelles sont nos priorités pour la semaine prochaine ?', + + // Reflection placeholders + placeholderAccomplishments: 'Décrivez vos principales réalisations…', + placeholderWentWell: 'Ce qui s’est bien passé et pourquoi…', + placeholderImprovements: 'Axes d’amélioration et prochaines étapes…', + startWriting: 'Commencez à rédiger votre réflexion…', + }, + + audit: { + title: 'Journal d’audit', + subtitle: 'Suivre et examiner toutes les actions administratives et les événements de sécurité', + event: 'Événement', + user: 'Utilisateur', + action: 'Action', + resource: 'Ressource', + timestamp: 'Horodatage', + details: 'Détails', + ipAddress: 'Adresse IP', + noEvents: 'Aucun événement d’audit trouvé', + }, + + connections: { + title: 'Connexions', + subtitle: 'Gérer les intégrations externes', + connection: 'Connexion', + createConnection: 'Créer une connexion', + editConnection: 'Modifier la connexion', + deleteConnection: 'Supprimer la connexion', + connectionType: 'Type de connexion', + noConnections: 'Aucune connexion trouvée', + connectionCreated: 'Connexion créée avec succès', + connectionUpdated: 'Connexion mise à jour avec succès', + connectionDeleted: 'Connexion supprimée', + connectionSuccessful: 'Connexion réussie', + testConnection: 'Tester la connexion', + }, + + migration: { + title: 'Migration', + subtitle: 'Migrer les données entre les systèmes', + migrateConfiguration: 'Migrer la configuration', + migrationCompleted: 'Migration terminée', + migrationSuccess: 'Tous les éléments ont été migrés avec succès', + targetWorkspace: 'Espace de travail cible', + targetWorkspaceRequired: 'L’espace de travail cible est requis', + }, + + members: { + title: 'Membres', + subtitle: 'Gérer les membres de l’équipe', + addMember: 'Ajouter un membre', + removeMember: 'Retirer le membre', + searchMembers: 'Rechercher des membres par nom ou e-mail…', + }, + + configuration: { + title: 'Configuration', + searchConfigurationSets: 'Rechercher des ensembles de configuration…', + }, + + auditLog: { + filters: 'Filtres', + actionType: 'Type d’action', + resourceType: 'Type de ressource', + status: 'Statut', + searchPlaceholder: 'Nom d’utilisateur, ressource…', + startDate: 'Date de début', + endDate: 'Date de fin', + applyFilters: 'Appliquer les filtres', + clearFilters: 'Effacer les filtres', + loadingAuditLogs: 'Chargement des journaux d’audit…', + noAuditLogs: 'Aucun journal d’audit trouvé', + exportCsv: 'Exporter en CSV', + exportJson: 'Exporter en JSON', + auditLogDetails: 'Détails du journal d’audit', + timestamp: 'Horodatage', + user: 'Utilisateur', + action: 'Action', + resource: 'Ressource', + resourceName: 'Nom de la ressource', + ipAddress: 'Adresse IP', + userAgent: 'Agent utilisateur', + errorMessage: 'Message d’erreur', + additionalDetails: 'Détails supplémentaires', + success: 'Succès', + failed: 'Échec', + all: 'Tous', + // Action types + allActions: 'Toutes les actions', + userCreated: 'Utilisateur créé', + userUpdated: 'Utilisateur mis à jour', + userDeleted: 'Utilisateur supprimé', + userActivated: 'Utilisateur activé', + userDeactivated: 'Utilisateur désactivé', + passwordReset: 'Réinitialisation du mot de passe', + apiTokenCreated: 'Jeton API créé', + apiTokenRevoked: 'Jeton API révoqué', + workspaceCreated: 'Espace de travail créé', + workspaceUpdated: 'Espace de travail mis à jour', + workspaceDeleted: 'Espace de travail supprimé', + groupCreated: 'Groupe créé', + groupUpdated: 'Groupe mis à jour', + groupDeleted: 'Groupe supprimé', + groupMemberAdded: 'Membre ajouté au groupe', + groupMemberRemoved: 'Membre retiré du groupe', + customFieldCreated: 'Champ personnalisé créé', + customFieldUpdated: 'Champ personnalisé mis à jour', + customFieldDeleted: 'Champ personnalisé supprimé', + itemTypeCreated: 'Type d’élément créé', + itemTypeUpdated: 'Type d’élément mis à jour', + itemTypeDeleted: 'Type d’élément supprimé', + permissionGranted: 'Autorisation accordée', + permissionRevoked: 'Autorisation révoquée', + roleAssigned: 'Rôle attribué', + roleRevoked: 'Rôle révoqué', + // Resource types + allResources: 'Toutes les ressources', + apiToken: 'Jeton API', + customField: 'Champ personnalisé', + itemType: 'Type d’élément', + permission: 'Autorisation', + group: 'Groupe', + }, + + migrationAssistant: { + configSetMigration: 'Migration de l’ensemble de configuration', + workflowMigration: 'Assistant de migration de flux de travail', + migratingFrom: 'Migration depuis', + to: 'vers', + configurationSet: 'Ensemble de configuration', + analyzingMigration: 'Analyse des exigences de migration…', + analysisFailed: 'Échec de l’analyse', + noMigrationRequired: 'Aucune migration requise', + allItemsCompatible: 'Tous les éléments ({count}) sont compatibles avec la nouvelle configuration.', + migrationRequired: 'Migration requise', + itemsNeedMigration: '{count} éléments nécessitent une migration. Veuillez vérifier les mappages ci-dessous.', + itemTypes: 'Types d’éléments', + fields: 'Champs', + status: 'Statut', + priority: 'Priorité', + itemTypeMigrations: 'Migrations des types d’éléments', + customFieldChanges: 'Modifications des champs personnalisés', + statusMigrations: 'Migrations des statuts', + priorityMigrations: 'Migrations des priorités', + noItemsToMigrate: 'Aucun élément à migrer.', + noStatusChanges: 'Aucun changement de statut détecté.', + noFieldChanges: 'Aucun changement de champ détecté.', + selectTargetType: 'Sélectionner le type cible…', + selectTargetStatus: 'Sélectionner le statut cible…', + selectTargetPriority: 'Sélectionner la priorité cible…', + compatible: 'Compatible', + requiresMigration: 'Nécessite une migration', + item: 'élément', + items: 'éléments', + kept: 'Conservé', + values: 'valeurs', + hiddenDataPreserved: 'Masqué (données conservées)', + newRequiredField: 'Nouveau champ obligatoire', + enterDefaultValue: 'Saisir la valeur par défaut…', + executeMigration: 'Exécuter la migration', + migrating: 'Migration en cours…', + migrationCompleted: 'Migration terminée', + allItemsMigrated: 'Tous les éléments ont été migrés avec succès.', + pleaseSelectTargetStatuses: + 'Veuillez sélectionner les statuts cibles pour tous les éléments nécessitant une migration.', + pleaseSelectTargetItemTypes: + 'Veuillez sélectionner les types d’éléments cibles pour tous les éléments nécessitant une migration.', + pleaseProvideDefaultValues: 'Veuillez fournir des valeurs par défaut pour tous les nouveaux champs obligatoires.', + pleaseSelectTargetPriorities: + 'Veuillez sélectionner les priorités cibles pour tous les éléments nécessitant une migration.', + }, + + setup: { + chooseLanguage: 'Choisissez votre langue', + chooseLanguageDesc: 'Sélectionnez la langue à utiliser pendant la configuration et dans l’application.', + language: 'Langue', + welcomeTo: 'Bienvenue dans {appName}', + setupMessage: 'Configurons votre système de gestion du travail', + setupProgress: 'Progression de la configuration', + step: 'Étape', + of: 'sur', + createAdminAccount: 'Créer un compte administrateur', + adminAccountDesc: 'Ce compte aura un accès complet pour gérer votre installation de {appName}.', + firstName: 'Prénom', + lastName: 'Nom', + emailAddress: 'Adresse e-mail', + username: 'Nom d’utilisateur', + password: 'Mot de passe', + confirmPassword: 'Confirmer le mot de passe', + configureModules: 'Configurer les modules', + configureModulesDesc: 'Choisissez les modules à activer. Vous pourrez modifier ces paramètres ultérieurement.', + testManagement: 'Gestion des tests', + testManagementDesc: 'Gérer les cas de test, les exécutions de tests et l’assurance qualité', + setupComplete: 'Configuration terminée !', + setupCompleteMessage: + '{appName} est prêt à être utilisé. Vous allez être redirigé vers l’application sous peu.', + whatsNext: 'Et ensuite ?', + whatsNextCreateWorkspace: 'Créer votre premier espace de travail', + whatsNextSetupWorkflows: 'Configurer les flux de travail et les écrans', + whatsNextInviteTeam: 'Inviter des membres de l’équipe', + whatsNextStartCreating: 'Commencer à créer des éléments de travail', + back: 'Retour', + next: 'Suivant', + completeSetup: 'Terminer la configuration', + settingUp: 'Configuration en cours…', + fillAllRequired: 'Veuillez remplir tous les champs obligatoires', + passwordsMustMatch: 'Les mots de passe ne correspondent pas', + invalidEmail: 'Veuillez saisir une adresse e-mail valide', + setupError: 'Une erreur est survenue pendant la configuration. Veuillez réespérer.', + goBackEsc: 'Retour (Échap)', + continueNextStepEnter: 'Passer à l’étape suivante (Entrée)', + completeSetupEnter: 'Terminer la configuration (Entrée)', + }, + + createModal: { + // Type labels + workItem: 'Élément de travail', + milestone: 'Jalon', + workspace: 'Espace de travail', + collection: 'Collection', + // Milestone status + planning: 'Planification', + inProgress: 'En cours', + completed: 'Terminé', + cancelled: 'Annulé', + // Headers + newChildItem: 'Nouvel élément enfant', + new: 'Nouveau', + // Placeholders + issueTitle: 'Titre du ticket', + milestoneName: 'Nom du jalon', + workspaceName: 'Nom du/de la {type}', + workspaceKeyPlaceholder: 'Clé de l’espace de travail (ex. PROJ, EQUIPE)', + addDescription: 'Ajouter une description…', + workspaceTemplate: 'Modèle', + workspaceTemplateBlank: 'Espace de travail vierge', + workspaceTemplateLoading: 'Chargement des modèles…', + workspaceTemplateError: 'Échec du chargement des modèles d’espace de travail', + workspaceTemplateMeta: '{templates} modèles · {items} éléments', + // Field labels + type: 'Type', + template: 'Modèle', + priority: 'Priorité', + noPriority: 'Aucune priorité', + assignee: 'Attribué à', + unassigned: 'Non attribué', + dueDate: 'Date d’échéance', + milestoneField: 'Jalon', + noMilestone: 'Aucun jalon', + additionalFields: 'Champs supplémentaires', + targetDate: 'Date cible', + status: 'Statut', + category: 'Catégorie', + noCategory: 'Aucune catégorie', + parent: 'Parent', + // Validation + fillRequiredFields: 'Veuillez remplir les champs obligatoires :', + selectWorkspaceFirst: 'Sélectionnez d’abord un espace de travail', + // Buttons + create: 'Créer', + }, + + scm: { + // CreateBranchModal + createBranch: 'Créer une branche', + createBranchFor: 'Créer une nouvelle branche pour {itemKey}', + repository: 'Dépôt', + selectRepository: 'Sélectionner un dépôt…', + branchName: 'Nom de la branche', + baseBranch: 'Branche de base', + baseBranchHelp: 'La branche à partir de laquelle créer la nouvelle branche. Par défaut, la branche par défaut du dépôt.', + creating: 'Création en cours…', + branchCreatedSuccess: 'Branche créée avec succès', + noReposLinked: 'Aucun dépôt lié à cet espace de travail', + linkReposHelp: 'Liez des dépôts dans Paramètres de l’espace de travail → Gestion de code source', + fillAllRequired: 'Veuillez remplir tous les champs obligatoires', + failedToLoadRepos: 'Échec du chargement des dépôts', + failedToCreateBranch: 'Échec de la création de la branche', + + // CreatePRFromBranchModal + createPullRequest: 'Créer une demande de tirage (PR)', + createPRFrom: 'Créer une PR à partir de la branche : {branch}', + prTitle: 'Titre de la PR', + description: 'Description', + baseBranchPR: 'Branche de base', + baseBranchPRHelp: 'Laissez vide pour utiliser la branche par défaut du dépôt.', + createPR: 'Créer la PR', + prCreatedSuccess: 'Demande de tirage #{prNumber} créée avec succès', + failedToCreatePR: 'Échec de la création de la demande de tirage', + noBranchLink: 'Aucun lien de branche fourni', + + // AddSCMLinkModal + linkDevResource: 'Lier une ressource de développement', + linkDevResourceDesc: 'Connecter une PR, une branche ou un commit à cet élément', + type: 'Type', + pr: 'PR', + branch: 'Branche', + commit: 'Commit', + prNumber: 'Numéro de PR', + commitSha: 'SHA du commit', + titleOptional: 'Titre (optionnel)', + linkResource: 'Lier la ressource', + linking: 'Liaison en cours…', + failedToCreateLink: 'Échec de la création du lien', + + // ItemSCMLinks component + development: 'Développement', + linkExisting: 'Lier une ressource existante', + failedToStartConnection: 'Échec du démarrage de la connexion', + failedToLoadLinks: 'Échec du chargement des liens', + confirmRemoveLink: 'Voulez-vous vraiment supprimer ce lien ?', + noRepositoriesLinked: 'Aucun dépôt n’est lié à cet espace de travail', + connectYourAccount: 'Connectez votre compte {provider}', + connectToCreate: 'Connectez-vous pour créer des branches et des demandes de tirage directement depuis les éléments de travail.', + connect: 'Connecter {provider}', + noLinksYet: 'Aucun lien de développement pour le moment', + pullRequests: 'Demandes de tirage (PR)', + branches: 'Branches', + commits: 'Commits', + smartCommitsTitle: 'Traiter les commits intelligents (smart commits)', + smartCommitsDescription: 'Lors de la fusion, appliquer les directives "#comment …" et "#" trouvées dans le corps de la demande de tirage et dans ses messages de commit.', + smartCommitsWarningTitle: 'Les adresses e-mail de committer non vérifiées seront acceptées', + smartCommitsWarningBody: 'Les actions de smart commit s’exécutent sous l’identité correspondant à l’adresse e-mail git du committer. Git n’authentifie pas ce champ ; un contributeur peut la définir sur n’importe quelle valeur. Activez cette option uniquement pour les dépôts où vous faites confiance à chaque fusion.', + smartCommitsUpdateFailed: 'Échec de la mise à jour du paramètre de smart commits', + }, + + organization: { + editOrganization: 'Modifier l’organisation', + newOrganization: 'Nouvelle organisation', + organizationName: 'Nom de l’organisation', + email: 'E-mail', + organizationAvatar: 'Avatar de l’organisation', + customAvatar: 'Avatar personnalisé', + uploadedImage: 'Image téléversée', + defaultAvatar: 'Avatar par défaut', + usingInitials: 'Utilisation des initiales', + changeAvatar: 'Changer d’avatar', + uploadAvatar: 'Téléverser un avatar', + attachmentsRequired: 'Les pièces jointes doivent être activées pour téléverser des avatars d’organisation', + uploadRecommendation: 'Recommandé : Images carrées, d’au moins 256x256 pixels pour une qualité optimale', + activeOrganization: 'Organisation active', + customFields: 'Champs personnalisés', + updateOrganization: 'Mettre à jour l’organisation', + createOrganization: 'Créer l’organisation', + pleaseSelectImage: 'Veuillez sélectionner un fichier image', + failedToUploadAvatar: 'Échec du téléversement de l’avatar', + }, + integrations: { + title: 'Intégrations', + linkPage: 'Lier la page', + searchPages: 'Rechercher des pages…', + noLinksYet: 'Aucune page liée pour le moment', + connectAccount: 'Connectez votre compte', + connectToLink: 'Connectez-vous pour lier des pages', + connect: 'Se connecter', + disconnect: 'Déconnecter', + connected: 'Connecté', + confirmRemoveLink: 'Supprimer ce lien ?', + confirmDisconnect: 'Déconnecter cette intégration ?', + failedToLoadLinks: 'Échec du chargement des liens', + failedToSearch: 'Échec de la recherche de pages', + refreshed: 'Lien actualisé', + linked: 'Page liée', + removed: 'Lien supprimé', + disconnected: 'Intégration déconnectée', + providerManager: 'Fournisseurs d’intégration', + providerManagerDesc: 'Configurer les fournisseurs d’intégration externe', + addProvider: 'Ajouter un fournisseur', + editProvider: 'Modifier le fournisseur', + providerType: 'Type de fournisseur', + oauthClientId: 'ID client OAuth', + oauthClientSecret: 'Secret client OAuth', + callbackUrl: 'URL de rappel (Callback)', + callbackUrlHint: 'Copiez cette URL dans les paramètres de votre application d’intégration', + providerConfig: 'Configuration du fournisseur (JSON)', + notion: 'Notion', + noProviders: 'Aucun fournisseur d’intégration configuré', + selectProvider: 'Sélectionner un fournisseur', + page: 'Page', + database: 'Base de données', + }, +}; \ No newline at end of file diff --git a/frontend/src/lib/locales/fr/navigation.js b/frontend/src/lib/locales/fr/navigation.js new file mode 100644 index 00000000..26e54ad9 --- /dev/null +++ b/frontend/src/lib/locales/fr/navigation.js @@ -0,0 +1,423 @@ +/** + * Navigation and general UI translations for French locale + * Contains: nav, commandPalette, dashboard, search, about, onboarding + */ + +export default { + nav: { + workspaces: 'Espaces de travail', + collections: 'Collections', + timeAndProjects: 'Temps et projets', + milestones: 'Jalons', + iterations: 'Itérations', + assets: 'Ressources', + channels: 'Canaux', + portalHub: 'Centre de portail', + organizations: 'Organisations', + teams: 'Équipes', + create: 'Créer', + search: 'Rechercher', + aiChat: 'Discussion IA', + terminal: 'Terminal', + admin: 'Admin', + notifications: 'Notifications', + profile: 'Profil', + expand: 'Développer', + collapse: 'Réduire', + manageWorkspaces: 'Gérer les espaces de travail', + manageWorkspacesSubtitle: 'Créer, modifier et organiser les espaces de travail', + searchWorkspaces: 'Rechercher des espaces de travail…', + noWorkspacesMatch: 'Aucun espace de travail ne correspond à votre recherche', + noWorkspacesFound: 'Aucun espace de travail trouvé', + searchToFindMore: 'Recherchez pour trouver d’autres espaces de travail…', + loadingSearch: 'Chargement de la recherche…', + loadingCreateForm: 'Chargement du formulaire de création…', + loading: 'Chargement…', + retry: 'Réessayer', + knowledgeBase: 'Base de connaissances', + }, + + commandPalette: { + searchPlaceholder: 'Rechercher partout…', + noCommandsFound: 'Aucune commande trouvée', + toSelect: 'pour sélectionner', + toNavigate: 'pour naviguer', + toClose: 'pour fermer', + pressToOpen: 'Appuyez sur {shortcut} pour ouvrir', + advancedSearch: 'Recherche avancée', + context: 'Contexte', + recentlyViewed: { + label: 'Éléments de travail récemment consultés', + description: 'Accéder directement à l’un de vos 20 derniers éléments consultés', + header: 'Récemment consultés', + searchPlaceholder: 'Rechercher dans les éléments récents…', + empty: 'Aucun élément consulté récemment', + loading: 'Chargement des éléments récents…', + backHint: 'pour revenir en arrière', + }, + // Libellés et descriptions des commandes + commands: { + // Navigation + workspaces: { label: 'Espaces de travail', description: 'Gérer les espaces de travail de projet' }, + search: { label: 'Recherche', description: 'Rechercher des éléments de travail et du contenu' }, + dashboard: { label: 'Tableau de bord', description: 'Afficher les analyses et rapports' }, + milestones: { label: 'Jalons', description: 'Suivre les jalons du projet' }, + channels: { label: 'Canaux', description: 'Canaux de communication et d’assistance' }, + collections: { label: 'Collections', description: 'Gérer les collections et vues d’éléments de travail' }, + adminPanel: { label: 'Panneau d’administration', description: 'Administration système et paramètres' }, + analytics: { + label: 'Analyses', + description: 'Tendances de vélocité, temps de cycle et prévisions', + }, + + // Gestion des tests (libellés alignés avec la navigation de l'espace de travail) + tests: { label: 'Tests', description: 'Gérer les cas de test, plans et exécutions' }, + testCases: { label: 'Cas de test', description: 'Afficher et gérer les cas de test' }, + testPlans: { label: 'Plans de test', description: 'Afficher et gérer les plans de test' }, + testTemplates: { label: 'Modèles', description: 'Modèles d’exécutions et étapes partagées' }, + testRuns: { label: 'Exécutions de test', description: 'Afficher et gérer les exécutions de test' }, + testReports: { label: 'Rapports', description: 'Afficher les rapports d’exécution de test' }, + createTestCase: { + label: 'Créer un cas de test', + description: 'Créer un nouveau cas de test pour l’assurance qualité', + }, + createTestPlan: { + label: 'Créer un plan de test', + description: 'Créer un nouveau plan de test contenant des cas de test', + }, + createTestRun: { + label: 'Créer une exécution de test', + description: 'Créer un nouveau modèle d’exécution de test', + }, + + // Gestion du temps + timeTracking: { label: 'Suivi du temps', description: 'Saisir et gérer les entrées de temps de travail' }, + timeReports: { + label: 'Rapports de temps', + description: 'Afficher les rapports de suivi du temps et les analyses', + }, + timeProjects: { label: 'Projets de temps', description: 'Gérer les projets de suivi du temps' }, + logTime: { label: 'Saisir du temps', description: 'Ajouter rapidement une nouvelle entrée de temps' }, + startTimer: { label: 'Démarrer le chronomètre', description: 'Démarrer le suivi du temps pour le travail actuel' }, + stopTimer: { label: 'Arrêter le chronomètre', description: 'Arrêter le suivi du temps en cours' }, + + // Commandes de création + createWorkItem: { label: 'Créer un élément de travail', description: 'Créer un nouvel élément de travail ou une tâche' }, + createWorkspace: { label: 'Créer un espace de travail', description: 'Créer un nouvel espace de travail de projet' }, + createMilestone: { label: 'Créer un jalon', description: 'Créer un nouveau jalon de projet' }, + createCollection: { + label: 'Créer une collection', + description: 'Créer une nouvelle collection d’éléments de travail', + }, + + // Commandes d'administration + adminCustomFields: { label: 'Champs personnalisés', description: 'Gérer les définitions de champs personnalisés' }, + adminScreens: { + label: 'Gestion des écrans', + description: 'Configurer les écrans de formulaires et les dispositions', + }, + adminHierarchyLevels: { + label: 'Niveaux de hiérarchie', + description: 'Configurer les niveaux hiérarchiques des éléments de travail', + }, + adminItemTypes: { + label: 'Types d’éléments', + description: 'Gérer les types d’éléments de travail avec icônes et couleurs', + }, + adminPriorities: { + label: 'Priorités', + description: 'Configurer les niveaux de priorité avec icônes et couleurs', + }, + adminConfigSets: { + label: 'Ensembles de configuration', + description: 'Gérer les ensembles de configuration avec flux de travail et écrans', + }, + adminStatuses: { label: 'Statuts', description: 'Gérer les statuts individuels des éléments de travail' }, + adminStatusCategories: { + label: 'Catégories de statut', + description: 'Gérer les catégories de statut et leurs couleurs', + }, + adminWorkflows: { + label: 'Concepteur de flux de travail', + description: 'Concevoir et gérer les transitions de flux de travail', + }, + adminConditionSets: { + label: 'Ensembles de conditions', + description: 'Définir des conditions qui restreignent les transitions de flux de travail', + }, + adminLinkTypes: { label: 'Types de liens', description: 'Gérer les types de liens entre éléments de travail' }, + adminScmProviders: { + label: 'Fournisseurs SCM', + description: 'Configurer les intégrations GitHub, GitLab, Gitea et Bitbucket', + }, + adminAttachments: { + label: 'Pièces jointes', + description: 'Gérer la configuration et les paramètres des pièces jointes', + }, + adminModules: { + label: 'Paramètres des modules', + description: 'Activer ou désactiver les modules de suivi du temps et de gestion des tests', + }, + adminThemes: { + label: 'Paramètres des thèmes', + description: 'Gérer les thèmes et l’apparence de l’application', + }, + adminUsers: { label: 'Gestion des utilisateurs', description: 'Gérer les utilisateurs, rôles et autorisations' }, + adminGroups: { label: 'Gestion des groupes', description: 'Gérer les groupes d’utilisateurs et leurs membres' }, + adminPermissions: { + label: 'Autorisations', + description: 'Gérer les autorisations des utilisateurs et le contrôle d’accès', + }, + adminPermissionSets: { + label: 'Ensembles d’autorisations', + description: 'Gérer les regroupements d’autorisations pour les ensembles de configuration', + }, + adminWorkspaceRoles: { + label: 'Rôles d’espace de travail', + description: 'Afficher les rôles d’espace de travail et leurs autorisations', + }, + adminSso: { + label: 'Authentification unique (SSO)', + description: 'Configurer les fournisseurs d’identité OIDC pour le SSO', + }, + adminSecurity: { + label: 'Paramètres de sécurité', + description: 'Configurer les flux de calendriers et l’exécution des plugins', + }, + adminSystemImport: { label: 'Importation système', description: 'Importer des données depuis d’autres systèmes' }, + adminAssets: { + label: 'Gestion des ressources', + description: 'Configurer les types et catégories de ressources', + }, + adminWorkspaces: { label: 'Administration des espaces de travail', description: 'Gérer les espaces de travail et leurs paramètres' }, + adminNotifications: { + label: 'Paramètres de notification', + description: 'Gérer les configurations de notification', + }, + adminEmailTemplates: { + label: 'Modèles de courriels', + description: 'Personnaliser le sujet et le corps des courriels transactionnels', + }, + + // Commandes système + quitApp: { + label: 'Quitter l’application', + description: 'Arrêter proprement le serveur de l’application', + }, + + // Divers + portal: { label: 'Portail', description: 'Gestion du portail client' }, + iterations: { label: 'Itérations', description: 'Planification et suivi des sprints' }, + createItem: { label: 'Créer un élément', description: 'Créer un nouvel élément de travail' }, + myProfile: { label: 'Mon profil', description: 'Afficher et modifier votre profil' }, + notifications: { label: 'Notifications', description: 'Afficher les notifications' }, + about: { label: 'À propos', description: 'À propos de cette application' }, + assets: { label: 'Ressources', description: 'Gérer les ensembles de ressources et les articles' }, + organizations: { label: 'Organisations', description: 'Gérer les organisations clientes' }, + + // Commandes dynamiques d'espace de travail + goToWorkspace: { label: 'Aller à {name}', description: 'Naviguer vers l’espace de travail {name}' }, + workspaceOverview: { + label: 'Aperçu de {name}', + description: 'Afficher le tableau de bord de l’espace de travail avec statistiques et graphiques', + }, + }, + }, + + dashboard: { + title: 'Tableau de bord', + subtitle: 'Aperçu de vos éléments de travail et projets', + welcome: 'Bienvenue', + overview: 'Aperçu', + recentActivity: 'Activité récente', + recentItems: 'Éléments récents', + myTasks: 'Mes tâches', + assignedToMe: 'Qui m’ont été attribués', + openTasks: 'Tâches ouvertes', + overdueItems: 'Éléments en retard', + upcomingDeadlines: 'Échéances à venir', + activeIterations: 'Itérations actives', + inProgressMilestones: 'Jalons en cours', + noRecentActivity: 'Aucune activité récente', + recentItemsHint: 'Les éléments que vous mettez à jour apparaîtront ici', + noActiveIterations: 'Aucune itération active', + iterationTimelinesHint: 'La chronologie des itérations apparaîtra ici', + noUpcomingDeadlines: 'Aucune échéance à venir', + dueDatesHint: 'Les éléments ayant une date d’échéance apparaîtront ici', + noOpenTasks: 'Aucune tâche ouverte ne vous est attribuée', + allCaughtUp: 'Vous êtes complètement à jour !', + noOverdueItems: 'Aucun élément en retard', + loadingRecentItems: 'Chargement des éléments récents…', + loadingTasks: 'Chargement de vos tâches…', + loadingOverdueItems: 'Chargement des éléments en retard…', + workItemStatusOverview: 'Aperçu des statuts d’éléments de travail', + statusOpen: 'Ouvert', + statusInProgress: 'En cours', + statusClosed: 'Fermé', + quickActions: 'Actions rapides', + createWorkItem: 'Créer un élément de travail', + createWorkItemDesc: 'Commencer le suivi d’un nouvel élément de travail', + manageMilestones: 'Gérer les jalons', + manageMilestonesDesc: 'Afficher et organiser les jalons', + manageWorkspaces: 'Gérer les espaces de travail', + manageWorkspacesDesc: 'Configurer les espaces de travail', + recentWorkItems: 'Éléments de travail récents', + whatsNew: 'Nouveautés', + noNotifications: 'Aucune notification pour le moment', + yourActivity: 'Votre activité', + viewed: 'Consulté', + edited: 'Modifié', + commented: 'Commenté', + noRecentlyViewed: 'Aucun élément consulté récemment', + noRecentlyEdited: 'Aucun élément modifié récemment', + noRecentlyCommented: 'Aucun élément commenté récemment', + quickAccess: 'Accès rapide', + quickAccessHint: 'Appuyez sur {shortcut} pour ouvrir la palette de commandes', + upcomingMilestones: 'Jalons à venir', + watching: 'Suivis', + recentWorkspaces: 'Espaces de travail récents', + noRecentWorkspaces: 'Aucun espace de travail récent', + goodMorning: 'Bonjour', + goodAfternoon: 'Bon après-midi', + goodEvening: 'Bonsoir', + goodNight: 'Bonne nuit', + dueIn: 'Échéance dans {days} j', + dueToday: 'Échéance aujourd’hui', + overdue: 'En retard de {days} j', + done: '{done}/{total} terminés', + there: '', + doneEditing: 'Terminer la modification', + customize: 'Personnaliser', + editModeHint: 'Mode modification : ajoutez, renommez, réordonnez ou supprimez des sections et des widgets', + sections: { + yourDay: 'Votre journée', yourDayDescription: 'Un résumé rapide de ce qui requiert votre attention', + work: 'Travail', workDescription: 'Votre liste personnelle et les éléments qui vous sont attribués', + workspaces: 'Espaces de travail', workspacesDescription: 'Reprendre le travail', new: 'Nouvelle section', section: 'Section du tableau de bord', + titlePlaceholder: 'Titre de la section', subtitlePlaceholder: 'Sous-titre (optionnel)', rename: 'Renommer la section', + deleteMessage: 'Supprimer cette section ? Tous les widgets qu’elle contient seront également supprimés.', noWidgets: 'Aucun widget dans cette section pour le moment', + addWidgetsHint: 'Choisissez Personnaliser pour ajouter des widgets', none: 'Aucune section configurée', noneHint: 'Choisissez Modifier pour ajouter une section au tableau de bord', + }, + customization: { + widgets: 'Widgets', activity: 'Activité', activityDescription: 'Briefings, flux d’activité et notifications', + work: 'Travail', workDescription: 'Éléments, jalons et travail qui vous est attribué', navigation: 'Navigation', + navigationDescription: 'Accès rapide aux espaces de travail', tipTitle: 'Astuce', tip: 'Faites glisser un widget dans n’importe quelle section du tableau de bord.', + }, + widgets: { + dailyBriefing: { name: 'Briefing quotidien', description: 'Résumé généré par l’IA de ce qui compte aujourd’hui' }, + yourActivity: { name: 'Votre activité', description: 'Éléments que vous avez récemment consultés, modifiés ou commentés' }, + whatsNew: { name: 'Nouveautés', description: 'Dernières notifications et mises à jour non lues' }, + personalTasks: { name: 'Tâches personnelles', description: 'Éléments de votre liste de tâches personnelle' }, + savedSearch: { name: 'Recherche enregistrée', description: 'Éléments de travail provenant d’une collection enregistrée' }, + assignedToMe: { name: 'Qui m’ont été attribués', description: 'Éléments ouverts qui vous sont attribués dans tous les espaces de travail' }, + watchedItems: { name: 'Éléments suivis', description: 'Éléments que vous suivez' }, + upcomingMilestones: { name: 'Jalons à venir', description: 'Jalons dont la date cible approche' }, + recentWorkspaces: { name: 'Espaces de travail récents', description: 'Espaces de travail que vous avez visités récemment' }, + quickAccess: { name: 'Accès rapide', description: 'Liens rapides vers les espaces de travail disponibles' }, + unknown: 'Type de widget inconnu : {type}', + }, + widgetContent: { + briefingUnavailable: 'Votre briefing quotidien n’est pas disponible pour le moment. Il nécessite une intégration IA ; si vous venez d’en configurer une, réessayez dans un moment.', + updated: 'Mis à jour le {date}', assignedLoadError: 'Impossible de charger les éléments qui vous sont attribués', assignedEmpty: 'Rien ne vous est attribué pour le moment', + personalLoadError: 'Impossible de charger vos tâches personnelles', personalEmpty: 'Votre liste de tâches personnelle est vide', noWorkspaces: 'Aucun espace de travail pour le moment', + createWorkspace: 'En créer un', workspaceAvatar: 'Avatar de {name}', visited: 'visité le {date}', watchedEmpty: 'Vous ne suivez aucun élément', + noUpcomingMilestones: 'Aucun jalon à venir', milestoneDone: '{done} sur {total} terminés', daysOverdue: '{count} jours de retard', daysOverdue_one: '{count} jour de retard', daysOverdue_other: '{count} jours de retard', daysLeft: '{count} jours restants', daysLeft_one: '{count} jour restant', daysLeft_other: '{count} jours restants', + priorityAria: 'Priorité : {name}', + }, + relative: { + unknown: 'Inconnu', justNow: 'À l’instant', minutesAgo: 'Il y a {count} minutes', minutesAgo_one: 'Il y a {count} minute', minutesAgo_other: 'Il y a {count} minutes', + hoursAgo: 'Il y a {count} heures', hoursAgo_one: 'Il y a {count} heure', hoursAgo_other: 'Il y a {count} heures', daysAgo: 'Il y a {count} jours', daysAgo_one: 'Il y a {count} jour', daysAgo_other: 'Il y a {count} jours', + }, + }, + + workspaceDashboard: { + overview: 'Aperçu', overviewDescription: 'Métriques clés et statistiques', progress: 'Suivi de la progression', progressDescription: 'Graphiques et chronologies', homepage: 'Page d’accueil', loading: 'Chargement des données de l’espace de travail…', noSectionsHint: 'Choisissez Modifier pour ajouter des sections à la page d’accueil de l’espace de travail', + customize: 'Personnaliser', doneEditing: 'Terminer la modification', editModeHint: 'Mode modification : ajoutez, renommez, réordonnez ou supprimez des sections et des widgets', weekOf: 'Semaine du {date}', + widgets: { + stats: { name: 'Aperçu des statistiques', description: 'Collections et nombre d’éléments par catégorie de statut' }, + completionChart: { name: 'Graphique de réalisation', description: 'Éléments terminés au cours des quatre dernières semaines' }, + createdChart: { name: 'Graphique de création', description: 'Éléments créés au cours des sept derniers jours' }, + milestoneProgress: { name: 'Progression des jalons', description: 'Jalons actifs et leur avancement' }, + recentItems: { name: 'Éléments récents', description: 'Éléments récemment mis à jour dans cet espace de travail' }, + myTasks: { name: 'Mes tâches', description: 'Éléments qui vous sont attribués' }, + savedSearch: { name: 'Recherche enregistrée', description: 'Éléments de travail provenant d’une collection enregistrée' }, + overdueItems: { name: 'Éléments en retard', description: 'Éléments dont la date d’échéance est dépassée' }, + upcomingDeadlines: { name: 'Échéances à venir', description: 'Éléments dont la date d’échéance approche' }, + iterationTimeline: { name: 'Chronologie des itérations', description: 'Calendrier des itérations actuelles et à venir' }, + testCoverage: { name: 'Couverture des tests', description: 'Exigences couvertes par des cas de test' }, + }, + customization: { + widgets: 'Widgets', builtIn: 'Widgets intégrés', builtInDescription: 'Métriques et graphiques principaux de l’espace de travail', + additional: 'Widgets supplémentaires', additionalDescription: 'Listes, calendriers et widgets de test', defaultWidth: 'Largeur par défaut : {width} sur 3', + tipTitle: 'Astuce', tip: 'Faites glisser un widget dans n’importe quelle section de la page d’accueil de l’espace de travail.', + }, + }, + + search: { + title: 'Recherche', + subtitle: 'Rechercher dans tous les espaces de travail avec des options de filtrage avancées', + searchPlaceholder: 'Rechercher…', + searchItems: 'Rechercher des éléments…', + searchResults: 'Résultats de recherche', + noSearchResults: 'Aucun résultat de recherche', + configureFilter: 'Configurez le filtre pour voir les éléments', + savedSearch: 'Recherche enregistrée', + saveSearch: 'Enregistrer la recherche', + clearSearch: 'Effacer la recherche', + advancedSearch: 'Recherche avancée', + recentSearches: 'Recherches récentes', + workItem: 'Élément de travail', + workspace: 'Espace de travail', + }, + + about: { + title: 'À propos de Windshift', + subtitle: 'Une plateforme complète de gestion du travail conçue pour les équipes modernes', + whatIs: 'Qu’est-ce que Windshift ?', + description: + 'Windshift est une application puissante de gestion du travail qui réunit le suivi de projet, la gestion des tests, le suivi du temps et la collaboration d’équipe au sein d’une plateforme unifiée. Conçue avec des technologies modernes et pensée pour la flexibilité, elle s’adapte au flux de travail unique de votre équipe.', + keyFeatures: 'Fonctionnalités clés', + projectManagement: 'Gestion de projet', + projectManagementDesc: + 'Organisez les éléments de travail de manière hiérarchique avec des champs personnalisés, des flux de travail et un suivi des statuts.', + testManagement: 'Gestion des tests', + testManagementDesc: + 'Créez des cas de test, organisez des plans de test, exécutez des sessions de test et suivez les anomalies efficacement.', + timeTracking: 'Suivi du temps', + timeTrackingDesc: + 'Suivez le temps passé sur les projets et les tâches grâce à des rapports et des analyses détaillés.', + builtWith: 'Bibliothèques notables', + libSvelte: 'Framework front-end', + libTailwind: 'Framework CSS orienté utilitaires', + libMilkdown: 'Éditeur de texte enrichi', + libExcalidraw: 'Schémas et croquis', + libMeltUI: 'Composants d’interface primitifs', + libExtism: 'Système de plugins WebAssembly', + libCharmbracelet: 'Accès TUI et SSH (Bubble Tea, Wish)', + libSvelteFlow: 'Diagrammes de flux', + versionInfo: 'Informations de version', + version: 'Version', + developedBy: 'Développé par', + reportIssues: 'Signaler un problème', + getStarted: 'Commencer', + }, + + onboarding: { + welcomeTo: 'Bienvenue dans Windshift', + getStartedMessage: 'Commençons par créer votre premier espace de travail et votre premier élément de travail', + progress: 'Progression', + completed: 'terminé(s)', + of: 'sur', + createWorkspace: 'Créer votre espace de travail', + workspacesHelp: 'Les espaces de travail vous aident à organiser vos projets et vos équipes', + createWorkspaceBtn: 'Créer un espace de travail', + createFirstWorkItem: 'Créer votre premier élément de travail', + trackTasks: 'Suivez les tâches, les bogues, les fonctionnalités et bien plus', + createWorkItemBtn: 'Créer un élément de travail', + dismissAssistant: 'Masquer l’assistant', + dismissOnboarding: 'Masquer l’intégration', + getStartedMember: 'Voici les espaces de travail à votre disposition', + selectWorkspace: 'Sélectionnez un espace de travail pour commencer', + noWorkspacesAvailable: + 'Aucun espace de travail n’est disponible pour le moment. Veuillez contacter votre administrateur pour obtenir l’accès à un espace de travail.', + }, +}; \ No newline at end of file diff --git a/frontend/src/lib/locales/fr/pages.js b/frontend/src/lib/locales/fr/pages.js new file mode 100644 index 00000000..2b220f06 --- /dev/null +++ b/frontend/src/lib/locales/fr/pages.js @@ -0,0 +1,426 @@ +/** + * French (fr) - Knowledge pages (wiki) translations + * + * Surfaces: + * - PagesView (left tree + editor pane) + * - PageMoveDialog + * - PagePermissionsDialog + */ +export default { + nav: { + workspaces: 'Espaces de travail', + collections: 'Collections', + timeAndProjects: 'Temps et projets', + milestones: 'Jalons', + iterations: 'Itérations', + assets: 'Ressources', + channels: 'Canaux', + portalHub: 'Centre de portail', + organizations: 'Organisations', + teams: 'Équipes', + create: 'Créer', + search: 'Rechercher', + aiChat: 'Discussion IA', + terminal: 'Terminal', + admin: 'Admin', + notifications: 'Notifications', + profile: 'Profil', + expand: 'Développer', + collapse: 'Réduire', + manageWorkspaces: 'Gérer les espaces de travail', + manageWorkspacesSubtitle: 'Créer, modifier et organiser les espaces de travail', + searchWorkspaces: 'Rechercher des espaces de travail…', + noWorkspacesMatch: 'Aucun espace de travail ne correspond à votre recherche', + noWorkspacesFound: 'Aucun espace de travail trouvé', + searchToFindMore: 'Recherchez pour trouver d’autres espaces de travail…', + loadingSearch: 'Chargement de la recherche…', + loadingCreateForm: 'Chargement du formulaire de création…', + loading: 'Chargement…', + retry: 'Réessayer', + knowledgeBase: 'Base de connaissances', + }, + + commandPalette: { + searchPlaceholder: 'Rechercher partout…', + noCommandsFound: 'Aucune commande trouvée', + toSelect: 'pour sélectionner', + toNavigate: 'pour naviguer', + toClose: 'pour fermer', + pressToOpen: 'Appuyez sur {shortcut} pour ouvrir', + advancedSearch: 'Recherche avancée', + context: 'Contexte', + recentlyViewed: { + label: 'Éléments de travail récemment consultés', + description: 'Accéder directement à l’un de vos 20 derniers éléments consultés', + header: 'Récemment consultés', + searchPlaceholder: 'Rechercher dans les éléments récents…', + empty: 'Aucun élément consulté récemment', + loading: 'Chargement des éléments récents…', + backHint: 'pour revenir en arrière', + }, + // Libellés et descriptions des commandes + commands: { + // Navigation + workspaces: { label: 'Espaces de travail', description: 'Gérer les espaces de travail de projet' }, + search: { label: 'Recherche', description: 'Rechercher des éléments de travail et du contenu' }, + dashboard: { label: 'Tableau de bord', description: 'Afficher les analyses et rapports' }, + milestones: { label: 'Jalons', description: 'Suivre les jalons du projet' }, + channels: { label: 'Canaux', description: 'Canaux de communication et d’assistance' }, + collections: { label: 'Collections', description: 'Gérer les collections et vues d’éléments de travail' }, + adminPanel: { label: 'Panneau d’administration', description: 'Administration système et paramètres' }, + analytics: { + label: 'Analyses', + description: 'Tendances de vélocité, temps de cycle et prévisions', + }, + + // Gestion des tests (libellés alignés avec la navigation de l'espace de travail) + tests: { label: 'Tests', description: 'Gérer les cas de test, plans et exécutions' }, + testCases: { label: 'Cas de test', description: 'Afficher et gérer les cas de test' }, + testPlans: { label: 'Plans de test', description: 'Afficher et gérer les plans de test' }, + testTemplates: { label: 'Modèles', description: 'Modèles d’exécutions et étapes partagées' }, + testRuns: { label: 'Exécutions de test', description: 'Afficher et gérer les exécutions de test' }, + testReports: { label: 'Rapports', description: 'Afficher les rapports d’exécution de test' }, + createTestCase: { + label: 'Créer un cas de test', + description: 'Créer un nouveau cas de test pour l’assurance qualité', + }, + createTestPlan: { + label: 'Créer un plan de test', + description: 'Créer un nouveau plan de test contenant des cas de test', + }, + createTestRun: { + label: 'Créer une exécution de test', + description: 'Créer un nouveau modèle d’exécution de test', + }, + + // Gestion du temps + timeTracking: { label: 'Suivi du temps', description: 'Saisir et gérer les entrées de temps de travail' }, + timeReports: { + label: 'Rapports de temps', + description: 'Afficher les rapports de suivi du temps et les analyses', + }, + timeProjects: { label: 'Projets de temps', description: 'Gérer les projets de suivi du temps' }, + logTime: { label: 'Saisir du temps', description: 'Ajouter rapidement une nouvelle entrée de temps' }, + startTimer: { label: 'Démarrer le chronomètre', description: 'Démarrer le suivi du temps pour le travail actuel' }, + stopTimer: { label: 'Arrêter le chronomètre', description: 'Arrêter le suivi du temps en cours' }, + + // Commandes de création + createWorkItem: { label: 'Créer un élément de travail', description: 'Créer un nouvel élément de travail ou une tâche' }, + createWorkspace: { label: 'Créer un espace de travail', description: 'Créer un nouvel espace de travail de projet' }, + createMilestone: { label: 'Créer un jalon', description: 'Créer un nouveau jalon de projet' }, + createCollection: { + label: 'Créer une collection', + description: 'Créer une nouvelle collection d’éléments de travail', + }, + + // Commandes d'administration + adminCustomFields: { label: 'Champs personnalisés', description: 'Gérer les définitions de champs personnalisés' }, + adminScreens: { + label: 'Gestion des écrans', + description: 'Configurer les écrans de formulaires et les dispositions', + }, + adminHierarchyLevels: { + label: 'Niveaux de hiérarchie', + description: 'Configurer les niveaux hiérarchiques des éléments de travail', + }, + adminItemTypes: { + label: 'Types d’éléments', + description: 'Gérer les types d’éléments de travail avec icônes et couleurs', + }, + adminPriorities: { + label: 'Priorités', + description: 'Configurer les niveaux de priorité avec icônes et couleurs', + }, + adminConfigSets: { + label: 'Ensembles de configuration', + description: 'Gérer les ensembles de configuration avec flux de travail et écrans', + }, + adminStatuses: { label: 'Statuts', description: 'Gérer les statuts individuels des éléments de travail' }, + adminStatusCategories: { + label: 'Catégories de statut', + description: 'Gérer les catégories de statut et leurs couleurs', + }, + adminWorkflows: { + label: 'Concepteur de flux de travail', + description: 'Concevoir et gérer les transitions de flux de travail', + }, + adminConditionSets: { + label: 'Ensembles de conditions', + description: 'Définir des conditions qui restreignent les transitions de flux de travail', + }, + adminLinkTypes: { label: 'Types de liens', description: 'Gérer les types de liens entre éléments de travail' }, + adminScmProviders: { + label: 'Fournisseurs SCM', + description: 'Configurer les intégrations GitHub, GitLab, Gitea et Bitbucket', + }, + adminAttachments: { + label: 'Pièces jointes', + description: 'Gérer la configuration et les paramètres des pièces jointes', + }, + adminModules: { + label: 'Paramètres des modules', + description: 'Activer ou désactiver les modules de suivi du temps et de gestion des tests', + }, + adminThemes: { + label: 'Paramètres des thèmes', + description: 'Gérer les thèmes et l’apparence de l’application', + }, + adminUsers: { label: 'Gestion des utilisateurs', description: 'Gérer les utilisateurs, rôles et autorisations' }, + adminGroups: { label: 'Gestion des groupes', description: 'Gérer les groupes d’utilisateurs et leurs membres' }, + adminPermissions: { + label: 'Autorisations', + description: 'Gérer les autorisations des utilisateurs et le contrôle d’accès', + }, + adminPermissionSets: { + label: 'Ensembles d’autorisations', + description: 'Gérer les regroupements d’autorisations pour les ensembles de configuration', + }, + adminWorkspaceRoles: { + label: 'Rôles d’espace de travail', + description: 'Afficher les rôles d’espace de travail et leurs autorisations', + }, + adminSso: { + label: 'Authentification unique (SSO)', + description: 'Configurer les fournisseurs d’identité OIDC pour le SSO', + }, + adminSecurity: { + label: 'Paramètres de sécurité', + description: 'Configurer les flux de calendriers et l’exécution des plugins', + }, + adminSystemImport: { label: 'Importation système', description: 'Importer des données depuis d’autres systèmes' }, + adminAssets: { + label: 'Gestion des ressources', + description: 'Configurer les types et catégories de ressources', + }, + adminWorkspaces: { label: 'Administration des espaces de travail', description: 'Gérer les espaces de travail et leurs paramètres' }, + adminNotifications: { + label: 'Paramètres de notification', + description: 'Gérer les configurations de notification', + }, + adminEmailTemplates: { + label: 'Modèles de courriels', + description: 'Personnaliser le sujet et le corps des courriels transactionnels', + }, + + // Commandes système + quitApp: { + label: 'Quitter l’application', + description: 'Arrêter proprement le serveur de l’application', + }, + + // Divers + portal: { label: 'Portail', description: 'Gestion du portail client' }, + iterations: { label: 'Itérations', description: 'Planification et suivi des sprints' }, + createItem: { label: 'Créer un élément', description: 'Créer un nouvel élément de travail' }, + myProfile: { label: 'Mon profil', description: 'Afficher et modifier votre profil' }, + notifications: { label: 'Notifications', description: 'Afficher les notifications' }, + about: { label: 'À propos', description: 'À propos de cette application' }, + assets: { label: 'Ressources', description: 'Gérer les ensembles de ressources et les articles' }, + organizations: { label: 'Organisations', description: 'Gérer les organisations clientes' }, + + // Commandes dynamiques d'espace de travail + goToWorkspace: { label: 'Aller à {name}', description: 'Naviguer vers l’espace de travail {name}' }, + workspaceOverview: { + label: 'Aperçu de {name}', + description: 'Afficher le tableau de bord de l’espace de travail avec statistiques et graphiques', + }, + }, + }, + + dashboard: { + title: 'Tableau de bord', + subtitle: 'Aperçu de vos éléments de travail et projets', + welcome: 'Bienvenue', + overview: 'Aperçu', + recentActivity: 'Activité récente', + recentItems: 'Éléments récents', + myTasks: 'Mes tâches', + assignedToMe: 'Qui m’ont été attribués', + openTasks: 'Tâches ouvertes', + overdueItems: 'Éléments en retard', + upcomingDeadlines: 'Échéances à venir', + activeIterations: 'Itérations actives', + inProgressMilestones: 'Jalons en cours', + noRecentActivity: 'Aucune activité récente', + recentItemsHint: 'Les éléments que vous mettez à jour apparaîtront ici', + noActiveIterations: 'Aucune itération active', + iterationTimelinesHint: 'La chronologie des itérations apparaîtra ici', + noUpcomingDeadlines: 'Aucune échéance à venir', + dueDatesHint: 'Les éléments ayant une date d’échéance apparaîtront ici', + noOpenTasks: 'Aucune tâche ouverte ne vous est attribuée', + allCaughtUp: 'Vous êtes complètement à jour !', + noOverdueItems: 'Aucun élément en retard', + loadingRecentItems: 'Chargement des éléments récents…', + loadingTasks: 'Chargement de vos tâches…', + loadingOverdueItems: 'Chargement des éléments en retard…', + workItemStatusOverview: 'Aperçu des statuts d’éléments de travail', + statusOpen: 'Ouvert', + statusInProgress: 'En cours', + statusClosed: 'Fermé', + quickActions: 'Actions rapides', + createWorkItem: 'Créer un élément de travail', + createWorkItemDesc: 'Commencer le suivi d’un nouvel élément de travail', + manageMilestones: 'Gérer les jalons', + manageMilestonesDesc: 'Afficher et organiser les jalons', + manageWorkspaces: 'Gérer les espaces de travail', + manageWorkspacesDesc: 'Configurer les espaces de travail', + recentWorkItems: 'Éléments de travail récents', + whatsNew: 'Nouveautés', + noNotifications: 'Aucune notification pour le moment', + yourActivity: 'Votre activité', + viewed: 'Consulté', + edited: 'Modifié', + commented: 'Commenté', + noRecentlyViewed: 'Aucun élément consulté récemment', + noRecentlyEdited: 'Aucun élément modifié récemment', + noRecentlyCommented: 'Aucun élément commenté récemment', + quickAccess: 'Accès rapide', + quickAccessHint: 'Appuyez sur {shortcut} pour ouvrir la palette de commandes', + upcomingMilestones: 'Jalons à venir', + watching: 'Suivis', + recentWorkspaces: 'Espaces de travail récents', + noRecentWorkspaces: 'Aucun espace de travail récent', + goodMorning: 'Bonjour', + goodAfternoon: 'Bon après-midi', + goodEvening: 'Bonsoir', + goodNight: 'Bonne nuit', + dueIn: 'Échéance dans {days} j', + dueToday: 'Échéance aujourd’hui', + overdue: 'En retard de {days} j', + done: '{done}/{total} terminés', + there: '', + doneEditing: 'Terminer la modification', + customize: 'Personnaliser', + editModeHint: 'Mode modification : ajoutez, renommez, réordonnez ou supprimez des sections et des widgets', + sections: { + yourDay: 'Votre journée', yourDayDescription: 'Un résumé rapide de ce qui requiert votre attention', + work: 'Travail', workDescription: 'Votre liste personnelle et les éléments qui vous sont attribués', + workspaces: 'Espaces de travail', workspacesDescription: 'Reprendre le travail', new: 'Nouvelle section', section: 'Section du tableau de bord', + titlePlaceholder: 'Titre de la section', subtitlePlaceholder: 'Sous-titre (optionnel)', rename: 'Renommer la section', + deleteMessage: 'Supprimer cette section ? Tous les widgets qu’elle contient seront également supprimés.', noWidgets: 'Aucun widget dans cette section pour le moment', + addWidgetsHint: 'Choisissez Personnaliser pour ajouter des widgets', none: 'Aucune section configurée', noneHint: 'Choisissez Modifier pour ajouter une section au tableau de bord', + }, + customization: { + widgets: 'Widgets', activity: 'Activité', activityDescription: 'Briefings, flux d’activité et notifications', + work: 'Travail', workDescription: 'Éléments, jalons et travail qui vous est attribué', navigation: 'Navigation', + navigationDescription: 'Accès rapide aux espaces de travail', tipTitle: 'Astuce', tip: 'Faites glisser un widget dans n’importe quelle section du tableau de bord.', + }, + widgets: { + dailyBriefing: { name: 'Briefing quotidien', description: 'Résumé généré par l’IA de ce qui compte aujourd’hui' }, + yourActivity: { name: 'Votre activité', description: 'Éléments que vous avez récemment consultés, modifiés ou commentés' }, + whatsNew: { name: 'Nouveautés', description: 'Dernières notifications et mises à jour non lues' }, + personalTasks: { name: 'Tâches personnelles', description: 'Éléments de votre liste de tâches personnelle' }, + savedSearch: { name: 'Recherche enregistrée', description: 'Éléments de travail provenant d’une collection enregistrée' }, + assignedToMe: { name: 'Qui m’ont été attribués', description: 'Éléments ouverts qui vous sont attribués dans tous les espaces de travail' }, + watchedItems: { name: 'Éléments suivis', description: 'Éléments que vous suivez' }, + upcomingMilestones: { name: 'Jalons à venir', description: 'Jalons dont la date cible approche' }, + recentWorkspaces: { name: 'Espaces de travail récents', description: 'Espaces de travail que vous avez visités récemment' }, + quickAccess: { name: 'Accès rapide', description: 'Liens rapides vers les espaces de travail disponibles' }, + unknown: 'Type de widget inconnu : {type}', + }, + widgetContent: { + briefingUnavailable: 'Votre briefing quotidien n’est pas disponible pour le moment. Il nécessite une intégration IA ; si vous venez d’en configurer une, réessayez dans un moment.', + updated: 'Mis à jour le {date}', assignedLoadError: 'Impossible de charger les éléments qui vous sont attribués', assignedEmpty: 'Rien ne vous est attribué pour le moment', + personalLoadError: 'Impossible de charger vos tâches personnelles', personalEmpty: 'Votre liste de tâches personnelle est vide', noWorkspaces: 'Aucun espace de travail pour le moment', + createWorkspace: 'En créer un', workspaceAvatar: 'Avatar de {name}', visited: 'visité le {date}', watchedEmpty: 'Vous ne suivez aucun élément', + noUpcomingMilestones: 'Aucun jalon à venir', milestoneDone: '{done} sur {total} terminés', daysOverdue: '{count} jours de retard', daysOverdue_one: '{count} jour de retard', daysOverdue_other: '{count} jours de retard', daysLeft: '{count} jours restants', daysLeft_one: '{count} jour restant', daysLeft_other: '{count} jours restants', + priorityAria: 'Priorité : {name}', + }, + relative: { + unknown: 'Inconnu', justNow: 'À l’instant', minutesAgo: 'Il y a {count} minutes', minutesAgo_one: 'Il y a {count} minute', minutesAgo_other: 'Il y a {count} minutes', + hoursAgo: 'Il y a {count} heures', hoursAgo_one: 'Il y a {count} heure', hoursAgo_other: 'Il y a {count} heures', daysAgo: 'Il y a {count} jours', daysAgo_one: 'Il y a {count} jour', daysAgo_other: 'Il y a {count} jours', + }, + }, + + workspaceDashboard: { + overview: 'Aperçu', overviewDescription: 'Métriques clés et statistiques', progress: 'Suivi de la progression', progressDescription: 'Graphiques et chronologies', homepage: 'Page d’accueil', loading: 'Chargement des données de l’espace de travail…', noSectionsHint: 'Choisissez Modifier pour ajouter des sections à la page d’accueil de l’espace de travail', + customize: 'Personnaliser', doneEditing: 'Terminer la modification', editModeHint: 'Mode modification : ajoutez, renommez, réordonnez ou supprimez des sections et des widgets', weekOf: 'Semaine du {date}', + widgets: { + stats: { name: 'Aperçu des statistiques', description: 'Collections et nombre d’éléments par catégorie de statut' }, + completionChart: { name: 'Graphique de réalisation', description: 'Éléments terminés au cours des quatre dernières semaines' }, + createdChart: { name: 'Graphique de création', description: 'Éléments créés au cours des sept derniers jours' }, + milestoneProgress: { name: 'Progression des jalons', description: 'Jalons actifs et leur avancement' }, + recentItems: { name: 'Éléments récents', description: 'Éléments récemment mis à jour dans cet espace de travail' }, + myTasks: { name: 'Mes tâches', description: 'Éléments qui vous sont attribués' }, + savedSearch: { name: 'Recherche enregistrée', description: 'Éléments de travail provenant d’une collection enregistrée' }, + overdueItems: { name: 'Éléments en retard', description: 'Éléments dont la date d’échéance est dépassée' }, + upcomingDeadlines: { name: 'Échéances à venir', description: 'Éléments dont la date d’échéance approche' }, + iterationTimeline: { name: 'Chronologie des itérations', description: 'Calendrier des itérations actuelles et à venir' }, + testCoverage: { name: 'Couverture des tests', description: 'Exigences couvertes par des cas de test' }, + }, + customization: { + widgets: 'Widgets', builtIn: 'Widgets intégrés', builtInDescription: 'Métriques et graphiques principaux de l’espace de travail', + additional: 'Widgets supplémentaires', additionalDescription: 'Listes, calendriers et widgets de test', defaultWidth: 'Largeur par défaut : {width} sur 3', + tipTitle: 'Astuce', tip: 'Faites glisser un widget dans n’importe quelle section de la page d’accueil de l’espace de travail.', + }, + }, + + search: { + title: 'Recherche', + subtitle: 'Rechercher dans tous les espaces de travail avec des options de filtrage avancées', + searchPlaceholder: 'Rechercher…', + searchItems: 'Rechercher des éléments…', + searchResults: 'Résultats de recherche', + noSearchResults: 'Aucun résultat de recherche', + configureFilter: 'Configurez le filtre pour voir les éléments', + savedSearch: 'Recherche enregistrée', + saveSearch: 'Enregistrer la recherche', + clearSearch: 'Effacer la recherche', + advancedSearch: 'Recherche avancée', + recentSearches: 'Recherches récentes', + workItem: 'Élément de travail', + workspace: 'Espace de travail', + }, + + about: { + title: 'À propos de Windshift', + subtitle: 'Une plateforme complète de gestion du travail conçue pour les équipes modernes', + whatIs: 'Qu’est-ce que Windshift ?', + description: + 'Windshift est une application puissante de gestion du travail qui réunit le suivi de projet, la gestion des tests, le suivi du temps et la collaboration d’équipe au sein d’une plateforme unifiée. Conçue avec des technologies modernes et pensée pour la flexibilité, elle s’adapte au flux de travail unique de votre équipe.', + keyFeatures: 'Fonctionnalités clés', + projectManagement: 'Gestion de projet', + projectManagementDesc: + 'Organisez les éléments de travail de manière hiérarchique avec des champs personnalisés, des flux de travail et un suivi des statuts.', + testManagement: 'Gestion des tests', + testManagementDesc: + 'Créez des cas de test, organisez des plans de test, exécutez des sessions de test et suivez les anomalies efficacement.', + timeTracking: 'Suivi du temps', + timeTrackingDesc: + 'Suivez le temps passé sur les projets et les tâches grâce à des rapports et des analyses détaillés.', + builtWith: 'Bibliothèques notables', + libSvelte: 'Framework front-end', + libTailwind: 'Framework CSS orienté utilitaires', + libMilkdown: 'Éditeur de texte enrichi', + libExcalidraw: 'Schémas et croquis', + libMeltUI: 'Composants d’interface primitifs', + libExtism: 'Système de plugins WebAssembly', + libCharmbracelet: 'Accès TUI et SSH (Bubble Tea, Wish)', + libSvelteFlow: 'Diagrammes de flux', + versionInfo: 'Informations de version', + version: 'Version', + developedBy: 'Développé par', + reportIssues: 'Signaler un problème', + getStarted: 'Commencer', + }, + + onboarding: { + welcomeTo: 'Bienvenue dans Windshift', + getStartedMessage: 'Commençons par créer votre premier espace de travail et votre premier élément de travail', + progress: 'Progression', + completed: 'terminé(s)', + of: 'sur', + createWorkspace: 'Créer votre espace de travail', + workspacesHelp: 'Les espaces de travail vous aident à organiser vos projets et vos équipes', + createWorkspaceBtn: 'Créer un espace de travail', + createFirstWorkItem: 'Créer votre premier élément de travail', + trackTasks: 'Suivez les tâches, les bogues, les fonctionnalités et bien plus', + createWorkItemBtn: 'Créer un élément de travail', + dismissAssistant: 'Masquer l’assistant', + dismissOnboarding: 'Masquer l’intégration', + getStartedMember: 'Voici les espaces de travail à votre disposition', + selectWorkspace: 'Sélectionnez un espace de travail pour commencer', + noWorkspacesAvailable: + 'Aucun espace de travail n’est disponible pour le moment. Veuillez contacter votre administrateur pour obtenir l’accès à un espace de travail.', + }, +}; \ No newline at end of file diff --git a/frontend/src/lib/locales/fr/teams.js b/frontend/src/lib/locales/fr/teams.js new file mode 100644 index 00000000..01a10f35 --- /dev/null +++ b/frontend/src/lib/locales/fr/teams.js @@ -0,0 +1,180 @@ +/** + * Teams + on-call translations. + */ + +export default { + teams: { + title: 'Équipes', + subtitle: 'Gérer les équipes, les membres et les roulements d’astreinte', + name: 'Nom', + description: 'Description', + descriptionOptional: 'Description (optionnel)', + namePlaceholder: 'ex. Astreinte Plateforme', + descriptionPlaceholder: 'Résumé optionnel de cette équipe', + members: 'Membres', + member: 'Membre', + role: 'Rôle', + email: 'E-mail', + groups: 'Groupes', + group: 'Groupe', + memberCount: 'Nombre de membres', + status: 'Statut', + active: 'Actif', + inactive: 'Inactif', + actions: 'Actions', + createTeam: 'Créer une équipe', + editTeam: 'Modifier l’équipe', + nameRequired: 'Le nom est obligatoire', + confirmDelete: 'Supprimer l’équipe « {name} » ? Cette action est irréversible.', + created: 'Équipe créée', + updated: 'Équipe mise à jour', + deleted: 'Équipe supprimée', + failedToLoad: 'Échec du chargement des équipes', + failedToSave: 'Échec de l’enregistrement de l’équipe', + failedToDelete: 'Échec de la suppression de l’équipe', + loading: 'Chargement…', + empty: 'Aucune équipe pour le moment', + backToTeams: 'Retour aux équipes', + noDescription: 'Aucune description', + iconAndColor: 'Icône et couleur', + avatar: 'Avatar', + identityTitle: 'Identité', + identitySubtitle: 'Choisissez une icône et une couleur, ou téléversez un avatar personnalisé.', + directMembers: 'Membres directs', + mappedGroups: 'Groupes associés', + resolvedMembers: 'Membres effectifs', + resolvedMembersHint: 'union des membres directs et des membres issus des groupes associés', + addMembers: 'Ajouter des membres', + addMember: 'Ajouter un membre', + pickUserFirst: 'Choisissez d’abord un utilisateur', + pickGroupFirst: 'Choisissez d’abord un groupe', + alreadyMember: 'L’utilisateur est déjà membre', + alreadyAttached: 'Le groupe est déjà associé', + searchUser: 'Rechercher un utilisateur…', + usersToAdd: 'Utilisateurs à ajouter', + membersAdded: 'Membres ajoutés', + failedToAddMembers: 'Échec de l’ajout des membres', + roleUpdated: 'Rôle mis à jour', + failedToUpdateRole: 'Échec de la mise à jour du rôle', + confirmRemoveMember: 'Retirer {name} de cette équipe ?', + memberRemoved: 'Membre retiré', + failedToRemoveMember: 'Échec du retrait du membre', + noDirectMembers: 'Aucun membre direct pour le moment', + noResolvedMembers: 'Aucun membre effectif', + source: 'Source', + sourceDirect: 'Direct', + sourceGroup: 'Groupe', + leaveStatus: 'Congé', + onLeave: 'En congé', + onLeaveWithSub: 'En congé (remplaçant : {name})', + attachGroup: 'Associer un groupe', + searchGroup: 'Rechercher un groupe…', + groupsToAdd: 'Groupes à ajouter', + groupsAdded: 'Groupes associés', + failedToAddGroups: 'Échec de l’association des groupes', + confirmRemoveGroup: 'Détacher le groupe « {name} » de cette équipe ?', + groupRemoved: 'Groupe détaché', + failedToRemoveGroup: 'Échec du détachement du groupe', + noMappedGroups: 'Aucun groupe associé', + tabs: { + overview: 'Aperçu', + members: 'Membres', + groups: 'Groupes', + onCall: 'Astreinte', + }, + oncall: { + schedules: 'Planning d’astreinte', + addSchedule: 'Ajouter un planning', + noSchedules: 'Aucun planning d’astreinte pour le moment', + createSchedule: 'Créer un planning', + editSchedule: 'Modifier le planning', + name: 'Nom', + timezone: 'Fuseau horaire', + nameRequired: 'Le nom est obligatoire', + scheduleCreated: 'Planning créé', + scheduleUpdated: 'Planning mis à jour', + scheduleDeleted: 'Planning supprimé', + confirmDeleteSchedule: 'Supprimer le planning « {name} » ? Cela supprimera également ses couches et ses remplacements.', + failedToLoad: 'Échec du chargement des plannings', + failedToSaveSchedule: 'Échec de l’enregistrement du planning', + failedToDeleteSchedule: 'Échec de la suppression du planning', + currentlyOnCall: 'Actuellement d’astreinte', + nobody: 'Personne d’assigné', + override: 'Remplacement', + layers: 'Couches', + addLayer: 'Ajouter une couche', + editLayer: 'Modifier la couche', + noLayers: 'Aucune couche configurée', + layerName: 'Nom de la couche', + layerNameRequired: 'Le nom de la couche est obligatoire', + priority: 'Priorité', + rotationType: 'Type de roulement', + daily: 'Quotidien', + weekly: 'Hebdomadaire', + custom: 'Personnalisé', + intervalDays: 'Intervalle (jours)', + handoffTime: 'Heure de passage de relais', + startDate: 'Date de début', + endDateOptional: 'Date de fin (optionnel)', + members: 'Membres', + addMembersHint: 'Ajoutez des membres dans l’ordre du roulement. Le premier membre est le premier d’astreinte.', + noMembers: 'Aucun membre pour le moment', + moveUp: 'Déplacer vers le haut', + moveDown: 'Déplacer vers le bas', + layerCreated: 'Couche créée', + layerUpdated: 'Couche mise à jour', + layerDeleted: 'Couche supprimée', + confirmDeleteLayer: 'Supprimer la couche « {name} » ?', + failedToSaveLayer: 'Échec de l’enregistrement de la couche', + failedToDeleteLayer: 'Échec de la suppression de la couche', + addOverride: 'Ajouter un remplacement', + createOverride: 'Créer un remplacement', + replacedUser: 'Utilisateur remplacé', + replacementUser: 'Remplaçant', + startTime: 'Début', + endTime: 'Fin', + reasonOptional: 'Motif (optionnel)', + overrideUsersRequired: 'Les deux utilisateurs doivent être sélectionnés', + overrideTimesRequired: 'Les heures de début et de fin sont obligatoires', + overrideCreated: 'Remplacement créé', + failedToCreateOverride: 'Échec de la création du remplacement', + overrides: 'Remplacements', + noOverrides: 'Aucun remplacement planifié', + overrideDeleted: 'Remplacement retiré', + confirmDeleteOverride: 'Retirer ce remplacement ?', + failedToDeleteOverride: 'Échec du retrait du remplacement', + }, + }, + profile: { + leave: { + title: 'Congé', + subtitle: 'Planifier des congés et choisir un remplaçant pour couvrir vos tours d’astreinte.', + tabLabel: 'Congé', + scheduleLeave: 'Planifier un congé', + editLeave: 'Modifier le congé', + startDate: 'Début', + endDate: 'Fin', + reason: 'Motif', + reasonOptional: 'Motif (optionnel)', + substitute: 'Remplaçant', + substituteOptional: 'Remplaçant (optionnel)', + pickSubstitute: 'Choisir un remplaçant…', + noSubstitute: 'Aucun remplaçant', + cannotBeSelf: 'Vous ne pouvez pas vous choisir comme remplaçant.', + status: 'Statut', + past: 'Passé', + upcoming: 'À venir', + active: 'Actif', + empty: 'Aucun congé planifié', + datesRequired: 'Les dates de début et de fin sont obligatoires', + endBeforeStart: 'La date de fin doit être égale ou postérieure à la date de début', + created: 'Congé planifié', + updated: 'Congé mis à jour', + deleted: 'Congé retiré', + confirmDelete: 'Retirer cette période de congé ?', + failedToLoad: 'Échec du chargement des congés', + failedToSave: 'Échec de l’enregistrement du congé', + failedToDelete: 'Échec de la suppression du congé', + }, + }, +}; \ No newline at end of file diff --git a/frontend/src/lib/locales/fr/testing.js b/frontend/src/lib/locales/fr/testing.js new file mode 100644 index 00000000..ea1ad410 --- /dev/null +++ b/frontend/src/lib/locales/fr/testing.js @@ -0,0 +1,180 @@ +/** + * Traduction en français (fr) - Équipes et astreintes + */ + +export default { + teams: { + title: 'Équipes', + subtitle: 'Gérer les équipes, les membres et les roulements d’astreinte', + name: 'Nom', + description: 'Description', + descriptionOptional: 'Description (optionnel)', + namePlaceholder: 'ex. Astreinte Plateforme', + descriptionPlaceholder: 'Résumé optionnel de cette équipe', + members: 'Membres', + member: 'Membre', + role: 'Rôle', + email: 'E-mail', + groups: 'Groupes', + group: 'Groupe', + memberCount: 'Nombre de membres', + status: 'Statut', + active: 'Actif', + inactive: 'Inactif', + actions: 'Actions', + createTeam: 'Créer une équipe', + editTeam: 'Modifier l’équipe', + nameRequired: 'Le nom est obligatoire', + confirmDelete: 'Supprimer l’équipe « {name} » ? Cette action est irréversible.', + created: 'Équipe créée', + updated: 'Équipe mise à jour', + deleted: 'Équipe supprimée', + failedToLoad: 'Échec du chargement des équipes', + failedToSave: 'Échec de l’enregistrement de l’équipe', + failedToDelete: 'Échec de la suppression de l’équipe', + loading: 'Chargement…', + empty: 'Aucune équipe pour le moment', + backToTeams: 'Retour aux équipes', + noDescription: 'Aucune description', + iconAndColor: 'Icône et couleur', + avatar: 'Avatar', + identityTitle: 'Identité', + identitySubtitle: 'Choisissez une icône et une couleur, ou téléversez un avatar personnalisé.', + directMembers: 'Membres directs', + mappedGroups: 'Groupes associés', + resolvedMembers: 'Membres effectifs', + resolvedMembersHint: 'union des membres directs et des membres issus des groupes associés', + addMembers: 'Ajouter des membres', + addMember: 'Ajouter un membre', + pickUserFirst: 'Choisissez d’abord un utilisateur', + pickGroupFirst: 'Choisissez d’abord un groupe', + alreadyMember: 'L’utilisateur est déjà membre', + alreadyAttached: 'Le groupe est déjà associé', + searchUser: 'Rechercher un utilisateur…', + usersToAdd: 'Utilisateurs à ajouter', + membersAdded: 'Membres ajoutés', + failedToAddMembers: 'Échec de l’ajout des membres', + roleUpdated: 'Rôle mis à jour', + failedToUpdateRole: 'Échec de la mise à jour du rôle', + confirmRemoveMember: 'Retirer {name} de cette équipe ?', + memberRemoved: 'Membre retiré', + failedToRemoveMember: 'Échec du retrait du membre', + noDirectMembers: 'Aucun membre direct pour le moment', + noResolvedMembers: 'Aucun membre effectif', + source: 'Source', + sourceDirect: 'Direct', + sourceGroup: 'Groupe', + leaveStatus: 'Congé', + onLeave: 'En congé', + onLeaveWithSub: 'En congé (remplaçant : {name})', + attachGroup: 'Associer un groupe', + searchGroup: 'Rechercher un groupe…', + groupsToAdd: 'Groupes à ajouter', + groupsAdded: 'Groupes associés', + failedToAddGroups: 'Échec de l’association des groupes', + confirmRemoveGroup: 'Détacher le groupe « {name} » de cette équipe ?', + groupRemoved: 'Groupe détaché', + failedToRemoveGroup: 'Échec du détachement du groupe', + noMappedGroups: 'Aucun groupe associé', + tabs: { + overview: 'Aperçu', + members: 'Membres', + groups: 'Groupes', + onCall: 'Astreinte', + }, + oncall: { + schedules: 'Planning d’astreinte', + addSchedule: 'Ajouter un planning', + noSchedules: 'Aucun planning d’astreinte pour le moment', + createSchedule: 'Créer un planning', + editSchedule: 'Modifier le planning', + name: 'Nom', + timezone: 'Fuseau horaire', + nameRequired: 'Le nom est obligatoire', + scheduleCreated: 'Planning créé', + scheduleUpdated: 'Planning mis à jour', + scheduleDeleted: 'Planning supprimé', + confirmDeleteSchedule: 'Supprimer le planning « {name} » ? Cela supprimera également ses couches et ses remplacements.', + failedToLoad: 'Échec du chargement des plannings', + failedToSaveSchedule: 'Échec de l’enregistrement du planning', + failedToDeleteSchedule: 'Échec de la suppression du planning', + currentlyOnCall: 'Actuellement d’astreinte', + nobody: 'Personne d’assigné', + override: 'Remplacement', + layers: 'Couches', + addLayer: 'Ajouter une couche', + editLayer: 'Modifier la couche', + noLayers: 'Aucune couche configurée', + layerName: 'Nom de la couche', + layerNameRequired: 'Le nom de la couche est obligatoire', + priority: 'Priorité', + rotationType: 'Type de roulement', + daily: 'Quotidien', + weekly: 'Hebdomadaire', + custom: 'Personnalisé', + intervalDays: 'Intervalle (jours)', + handoffTime: 'Heure de passage de relais', + startDate: 'Date de début', + endDateOptional: 'Date de fin (optionnel)', + members: 'Membres', + addMembersHint: 'Ajoutez des membres dans l’ordre du roulement. Le premier membre est le premier d’astreinte.', + noMembers: 'Aucun membre pour le moment', + moveUp: 'Déplacer vers le haut', + moveDown: 'Déplacer vers le bas', + layerCreated: 'Couche créée', + layerUpdated: 'Couche mise à jour', + layerDeleted: 'Couche supprimée', + confirmDeleteLayer: 'Supprimer la couche « {name} » ?', + failedToSaveLayer: 'Échec de l’enregistrement de la couche', + failedToDeleteLayer: 'Échec de la suppression de la couche', + addOverride: 'Ajouter un remplacement', + createOverride: 'Créer un remplacement', + replacedUser: 'Utilisateur remplacé', + replacementUser: 'Remplaçant', + startTime: 'Début', + endTime: 'Fin', + reasonOptional: 'Motif (optionnel)', + overrideUsersRequired: 'Les deux utilisateurs doivent être sélectionnés', + overrideTimesRequired: 'Les heures de début et de fin sont obligatoires', + overrideCreated: 'Remplacement créé', + failedToCreateOverride: 'Échec de la création du remplacement', + overrides: 'Remplacements', + noOverrides: 'Aucun remplacement planifié', + overrideDeleted: 'Remplacement retiré', + confirmDeleteOverride: 'Retirer ce remplacement ?', + failedToDeleteOverride: 'Échec du retrait du remplacement', + }, + }, + profile: { + leave: { + title: 'Congé', + subtitle: 'Planifier des congés et choisir un remplaçant pour couvrir vos tours d’astreinte.', + tabLabel: 'Congé', + scheduleLeave: 'Planifier un congé', + editLeave: 'Modifier le congé', + startDate: 'Début', + endDate: 'Fin', + reason: 'Motif', + reasonOptional: 'Motif (optionnel)', + substitute: 'Remplaçant', + substituteOptional: 'Remplaçant (optionnel)', + pickSubstitute: 'Choisir un remplaçant…', + noSubstitute: 'Aucun remplaçant', + cannotBeSelf: 'Vous ne pouvez pas vous choisir comme remplaçant.', + status: 'Statut', + past: 'Passé', + upcoming: 'À venir', + active: 'Actif', + empty: 'Aucun congé planifié', + datesRequired: 'Les dates de début et de fin sont obligatoires', + endBeforeStart: 'La date de fin doit être égale ou postérieure à la date de début', + created: 'Congé planifié', + updated: 'Congé mis à jour', + deleted: 'Congé retiré', + confirmDelete: 'Retirer cette période de congé ?', + failedToLoad: 'Échec du chargement des congés', + failedToSave: 'Échec de l’enregistrement du congé', + failedToDelete: 'Échec de la suppression du congé', + }, + }, +}; \ No newline at end of file diff --git a/frontend/src/lib/locales/fr/time.js b/frontend/src/lib/locales/fr/time.js new file mode 100644 index 00000000..ef379e41 --- /dev/null +++ b/frontend/src/lib/locales/fr/time.js @@ -0,0 +1,265 @@ +/** + * Traduction en français (fr) - Suivi du temps + * Comprend les sections : time, timeProject, timeProjectCategory + */ + +export default { + time: { + title: 'Suivi du temps', + subtitle: 'Suivre le temps passé sur les éléments de travail', + logTime: 'Saisir du temps', + editTimeEntry: 'Modifier la saisie de temps', + updateEntry: 'Mettre à jour la saisie', + timeSpent: 'Temps passé', + remaining: 'Restant', + estimate: 'Estimation', + originalEstimate: 'Estimation initiale', + hours: 'heures', + minutes: 'minutes', + days: 'jours', + weeks: 'semaines', + startTimer: 'Démarrer le chronomètre', + stopTimer: 'Arrêter le chronomètre', + pauseTimer: 'Mettre en pause', + resumeTimer: 'Reprendre', + timeLogged: 'Temps enregistré avec succès', + timeTrackingProject: 'Projet de suivi du temps', + workItemOptional: 'Élément de travail (optionnel)', + whatDidYouWorkOn: 'Sur quoi avez-vous travaillé ?', + start: 'Début', + end: 'Fin', + duration: 'Durée', + durationHelperText: + 'Saisissez l’heure de début + la durée (2h) pour calculer automatiquement l’heure de fin, ou saisissez les heures de début + de fin pour calculer la durée. Formats de temps : 1h, 30m, 1h30m, 2h15m, 1d (=8h)', + + // Feuille de temps + timesheet: { + title: 'Feuille de temps', + subtitle: 'Aperçu hebdomadaire de la feuille de temps', + total: 'Total', + noEntries: 'Aucune saisie de temps cette semaine. Ajoutez un projet pour commencer.', + projectItem: 'Projet / Élément de travail', + addProject: 'Ajouter un projet à la feuille de temps…', + removeProject: 'Retirer de la feuille de temps', + showWeekends: 'Fins de semaine', + }, + + // Intégration / Configuration + onboarding: { + title: 'Configurer le suivi du temps', + subtitle: 'Créons votre première organisation cliente et votre premier projet pour commencer', + setupProgress: 'Progression de la configuration', + stepOf: 'Étape {current} sur {total}', + createCustomerTitle: 'Créer votre première organisation cliente', + createCustomerDescription: + 'Une organisation cliente représente l’entreprise ou l’entité pour laquelle vous travaillez. Il peut s’agir d’un client, de votre employeur ou de votre propre entreprise.', + createProjectTitle: 'Créer votre premier projet', + createProjectDescription: + 'Les projets permettent d’organiser votre travail au sein d’un client. Vous pouvez suivre le temps consacré à des projets spécifiques.', + organizationNameRequired: 'Le nom de l’organisation est obligatoire', + projectNameRequired: 'Le nom du projet est obligatoire', + failedToCreateCustomer: 'Échec de la création de l’organisation cliente. Veuillez réessayer.', + failedToCreateProject: 'Échec de la création du projet. Veuillez réessayer.', + customerCreatedSuccess: 'Organisation cliente « {name} » créée avec succès !', + organizationNamePlaceholder: 'ex. Acme Corporation, TechStart Inc, Travail indépendant', + emailPlaceholder: 'facturation@client.com', + contactPersonPlaceholder: 'Jean Dupont', + projectNamePlaceholder: 'ex. Développement Web, Conseil, Travaux généraux', + projectDescriptionPlaceholder: 'Brève description du projet…', + hourlyRateHint: 'Vous pourrez le définir plus tard si vous n’êtes pas sûr', + skipForNow: 'Ignorer pour l’instant', + completeSetup: 'Terminer la configuration', + }, + + // Catégories + categories: { + title: 'Catégories de projets', + subtitle: 'Organiser les projets par catégories pour une meilleure gestion', + newCategory: 'Nouvelle catégorie', + noCategories: 'Aucune catégorie pour le moment', + createFirstHint: 'Créez votre première catégorie pour organiser vos projets', + failedToSave: 'Échec de l’enregistrement de la catégorie', + failedToDelete: 'Échec de la suppression de la catégorie', + confirmDelete: 'Voulez-vous vraiment supprimer « {name} » ?', + }, + + // Rapports + reports: { + title: 'Rapports', + subtitle: 'Analyser vos données de suivi du temps et exporter des rapports', + exportCSV: 'Exporter en CSV', + exportPDF: 'Exporter en PDF', + filters: 'Filtres', + customer: 'Client', + allCustomers: 'Tous les clients', + allProjects: 'Tous les projets', + descriptionFilter: 'Filtre de description', + searchDescriptions: 'Rechercher dans les descriptions…', + fromDate: 'Date de début', + toDate: 'Date de fin', + applyFilters: 'Appliquer les filtres', + totalHours: 'Total des heures', + totalEntries: 'Total des saisies', + averagePerDay: 'Moyenne par jour', + topProject: 'Projet principal', + loadingReports: 'Chargement des rapports…', + noEntriesFound: 'Aucune saisie de temps trouvée pour les filtres sélectionnés.', + totalTime: 'Temps total', + entriesShown: '{count} saisies affichées', + // Rapports de projet + projectReports: 'Rapports de projet', + personal: 'Personnel', + project: 'Projet', + selectProject: 'Sélectionner un projet', + budget: 'Budget', + budgetUsage: 'Utilisation du budget', + contributors: 'Contributeurs', + memberBreakdown: 'Répartition par équipe', + member: 'Membre', + hoursLogged: 'Heures enregistrées', + entries: 'Saisies', + avgPerDay: 'Moy./jour', + dailyHours: 'Heures quotidiennes', + noBudgetSet: 'Aucun budget défini', + noProjectSelected: 'Sélectionnez un projet pour afficher son rapport', + printLoading: 'Préparation du rapport de temps pour l’impression…', + printUnavailable: 'Ce rapport de temps n’est plus disponible. Retournez aux rapports et exportez-le à nouveau.', + backToReports: 'Retour aux rapports', + }, + + // Chronomètre + timer: { + goToWorkItem: 'Aller à l’élément de travail : {title}', + expandTimer: 'Agrandir le chronomètre', + collapseTimer: 'Réduire le chronomètre', + project: 'Projet', + workspace: 'Espace de travail', + }, + + // Projets + projects: { + title: 'Projets', + subtitle: 'Gérer les projets globaux pour le suivi du temps dans les espaces de travail', + addProject: 'Ajouter un projet', + projectsTab: 'Projets', + categoriesTab: 'Catégories', + searchProjects: 'Rechercher des projets…', + allCategories: 'Toutes les catégories', + allStatuses: 'Tous les statuts', + statusCount: '{count} statuts', + noProjects: 'Aucun projet trouvé. Créez votre premier projet pour commencer à suivre votre temps.', + noProjectsInCategory: 'Aucun projet dans cette catégorie.', + failedToSave: 'Échec de l’enregistrement du projet', + deleteProject: 'Supprimer le projet', + confirmDelete: 'Voulez-vous vraiment supprimer « {name} » ?', + unknownCustomer: 'Client inconnu', + project: 'Projet', + customer: 'Client', + rate: 'Taux', + projectName: 'Nom du projet', + descriptionOptional: 'Description (optionnel)', + hourlyRateOptional: 'Taux horaire (optionnel)', + }, + + // Calendrier + calendar: { + title: 'Calendrier hebdomadaire', + itemCount: '{count} éléments', + exportWeekToICS: 'Exporter la semaine en ICS', + myWorkItems: 'Mes éléments de travail', + dragToSchedule: 'Faites glisser les éléments pour les planifier', + noWorkItems: 'Aucun élément de travail assigné', + workItemsWillAppear: 'Les éléments de travail apparaîtront ici lorsqu’ils vous seront assignés', + itemsCompleted: '{completed} sur {total} éléments terminés', + previousWeek: 'Semaine précédente', + thisWeek: 'cette semaine', + nextWeek: 'Semaine suivante', + newTaskPlaceholder: 'Titre de la nouvelle tâche…', + failedToCreateTask: 'Échec de la création de la tâche', + }, + + // Saisie de temps + entry: { + title: 'Saisie de temps', + subtitle: 'Enregistrer vos heures de travail et gérer les saisies de temps', + addTimeEntry: 'Ajouter une nouvelle saisie de temps', + failedToSave: 'Échec de l’enregistrement de la saisie de temps. Veuillez vérifier votre saisie.', + confirmDelete: 'Voulez-vous vraiment supprimer cette saisie de temps ?', + needProjects: 'Vous devez créer des projets actifs avant d’enregistrer du temps.', + goToProjects: 'Aller aux projets', + startSetupWizard: 'démarrer l’assistant de configuration', + applyFiltersTitle: 'Appliquer les filtres sélectionnés à la liste des saisies de temps', + clearFiltersTitle: 'Effacer tous les filtres et afficher toutes les saisies de temps', + noEntries: 'Aucune saisie de temps trouvée. Enregistrez votre première saisie de temps pour commencer.', + clickToView: 'Cliquer pour afficher {key}-{number}', + budgetExceeded: '- budget dépassé', + submitted: 'Soumis', + }, + + // Organisations (anciennement Clients) + organizations: { + title: 'Organisations', + subtitle: 'Gérer vos organisations clientes', + addOrganization: 'Ajouter une organisation', + noOrganizations: 'Aucune organisation trouvée. Créez votre première organisation pour commencer.', + name: 'Nom de l’organisation', + emailOptional: 'E-mail (optionnel)', + contactPersonOptional: 'Personne de contact (optionnel)', + failedToSave: 'Échec de l’enregistrement de l’organisation', + deleteOrganization: 'Supprimer l’organisation', + confirmDelete: 'Voulez-vous vraiment supprimer « {name} » ?', + }, + + // Permissions + permissions: { + title: 'Autorisations du projet', + managePermissions: 'Gérer les autorisations', + managers: 'Responsables', + members: 'Membres', + addManager: 'Ajouter un responsable', + addMember: 'Ajouter un membre', + removeManager: 'Retirer le responsable', + removeMember: 'Retirer le membre', + noManagers: 'Aucun responsable assigné', + noManagersHint: 'Lorsqu’aucun responsable n’est assigné, n’importe qui peut gérer ce projet', + noMembers: 'Aucun membre assigné', + noMembersHint: 'Lorsqu’aucun membre n’est assigné, n’importe qui peut imputer du temps sur ce projet', + grantedAt: 'Ajouté', + confirmRemove: 'Voulez-vous vraiment retirer {name} ?', + failedToAdd: 'Échec de l’ajout', + failedToRemove: 'Échec du retrait', + managersNote: 'Responsables :', + managersNoteText: + 'Peuvent modifier les paramètres du projet, gérer les membres et voir toutes les saisies de temps pour ce projet.', + membersNote: 'Membres :', + membersNoteText: 'Peuvent imputer du temps sur ce projet et voir leurs propres saisies de temps.', + }, + }, + + timeProject: { + editProject: 'Modifier le projet', + newProject: 'Nouveau projet', + projectName: 'Nom du projet', + status: 'Statut', + customerOptional: 'Client (optionnel)', + none: 'Aucun', + categoryOptional: 'Catégorie (optionnel)', + hourlyRate: 'Taux horaire ($)', + maxHours: 'Heures max', + maxHoursPlaceholder: 'Aucune limite', + maxHoursHint: 'Budget optionnel pour les rapports', + projectColor: 'Couleur du projet', + updateProject: 'Mettre à jour le projet', + createProject: 'Créer le projet', + }, + + timeProjectCategory: { + editCategory: 'Modifier la catégorie', + newCategory: 'Nouvelle catégorie', + categoryName: 'Nom de la catégorie', + categoryNamePlaceholder: 'Développement, Marketing, Opérations…', + optionalDescription: 'Description optionnelle…', + updateCategory: 'Mettre à jour la catégorie', + createCategory: 'Créer la catégorie', + }, +}; \ No newline at end of file diff --git a/frontend/src/lib/locales/fr/ui.js b/frontend/src/lib/locales/fr/ui.js new file mode 100644 index 00000000..c6081d40 --- /dev/null +++ b/frontend/src/lib/locales/fr/ui.js @@ -0,0 +1,621 @@ +/** + * Traduction en français (fr) - Interface utilisateur + * Comprend : sélecteurs, éditeurs, boîtes de dialogue, composants, aria, disposition, widgets, pied de page + */ + +export default { + pickers: { + selectStatus: 'Sélectionner un statut', + // Général + select: 'Sélectionner', + search: 'Rechercher', + options: 'Options', + clearSelection: 'Effacer la sélection', + noResultsFor: 'Aucun résultat pour « {query} »', + createItem: 'Créer « {value} »', + noItemsFound: 'Aucun élément trouvé', + noItemsAvailable: 'Aucun élément disponible', + startTypingToSearch: 'Commencez à taper pour rechercher…', + searchPages: 'Rechercher des pages…', + + // Sélecteur d'actifs + selectAsset: 'Sélectionner un actif', + noTag: 'Aucune étiquette', + showingOfTotal: 'Affichage de {shown} sur {total} — tapez pour rechercher', + + // Sélecteur d'utilisateur / d'attribué + selectUser: 'Sélectionner un utilisateur', + searchUsers: 'Rechercher des utilisateurs…', + users: 'Utilisateurs', + noUsersFound: 'Aucun utilisateur trouvé', + noUsersAvailable: 'Aucun utilisateur disponible', + assignTo: 'Attribuer à', + unassigned: 'Non attribué', + assignee: 'Attribué à', + user: 'Utilisateur', + group: 'Groupe', + searchUser: 'Rechercher un utilisateur…', + searchGroup: 'Rechercher un groupe…', + + // Présence de l'agent dans les sélecteurs d'attribution (WI-272) + agentOnline: 'Agent en ligne — un exécuteur actif prendra en charge les éléments attribués', + agentOffline: 'Agent hors ligne — son pool d’exécuteurs n’a aucun exécuteur actif ; les éléments attribués seront mis en attente', + agentLocal: 'L’agent s’exécute sur ce serveur', + agentUnbound: 'Aucune liaison d’agent dans cet espace de travail — l’attribution ne démarrera pas d’exécution', + + // Sélecteur de groupe + selectGroup: 'Sélectionner un groupe', + + // Sélecteur de catégories + selectCategories: 'Sélectionner des catégories', + removeCategory: 'Retirer la catégorie', + categoriesSelected: '{count} catégories sélectionnées', + searchCategories: 'Rechercher des catégories…', + noCategoriesFound: 'Aucune catégorie trouvée', + + // Sélecteur de collections + selectCollections: 'Sélectionner des collections', + + // Sélecteur d'espaces de travail + selectWorkspaces: 'Sélectionner des espaces de travail', + searchWorkspaces: 'Rechercher des espaces de travail…', + noWorkspacesFound: 'Aucun espace de travail trouvé', + + // Sélecteur de jeux de configuration + selectConfigurationSet: 'Sélectionner un jeu de configuration', + searchConfigurationSets: 'Rechercher des jeux de configuration…', + configurationSets: 'Jeux de configuration', + defaultConfiguration: 'Configuration par défaut', + defaultConfigurationDescription: 'Utilise les paramètres par défaut de l’espace de travail', + noConfigurationSetsFound: 'Aucun jeu de configuration trouvé', + + // Sélecteur d'entité de jeu de configuration + entityAlreadyAssigned: '{label} est déjà attribué', + itemType: 'Type d’élément', + priorities: 'Priorités', + itemTypes: 'Types d’éléments', + level: 'Niveau {level}', + assigned: 'Attribué', + noEntitiesAssigned: 'Aucun {entities} attribué', + available: 'Disponible', + noEntitiesMatchSearch: 'Aucun {entities} ne correspond à votre recherche', + allEntitiesAssigned: 'Tous les {entities} sont attribués', + inConfigSet: 'Dans le jeu de config', + searchEntities: 'Rechercher {entities}…', + + // Sélecteur de champs + selectField: 'Sélectionner un champ', + searchFields: 'Rechercher des champs…', + noFieldsFound: 'Aucun champ trouvé', + customFields: 'Champs personnalisés', + custom: 'Personnalisé', + customFieldDesc: 'Champ personnalisé', + fieldTypes: { + text: 'Texte', + number: 'Nombre', + date: 'Date', + select: 'Sélection', + multiselect: 'Sélection multiple', + checkbox: 'Case à cocher', + url: 'URL', + email: 'E-mail', + phone: 'Téléphone', + textarea: 'Zone de texte', + textArea: 'Zone de texte', + user: 'Utilisateur', + rating: 'Évaluation', + boolean: 'Booléen', + reference: 'Référence', + identifier: 'Identifiant', + }, + fieldCategories: { + basic: 'Champs de base', + dates: 'Champs de date', + people: 'Personnes', + workflow: 'Flux de travail', + custom: 'Champs personnalisés', + }, + fields: { + title: { name: 'Titre', description: 'Titre de l’élément' }, + description: { name: 'Description', description: 'Description de l’élément' }, + status: { name: 'Statut', description: 'Statut actuel' }, + priority: { name: 'Priorité', description: 'Niveau de priorité' }, + type: { name: 'Type', description: 'Type d’élément' }, + assignee: { name: 'Attribué à', description: 'Utilisateur attribué' }, + reporter: { name: 'Rapporteur', description: 'Personne ayant signalé l’élément' }, + createdAt: { name: 'Créé le', description: 'Date de création de l’élément' }, + updatedAt: { name: 'Mis à jour le', description: 'Dernière mise à jour de l’élément' }, + dueDate: { name: 'Date d’échéance', description: 'Date d’échéance de l’élément' }, + startDate: { name: 'Date de début', description: 'Début des travaux' }, + estimate: { name: 'Estimation', description: 'Effort estimé' }, + labels: { name: 'Étiquettes', description: 'Étiquettes de l’élément' }, + sprint: { name: 'Sprint', description: 'Sprint associé' }, + iteration: { name: 'Itération', description: 'Itération associée (sprint, version, etc.)' }, + milestone: { name: 'Jalon', description: 'Jalon ciblé' }, + parent: { name: 'Parent', description: 'Élément parent' }, + children: { name: 'Enfants', description: 'Éléments enfants' }, + links: { name: 'Liens', description: 'Éléments liés' }, + attachments: { name: 'Pièces jointes', description: 'Fichiers joints' }, + comments: { name: 'Commentaires', description: 'Commentaires de discussion' }, + watchers: { name: 'Observateurs', description: 'Utilisateurs qui suivent cet élément' }, + }, + + // Sélecteur d'icône + iconAndColor: 'Icône et couleur', + searchIcons: 'Rechercher des icônes…', + icons: 'Icônes', + colors: 'Couleurs', + icon: 'Icône', + color: 'Couleur', + + // Combobox d'étiquettes + allLabels: 'Toutes les étiquettes', + selectLabels: 'Sélectionner des étiquettes', + noLabelsFoundFor: 'Aucune étiquette trouvée pour « {query} »', + labelCommaNotAllowed: 'Les noms d’étiquettes ne peuvent pas contenir de virgule', + + // Sélecteur de mention + mentionUsers: 'Mentionner des utilisateurs', + searching: 'Recherche en cours…', + noNotificationPersonalTask: 'Les tâches personnelles n’envoient pas de notifications', + + // Combobox de jalon + selectMilestone: 'Sélectionner un jalon', + selectMilestones: 'Sélectionner des jalons', + noMilestone: 'Aucun jalon', + milestones: 'Jalons', + milestonesSelected: '{count} jalons sélectionnés', + milestonesSelected_one: '{count} jalon sélectionné', + milestonesSelected_other: '{count} jalons sélectionnés', + noMilestonesFound: 'Aucun jalon trouvé', + showCompletedMilestones: 'Afficher les terminés', + + // Combobox d'itération + selectIteration: 'Sélectionner une itération', + noIteration: 'Aucune itération', + + // Sélecteur de priorité + selectPriority: 'Sélectionner une priorité', + noPriority: 'Aucune priorité', + loadingPriorities: 'Chargement des priorités…', + noPrioritiesConfigured: 'Aucune priorité configurée', + + // Sélecteur de projet + selectProject: 'Sélectionner un projet', + + // Sélecteur de dépôt + linkRepositories: 'Lier des dépôts', + selectRepositoriesFrom: 'Sélectionner des dépôts depuis {provider}', + searchRepositories: 'Rechercher des dépôts…', + loadingRepositories: 'Chargement des dépôts…', + noRepositoriesMatchSearch: 'Aucun dépôt ne correspond à votre recherche', + noRepositoriesAvailable: 'Aucun dépôt disponible', + alreadyLinked: 'Déjà lié', + linkSelected: 'Lier la sélection', + linking: 'Liaison en cours…', + repositoriesSelected: '{count} sélectionnés', + + // Sélecteur de rôle + selectRole: 'Sélectionner un rôle', + + // Sélecteur d'écran + selectScreen: 'Sélectionner un écran', + + // Sélecteur de cas de test + searchTestCases: 'Rechercher des cas de test…', + + // Sélecteur de flux de travail + selectWorkflow: 'Sélectionner un flux de travail', + + // Sélecteur de jeu de conditions + selectConditionSet: 'Sélectionner un jeu de conditions', + + // Sélecteur de jeu d'approbations + selectApprovalSet: 'Sélectionner un jeu d’approbations', + }, + + editors: { + enterText: 'Saisir du texte…', + selectDate: 'Sélectionner une date…', + clickToChangeColor: 'Cliquer pour changer la couleur', + saveEnter: 'Enregistrer (Entrée)', + cancelEscape: 'Annuler (Échap)', + availableFields: 'Champs disponibles', + selectedFields: 'Champs sélectionnés', + dragFieldsToAdd: 'Faites glisser les champs pour les ajouter', + dragToReorderOrDrop: 'Faites glisser pour réordonner ou déposer les champs ici', + dropFieldsHere: 'Déposez les champs ici pour les configurer', + noFieldsMatchSearch: 'Aucun champ ne correspond à votre recherche', + noFieldsAvailable: 'Aucun champ disponible', + allFieldsAdded: 'Tous les champs disponibles ont été ajoutés', + bold: 'Gras (Ctrl+B)', + italic: 'Italique (Ctrl+I)', + strikethrough: 'Barré', + inlineCode: 'Code en ligne', + bulletList: 'Liste à puces', + numberedList: 'Liste numérotée', + insertImage: 'Insérer une image', + userNotFound: 'Utilisateur introuvable', + insertDiagram: 'Insérer un schéma', + diagramEdit: 'Modifier le schéma', + diagramOpen: 'Ouvrir le schéma', + diagramUntitled: 'Schéma sans titre', + diagramNamePlaceholder: 'Nom du schéma', + diagramUnsaved: 'Modifications non enregistrées', + diagramUnsavedConfirm: 'Abandonner les modifications non enregistrées du schéma ?', + diagramDeleted: 'Le schéma a été supprimé', + diagramRenderError: 'Impossible d’afficher le schéma', + diagramLoadError: 'Impossible de charger le schéma', + diagramSaveError: 'Impossible d’enregistrer le schéma', + mermaidRendering: 'Rendu du schéma en cours', + mermaidParseError: 'Erreur d’analyse Mermaid', + mermaidEmpty: 'Bloc Mermaid vide', + }, + + dialogs: { + cancel: 'Annuler', + confirm: 'Confirmer', + save: 'Enregistrer', + close: 'Fermer', + delete: 'Supprimer', + update: 'Mettre à jour', + // Messages de confirmation pour les boîtes de dialogue confirm() + confirmations: { + deleteItem: 'Voulez-vous vraiment supprimer « {name} » ? Cette action est irréversible.', + deleteSection: 'Voulez-vous vraiment supprimer cette section ?', + discardChanges: 'Vous avez des modifications non enregistrées. Voulez-vous vraiment annuler ?', + dismissAllNotifications: + 'Voulez-vous vraiment ignorer toutes les notifications ? Cette action est irréversible.', + removeAvatar: 'Voulez-vous vraiment supprimer votre photo de profil ?', + revokeCalendarFeed: + 'Voulez-vous vraiment révoquer l’URL de votre flux de calendrier ? Tous les calendriers utilisant cette URL cesseront de se synchroniser.', + deleteTheme: 'Voulez-vous vraiment supprimer ce thème ? Cette action est irréversible.', + resetBoardConfig: + 'Voulez-vous vraiment réinitialiser la configuration du tableau par défaut ? Cela supprimera votre configuration personnalisée.', + deleteCustomField: + 'Voulez-vous vraiment supprimer le champ personnalisé « {name} » ? Cela le retirera de tous les projets.', + deleteLinkType: + 'Voulez-vous vraiment supprimer ce type de lien ? Cela supprimera également tous les liens de ce type.', + deleteAsset: 'Voulez-vous vraiment supprimer cet actif ?', + deleteAssetSet: + 'Voulez-vous vraiment supprimer cet ensemble d’actifs ? Cela supprimera tous les actifs, types et catégories qu’il contient.', + deleteAssetType: + 'Voulez-vous vraiment supprimer ce type d’actif ? Les actifs utilisant ce type n’auront plus de type assigné.', + deleteCategory: + 'Voulez-vous vraiment supprimer cette catégorie ? Les sous-catégories seront déplacées vers la catégorie parente.', + revokeRole: 'Voulez-vous vraiment révoquer ce rôle ?', + quitApplication: 'Voulez-vous vraiment quitter l’application ? Le serveur va s’arrêter.', + deleteConnection: + 'Voulez-vous vraiment supprimer cette connexion ? Cette action est irréversible.', + deleteWidget: 'Supprimer cette section ? Tous les widgets de cette section seront retirés.', + deleteScreen: + 'Voulez-vous vraiment supprimer l’écran « {name} » ? Cela affectera tous les espaces de travail utilisant cet écran.', + }, + // Messages d'alerte pour les boîtes de dialogue alert() + alerts: { + nameRequired: 'Le nom est obligatoire', + pleaseSelectImage: 'Veuillez sélectionner un fichier image', + timerAlreadyRunning: 'Un chronomètre est déjà en cours d’exécution. Veuillez l’arrêter avant d’en démarrer un nouveau.', + noTimerRunning: 'Aucun chronomètre n’est actuellement en cours d’exécution.', + timerSyncing: 'Le chronomètre est en cours de synchronisation. Veuillez patienter et réessayer.', + startTimerFromItem: 'Veuillez démarrer un chronomètre depuis un élément de travail pour fournir du contexte.', + cannotDeleteDefaultScreen: + 'Impossible de supprimer l’écran par défaut. Cet écran est requis pour les espaces de travail sans jeu de configuration.', + applicationShuttingDown: 'L’application est en cours d’arrêt…', + pdfExportComingSoon: 'L’exportation PDF arrive bientôt pour la vue par blocs de temps', + configUpdatedSuccess: + 'Jeu de configuration mis à jour avec succès. Tous les éléments de travail utilisent déjà les statuts du nouveau flux de travail.', + failedToSave: 'Échec de l’enregistrement : {error}', + failedToDelete: 'Échec de la suppression : {error}', + shutdownFailed: 'Échec de l’arrêt de l’application', + failedToUpdate: 'Échec de la mise à jour : {error}', + failedToLoad: 'Échec du chargement : {error}', + stopTimerFailed: 'Échec de l’arrêt du chronomètre', + failedToCreate: 'Échec de la création : {error}', + failedToUpload: 'Échec du téléversement : {error}', + failedToGeneratePdf: 'Échec de la génération du PDF. Veuillez réessayer.', + failedToApplyConfig: 'Échec de l’application du changement de configuration : {error}', + failedToAddManager: 'Échec de l’ajout du responsable : {error}', + failedToRemoveManager: 'Échec du retrait du responsable : {error}', + failedToSaveWorkspace: 'Échec de l’enregistrement du projet. Veuillez vérifier votre saisie et réessayer.', + failedToResetConfig: 'Échec de la réinitialisation de la configuration : {error}', + failedToToggleStatus: 'Échec du basculement du statut du type de lien : {error}', + failedToAssignRole: 'Échec de l’attribution du rôle : {error}', + failedToRevokeRole: 'Échec de la révocation du rôle : {error}', + failedToUpdateRole: 'Échec de la mise à jour du rôle de chacun : {error}', + failedToLoadFields: 'Échec du chargement des champs : {error}', + failedToSaveFields: 'Échec de l’enregistrement des attributions de champs : {error}', + errorAddingTestCase: 'Erreur lors de l’ajout du cas de test : {error}', + failedToCreateLabel: 'Échec de la création de l’étiquette : {error}', + failedToSaveLayout: 'Échec de l’enregistrement des modifications de disposition', + statusInUseByTransitions: + 'Impossible de supprimer « {name} » car il est utilisé dans {count} transitions du flux de travail. Pour supprimer ce statut, allez dans la gestion du flux de travail, retirez toutes les transitions qui l’utilisent, puis réessayez de supprimer le statut.', + statusInUseByTransitions_one: + 'Impossible de supprimer « {name} » car il est utilisé dans {count} transition du flux de travail. Pour supprimer ce statut, allez dans la gestion du flux de travail, retirez toutes les transitions qui l’utilisent, puis réessayez de supprimer le statut.', + statusInUseByTransitions_other: + 'Impossible de supprimer « {name} » car il est utilisé dans {count} transitions du flux de travail. Pour supprimer ce statut, allez dans la gestion du flux de travail, retirez toutes les transitions qui l’utilisent, puis réessayez de supprimer le statut.', + }, + }, + + components: { + // Composant Avatar + avatar: { + defaultAlt: 'Avatar', + }, + + // Composant DataTable + dataTable: { + showingRange: 'Affichage de {start} à {end} sur {total}', + }, + + // Composants de schéma + diagram: { + loading: 'Chargement des schémas…', + loadError: 'Échec du chargement des schémas', + deleteError: 'Échec de la suppression du schéma', + confirmDelete: 'Voulez-vous vraiment supprimer ce schéma ?', + edit: 'Modifier le schéma', + untitled: 'Schéma sans titre', + namePlaceholder: 'Nom du schéma', + nameRequired: 'Veuillez saisir un nom de schéma', + saveError: 'Échec de l’enregistrement du schéma', + unsavedChanges: 'Modifications non enregistrées', + unsavedChangesConfirm: 'Vous avez des modifications non enregistrées. Voulez-vous vraiment fermer ?', + }, + + // Composant ErrorState + errorState: { + title: 'Une erreur est survenue', + }, + + // Composant Pagination + pagination: { + showingRange: 'Affichage de {start} à {end} sur {total}', + limitedTo: 'limité à {max} éléments', + itemsPerPage: 'Éléments par page :', + previousPage: 'Page précédente', + nextPage: 'Page suivante', + goToPage: 'Aller à la page {page}', + pageOf: 'Page {current} sur {total}', + }, + + // Composant UserAvatar + userAvatar: { + myWorkspace: 'Mon espace de travail', + myWorkspaceSubtitle: 'Espace de travail personnel pour les tâches et notes', + profileSubtitle: 'Gérer votre profil et vos paramètres', + security: 'Sécurité', + securitySubtitle: 'Gérer les mots de passe, le 2FA et les jetons d’API', + themeTitle: 'Thème : {mode}', + themeLight: 'Clair', + themeDark: 'Sombre', + themeSystem: 'Système', + desktopSite: 'Version pour ordinateur', + addToHomeScreen: 'Ajouter à l’écran d’accueil', + }, + }, + + aria: { + close: 'Fermer', + dragToReorder: 'Faire glisser pour réordonner', + refresh: 'Actualiser', + removeField: 'Retirer le champ', + removeFromSection: 'Retirer de la section', + addNewStep: 'Ajouter une nouvelle étape', + removeCurrentStep: 'Retirer l’étape actuelle', + dismissNotification: 'Masquer la notification', + mainNavigation: 'Navigation principale', + mentionUsers: 'Mentionner des utilisateurs', + notifications: 'Notifications', + adminSettings: 'Paramètres d’administration', + userMenu: 'Menu utilisateur', + clearSearch: 'Effacer la recherche', + }, + + layout: { + addSection: 'Ajouter une section', + moveUp: 'Déplacer la section vers le haut', + moveDown: 'Déplacer la section vers le bas', + deleteSection: 'Supprimer la section', + editMode: 'Mode édition', + editDisplaySettings: 'Modifier les paramètres d’affichage', + items: 'éléments', + }, + + widgets: { + stats: { collections: 'Collections', totalItems: 'Total des éléments' }, + removeWidget: 'Retirer le widget', + defaultWidth: 'Par défaut : largeur {width}/{columns}', + widthQuarter: 'Quart', + widthThird: 'Tiers', + widthHalf: 'Moitié', + widthTwoThirds: 'Deux tiers', + widthFull: 'Plein écran', + resizeAriaLabel: 'Redimensionner le widget', + resizeColumnsValue: '{count} sur 12 colonnes', + rowCount: 'Nombre de lignes', + density: 'Densité', + densityComfortable: 'Aéré', + densityCompact: 'Compact', + rowCount5: '5 lignes', + rowCount10: '10 lignes', + rowCount15: '15 lignes', + rowCountAll: 'Toutes les lignes', + narrowWidth: 'Étroit (1/3 de largeur)', + mediumWidth: 'Moyen (2/3 de largeur)', + fullWidth: 'Pleine largeur', + chart: { + items: 'éléments', + noDataAvailable: 'Aucune donnée disponible', + }, + completionChart: { + title: 'Graphique de réalisation', + emptyMessage: 'Aucune donnée de réalisation disponible', + }, + createdChart: { + title: 'Graphique des créations', + emptyMessage: 'Aucune donnée de création disponible', + }, + recentItems: { + loadingText: 'Chargement des éléments récents…', + emptyTitle: 'Aucun élément récent', + emptySubtitle: 'Les éléments consultés récemment apparaîtront ici', + loadError: 'Échec du chargement des éléments récents', + }, + savedSearch: { + loadingCollections: 'Chargement des collections enregistrées…', + setupTitle: 'Choisir une collection enregistrée', + setupSubtitle: 'Sélectionnez une collection pour afficher ses éléments de travail ici.', + selectCollection: 'Sélectionner une collection', + noCollections: 'Aucune collection enregistrée disponible', + collectionUnavailable: 'Collection enregistrée indisponible', + itemCount: '{count} éléments', + emptyTitle: 'Aucun élément de travail correspondant', + emptySubtitle: 'Cette collection enregistrée ne contient aucun élément correspondant', + loadError: 'Échec du chargement de la collection enregistrée', + }, + milestoneProgress: { + emptyTitle: 'Aucun jalon', + emptySubtitle: 'Créez des jalons pour suivre la progression', + due: 'Échéance', + done: 'terminé', + item: 'élément', + items: 'éléments', + noItems: 'Aucun élément', + noStatus: 'Aucun statut', + activeMilestone: 'Actif', + noCategorizedWork: 'Aucun travail catégorisé', + chartAria: 'Répartition du statut des jalons', + }, + myTasks: { + loadingText: 'Chargement de vos tâches…', + emptyTitle: 'Aucune tâche ne vous est attribuée', + emptySubtitle: 'Les tâches qui vous sont attribuées apparaîtront ici', + loadError: 'Échec du chargement de vos tâches', + }, + overdueItems: { + loadingStatus: 'Chargement…', + itemCount: '{count} éléments en retard', + refreshAriaLabel: 'Actualiser les éléments en retard', + loadingText: 'Chargement des éléments en retard…', + emptyTitle: 'Aucun élément en retard', + emptySubtitle: 'Tous les éléments sont dans les temps', + loadError: 'Échec du chargement des éléments en retard', + daysOverdue: '{days}j de retard', + }, + upcomingDeadlines: { + loadingStatus: 'Chargement…', + itemCount: '{count} échéances à venir', + refreshAriaLabel: 'Actualiser les échéances à venir', + loadingText: 'Chargement des échéances à venir…', + emptyTitle: 'Aucune échéance à venir', + emptySubtitle: 'Les éléments avec des dates d’échéance apparaîtront ici', + loadError: 'Échec du chargement des échéances à venir', + }, + iterationTimeline: { + loadingStatus: 'Chargement…', + iterationCount: '{count} itérations', + refreshAriaLabel: 'Actualiser les itérations', + loadingText: 'Chargement des itérations…', + emptyTitle: 'Aucune itération active', + emptySubtitle: 'La chronologie des itérations apparaîtra ici', + loadError: 'Échec du chargement des itérations', + active: 'Active', + planned: 'Planifiée', + completed: 'Terminée', + }, + testCoverage: { + loading: 'Chargement des données de couverture…', + loadError: 'Échec du chargement des données de couverture', + emptyTitle: 'Aucune exigence configurée', + emptyDescription: 'Configurez les types d’exigences dans les rapports de test pour voir les données de couverture.', + chartAria: 'Répartition de la couverture des tests', + covered: 'Couvert', + notCovered: 'Non couvert', + requirementCount: '{covered} sur {total} exigences', + requirements: '{count} exigences', + requirements_other: '{count} exigences', + requirements_one: '{count} exigence', + linkedCases: 'ont des cas de test liés', + }, + }, + + recurrence: { + title: 'Récurrence', + description: 'Gérer les règles de tâches récurrentes', + frequency: 'Fréquence', + interval: 'Répéter tous les', + daily: 'Quotidien', + weekly: 'Hebdomadaire', + monthly: 'Mensuel', + yearly: 'Annuel', + daysOfWeek: 'Jours de la semaine', + dayOfMonth: 'Jour du mois', + endCondition: 'Se termine', + never: 'Jamais', + onDate: 'À la date', + afterOccurrences: 'Après un nombre d’occurrences', + occurrences: 'occurrences', + preview: 'Prochaines occurrences', + previewLoading: 'Chargement de l’aperçu…', + previewError: 'Échec du chargement de l’aperçu', + copySettings: 'Copier à partir du modèle', + copyAssignee: 'Copier l’attribué', + copyPriority: 'Copier la priorité', + copyCustomFields: 'Copier les champs personnalisés', + copyDescription: 'Copier la description', + leadTime: 'Générer à l’avance (jours)', + statusOnCreate: 'Statut à la création', + active: 'Actif', + inactive: 'Inactif', + instances: 'Instances générées', + instanceCount: '{count} instances', + forceGenerate: 'Générer maintenant', + generating: 'Génération en cours…', + generated: '{count} instances générées', + addRecurrence: 'Ajouter une récurrence', + noRule: 'Aucune récurrence définie', + setUp: 'Configurer la récurrence', + editRule: 'Modifier la récurrence', + deleteRule: 'Supprimer la récurrence', + deleteConfirm: 'Voulez-vous vraiment supprimer cette règle de récurrence ? Les instances générées ne seront pas affectées.', + startDate: 'Date de début', + endDate: 'Date de fin (optionnel)', + timezone: 'Fuseau horaire', + templateItem: 'Élément modèle', + scheduledDate: 'Date programmée', + sequenceNumber: 'Séquence', + noInstances: 'Aucune instance générée pour le moment', + settingsTab: 'Paramètres', + instancesTab: 'Instances', + searchPlaceholder: 'Rechercher des règles de récurrence…', + noMatchingResults: 'Aucune règle de récurrence ne correspond à votre recherche', + empty: 'Aucune règle de récurrence', + emptyDesc: 'Les règles de récurrence apparaîtront ici lorsque des plannings récurrents seront configurés sur des éléments.', + createFromItem: 'Pour créer une règle de récurrence, ouvrez un élément et configurez la récurrence dans la barre latérale des détails.', + rule: 'Règle', + everyDay: 'jour', + everyDays: 'jours', + everyWeek: 'semaine', + everyWeeks: 'semaines', + everyMonth: 'mois', + everyMonths: 'mois', + everyYear: 'an', + everyYears: 'ans', + mon: 'Lun', + tue: 'Mar', + wed: 'Mer', + thu: 'Jeu', + fri: 'Ven', + sat: 'Sam', + sun: 'Dim', + }, + + footer: { + platformName: 'Plateforme de gestion du travail Windshift', + aboutWindshift: 'À propos de Windshift', + apiReference: 'Référence API', + licenses: 'Licences', + reportProblem: 'Signaler un problème', + }, +}; \ No newline at end of file diff --git a/frontend/src/lib/locales/fr/workflows.js b/frontend/src/lib/locales/fr/workflows.js new file mode 100644 index 00000000..94d697d2 --- /dev/null +++ b/frontend/src/lib/locales/fr/workflows.js @@ -0,0 +1,376 @@ +/** + * Module de localisation pour les flux de travail (Français) + * Contient les traductions pour les statuts, les priorités, les flux de travail, les écrans et les catégories de statuts + */ + +export default { + statuses: { + title: 'Statuts', + subtitle: 'Gérer les statuts individuels', + status: 'Statut', + statuses: 'Statuts', + statusCategories: 'Catégories de statuts', + statuses_one: '{count} statut', + statuses_other: '{count} statuts', + createStatus: 'Créer un statut', + editStatus: 'Modifier le statut', + deleteStatus: 'Supprimer le statut', + statusName: 'Nom du statut', + statusCategory: 'Catégorie', + noStatuses: 'Aucun statut trouvé', + namePlaceholder: 'ex. À faire, En cours, Résolu', + transitionCount: 'Transitions : {count}', + defaults: { + open: { name: 'Ouvert', description: 'Nouvel élément de travail, pas encore commencé' }, + inProgress: { name: 'En cours', description: 'En cours de traitement' }, + done: { name: 'Terminé', description: 'Le travail a été effectué' }, + }, + statusCreated: 'Statut créé avec succès', + statusUpdated: 'Statut mis à jour avec succès', + statusDeleted: 'Statut supprimé avec succès', + }, + + priorities: { + title: 'Priorités', + subtitle: 'Configurer les niveaux de priorité avec des icônes et des couleurs', + priority: 'Priorité', + priorities_one: '{count} priorité', + priorities_other: '{count} priorités', + createPriority: 'Créer une priorité', + editPriority: 'Modifier la priorité', + deletePriority: 'Supprimer la priorité', + priorityName: 'Nom de la priorité', + noPriorities: 'Aucune priorité trouvée', + namePlaceholder: 'ex. Critique, Haute, Moyenne, Basse', + nameRequired: 'Le nom de la priorité est obligatoire.', + failedToLoad: 'Échec du chargement des priorités.', + failedToSave: 'Échec de l’enregistrement de la priorité.', + deleteFailed: 'Échec de la suppression de la priorité.', + confirmDelete: 'Supprimer « {name} » ? Cette action est irréversible.', + priorityCreated: 'Priorité créée avec succès', + priorityUpdated: 'Priorité mise à jour avec succès', + priorityDeleted: 'Priorité supprimée avec succès', + // Noms de priorités courants + critical: 'Critique', + high: 'Haute', + medium: 'Moyenne', + low: 'Basse', + lowest: 'La plus basse', + }, + + workflows: { + title: 'Flux de travail', + subtitle: 'Concevoir et gérer les transitions de flux de travail entre les statuts.', + workflow: 'Flux de travail', + workflows_one: '{count} flux de travail', + workflows_other: '{count} flux de travail', + count: '{count} flux de travail', + createWorkflow: 'Créer un flux de travail', + editWorkflow: 'Modifier le flux de travail', + deleteWorkflow: 'Supprimer le flux de travail', + workflowName: 'Nom du flux de travail', + transitions: 'Transitions', + addTransition: 'Ajouter une transition', + searchWorkflows: 'Rechercher des flux de travail…', + noWorkflows: 'Aucun flux de travail trouvé', + noWorkflowsFound: 'Aucun flux de travail trouvé. Créez votre premier flux de travail pour commencer.', + workflowCreated: 'Flux de travail créé avec succès', + workflowUpdated: 'Flux de travail mis à jour avec succès', + workflowDeleted: 'Flux de travail supprimé avec succès', + enterWorkflowName: 'Veuillez saisir un nom de flux de travail', + confirmDeleteWorkflow: + 'Voulez-vous vraiment supprimer « {name} » ? Cette action est irréversible.', + confirmDeleteTransition: 'Voulez-vous vraiment supprimer cette transition ?', + design: 'Conception', + noStatusesAvailable: 'Aucun statut disponible', + createStatusesFirst: 'Vous devez créer des statuts avant de pouvoir créer des flux de travail.', + manageStatuses: 'Gérer les statuts', + workflowNamePlaceholder: 'ex. Flux de travail par défaut', + descriptionPlaceholder: 'Description optionnelle pour ce flux de travail', + setAsDefault: 'Définir comme flux de travail par défaut', + defaults: { default: { name: 'Flux de travail par défaut', description: 'Flux de travail de base pour accomplir les tâches' } }, + backToWorkflows: 'Retour aux flux de travail', + workflowDesigner: 'Concepteur de flux de travail', + designerHint: + 'Faites glisser les statuts depuis le panneau de gauche, connectez-les en faisant glisser depuis les points de connexion', + failedToLoadDesigner: + 'Échec du chargement du concepteur de flux de travail. Veuillez rafraîchir la page et réessayer.', + loadingWorkflowData: 'Chargement des données du flux de travail…', + loadingDesigner: 'Chargement du concepteur de flux de travail…', + failedToLoadDesignerTitle: 'Échec du chargement du concepteur de flux de travail', + refreshAndTryAgain: 'Veuillez rafraîchir la page et réessayer', + availableStatuses: 'Statuts disponibles', + transitionHintTitle: 'Comment créer des transitions :', + transitionHint1: 'Faites glisser d’un statut à un autre pour créer une transition', + transitionHint2: 'Les flèches indiquent la direction : statut DEPART -> statut ARRIVEE', + transitionHint3: 'Cliquez et faites glisser les liaisons pour les reconnecter', + allStatusesAdded: 'Tous les statuts ont été ajoutés au flux de travail', + statusesInWorkflow: '{count} statuts dans le flux de travail', + transitionsDefined: '{count} transitions définies', + startDesigning: 'Commencez à concevoir votre flux de travail', + clickStatusesToAdd: 'Cliquez sur les statuts dans le panneau de gauche pour les ajouter à la zone de travail', + connectByDragging: 'Connectez les statuts en faisant glisser depuis les points de connexion', + saveWorkflow: 'Enregistrer le flux de travail', + failedToSaveDesign: 'Échec de l’enregistrement de la conception du flux de travail', + initialStatus: 'Statut initial', + setAsInitialStatus: 'Définir comme statut initial', + initial: 'Initial', + setStart: 'Définir le début', + removeFromWorkflow: 'Retirer du flux de travail', + swapDirection: 'Inverser la direction', + allStatuses: 'Tous', + fromAllStatuses: 'Autoriser les transitions depuis tous les autres statuts', + transitionHint4: 'Cochez « Tous » sur un statut pour autoriser les transitions depuis n’importe quel autre statut', + }, + + screens: { + title: 'Écrans', + subtitle: 'Définir les dispositions de champs pour les demandes et les projets', + screen: 'Écran', + screens: '{count} écrans', + screens_one: '{count} écran', + screens_other: '{count} écrans', + createScreen: 'Créer un écran', + editScreen: 'Modifier l’écran', + deleteScreen: 'Supprimer l’écran', + screenName: 'Nom de l’écran', + noScreens: 'Aucun écran trouvé', + screenCreated: 'Écran créé avec succès', + screenUpdated: 'Écran mis à jour avec succès', + screenDeleted: 'Écran supprimé avec succès', + cannotDeleteDefault: 'Impossible de supprimer l’écran par défaut', + }, + + screensPage: { + availableFields: 'Champs disponibles', + dragFieldsHint: 'Faites glisser les champs vers la configuration de l’écran sur la droite', + searchFields: 'Rechercher des champs…', + systemFields: 'Champs système', + customFields: 'Champs personnalisés', + noFieldsMatch: 'Aucun champ ne correspond à votre recherche', + allFieldsAdded: 'Tous les champs disponibles ont été ajoutés à l’écran', + screenFields: 'Champs de l’écran', + dragToReorder: 'Faites glisser pour réordonner les champs ou déposez les champs disponibles ici', + dropFieldsHere: 'Déposez les champs ici pour configurer l’écran', + required: 'Obligatoire', + removeField: 'Retirer le champ', + editScreen: 'Modifier l’écran', + createScreen: 'Créer un écran', + screenName: 'Nom de l’écran', + screenNamePlaceholder: 'ex. Formulaire de demande standard, Formulaire de rapport de bogue', + description: 'Description', + optionalDescription: 'Description optionnelle pour cet écran', + addScreen: 'Ajouter un écran', + updateScreen: 'Mettre à jour l’écran', + configureFields: 'Configurer les champs', + screen: 'Écran', + screenSubtitle: 'Écran : {name}', + saveFields: 'Enregistrer les champs', + system: 'système', + custom: 'personnalisé', + fields: 'Champs', + noScreens: 'Aucun écran trouvé. Créez votre premier écran pour commencer.', + cannotDeleteDefault: + 'Impossible de supprimer l’écran par défaut. Cet écran est requis pour les espaces de travail sans jeu de configuration.', + confirmDeleteScreen: + 'Voulez-vous vraiment supprimer l’écran « {name} » ? Cela affectera tous les espaces de travail utilisant cet écran.', + defaults: { default: { name: 'Écran par défaut', description: 'Écran par défaut avec les champs essentiels des éléments de travail' } }, + fieldWidths: { + full: 'Pleine largeur', + half: 'Demi-largeur', + third: 'Un tiers', + quarter: 'Un quart', + }, + }, + + conditionSets: { + title: 'Jeux de conditions', + subtitle: 'Définir des conditions qui restreignent les transitions du flux de travail', + add: 'Ajouter un jeu de conditions', + new: 'Nouveau jeu de conditions', + empty: 'Aucun jeu de conditions', + emptyDesc: 'Créez des jeux de conditions pour restreindre les transitions de flux de travail en fonction des rôles d’utilisateur, des groupes, des valeurs de champs ou des scripts.', + createFirst: 'Créer un jeu de conditions', + searchPlaceholder: 'Rechercher des jeux de conditions…', + noMatchingResults: 'Aucun jeu de conditions ne correspond à votre recherche.', + workflow: 'Flux de travail', + transitions: 'Transitions', + general: 'Général', + name: 'Nom', + namePlaceholder: 'ex. Restrictions développeur', + description: 'Description', + descriptionPlaceholder: 'Description optionnelle pour ce jeu de conditions', + detailDesc: 'Configurer les conditions qui restreignent des transitions spécifiques du flux de travail', + backToList: 'Retour aux jeux de conditions', + selectWorkflow: 'Sélectionner un flux de travail…', + selectWorkflowFirst: 'Sélectionnez un flux de travail pour activer la sélection du jeu de conditions.', + workflowLocked: 'Le flux de travail ne peut plus être modifié après la création.', + workflowRequired: 'Un flux de travail est requis.', + nameRequired: 'Le nom est obligatoire.', + changeWorkflow: 'Changer de flux de travail', + changeWorkflowConfirm: 'Changer de flux de travail supprimera toutes les conditions de transition existantes. Continuer ?', + transitionConditions: 'Conditions de transition', + transitionConditionsDesc: 'Ajouter des conditions à des transitions spécifiques pour restreindre les personnes pouvant les exécuter.', + unconditionedTransitions: 'Transitions sans conditions', + addConditions: 'Ajouter des conditions', + addCondition: 'Ajouter une condition', + removeConditions: 'Retirer toutes les conditions', + removeCondition: 'Retirer la condition', + logicMode: 'Mode logique', + type: 'Type', + role: 'Rôle', + group: 'Groupe', + selectGroup: 'Sélectionner un groupe…', + fieldIdentifier: 'Identifiant du champ', + pattern: 'Motif Regex', + script: 'Script', + scriptHelp: 'JavaScript exécuté via Sobek. Variables disponibles : item, user_id. Doit retourner un booléen.', + scriptReference: 'Référence du script', + scriptReferenceDesc: 'Variables et propriétés disponibles dans les conditions de script.', + scriptRefVariable: 'Variable', + scriptRefType: 'Type', + scriptRefDescription: 'Description', + scriptRefAlwaysPresent: 'Toujours présent', + scriptRefIfSet: 'Si défini', + scriptRefGlobals: 'Variables globales', + scriptRefItemProps: 'Propriétés de l’élément', + noTransitions: 'Ce flux de travail n’a aucune transition. Concevez d’abord le flux de travail.', + userEvaluated: 'Utilisateur', + againstRole: 'Rôle', + againstGroup: 'Groupe', + selectRole: 'Sélectionner un rôle…', + userField: 'Champ utilisateur', + selectUserField: 'Sélectionner un champ utilisateur…', + conditionMode: 'Mode', + modeCondition: 'Condition', + modeValidator: 'Validateur', + modeConditionDesc: 'Masque la transition si la règle échoue', + modeValidatorDesc: 'Affiche une erreur lors de la tentative de transition', + errorMessage: 'Message d’erreur', + errorMessagePlaceholder: 'ex. Seuls les testeurs peuvent déplacer des éléments vers QA', + errorMessageHelp: 'Affiché à l’utilisateur lorsque cette condition échoue. Laissez vide pour le message par défaut.', + }, + + approvalSets: { + title: 'Jeux d’approbations', + subtitle: 'Approbations multi-étapes qui contrôlent les transitions sortantes d’un statut.', + add: 'Ajouter un jeu d’approbations', + new: 'Nouveau jeu d’approbations', + empty: 'Aucun jeu d’approbations', + emptyDesc: 'Les jeux d’approbations se lient à un statut et configurent les deux transitions pilotées par une approbation (approuver et refuser).', + createFirst: 'Créer un jeu d’approbations', + searchPlaceholder: 'Rechercher des jeux d’approbations…', + noMatchingResults: 'Aucun jeu d’approbations ne correspond à votre recherche.', + workflow: 'Flux de travail', + statuses: 'Statuts', + general: 'Général', + name: 'Nom', + namePlaceholder: 'ex. Approbation standard de version', + description: 'Description', + descriptionPlaceholder: 'Description optionnelle pour ce jeu d’approbations', + detailDesc: 'Configurer des approbations multi-étapes liées au statut.', + backToList: 'Retour aux jeux d’approbations', + selectWorkflow: 'Sélectionner un flux de travail…', + workflowLocked: 'Le flux de travail ne peut plus être modifié après la création.', + workflowRequired: 'Un flux de travail est requis.', + nameRequired: 'Le nom est obligatoire.', + + setStatuses: 'Statuts d’approbation', + setStatusesDesc: 'Lorsqu’un élément entre dans l’un de ces statuts, une demande d’approbation s’ouvre. Les transitions approuver/refuser configurées ne peuvent plus être invoquées directement par les utilisateurs — seul le moteur d’approbation les pilote.', + addStatus: 'Ajouter un statut d’approbation', + removeStatus: 'Retirer le statut', + status: 'Statut', + selectStatus: 'Sélectionner un statut…', + approveTransition: 'En cas d’approbation, passer à', + denyTransition: 'En cas de refus, passer à', + selectTransition: 'Sélectionner une transition…', + transitionsMustExitStatus: 'Les transitions d’approbation et de refus doivent provenir du statut configuré.', + transitionsMustDiffer: 'Les transitions d’approbation et de refus doivent être différentes.', + stepMode: 'Mode d’étape', + stepModeSequential: 'Séquentiel', + stepModeParallel: 'Parallèle', + stepModeSequentialDesc: 'Les étapes s’exécutent l’une après l’autre. Chaque étape ne s’ouvre que lorsque la précédente est approuvée.', + stepModeParallelDesc: 'Toutes les étapes s’ouvrent en même temps. La demande est approuvée uniquement lorsque toutes les étapes sont approuvées ; elle est rejetée au premier refus.', + + steps: 'Étapes', + addStep: 'Ajouter une étape', + removeStep: 'Retirer l’étape', + stepName: 'Nom de l’étape', + stepNamePlaceholder: 'ex. Relecteur principal', + stepNameRequired: 'Le nom de l’étape est obligatoire.', + + quorum: 'Quorum', + quorumAny: 'Quelconque (un seul approbateur suffit)', + quorumAll: 'Tous (chaque approbateur doit approuver)', + quorumCount: 'Nombre (exactement N approbateurs)', + quorumPercent: 'Pourcentage (N % du groupe, arrondi au supérieur)', + quorumCountValue: 'Nombre', + quorumPercentValue: 'Pourcentage (1–100)', + rejectionPolicy: 'Rejet', + rejectionAnyFails: 'Tout rejet fait échouer l’étape', + rejectionRequiresQuorum: 'Nécessite le quorum pour échouer', + + approverSource: 'Source de l’approbateur', + approverSourceCreator: 'Créateur de l’élément', + approverSourceAssignee: 'Personne actuellement attribuée', + approverSourceCurrentUser: 'Personne ayant déclenché l’approbation', + approverSourceUser: 'Utilisateur spécifique', + approverSourceRegularField: 'Champ standard (assignee_id, creator_id, …)', + approverSourceCustomField: 'Champ personnalisé (de type utilisateur)', + approverSourceRole: 'Rôle d’espace de travail', + approverSourceGroup: 'Groupe', + approverFieldIdentifier: 'Identifiant du champ', + approverFieldIdentifierHelp: 'Champs autorisés : assignee_id, creator_id, reporter_id.', + approverFieldId: 'Champ personnalisé', + approverRole: 'Rôle', + approverGroup: 'Groupe', + approverUser: 'Utilisateur', + allowSelfApproval: 'Autoriser l’auto-approbation — permettre au même utilisateur de changer l’élément d’état et de l’approuver', + + onLeave: 'En cas d’absence de l’approbateur', + onLeaveUseSubstitute: 'Utiliser un remplaçant (par défaut)', + onLeaveUseSubstituteDesc: 'Reste conforme au remplaçant défini sur le registre d’absence. Bascule sur l’escalade si aucun remplaçant n’est configuré.', + onLeaveSkip: 'Ignorer l’approbateur', + onLeaveSkipDesc: 'Retirer du groupe l’utilisateur absent. Déclenche une escalade si le groupe devient vide.', + onLeaveKeep: 'Conserver l’approbateur', + onLeaveKeepDesc: 'Le maintient dans le groupe. L’approbateur d’origine doit répondre malgré son absence.', + + fallbackTitle: 'Si l’approbateur ne peut pas décider', + fallbackDesc: 'Ces deux règles forment une chaîne de secours unique. La première gère les approbateurs en congé planifié ; la seconde sert de filet de sécurité lorsque la gestion des absences échoue ou qu’aucune décision n’intervient à temps.', + fallbackConnector: 'Si la règle d’absence ci-dessus produit un groupe vide (pas de remplaçant ou l’exclusion vide le groupe), l’escalade se déclenche immédiatement sans attendre la minuterie.', + + escalation: 'Lorsqu’aucune décision n’intervient à temps', + escalationDisabled: 'Pas d’escalade', + escalationDisabledDesc: 'Attendre indéfiniment. L’étape ne se résout jamais automatiquement ; un administrateur doit intervenir.', + escalationActionReassignDesc: 'Transférer l’étape à une autre personne, rôle ou groupe. Se réarme après chaque délai consécutif jusqu’à max_escalations.', + escalationActionSkipStepDesc: 'Marquer l’étape comme approuvée et laisser la demande se poursuivre dans la chaîne.', + escalationActionAutoRejectDesc: 'Rejeter la demande et déclencher la transition de refus configurée.', + escalationAfterHours: 'Escalader après (heures)', + escalationAction: 'En cas d’escalade', + escalationActionReassign: 'Réattribuer à une cible', + escalationActionSkipStep: 'Ignorer l’étape (traiter comme approuvée)', + escalationActionAutoReject: 'Rejeter automatiquement la demande', + escalationTarget: 'Cible d’escalade', + maxEscalations: 'Nombre max d’escalades (vide = illimité)', + chainedNote: 'En cas de réattribution, escalation_due_at se réarme après chaque délai jusqu’à cette limite.', + + overrideWarningTitle: 'Attention : cette transition est également régie par des conditions', + overrideWarningBody: 'Les conditions sur une transition pilotée par une approbation sont ignorées silencieusement lorsque le moteur d’approbation déclenche la transition. Les conditions s’exécutent toujours pour les tentatives directes des utilisateurs (que l’approbation bloquera de toute façon). Pour éviter les surprises, envisagez de retirer ces conditions ou de les déplacer vers la transition d’entrée.', + overrideWarningLearnMore: 'Voir les règles de conditions affectées', + overrideWarningCondReverseTitle: 'Attention : un jeu d’approbations pilote cette transition', + overrideWarningCondReverseBody: 'Lorsqu’une approbation est finalisée, la transition configurée se déclenche sans réexécuter les conditions. Les conditions sur cette transition ne s’appliquent qu’aux tentatives directes des utilisateurs (que les approbations bloquent de toute façon). Déplacez plutôt les règles d’entrée vers la transition qui MÈNE au statut d’approbation.', + }, + + statusCategory: { + editStatusCategory: 'Modifier la catégorie de statut', + createStatusCategory: 'Créer une catégorie de statut', + namePlaceholder: 'ex. À faire, En cours, Terminé', + color: 'Couleur', + setAsDefault: 'Définir comme catégorie par défaut', + marksWorkCompleted: 'Marque le travail comme terminé', + marksWorkCompletedHelp: + 'Les éléments déplacés vers des statuts de cette catégorie seront traités comme terminés dans les rapports et revues.', + updateCategory: 'Mettre à jour la catégorie', + createCategory: 'Créer la catégorie', + }, +}; \ No newline at end of file diff --git a/frontend/src/lib/locales/fr/workspace.js b/frontend/src/lib/locales/fr/workspace.js new file mode 100644 index 00000000..831f254b --- /dev/null +++ b/frontend/src/lib/locales/fr/workspace.js @@ -0,0 +1,945 @@ +export default { + workspaces: { + title: 'Espaces de travail', + subtitle: 'Gérez vos espaces de travail et vos projets', + workspace: 'Espace de travail', + template: 'Modèle', + workspaces_one: '{count} espace de travail', + workspaces_other: '{count} espaces de travail', + createWorkspace: 'Créer un espace de travail', + editWorkspace: 'Modifier l’espace de travail', + deleteWorkspace: 'Supprimer l’espace de travail', + switchWorkspace: 'Changer d’espace de travail', + workspaceName: 'Nom de l’espace de travail', + workspaceKey: 'Clé de l’espace de travail', + workspaceDescription: 'Description', + members: 'Membres', + settings: 'Paramètres de l’espace de travail', + noWorkspaces: 'Aucun espace de travail trouvé', + listSubtitle: 'Organisez et gérez vos projets au sein des espaces de travail', + empty: 'Aucun espace de travail trouvé. Créez votre premier espace de travail pour commencer.', + personal: 'Personnel', + confirmDelete: 'Voulez-vous vraiment supprimer l’espace de travail « {name} » ? Cela affectera tous les projets associés.', + selectWorkspace: 'Sélectionner un espace de travail', + currentWorkspace: 'Espace de travail actuel', + workspaceCreated: 'Espace de travail créé avec succès', + workspaceUpdated: 'Espace de travail mis à jour avec succès', + workspaceDeleted: 'Espace de travail supprimé avec succès', + customers: { + title: 'Clients', + subtitle: 'Gérer les clients du portail et les organisations', + addCustomer: 'Ajouter un client', + unassignedCustomers: 'Clients non assignés', + customerCount: '{count} clients', + customerCount_one: '{count} client', + customerCount_other: '{count} clients', + failedToLoadCustomers: 'Échec du chargement des clients', + failedToLoadOrganisations: 'Échec du chargement des organisations', + failedToAssignCustomer: 'Échec de l’assignation du client à l’organisation', + deleteCustomer: 'Supprimer le client', + confirmDeleteCustomer: 'Voulez-vous vraiment supprimer « {name} » ?', + manageOrganisations: 'Gérer les organisations', + searchOrganisations: 'Rechercher des organisations...', + noOrganisationsFound: 'Aucune organisation trouvée', + noCustomersFound: 'Aucun client trouvé', + unassigned: 'Non assigné', + allCustomersAssigned: 'Tous les clients sont assignés à des organisations', + searchCustomers: 'Rechercher des clients...', + tryAdjustingSearch: 'Essayez d’ajuster votre recherche', + dragCustomersHere: 'Faites glisser les clients ici pour les assigner à cette organisation', + noOrgFiles: 'Les documents associés à cette organisation apparaîtront ici.', + noOrgTickets: 'Les tickets créés par les contacts de cette organisation apparaîtront ici.', + contact: 'Contact', + linked: 'Lié : ', + loadMore: 'Charger plus ({count} restants)', + searchHintOrganisations: 'Utilisez la recherche pour trouver rapidement une organisation', + addPortalCustomer: 'Ajouter un client du portail', + editPortalCustomer: 'Modifier le client du portail', + createCustomer: 'Créer un client', + noneUnassigned: 'Aucun (Non assigné)', + customFields: 'Champs personnalisés', + fields: { + name: 'Nom', + email: 'E-mail', + phone: 'Téléphone', + customerOrganisation: 'Organisation du client', + }, + placeholders: { + name: 'Entrez le nom du client', + email: 'client@exemple.com', + phone: '+1 (555) 123-4567', + }, + metadata: { + created: 'Créé :', + updated: 'Mis à jour :', + linkedUser: 'Utilisateur lié :', + }, + contacts: 'Contacts', + backToCustomers: 'Retour aux clients', + failedToLoadCustomer: 'Échec du chargement du client', + submissions: 'Soumissions', + channels: 'Canaux', + noSubmissions: 'Aucune soumission pour le moment', + noChannels: 'Aucun canal pour le moment', + }, + }, + + items: { + title: 'Éléments', + subtitle: 'Afficher et gérer les éléments de travail', + item: 'Élément', + items_one: '{count} élément', + items_other: '{count} éléments', + createItem: 'Créer un élément', + editItem: 'Modifier l’élément', + deleteItem: 'Supprimer l’élément', + viewItem: 'Afficher l’élément', + itemKey: 'Clé', + itemTitle: 'Titre', + itemDescription: 'Description', + itemType: 'Type d’élément', + itemStatus: 'Statut', + itemPriority: 'Priorité', + assignee: 'Assigné à', + reporter: 'Rapporteur', + dueDate: 'Date d’échéance', + startDate: 'Date de début', + estimate: 'Estimation', + timeSpent: 'Temps passé', + remaining: 'Restant', + parent: 'Parent', + children: 'Enfants', + subtasks: 'Sous-tâches', + linkedItems: 'Éléments liés', + attachments: 'Pièces jointes', + comments: 'Commentaires', + activity: 'Activité', + history: 'Historique', + selectAssignee: 'Sélectionner l’assigné', + inheritProject: 'Hériter du projet', + inheritProjectFrom: 'Hériter de {name}', + projectInherited: '{name} (hérité)', + noProject: 'Aucun projet', + setProject: 'Sélectionner un projet', + failedToSaveLabels: 'Échec de l’enregistrement des étiquettes', + approvals: { + title: 'Approbations', + actionRequired: 'Décision requise', + statuses: { pending: 'En attente', approved: 'Approuvé', rejected: 'Rejeté', cancelled: 'Annulé' }, + decisionRequiredHint: 'En attente d’approbation — votre décision est requise', + transitionsHiddenHint: 'En attente d’approbation — les transitions restreintes sont masquées', + approveTitle: 'Approuver la demande ?', + rejectTitle: 'Rejeter la demande ?', + approveMessage: 'L’approbation exécutera la transition d’approbation configurée pour cet élément.', + rejectMessage: 'Le rejet exécutera la transition de rejet configurée pour cet élément.', + approve: 'Approuver', + reject: 'Rejeter', + decisionRecorded: 'Décision enregistrée : {decision}', + cancelTitle: 'Annuler la demande d’approbation ?', + cancelMessage: 'L’élément reviendra à son statut précédent et la demande sera annulée.', + cancelRequest: 'Annuler la demande', + keepOpen: 'Maintenir ouvert', + cancelledSuccess: 'Approbation annulée ; l’élément est revenu à son statut précédent', + decisions: { + approve: 'a approuvé', reject: 'a rejeté', comment: 'a commenté', cancel: 'a annulé la demande', + delegate: 'a délégué à l’utilisateur #{userId}', reassign: 'a réassigné les approbateurs', escalate: 'a été escaladé', + substitute: 'a utilisé un remplaçant', requested: 'a ouvert la demande', completed: 'a finalisé la demande', + }, + loading: 'Chargement des approbations…', + emptyTitle: 'Aucune approbation', + emptyDescription: 'Il n’y a eu aucune activité d’approbation pour cet élément.', + requestNumber: 'Approbation #{id}', + opened: 'Ouvert le {date}', + closed: 'Fermé le {date}', + stepNumber: 'Étape {number}', + escalatedCount: '↑ escaladé {count}×', + approvers: 'Approbateurs', + noEligibleApprovers: 'Aucun approbateur éligible n’a été trouvé.', + selfApprovalHint: 'Vous avez ouvert cette demande. Si vous êtes également l’approbateur configuré, l’auto-approbation peut être désactivée. Annulez-la et demandez à un autre utilisateur de la rouvrir, ou activez d’abord l’auto-approbation.', + approverSourceHint: 'Vérifiez la source d’approbateur pour cette étape, corrigez-la, puis annulez et rouvrez la demande.', + escalates: 'Escalade le {date}', + decisionRequired: 'Votre décision est requise', + comment: 'Commentaire', + commentPlaceholder: 'Commentaire optionnel…', + auditLog: 'Journal d’audit', + userNumber: 'Utilisateur #{id}', + system: 'Système', + }, + noItems: 'Aucun élément trouvé', + noItemsInFilter: 'Aucun élément ne correspond au filtre actuel', + createToStart: 'Créez un élément pour commencer', + itemCreated: 'Élément créé avec succès', + itemUpdated: 'Élément mis à jour avec succès', + itemDeleted: 'Élément supprimé avec succès', + assignedToYou: 'Assigné à vous', + createdByYou: 'Créé par vous', + recentlyViewed: 'Récemment consultés', + recentlyUpdated: 'Récemment mis à jour', + // Item Detail Tabs + timeTracking: 'Suivi du temps', + details: 'Détails', + agentLog: 'Journal d’agent', + agentLogEmpty: 'Aucune exécution d’agent pour cet élément pour le moment', + agentLogTriggeredBy: 'déclenché par', + agentLogWaiting: 'En attente de résultat…', + agentRunSingular: 'exécution', + agentRunPlural: 'exécutions', + agentRerunLabel: 'Réexécuter l’agent', + agentRerunStarting: 'Démarrage…', + agentRerunBusy: 'Exécution en cours', + agentRerunTitle: 'Exécuter à nouveau l’agent sur cet élément', + agentRerunFailed: 'Impossible de démarrer une nouvelle exécution', + agentReviewFlagTitle: 'Nécessite une révision humaine', + agentReviewFlagBody: 'L’agent a mal utilisé un outil et ne s’est pas rétabli — son résultat peut ne pas être fiable.', + created: 'Créé', + lastUpdated: 'Dernière mise à jour', + healthOverview: 'Santé de l’élément', + healthOverviewDescription: 'Signaux basés sur l’activité récente, la date d’échéance et l’historique des statuts.', + activityHealthCompleted: 'Terminé', + activityHealthUnknown: 'Inconnu', + activityHealthStale: 'Obsolète', + activityHealthActive: 'Actif', + activityMonitoringComplete: 'Surveillance de l’activité terminée', + activityToday: 'Actif aujourd’hui', + activityIdleDays: '{count} jours inactif', + activityIdleDays_one: '{count} jour inactif', + activityIdleDays_other: '{count} jours inactifs', + lastActivityAt: 'Dernière activité le {date}', + activityUnavailable: 'Horodatage d’activité indisponible', + dueHealthCompleted: 'Terminé', + dueHealthUnscheduled: 'Non planifié', + dueHealthOverdue: 'En retard', + dueHealthToday: 'À échéance aujourd’hui', + dueHealthSoon: 'À échéance bientôt', + dueHealthScheduled: 'Planifié', + workCompleted: 'Travail terminé', + completedOn: 'Terminé le {date}', + dueOn: 'Échéance le {date}', + dueDateUnavailable: 'Aucune date d’échéance n’est définie', + timeInStatus: 'Temps dans le statut', + unknown: 'Inconnu', + statusSince: 'Dans ce statut depuis le {date}', + statusHistoryUnavailable: 'La chronologie des statuts n’est pas disponible', + statusDurationsDescription: 'Temps réel total accumulé dans chaque statut.', + statusDurationsLoading: 'Calcul des durées de statut…', + statusDurationsLoadError: 'Impossible de calculer les durées de statut.', + statusDurationsRetry: 'Réessayer', + statusDurationsEmpty: 'Aucun historique de statut disponible pour le moment.', + currentStatus: 'Actuel', + durationLessThanMinute: '< 1m', + timeline: 'Chronologie', + by: 'par', + workItemInformation: 'Informations sur l’élément de travail', + id: 'ID', + type: 'Type', + workItem: 'Élément de travail', + noProjectConfigured: 'Aucun projet configuré pour le suivi du temps', + setDefaultProject: 'Définissez un projet par défaut dans les paramètres de l’espace de travail ou de l’élément pour enregistrer du temps', + timeEntries: 'Saisies de temps', + startTimer: 'Démarrer le minuteur', + logTime: 'Saisir du temps', + startTimerTitle: 'Démarrer le suivi du temps pour cet élément de travail', + logTimeTitle: 'Saisir manuellement le temps travaillé sur cet élément', + noTimeLogged: 'Aucun temps enregistré pour le moment', + logged: 'enregistré', + loggedOf: 'enregistré sur', + estimated: 'estimé', + includeChildItems: 'Inclure les éléments enfants', + rollupTruncated: 'Limité aux {max} premiers éléments enfants', + rollupItemCount: 'cumulé sur {count} éléments', + rollupItemCount_one: 'cumulé sur {count} élément', + rollupItemCount_other: 'cumulé sur {count} éléments', + + // Item Detail additional translations + workItemDetails: 'Détails de l’élément de travail', + fullDetails: 'Tous les détails', + errorLoadingWorkItem: 'Erreur lors du chargement de l’élément de travail', + workItemNotFound: 'Élément de travail introuvable', + timerBusy: 'Minuteur occupé', + timerSyncingMessage: 'Le minuteur est en cours de synchronisation, veuillez patienter un instant et réessayer.', + timerAlreadyRunning: 'Le minuteur est déjà en cours d’exécution', + stopTimerFirst: 'Veuillez arrêter le minuteur actuel avant d’en démarrer un nouveau.', + workingOn: 'Travail sur {title}', + failedToStartTimer: 'Échec du démarrage du minuteur', + failedToSaveTimeEntry: 'Échec de l’enregistrement de la saisie de temps', + failedToDeleteTimeEntry: 'Échec de la suppression de la saisie de temps', + deleteTimeEntry: 'Supprimer la saisie de temps', + deleteTimeEntryConfirm: + 'Voulez-vous vraiment supprimer cette saisie de temps ? Cette action ne peut pas être annulée.', + noDescription: 'Aucune description', + itemCopiedAs: 'Élément de travail copié avec succès sous {key}', + clickToViewCopied: 'Cliquer pour afficher l’élément copié', + failedToCopy: 'Échec de la copie de l’élément', + moveWorkspaceMenu: 'Déplacer vers l’espace de travail', + moveWorkspaceTitle: 'Déplacer « {title} »', + moveWorkspaceSubtitle: 'Choisissez la configuration de destination, puis vérifiez chaque champ avant de déplacer.', + moveWorkspaceDestination: 'Espace de travail de destination', + moveWorkspaceDestinationPlaceholder: 'Sélectionner un espace de travail…', + moveWorkspaceLoadingPreview: 'Génération de l’aperçu exact du déplacement…', + moveWorkspaceNewKeyHint: 'Le prochain numéro disponible dans la destination est attribué lorsque vous confirmez.', + moveWorkspaceChildrenDetached: '{count} enfants directs deviendront des racines dans l’espace de travail d’origine', + moveWorkspaceDeadKeyWarning: '{key} cessera de fonctionner et ne sera jamais réutilisée.', + moveWorkspaceNoPriority: 'Aucune priorité', + moveWorkspaceConfirm: 'Déplacer l’élément', + moveWorkspaceCancel: 'Annuler', + moveWorkspaceMoving: 'Déplacement…', + moveWorkspaceSuccess: 'Élément déplacé sous {key}', + moveWorkspaceLoadError: 'Échec du chargement des espaces de travail de destination', + moveWorkspacePreviewError: 'Échec de la génération de l’aperçu du déplacement', + moveWorkspaceError: 'Échec du déplacement de l’élément', + deleteWorkItem: 'Supprimer l’élément de travail', + confirmDeleteItem: 'Voulez-vous vraiment supprimer « {title} » ? Cette action ne peut pas être annulée.', + failedToDelete: 'Échec de la suppression de l’élément', + + // Cascade delete dialog + deleteItemWithChildren: 'Supprimer l’élément avec ses enfants', + itemHasChildren: 'Cet élément possède {count} éléments enfants.', + itemHasChildrenSingular: 'Cet élément possède 1 élément enfant.', + deleteAllOption: 'Tout supprimer ({count} éléments)', + deleteAllDescription: 'Supprimer définitivement cet élément et tous ses descendants', + reparentOption: 'Réattribuer le parent des enfants', + reparentDescription: "Déplacer les enfants vers le parent de cet élément, puis supprimer uniquement cet élément", + typeToConfirm: 'Saisissez « {title} » pour confirmer la suppression', + confirmationPlaceholder: 'Saisissez le titre pour confirmer...', + deleteAllItems: 'Supprimer tous les éléments', + reparentAndDelete: 'Réattribuer & supprimer', + reparentFailed: 'Échec de la réattribution du parent des enfants', + cascadeDeleteFailed: 'Échec de la suppression de l’arborescence de l’élément', + deletedItemsCount: '{count} éléments supprimés', + reparentedAndDeleted: 'Enfants réattribués et élément supprimé', + selectNewParent: 'Sélectionner un nouveau parent pour les enfants', + selectNewParentPlaceholder: 'Choisir un élément parent...', + makeRootItem: 'Transformer en éléments racines (sans parent)', + reparentLevelHint: 'Affichage uniquement des éléments du même niveau de hiérarchie', + noOtherItemsAtLevel: + 'Aucun autre élément à ce niveau - sélectionnez « Transformer en éléments racines » ou choisissez ci-dessus', + reparentToGrandparent: 'Les enfants seront déplacés vers le grand-parent', + childrenWillBecomeRoot: 'Les enfants deviendront des éléments racines', + failedToUpdateWatchStatus: 'Échec de la mise à jour du statut de suivi', + copyWorkItem: 'Copier l’élément de travail', + unwatchWorkItem: 'Ne plus suivre l’élément de travail', + watchWorkItem: 'Suivre l’élément de travail', + noSubIssueTypes: 'Aucun type de sous-demande disponible', + cannotCreateChildItems: 'Impossible de créer des éléments de travail enfants pour ce niveau d’élément.', + + // Item Detail Breadcrumbs + workItems: 'Éléments de travail', + linkedTo: 'lié à', + goToLinkedWorkItem: 'Aller à l’élément de travail lié', + goTo: 'Aller à {title}', + noParent: 'Aucun parent', + setParent: 'Définir le parent', + changeParent: 'Changer le parent', + searchForParentItem: 'Rechercher un élément parent...', + showingItemsFromLevel: 'Affichage uniquement des éléments du niveau hiérarchique {level}', + oneLevelAbove: 'un niveau au-dessus de {name}', + searchParentAcrossWorkspaces: 'Rechercher un élément parent parmi tous les espaces de travail', + removeParent: 'Supprimer le parent', + noItemsAtLevel: 'Aucun élément trouvé au niveau hiérarchique {level}', + failedToUpdateParent: 'Échec de la mise à jour du parent', + failedToRemoveParent: 'Échec de la suppression du parent', + clickToCopyKey: 'Cliquer pour copier la clé dans le presse-papiers', + hierarchyMismatchTitle: 'Cet élément ne correspond plus à son parent', + hierarchyMismatchDescription: + '{type} est configuré au niveau {currentLevel}, tandis que son parent existant est également au niveau {parentLevel}. Le lien est conservé, mais la modification du parent nécessite désormais un parent de niveau {requiredLevel}. Choisissez un type d’élément compatible avec le niveau {compatibleLevel} pour fonctionner avec le niveau de parent actuel.', + chooseCompatibleType: 'Choisir un type compatible', + parentPickerMismatchTitle: 'La configuration hiérarchique actuelle diffère de ce lien enregistré', + parentPickerMismatchDescription: + '{type} accepte désormais les parents de niveau {requiredLevel}. Votre parent existant est au niveau {currentParentLevel} ; pour choisir un autre parent à ce niveau, utilisez d’abord un type d’élément de niveau {compatibleLevel}.', + compatibleTypeHint: + 'Un type de niveau {compatibleLevel} correspond au parent actuel de niveau {parentLevel}. Les liens existants restent en place lorsque vous changez de type.', + hierarchyLevelLabel: 'Niveau hiérarchique {level}', + genericSubtaskLevelLabel: 'Sous-tâche générique · tout niveau parent', + genericSubtaskParentHint: 'Les sous-tâches génériques peuvent être déplacées sous n’importe quel élément hiérarchique standard.', + fitsCurrentParent: 'Correspond au parent actuel', + + // Item Detail Description + enterDescription: 'Entrez une description...', + clickToEditDescription: 'Cliquer pour modifier la description', + clickToAddDescription: 'Cliquer pour ajouter une description', + noDescriptionProvided: 'Aucune description fournie - cliquer pour en ajouter une', + addLink: 'Ajouter un lien', + createChild: 'Créer un enfant', + child: 'Enfant', + attachFile: 'Joindre un fichier', + attach: 'Joindre', + newDiagram: 'Nouveau diagramme', + diagram: 'Diagramme', + + // Item Detail Header + previousValueRemains: 'La valeur précédente reste inchangée', + titleCannotBeEmpty: 'Le titre ne peut pas être vide', + enterTitle: 'Entrez le titre...', + clickToEditTitle: 'Cliquer pour modifier le titre', + + // Item Detail Links + searchTestCases: 'Rechercher des cas de test...', + searchWorkItems: 'Rechercher des éléments de travail...', + loadingLinks: 'Chargement des liens...', + removeLink: 'Supprimer le lien', + linkType: 'Type de lien', + chooseRelationshipType: 'Choisir le type de relation...', + linkToTestCase: 'Lier cet élément à un cas de test.', + linkToPage: 'Lier cet élément à une page de connaissances dans le même espace de travail.', + targetItem: 'Élément cible', + targetPage: 'Page cible', + testCase: 'Cas de test', + page: 'Page', + linkedPages: 'Pages', + pagePickerPlaceholder: 'Rechercher des pages…', + selectLinkTypeToSearch: 'Sélectionnez un type de lien pour commencer la recherche.', + childWorkItems: 'Éléments de travail enfants', + loadingChildItems: 'Chargement des éléments de travail enfants...', + + // Item Detail Sidebar + setStatus: 'Définir le statut', + unassigned: 'Non assigné', + milestone: 'Jalon', + iteration: 'Itération', + labels: 'Étiquettes', + addLabel: '+ Ajouter une étiquette', + storyPoints: 'Story Points', + project: 'Projet', + clickToViewDetails: 'Cliquer pour afficher les détails de l’élément', + + // Clipboard + itemLinkCopied: 'Lien de l’élément copié dans le presse-papiers', + failedToCopyToClipboard: 'Échec de la copie dans le presse-papiers', + copyError: 'Erreur de copie', + + // Custom field placeholders + setField: 'Définir {field}', + selectField: 'Sélectionner {field}', + enterField: 'Saisir {field}', + notSet: 'Non défini', + selectIteration: 'Sélectionner une itération', + noIteration: 'Aucune itération', + selectOrCreateLabels: 'Sélectionner ou créer des étiquettes', + }, + + comments: { + failedToLoad: 'Échec du chargement des commentaires', + failedToCreate: 'Échec de la publication du commentaire', + confirmDelete: 'Voulez-vous vraiment supprimer ce commentaire ?', + failedToDelete: 'Échec de la suppression du commentaire', + failedToUpdate: 'Échec de la mise à jour du commentaire', + edited: 'modifié', + newCommentsAvailable: '{count} nouveau(x)', + editComment: 'Modifier le commentaire', + deleteComment: 'Supprimer le commentaire', + editPlaceholder: 'Modifiez votre commentaire...', + writePlaceholder: 'Ajouter un commentaire...', + markdownSupported: 'Markdown pris en charge', + posting: 'Publication…', + comment: 'Commentaire', + noComments: 'Aucun commentaire pour le moment', + beFirstToComment: 'Soyez le premier à commenter cet élément.', + internalNote: 'Note interne', + internalNoteHint: 'Non visible sur le portail', + internalNoteHintGeneral: 'Uniquement visible par les membres de l’équipe', + internal: 'Interne', + oldestFirst: 'Plus anciens en premier', + newestFirst: 'Plus récents en premier', + agentAuthored: 'Rédigé par un agent IA', + agentOwnedBy: 'Agent IA détenu par {owner}', + }, + + todo: { + failedToCreate: 'Échec de la création de la tâche', + confirmDelete: 'Voulez-vous vraiment supprimer cette tâche ?', + deleteTask: 'Supprimer la tâche', + failedToDelete: 'Échec de la suppression de la tâche', + loadingTasks: 'Chargement des tâches...', + myPersonalTasks: 'Mes tâches personnelles', + whatNeedsToBeDone: 'Que faut-il faire ?', + addPersonalTask: 'Ajouter une tâche personnelle', + noPersonalTasks: 'Aucune tâche personnelle', + addFirstTask: 'Ajoutez votre première tâche pour garder une trace de ce que vous devez faire.', + ofPersonalTasksRemaining: '{count} sur {total} tâches personnelles restantes', + assignedToMe: 'Assigné à moi', + noAssignedWork: 'Aucun travail assigné', + assignedItemsWillAppear: 'Les éléments de travail qui vous sont assignés apparaîtront ici.', + ofAssignedItemsRemaining: '{count} sur {total} éléments assignés restants', + task: 'Tâche', + dueDate: 'Date d’échéance', + progress: 'Progrès', + doneFilterLabel: 'Historique des tâches terminées', + completedHistoryHint: 'Les tâches ouvertes sont toujours affichées.', + customDateLabel: 'Depuis', + customDateAriaLabel: 'Afficher les tâches terminées depuis la date', + range7d: 'Les 7 derniers jours', + range30d: 'Les 30 derniers jours', + range90d: 'Les 90 derniers jours', + rangeAll: 'Tout le temps', + }, + + collectionTree: { + loading: 'Chargement...', + tree: 'Arborescence', + noWorkItemsYet: 'Aucun élément de travail pour le moment', + createFirstWorkItem: 'Créez votre premier élément de travail pour voir l’arborescence hiérarchique.', + expandAll: 'Tout développer', + collapseAll: 'Tout réduire', + showTests: 'Afficher les tests', + hideTests: 'Masquer les tests', + showingRootItems: 'Affichage de {start}-{end} sur {total} éléments racines', + page: 'Page', + pageOfTotal: 'Page {current} sur {total}', + itemKey: 'Clé d’élément', + noStatus: 'Aucun statut', + workspaceNotFound: 'Espace de travail introuvable.', + }, + + collections: { + // Page titles and headers + title: 'Collections', + subtitle: 'Requêtes et filtres enregistrés', + allGlobal: 'Toutes les globales', + globalCollection: 'Collection globale', + workspaceCollections: 'Collections d’espace de travail', + workspaceCollectionsTitle: 'Collections d’espace de travail', + allGlobalCollections: 'Toutes les collections globales', + categoryCollections: 'Collections {category}', + + // Collection management + newCollection: 'Nouvelle collection', + createCollection: 'Créer une collection', + editCollection: 'Modifier la collection', + deleteCollection: 'Supprimer la collection', + viewCollection: 'Afficher la collection', + saveCollection: 'Enregistrer la collection', + updateCollection: 'Mettre à jour la collection', + + // Collection properties + collectionName: 'Nom de la collection', + collectionDescription: 'Description', + noQuery: 'Aucune requête', + noFiltersApplied: 'Aucun filtre appliqué', + selectType: 'Sélectionner le type', + + // Workspace association + associateWorkspace: 'Associer un espace de travail', + changeWorkspace: 'Changer d’espace de travail', + changeWorkspaceAssociation: 'Changer l’association d’espace de travail', + associateWithWorkspace: 'Associer à un espace de travail', + workspaceAssociationDesc: + 'La sélection d’un espace de travail restreindra cette collection à cet espace. Laissez non assigné pour la conserver globale.', + saveAssociation: 'Enregistrer l’association', + workspaceAssociationNote: + 'Un seul espace de travail peut être associé à la fois. Supprimer la sélection retransforme la collection en vue globale.', + searchWorkspace: 'Rechercher un espace de travail...', + + // Categories + manageCategories: 'Gérer les catégories', + noCategory: 'Sans catégorie', + + // Filters and query + filters: 'Filtres', + expandSidebar: 'Développer la barre latérale', + collapseSidebar: 'Réduire la barre latérale', + workspaces: 'Espaces de travail', + selectWorkspaces: 'Sélectionner les espaces de travail...', + status: 'Statut', + selectStatuses: 'Sélectionner les statuts...', + priority: 'Priorité', + selectPriorities: 'Sélectionner les priorités...', + searchItems: 'Rechercher des éléments...', + addFieldFilter: 'Ajouter un filtre de champ', + clearSearch: 'Effacer la recherche', + + // Query editor + query: 'Requête', + queryLanguage: 'Langage de requête', + queryPlaceholder: 'Exemple : workspace = "Mon Projet" AND status = "open"', + edit: 'Modifier', + hide: 'Masquer', + clear: 'Effacer', + execute: 'Exécuter', + executeShortcut: '{shortcut} pour exécuter', + error: 'Erreur', + editCqlManually: 'Modifier le CQL manuellement', + resetToBuilder: 'Réinitialiser vers le constructeur', + rawModeConfirmTitle: 'Passer au CQL manuel ?', + rawModeConfirmMessage: 'Modifier le CQL manuellement désactivera le constructeur visuel jusqu’à ce que vous réinitialisiez la requête. L’état actuel de votre constructeur sera effacé.', + rawModeConfirmAccept: 'Passer au manuel', + builderDisabled: 'Constructeur désactivé — le CQL manuel est actif', + builderDisabledDesc: 'Cliquez sur « Réinitialiser vers le constructeur » dans la barre de requête pour réactiver ces champs.', + builderRecoveryDropped: 'Certaines parties de votre requête (comme NOT, les groupes OR ou les champs non pris en charge) n’ont pas pu être reconstruites et ont été abandonnées.', + + // Search modal + searchItemsTitle: 'Rechercher des éléments', + enterSearchText: 'Entrez le texte de recherche...', + apply: 'Appliquer', + + // Views + board: 'Tableau', + backlog: 'Backlog', + configure: 'Configurer', + map: 'Carte', + roadmap: 'Feuille de route', + publicBoard: 'Tableau public', + + // Roadmap view + roadmapSettings: 'Paramètres de la feuille de route', + roadmapStartField: 'Champ Date de début', + roadmapEndField: 'Champ Date de fin', + roadmapDependencyLinkType: 'Type de lien de dépendance', + roadmapNoConfig: 'Configurez les champs de date de début et de fin pour utiliser la vue feuille de route.', + roadmapNone: 'Aucun', + roadmapZoomWeek: 'Semaine', + roadmapZoomMonth: 'Mois', + roadmapZoomQuarter: 'Trimestre', + roadmapNoItems: 'Aucun élément avec des dates dans la plage actuelle.', + roadmapToday: 'Aujourd’hui', + roadmapHierarchyMode: 'Dates de hiérarchie', + roadmapHierarchyOff: 'Désactivé', + roadmapRollup: 'Remonter (Roll up)', + roadmapRolldown: 'Descendre (Roll down)', + roadmapAdjustRelatedDates: 'Ajuster les dates liées', + roadmapRollupAdjustmentHint: 'Mettre à jour les dates parents après la modification d’une date enfant.', + roadmapRolldownAdjustmentHint: 'Déplacer uniquement les dates enfants qui tombent en dehors d’un parent modifié.', + roadmapHierarchyCanonicalHint: 'Sélectionnez la date de début et la date de fin ci-dessus pour utiliser la planification hiérarchique.', + roadmapHierarchyLoadError: 'Impossible de charger les dates de hiérarchie. Réessayez.', + roadmapHierarchyTruncated: 'Cette hiérarchie est trop grande pour être ajustée en toute sécurité.', + roadmapHierarchyNotReady: 'Les dates de hiérarchie sont toujours en cours de chargement. Réessayez la modification.', + roadmapDateUpdateError: 'Impossible de mettre à jour les dates de la feuille de route. Réessayez.', + roadmapUnscheduledHint: '{count} éléments nécessitent des dates — sélectionnez une ligne de la chronologie pour les planifier.', + roadmapUnscheduledHint_one: '{count} élément nécessite des dates — sélectionnez sa ligne de chronologie pour le planifier.', + roadmapUnscheduledHint_other: '{count} éléments nécessitent des dates — sélectionnez une ligne de la chronologie pour les planifier.', + roadmapItems: 'Éléments ({count})', + roadmapScheduleItem: 'Planifier {title}', + + // Backlog view + noItemsInBacklog: 'Aucun élément dans le backlog', + noItemsInBacklogDesc: 'Tous les éléments de travail sont terminés ou aucun élément n’existe encore.', + showingItemsFromBacklog: 'Affichage de {count} éléments du backlog', + dragItemsHere: 'Glissez des éléments ici pour les ajouter à ce sprint', + backlogItemActions: 'Actions du backlog pour {title}', + toBeginningOfBacklog: 'Au début du backlog', + sendToEndOfBacklog: 'Envoyer à la fin du backlog', + assignToIteration: 'Assigner à l’itération…', + movedToBeginningOfBacklog: '« {title} » a été déplacé au début du backlog', + sentToEndOfBacklog: '« {title} » a été envoyé à la fin du backlog', + assignedToIteration: '« {title} » a été assigné à {iteration}', + backlogActionFailed: 'Impossible de mettre à jour l’élément du backlog. Veuillez réessayer.', + + // Map view + loadingStoryMap: 'Chargement de la story map...', + rootLevel: 'Niveau racine', + currentLevel: 'Niveau actuel', + showingItems: 'Affichage de {count} {type}{plural}', + childItems: '{count} élément{plural} enfant', + childWorkItems: 'Éléments de travail enfants ({count})', + noChildItems: 'Aucun élément enfant pour le moment', + noChildItemsLowest: 'Aucun élément enfant (niveau hiérarchique le plus bas)', + addCard: 'Ajouter une carte', + collapseColumn: 'Réduire la colonne', + expandColumn: 'Développer la colonne ({name})', + create: 'Créer', + enterSummary: 'Entrez un résumé...', + selectWorkspace: 'Sélectionner un espace de travail', + selectItemType: 'Sélectionner le type d’élément', + noTypesAvailable: 'Aucun type disponible', + drillDown: 'Explorer en détail pour afficher les enfants comme colonne vertébrale', + noTopLevelItems: 'Aucun élément de niveau supérieur trouvé', + noTopLevelItemsDesc: 'Créez des éléments de travail pour afficher votre story map', + workspaceNotFound: 'Espace de travail introuvable.', + + // Collections list + collection: 'Collection', + queryColumn: 'Requête', + created: 'Créé', + actions: 'Actions', + public: 'Public', + workspaceFilter: 'Filtre d’espace de travail', + allWorkspaces: 'Tous les espaces de travail', + noCollectionsTitle: 'Aucune collection trouvée.', + noCollectionsFound: 'Créez votre première collection pour enregistrer et réutiliser des requêtes d’éléments de travail.', + collectionCount: '{count} collection', + collectionCountPlural: '{count} collections', + + // Results + addFiltersToStart: 'Ajoutez des filtres pour commencer', + addFiltersDesc: 'Utilisez les filtres de la barre latérale ou écrivez une requête pour rechercher des éléments de travail.', + loadingWorkspaces: 'Chargement des espaces de travail...', + loadingWorkItems: 'Chargement des éléments de travail...', + noWorkItemsFound: 'Aucun élément de travail trouvé', + tryAdjustingFilters: 'Essayez d’ajuster vos filtres ou vos termes de recherche.', + showingWorkItems: 'Affichage de {count} éléments de travail', + + // Confirmations + confirmDeleteCollection: + 'Voulez-vous vraiment supprimer la collection « {name} » ? Cette action ne peut pas être annulée.', + confirmDeleteItem: 'Voulez-vous vraiment supprimer « {title} » ? Cette action ne peut pas être annulée.', + noQueryToSave: 'Aucune requête à enregistrer. Veuillez d’abord configurer des filtres ou entrer une requête QL.', + + // Board view + boardSummary: 'Total : {itemCount} éléments de travail dans {columnCount} colonnes', + allItems: 'Tous les éléments', + transition_failed: 'Échec de la transition de statut. Le tableau a été rafraîchi.', + anotherWorkspace: 'un autre espace de travail', + collectionFiltersReason: 'les filtres de la collection', + workspaceFilterReason: 'le filtre actuel d’espace de travail', + cardCreatedOutsideView: 'La carte a été créée dans {workspace}, mais est masquée par {reason}.', + cardCreatedSuccess: 'Carte créée', + boardItemsTruncated: 'Certains éléments du tableau sont masqués. Sélectionnez « Charger plus » pour tous les afficher.', + combinedBoardMoveUnsupported: 'Changez le couloir et le statut séparément.', + swimlaneMoveFailed: 'Impossible de déplacer l’élément vers ce couloir.', + manual_sort_unavailable_title: 'Tri manuel indisponible', + manual_sort_unavailable: 'Passez au mode Rang pour organiser les cartes manuellement.', + // Board card dependency hover summary + blockedBy: 'Bloqué par', + blocking: 'Bloque', + blockersCount: '{count} bloquants', + blockersCount_one: '{count} bloquant', + blockersCount_other: '{count} bloquants', + blockingCount: '{count} bloqués', + blockingCount_one: '{count} bloqué', + blockingCount_other: '{count} bloqués', + }, + + links: { + title: 'Liens', + subtitle: 'Gérer les liens de l’élément', + addLink: 'Ajouter un lien', + removeLink: 'Supprimer le lien', + linkText: 'Texte du lien', + linkUrl: 'URL', + }, + + workspaceSettings: { + // Tab navigation + tabs: { + general: 'Général', + appearance: 'Apparence', + categories: 'Catégories', + members: 'Membres', + configurationSets: 'Ensembles de configuration', + sourceControl: 'Gestionnaire de source', + codingAgents: 'Agents', + issueSync: 'Synchro de demandes', + recurrence: 'Récurrence', + templates: 'Modèles', + removeWorkspace: 'Supprimer l’espace de travail', + }, + + // Page header + title: 'Paramètres', + subtitle: 'Configurer les paramètres pour {name}', + breadcrumbs: { + workspaces: 'Espaces de travail', + settings: 'Paramètres', + }, + + // Per-module page header subtitles (folded admin sidebar) + headers: { + general: 'Détails de base et valeurs par défaut de l’espace de travail', + categories: 'Restreindre les catégories de projets applicables', + members: 'Gérer qui peut accéder à cet espace de travail', + configuration: 'Attribuer des ensembles de configuration à cet espace de travail', + sourceControl: 'Connecter les dépôts et le gestionnaire de source', + codingAgents: 'Configurer les agents de codage et leurs compétences', + issueSync: 'Synchroniser des demandes externes dans cet espace de travail', + recurrence: 'Gérer les règles d’éléments récurrents', + templates: 'Squelettes de description réutilisables pour les nouveaux éléments', + danger: 'Supprimer définitivement cet espace de travail', + }, + + // Access denied + accessDenied: 'Accès refusé', + accessDeniedDescription: 'Vous devez disposer des autorisations d’administrateur de l’espace de travail pour accéder aux paramètres.', + backToWorkspace: 'Retour à l’espace de travail', + + // General tab + basicInformation: 'Informations de base', + workspaceName: 'Nom de l’espace de travail', + workspaceNamePlaceholder: 'Entrez le nom de l’espace de travail', + workspaceKey: 'Clé de l’espace de travail', + workspaceKeyPlaceholder: 'ex. DEV, TEST, PROD', + workspaceKeyHelp: 'Utilisé comme préfixe pour les éléments (ex. DEV-123). Lettres majuscules et chiffres uniquement.', + description: 'Description', + descriptionPlaceholder: 'Description optionnelle pour cet espace de travail', + defaultTimeProject: 'Projet de suivi du temps par défaut', + noDefaultProject: 'Aucun projet par défaut', + defaultTimeProjectHelp: + 'Projet par défaut utilisé lors de la saisie de temps pour les éléments de travail de cet espace. Peut être remplacé par élément de travail.', + defaultView: 'Vue par défaut de l’espace de travail', + defaultViewHelp: 'Vue par défaut affichée lors de l’entrée dans cet espace de travail.', + activeWorkspace: 'Espace de travail actif', + activeWorkspaceHelp: + 'Lorsqu’il est inactif, seuls les administrateurs système et de l’espace de travail peuvent accéder à cet espace. Toutes les données sont conservées.', + enableInternalComments: 'Activer les commentaires internes', + enableInternalCommentsHint: + 'Autoriser le marquage des commentaires comme notes internes sur tous les éléments, pas seulement sur les demandes du portail.', + availableAsTemplate: 'Disponible comme modèle', + availableAsTemplateHelp: + 'Les créateurs d’espaces de travail peuvent démarrer un nouvel espace à partir d’une copie de l’ensemble de configuration, des modèles d’éléments et des éléments actuels de cet espace. Les membres, l’historique et les fichiers ne sont jamais copiés.', + + // View options + views: { + board: 'Tableau', + backlog: 'Backlog', + list: 'Liste', + tree: 'Arborescence', + map: 'Carte', + overview: 'Vue d’ensemble', + }, + + // Appearance tab + visualIdentity: 'Identité visuelle', + visualIdentityDescription: + 'Personnalisez l’apparence visuelle de votre espace de travail avec des icônes, des couleurs et des avatars.', + workspaceIconColor: 'Icône & couleur de l’espace de travail', + workspaceAvatar: 'Avatar de l’espace de travail', + customAvatar: 'Avatar personnalisé', + imageUploadedSuccessfully: 'Image téléchargée avec succès', + defaultIcon: 'Icône par défaut', + usingSelectedIconColor: 'Utilisation de l’icône et de la couleur sélectionnées', + changeAvatar: 'Changer d’avatar', + uploadAvatar: 'Télécharger un avatar', + attachmentsRequired: 'Les pièces jointes doivent être activées pour télécharger des icônes d’espace de travail', + uploadRecommendation: 'Recommandé : Images carrées, d’au moins 256x256 pixels pour une meilleure qualité', + avatarOrIconNote: + 'Vous pouvez utiliser soit une image d’avatar personnalisée, soit la combinaison icône & couleur ci-dessus.', + uploading: 'Téléchargement...', + avatarUploadedSuccess: 'Avatar téléchargé avec succès', + + // Categories tab + projectCategoryRestrictions: 'Restrictions des catégories de projets', + selectProjectCategories: 'Sélectionner des catégories de projets...', + categoryRestrictionsHelp: + 'Optionnellement, restreignez la sélection de projets à des catégories spécifiques pour cet espace de travail. Une fois configuré, les utilisateurs ne pourront choisir que parmi les projets des catégories sélectionnées.', + leaveEmptyNote: 'Remarque : Laissez vide pour autoriser la sélection parmi toutes les catégories de projets.', + + // Configuration tab + activeConfiguration: 'Configuration active', + + // Danger zone + permanentRemoval: 'Suppression définitive', + removeWarningIntro: 'La suppression de cet espace de travail effacera définitivement :', + removeWarningItems: 'Tous les éléments de travail et projets de cet espace de travail', + removeWarningFields: 'Toutes les configurations de champs personnalisés', + removeWarningScreens: 'Toutes les configurations d’écrans', + removeWarningFiles: 'Tous les fichiers téléchargés associés aux éléments de travail', + removeWarningFinal: 'Cette action ne peut pas être annulée.', + removeWorkspaceButton: 'Supprimer l’espace de travail', + typeToConfirm: 'Saisissez {name} pour confirmer la suppression :', + typeNameHere: "Saisissez '{name}' ici", + yesRemoveWorkspace: 'Oui, supprimer l’espace de travail', + + // Actions and messages + saveChanges: 'Enregistrer les modifications', + saving: 'Enregistrement...', + reset: 'Réinitialiser', + remove: 'Supprimer', + cancel: 'Annuler', + workspaceNotFound: 'Espace de travail introuvable.', + workspaceNameRequired: 'Le nom de l’espace de travail est requis', + workspaceKeyRequired: 'La clé de l’espace de travail est requise', + savedSuccessfully: 'Paramètres de l’espace de travail enregistrés avec succès', + failedToSave: 'Échec de l’enregistrement des paramètres de l’espace de travail : {error}', + deletedSuccessfully: 'Espace de travail « {name} » supprimé avec succès', + failedToDelete: 'Échec de la suppression de l’espace de travail : {error}', + pleaseConfirmDeletion: 'Veuillez saisir le nom exact de l’espace de travail tel qu’indiqué pour confirmer la suppression', + pleaseSelectImage: 'Veuillez sélectionner un fichier image', + failedToUploadAvatar: 'Échec du téléchargement de l’avatar : {error}', + }, + + lookAndFeel: { + title: 'Apparence', + subtitle: 'Personnalisez l’apparence et la disposition de votre espace de travail', + gradientTitle: 'Arrière-plan & dégradé', + gradientDescription: 'Choisissez un thème de couleur pour votre espace de travail', + gradients: 'Dégradés', + backgroundImages: 'Images d’arrière-plan', + currentBackground: 'Arrière-plan actuel', + uploadCustomImage: 'Télécharger une image personnalisée', + backgroundUploadRecommendation: + 'Recommandé : Images haute résolution (1920x1080 ou plus) pour une meilleure qualité', + backgroundUploadedSuccess: 'Image d’arrière-plan téléchargée avec succès', + failedToUploadBackground: 'Échec du téléchargement de l’arrière-plan : {error}', + identityTitle: 'Identité de l’espace de travail', + identityDescription: 'Personnalisez l’icône, la couleur et l’avatar de votre espace de travail', + savedSuccessfully: 'Paramètres d’apparence enregistrés avec succès', + failedToSave: 'Échec de l’enregistrement des paramètres d’apparence : {error}', + // Logo + logo: 'Logo', + currentLogo: 'Logo actuel', + noLogoSet: 'Aucun logo défini', + uploadLogo: 'Télécharger le logo', + logoRecommendation: + 'Recommandé : PNG ou SVG avec fond transparent. Hauteur maximale dans l’en-tête : 40-50px.', + }, + + issueSync: { + title: 'Synchronisation des demandes GitHub', + subtitle: 'Synchroniser les demandes GitHub dans cet espace sous forme d’éléments', + noConfig: 'La synchronisation des demandes n’est pas configurée pour cet espace de travail.', + noConfigDescription: + 'Liez un dépôt et configurez la façon dont les demandes GitHub doivent être synchronisées dans les éléments Windshift.', + configure: 'Configurer la synchronisation des demandes', + enabled: 'Synchronisation activée', + enabledDescription: + 'Lorsqu’elle est activée, les demandes GitHub seront régulièrement synchronisées dans cet espace de travail.', + repository: 'Dépôt', + repositoryDescription: 'Sélectionnez un dépôt lié depuis lequel synchroniser les demandes.', + selectRepository: 'Sélectionner un dépôt', + statusMapping: 'Correspondance des statuts', + statusMappingDescription: 'Associez les états des demandes GitHub aux statuts Windshift.', + workflowBypassWarning: + 'Les états GitHub synchronisés remplacent les règles du flux de travail. Les changements de statut effectués par la synchronisation peuvent ignorer les transitions, conditions, validateurs et approbations.', + githubOpen: 'GitHub "ouvert"', + githubClosed: 'GitHub "fermé"', + mapsTo: 'correspond à', + selectStatus: 'Sélectionner le statut', + reverseStatusMapping: 'Correspondance inverse des statuts', + reverseStatusMappingDescription: 'Associez les statuts Windshift aux états GitHub pour la rétroaction.', + labelSync: 'Synchronisation des étiquettes', + labelSyncDescription: 'Comment gérer les étiquettes GitHub sur les demandes synchronisées.', + labelModeNone: 'Aucune', + labelModeNoneDescription: 'Ne pas synchroniser les étiquettes.', + labelModeMirror: 'Miroir', + labelModeMirrorDescription: 'Créer automatiquement des étiquettes Windshift correspondant aux étiquettes GitHub.', + labelModeMapped: 'Mappée', + labelModeMappedDescription: 'Associer manuellement les étiquettes GitHub aux étiquettes Windshift existantes.', + filterLabels: 'Filtrer les étiquettes', + filterLabelsDescription: + 'Synchroniser uniquement les demandes qui possèdent l’une de ces étiquettes GitHub. Laissez vide pour tout synchroniser.', + filterLabelsPlaceholder: 'Saisissez un nom d’étiquette et appuyez sur Entrée', + assigneeMapping: 'Correspondance des assignés', + assigneeMappingDescription: 'Associez les noms d’utilisateur GitHub aux utilisateurs Windshift.', + githubUsername: 'Nom d’utilisateur GitHub', + windshiftUser: 'Utilisateur Windshift', + addMapping: 'Ajouter une correspondance', + milestoneMapping: 'Correspondance des jalons', + milestoneMappingDescription: 'Associez les jalons GitHub aux jalons Windshift.', + githubMilestone: 'Jalon GitHub', + windshiftMilestone: 'Jalon Windshift', + itemType: 'Type d’élément', + itemTypeDescription: + 'Sélectionnez le type d’élément pour les demandes synchronisées. Cela détermine quels statuts de flux de travail sont disponibles pour le mappage.', + selectItemType: 'Sélectionner un type d’élément', + selectItemTypeHint: 'Sélectionnez un type d’élément ci-dessus pour configurer la correspondance des statuts.', + defaultPriority: 'Priorité par défaut', + defaultPriorityDescription: 'Priorité à utiliser pour les demandes nouvellement importées.', + comments: 'Synchronisation des commentaires', + commentsDescription: 'Renvoyer les commentaires Windshift vers la demande GitHub liée.', + syncStatus: 'Statut de synchronisation', + lastSync: 'Dernière synchronisation', + lastSyncError: 'Dernière erreur de synchronisation', + syncedItems: 'Éléments synchronisés', + syncNow: 'Synchroniser maintenant', + syncing: 'Synchronisation...', + syncTriggered: 'Synchronisation déclenchée avec succès', + noLinkedRepos: + 'Aucun dépôt n’est lié à cet espace de travail. Liez d’abord un dépôt dans les paramètres du gestionnaire de source.', + save: 'Enregistrer la configuration', + saved: 'Configuration de la synchronisation enregistrée', + deleted: 'Configuration de la synchronisation supprimée', + confirmDelete: + 'Voulez-vous vraiment supprimer la configuration de synchronisation ? Les éléments synchronisés resteront mais ne seront plus mis à jour.', + deleteConfig: 'Supprimer la configuration', + never: 'Jamais', + noErrors: 'Aucune erreur', + }, +}; \ No newline at end of file diff --git a/frontend/src/lib/stores/i18n-utils.spec.js b/frontend/src/lib/stores/i18n-utils.spec.js index 95604f5a..817ffd73 100644 --- a/frontend/src/lib/stores/i18n-utils.spec.js +++ b/frontend/src/lib/stores/i18n-utils.spec.js @@ -17,6 +17,7 @@ const supportedLocales = [ { code: 'pt-BR' }, { code: 'ru' }, { code: 'zh-CN' }, + { code: 'fr' }, ]; describe('negotiateLocale', () => { @@ -30,7 +31,7 @@ describe('negotiateLocale', () => { ['ru', 'ru'], ['ru-RU', 'ru'], ['ru_RU', 'ru'], - ['fr-FR', 'en'], + ['fr-FR', 'fr'], ])('maps %s to %s', (browserLocale, expected) => { expect(negotiateLocale(browserLocale, supportedLocales, 'en')).toBe(expected); }); diff --git a/frontend/src/lib/stores/i18n.svelte.js b/frontend/src/lib/stores/i18n.svelte.js index 373e748b..62968f44 100644 --- a/frontend/src/lib/stores/i18n.svelte.js +++ b/frontend/src/lib/stores/i18n.svelte.js @@ -15,6 +15,7 @@ export const SUPPORTED_LOCALES = [ { code: 'pt-BR', name: 'Português (Brasil)', direction: 'ltr' }, { code: 'ru', name: 'Русский', direction: 'ltr' }, { code: 'zh-CN', name: '简体中文', direction: 'ltr' }, + { code: 'fr', name: 'Français', direction: 'ltr' }, ]; // Reactive locale state.