From e739653b3fc97c4361561e71c563009a74c5f12d Mon Sep 17 00:00:00 2001 From: FabLrc Date: Tue, 7 Apr 2026 12:05:36 +0200 Subject: [PATCH 1/3] feat(i18n): add French translations for various application components --- src/i18n/config.ts | 2 +- src/i18n/locales/fr/common.json | 29 ++++++ src/i18n/locales/fr/dialogs.json | 68 ++++++++++++ src/i18n/locales/fr/editor.json | 35 +++++++ src/i18n/locales/fr/launch.json | 37 +++++++ src/i18n/locales/fr/settings.json | 159 +++++++++++++++++++++++++++++ src/i18n/locales/fr/shortcuts.json | 36 +++++++ src/i18n/locales/fr/timeline.json | 50 +++++++++ 8 files changed, 415 insertions(+), 1 deletion(-) create mode 100644 src/i18n/locales/fr/common.json create mode 100644 src/i18n/locales/fr/dialogs.json create mode 100644 src/i18n/locales/fr/editor.json create mode 100644 src/i18n/locales/fr/launch.json create mode 100644 src/i18n/locales/fr/settings.json create mode 100644 src/i18n/locales/fr/shortcuts.json create mode 100644 src/i18n/locales/fr/timeline.json diff --git a/src/i18n/config.ts b/src/i18n/config.ts index a96c7ef..38680f9 100644 --- a/src/i18n/config.ts +++ b/src/i18n/config.ts @@ -1,5 +1,5 @@ export const DEFAULT_LOCALE = "en" as const; -export const SUPPORTED_LOCALES = ["en", "zh-CN", "es"] as const; +export const SUPPORTED_LOCALES = ["en", "zh-CN", "es", "fr"] as const; export const I18N_NAMESPACES = [ "common", "dialogs", diff --git a/src/i18n/locales/fr/common.json b/src/i18n/locales/fr/common.json new file mode 100644 index 0000000..7eb7f83 --- /dev/null +++ b/src/i18n/locales/fr/common.json @@ -0,0 +1,29 @@ +{ + "actions": { + "cancel": "Annuler", + "save": "Enregistrer", + "delete": "Supprimer", + "close": "Fermer", + "share": "Partager", + "done": "Terminer", + "open": "Ouvrir", + "upload": "Téléverser", + "export": "Exporter", + "file": "Fichier", + "edit": "Éditer", + "view": "Affichage", + "window": "Fenêtre", + "quit": "Quitter", + "stopRecording": "Arrêter l'enregistrement" + }, + "playback": { + "play": "Lecture", + "pause": "Pause", + "fullscreen": "Plein écran", + "exitFullscreen": "Quitter le plein écran" + }, + "locale": { + "name": "Français", + "short": "FR" + } +} diff --git a/src/i18n/locales/fr/dialogs.json b/src/i18n/locales/fr/dialogs.json new file mode 100644 index 0000000..b4056a5 --- /dev/null +++ b/src/i18n/locales/fr/dialogs.json @@ -0,0 +1,68 @@ +{ + "export": { + "complete": "Export terminé", + "yourFormatReady": "Votre {{format}} est prêt", + "showInFolder": "Afficher dans le dossier", + "finalizingVideo": "Finalisation de l'export vidéo...", + "compilingGifProgress": "Compilation du GIF... {{progress}}%", + "compilingGifWait": "Compilation du GIF... Cela peut prendre un moment", + "takeMoment": "Cela peut prendre un moment...", + "failed": "Export échoué", + "tryAgain": "Veuillez réessayer", + "finalizingVideoTitle": "Finalisation de la vidéo", + "compilingGif": "Compilation du GIF", + "exportingFormat": "Export de {{format}}", + "compiling": "Compilation en cours", + "renderingFrames": "Rendu des images", + "processing": "Traitement en cours...", + "finalizing": "Finalisation...", + "compilingStatus": "Compilation...", + "status": "Statut", + "format": "Format", + "frames": "Images", + "cancelExport": "Annuler l'export", + "savedSuccessfully": "{{format}} enregistré avec succès !" + }, + "tutorial": { + "triggerLabel": "Comment fonctionne la coupe", + "title": "Comment fonctionne la coupe", + "description": "Comprendre comment supprimer les parties indésirables de votre vidéo.", + "explanation": "L'outil Coupe fonctionne en définissant les segments que vous souhaitez", + "explanationRemove": "supprimer", + "explanationCovered": "couvert", + "explanationEnd": "par un segment de coupe rouge sera coupé lors de l'export.", + "visualExample": "Exemple visuel", + "removed": "SUPPRIMÉ", + "kept": "Conservé", + "part1": "Partie 1", + "part2": "Partie 2", + "part3": "Partie 3", + "finalVideo": "Vidéo finale", + "step1Title": "1. Ajouter une coupe", + "step1Description": "Appuyez sur T ou cliquez sur l'icône ciseaux pour marquer une section à supprimer.", + "step2Title": "2. Ajuster", + "step2Description": "Faites glisser les bords de la région rouge pour couvrir exactement ce que vous souhaitez couper." + }, + "unsavedChanges": { + "title": "Modifications non enregistrées", + "message": "Vous avez des modifications non enregistrées.", + "detail": "Voulez-vous enregistrer votre projet avant de fermer ?", + "saveAndClose": "Enregistrer et fermer", + "discardAndClose": "Ignorer et fermer", + "loadProject": "Charger un projet…", + "saveProject": "Enregistrer le projet…", + "saveProjectAs": "Enregistrer le projet sous…" + }, + "fileDialogs": { + "saveGif": "Enregistrer le GIF exporté", + "saveVideo": "Enregistrer la vidéo exportée", + "selectVideo": "Sélectionner un fichier vidéo", + "saveProject": "Enregistrer le projet OpenScreen", + "openProject": "Ouvrir un projet OpenScreen", + "gifImage": "Image GIF", + "mp4Video": "Vidéo MP4", + "videoFiles": "Fichiers vidéo", + "openscreenProject": "Projet OpenScreen", + "allFiles": "Tous les fichiers" + } +} diff --git a/src/i18n/locales/fr/editor.json b/src/i18n/locales/fr/editor.json new file mode 100644 index 0000000..779bcd7 --- /dev/null +++ b/src/i18n/locales/fr/editor.json @@ -0,0 +1,35 @@ +{ + "errors": { + "noVideoLoaded": "Aucune vidéo chargée", + "videoNotReady": "Vidéo non prête", + "unableToDetermineSourcePath": "Impossible de déterminer le chemin de la vidéo source", + "failedToSaveGif": "Échec de l'enregistrement du GIF", + "gifExportFailed": "L'export du GIF a échoué", + "failedToSaveVideo": "Échec de l'enregistrement de la vidéo", + "exportFailed": "L'export a échoué", + "exportFailedWithError": "L'export a échoué : {{error}}", + "failedToSaveExport": "Échec de l'enregistrement de l'export", + "failedToSaveExportedVideo": "Échec de l'enregistrement de la vidéo exportée", + "failedToRevealInFolder": "Erreur lors de l'affichage dans le dossier : {{error}}" + }, + "export": { + "canceled": "Export annulé", + "exportedSuccessfully": "{{format}} exporté avec succès" + }, + "project": { + "saveCanceled": "Enregistrement du projet annulé", + "failedToSave": "Échec de l'enregistrement du projet", + "savedTo": "Projet enregistré dans {{path}}", + "failedToLoad": "Échec du chargement du projet", + "invalidFormat": "Format de fichier projet invalide", + "loadedFrom": "Projet chargé depuis {{path}}" + }, + "recording": { + "failedCameraAccess": "Échec de la demande d'accès à la caméra.", + "cameraBlocked": "L'accès à la caméra est bloqué. Activez-le dans les paramètres système pour utiliser la webcam.", + "systemAudioUnavailable": "Audio système non disponible. Enregistrement sans audio système.", + "microphoneDenied": "Accès au microphone refusé. L'enregistrement continuera sans audio.", + "cameraDenied": "Accès à la caméra refusé. L'enregistrement continuera sans webcam.", + "permissionDenied": "Permission d'enregistrement refusée. Veuillez autoriser l'enregistrement d'écran." + } +} diff --git a/src/i18n/locales/fr/launch.json b/src/i18n/locales/fr/launch.json new file mode 100644 index 0000000..f4bfb27 --- /dev/null +++ b/src/i18n/locales/fr/launch.json @@ -0,0 +1,37 @@ +{ + "tooltips": { + "hideHUD": "Masquer le HUD", + "closeApp": "Fermer l'application", + "restartRecording": "Redémarrer l'enregistrement", + "cancelRecording": "Annuler l'enregistrement", + "pauseRecording": "Mettre en pause l'enregistrement", + "resumeRecording": "Reprendre l'enregistrement", + "openVideoFile": "Ouvrir un fichier vidéo", + "openProject": "Ouvrir un projet" + }, + "audio": { + "enableSystemAudio": "Activer l'audio système", + "disableSystemAudio": "Désactiver l'audio système", + "enableMicrophone": "Activer le microphone", + "disableMicrophone": "Désactiver le microphone", + "defaultMicrophone": "Microphone par défaut" + }, + "webcam": { + "enableWebcam": "Activer la webcam", + "disableWebcam": "Désactiver la webcam", + "defaultCamera": "Caméra par défaut", + "searching": "Recherche en cours...", + "noneFound": "Aucune caméra trouvée", + "unavailable": "Caméra non disponible" + }, + "sourceSelector": { + "loading": "Chargement des sources...", + "screens": "Écrans ({{count}})", + "windows": "Fenêtres ({{count}})", + "defaultSourceName": "Écran" + }, + "recording": { + "selectSource": "Veuillez sélectionner une source à enregistrer" + }, + "language": "Langue" +} diff --git a/src/i18n/locales/fr/settings.json b/src/i18n/locales/fr/settings.json new file mode 100644 index 0000000..dd7610f --- /dev/null +++ b/src/i18n/locales/fr/settings.json @@ -0,0 +1,159 @@ +{ + "zoom": { + "level": "Niveau de zoom", + "selectRegion": "Sélectionnez une région de zoom à ajuster", + "deleteZoom": "Supprimer le zoom", + "focusMode": { + "title": "Mode focus", + "manual": "Manuel", + "auto": "Auto", + "autoDescription": "La caméra suit la position du curseur enregistré" + } + }, + "speed": { + "playbackSpeed": "Vitesse de lecture", + "selectRegion": "Sélectionnez une région de vitesse à ajuster", + "deleteRegion": "Supprimer la région de vitesse" + }, + "trim": { + "deleteRegion": "Supprimer la région de coupe" + }, + "layout": { + "title": "Mise en page", + "preset": "Préréglage", + "selectPreset": "Choisir un préréglage", + "pictureInPicture": "Incrustation d'image", + "verticalStack": "Empilement vertical", + "webcamShape": "Forme de la caméra" + }, + "effects": { + "title": "Effets vidéo", + "blurBg": "Flou arrière-plan", + "motionBlur": "Flou de mouvement", + "off": "désactivé", + "shadow": "Ombre", + "roundness": "Arrondi", + "padding": "Marge" + }, + "background": { + "title": "Arrière-plan", + "image": "Image", + "color": "Couleur", + "gradient": "Dégradé", + "uploadCustom": "Téléverser une image", + "gradientLabel": "Dégradé {{index}}" + }, + "crop": { + "title": "Recadrage", + "cropVideo": "Recadrer la vidéo", + "dragInstruction": "Faites glisser chaque côté pour ajuster la zone de recadrage", + "ratio": "Ratio", + "free": "Libre", + "done": "Terminer", + "lockAspectRatio": "Verrouiller le ratio", + "unlockAspectRatio": "Déverrouiller le ratio" + }, + "exportFormat": { + "mp4": "MP4", + "gif": "GIF", + "mp4Video": "Vidéo MP4", + "mp4Description": "Fichier vidéo haute qualité", + "gifAnimation": "Animation GIF", + "gifDescription": "Image animée pour le partage" + }, + "exportQuality": { + "title": "Qualité d'export", + "low": "Faible", + "medium": "Moyenne", + "high": "Haute" + }, + "gifSettings": { + "frameRate": "Fréquence d'images GIF", + "size": "Taille du GIF", + "loop": "GIF en boucle" + }, + "project": { + "save": "Enregistrer le projet", + "load": "Charger un projet" + }, + "export": { + "videoButton": "Exporter la vidéo", + "gifButton": "Exporter le GIF", + "chooseSaveLocation": "Choisir l'emplacement d'enregistrement" + }, + "links": { + "reportBug": "Signaler un bug", + "starOnGithub": "Étoile sur GitHub" + }, + "imageUpload": { + "invalidFileType": "Type de fichier invalide", + "jpgOnly": "Veuillez téléverser un fichier image JPG ou JPEG.", + "uploadSuccess": "Image personnalisée téléversée avec succès !", + "failedToUpload": "Échec du téléversement de l'image", + "errorReading": "Une erreur s'est produite lors de la lecture du fichier." + }, + "annotation": { + "title": "Paramètres d'annotation", + "active": "Actif", + "typeText": "Texte", + "typeImage": "Image", + "typeArrow": "Flèche", + "textContent": "Contenu du texte", + "textPlaceholder": "Saisissez votre texte...", + "fontStyle": "Style de police", + "selectStyle": "Choisir un style", + "size": "Taille", + "customFonts": "Polices personnalisées", + "textColor": "Couleur du texte", + "background": "Arrière-plan", + "none": "Aucun", + "color": "Couleur", + "clearBackground": "Supprimer l'arrière-plan", + "uploadImage": "Téléverser une image", + "supportedFormats": "Formats supportés : JPG, PNG, GIF, WebP", + "arrowDirection": "Direction de la flèche", + "strokeWidth": "Épaisseur du trait : {{width}}px", + "arrowColor": "Couleur de la flèche", + "deleteAnnotation": "Supprimer l'annotation", + "shortcutsAndTips": "Raccourcis & Astuces", + "tipMovePlayhead": "Déplacez la tête de lecture sur la section d'annotation et sélectionnez un élément.", + "tipTabCycle": "Utilisez Tab pour cycler entre les éléments superposés.", + "tipShiftTabCycle": "Utilisez Shift+Tab pour cycler en sens inverse.", + "invalidImageType": "Type de fichier invalide", + "imageFormatsOnly": "Veuillez téléverser un fichier image JPG, PNG, GIF ou WebP.", + "imageUploadSuccess": "Image téléversée avec succès !", + "failedImageUpload": "Échec du téléversement de l'image" + }, + "fontStyles": { + "classic": "Classique", + "editor": "Éditeur", + "strong": "Gras", + "typewriter": "Machine à écrire", + "deco": "Déco", + "simple": "Simple", + "modern": "Moderne", + "clean": "Épuré" + }, + "customFont": { + "dialogTitle": "Ajouter une police Google", + "urlLabel": "URL d'import Google Fonts", + "urlPlaceholder": "https://fonts.googleapis.com/css2?family=Roboto&display=swap", + "urlHelp": "Obtenez-la depuis Google Fonts : Sélectionnez une police → Cliquez sur « Obtenir la police » → Copiez l'URL @import", + "nameLabel": "Nom d'affichage", + "namePlaceholder": "Ma police personnalisée", + "nameHelp": "C'est ainsi que la police apparaîtra dans le sélecteur de polices", + "addButton": "Ajouter la police", + "addingButton": "Ajout en cours...", + "errorEmptyUrl": "Veuillez saisir une URL d'import Google Fonts", + "errorInvalidUrl": "Veuillez saisir une URL Google Fonts valide", + "errorEmptyName": "Veuillez saisir un nom de police", + "errorExtractFailed": "Impossible d'extraire la famille de polices depuis l'URL", + "successMessage": "Police « {{fontName}} » ajoutée avec succès", + "failedToAdd": "Échec de l'ajout de la police", + "errorTimeout": "La police a mis trop de temps à charger. Vérifiez l'URL et réessayez.", + "errorLoadFailed": "La police n'a pas pu être chargée. Vérifiez que l'URL Google Fonts est correcte." + }, + "language": { + "title": "Langue" + } +} diff --git a/src/i18n/locales/fr/shortcuts.json b/src/i18n/locales/fr/shortcuts.json new file mode 100644 index 0000000..ebd2181 --- /dev/null +++ b/src/i18n/locales/fr/shortcuts.json @@ -0,0 +1,36 @@ +{ + "title": "Raccourcis clavier", + "customize": "Personnaliser", + "configurable": "Configurable", + "fixed": "Fixe", + "pressKey": "Appuyez sur une touche…", + "clickToChange": "Cliquez pour modifier", + "pressEscToCancel": "Appuyez sur Échap pour annuler", + "helpText": "Cliquez sur un raccourci puis appuyez sur la nouvelle combinaison de touches. Appuyez sur Échap pour annuler.", + "resetToDefaults": "Réinitialiser les valeurs par défaut", + "alreadyUsedBy": "Déjà utilisé par {{action}}", + "swap": "Échanger", + "reservedShortcut": "Ce raccourci est réservé pour « {{label}} » et ne peut pas être réassigné.", + "savedToast": "Raccourcis clavier enregistrés", + "resetToast": "Réinitialisé aux raccourcis par défaut — cliquez sur Enregistrer pour appliquer", + "actions": { + "addZoom": "Ajouter un zoom", + "addTrim": "Ajouter une coupe", + "addSpeed": "Ajouter une vitesse", + "addAnnotation": "Ajouter une annotation", + "addKeyframe": "Ajouter une image-clé", + "deleteSelected": "Supprimer la sélection", + "playPause": "Lecture / Pause" + }, + "fixedActions": { + "undo": "Annuler", + "redo": "Rétablir", + "cycleAnnotationsForward": "Cycler les annotations en avant", + "cycleAnnotationsBackward": "Cycler les annotations en arrière", + "deleteSelectedAlt": "Supprimer la sélection (alt)", + "panTimeline": "Panoramique de la timeline", + "zoomTimeline": "Zoom de la timeline", + "frameBack": "Image précédente", + "frameForward": "Image suivante" + } +} diff --git a/src/i18n/locales/fr/timeline.json b/src/i18n/locales/fr/timeline.json new file mode 100644 index 0000000..abee16c --- /dev/null +++ b/src/i18n/locales/fr/timeline.json @@ -0,0 +1,50 @@ +{ + "buttons": { + "addZoom": "Ajouter un zoom (Z)", + "suggestZooms": "Suggérer des zooms depuis le curseur", + "addTrim": "Ajouter une coupe (T)", + "addAnnotation": "Ajouter une annotation (A)", + "addSpeed": "Ajouter une vitesse (S)" + }, + "hints": { + "pressZoom": "Appuyez sur Z pour ajouter un zoom", + "pressTrim": "Appuyez sur T pour ajouter une coupe", + "pressAnnotation": "Appuyez sur A pour ajouter une annotation", + "pressSpeed": "Appuyez sur S pour ajouter une vitesse" + }, + "labels": { + "pan": "Panoramique", + "zoom": "Zoom", + "zoomItem": "Zoom {{index}}", + "trimItem": "Coupe {{index}}", + "speedItem": "Vitesse {{index}}", + "annotationItem": "Annotation", + "imageItem": "Image", + "emptyText": "Texte vide" + }, + "emptyState": { + "noVideo": "Aucune vidéo chargée", + "dragAndDrop": "Glissez-déposez une vidéo pour commencer à éditer" + }, + "errors": { + "cannotPlaceZoom": "Impossible de placer le zoom ici", + "zoomExistsAtLocation": "Un zoom existe déjà à cet emplacement ou l'espace disponible est insuffisant.", + "zoomSuggestionUnavailable": "Gestionnaire de suggestions de zoom non disponible", + "noCursorTelemetry": "Aucune télémétrie de curseur disponible", + "noCursorTelemetryDescription": "Enregistrez d'abord un screencast pour générer des suggestions basées sur le curseur.", + "noUsableTelemetry": "Aucune télémétrie de curseur utilisable", + "noUsableTelemetryDescription": "L'enregistrement ne contient pas suffisamment de données de mouvement du curseur.", + "noDwellMoments": "Aucun moment de pause du curseur trouvé", + "noDwellMomentsDescription": "Essayez un enregistrement avec des pauses plus lentes du curseur sur les actions importantes.", + "noAutoZoomSlots": "Aucun emplacement de zoom automatique disponible", + "noAutoZoomSlotsDescription": "Les points de pause détectés chevauchent des régions de zoom existantes.", + "cannotPlaceTrim": "Impossible de placer la coupe ici", + "trimExistsAtLocation": "Une coupe existe déjà à cet emplacement ou l'espace disponible est insuffisant.", + "cannotPlaceSpeed": "Impossible de placer la vitesse ici", + "speedExistsAtLocation": "Une région de vitesse existe déjà à cet emplacement ou l'espace disponible est insuffisant." + }, + "success": { + "addedZoomSuggestions": "{{count}} suggestion de zoom basée sur le curseur ajoutée", + "addedZoomSuggestionsPlural": "{{count}} suggestions de zoom basées sur le curseur ajoutées" + } +} From 7a8fb807e6f2be2bf273f066d71aca071b51595e Mon Sep 17 00:00:00 2001 From: FabLrc Date: Tue, 7 Apr 2026 12:17:10 +0200 Subject: [PATCH 2/3] feat(i18n): add French translations for common and dialogs namespaces --- electron/i18n.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/electron/i18n.ts b/electron/i18n.ts index b385008..2dfb4d3 100644 --- a/electron/i18n.ts +++ b/electron/i18n.ts @@ -5,10 +5,12 @@ import commonEn from "../src/i18n/locales/en/common.json"; import dialogsEn from "../src/i18n/locales/en/dialogs.json"; import commonEs from "../src/i18n/locales/es/common.json"; import dialogsEs from "../src/i18n/locales/es/dialogs.json"; +import commonFr from "../src/i18n/locales/fr/common.json"; +import dialogsFr from "../src/i18n/locales/fr/dialogs.json"; import commonZh from "../src/i18n/locales/zh-CN/common.json"; import dialogsZh from "../src/i18n/locales/zh-CN/dialogs.json"; -type Locale = "en" | "zh-CN" | "es"; +type Locale = "en" | "zh-CN" | "es" | "fr"; type Namespace = "common" | "dialogs"; type MessageMap = Record; @@ -16,12 +18,13 @@ const messages: Record> = { en: { common: commonEn, dialogs: dialogsEn }, "zh-CN": { common: commonZh, dialogs: dialogsZh }, es: { common: commonEs, dialogs: dialogsEs }, + fr: { common: commonFr, dialogs: dialogsFr }, }; let currentLocale: Locale = "en"; export function setMainLocale(locale: string) { - if (locale === "en" || locale === "zh-CN" || locale === "es") { + if (locale === "en" || locale === "zh-CN" || locale === "es" || locale === "fr") { currentLocale = locale; } } From 1f56bb42c31ac02d96511545dcb199381df0b788 Mon Sep 17 00:00:00 2001 From: FabLrc Date: Tue, 7 Apr 2026 12:17:53 +0200 Subject: [PATCH 3/3] fix(i18n): update French translations for cycle annotations shortcuts --- src/i18n/locales/fr/shortcuts.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/i18n/locales/fr/shortcuts.json b/src/i18n/locales/fr/shortcuts.json index ebd2181..5c6e494 100644 --- a/src/i18n/locales/fr/shortcuts.json +++ b/src/i18n/locales/fr/shortcuts.json @@ -25,8 +25,8 @@ "fixedActions": { "undo": "Annuler", "redo": "Rétablir", - "cycleAnnotationsForward": "Cycler les annotations en avant", - "cycleAnnotationsBackward": "Cycler les annotations en arrière", + "cycleAnnotationsForward": "Parcourir les annotations en avant", + "cycleAnnotationsBackward": "Parcourir les annotations en arrière", "deleteSelectedAlt": "Supprimer la sélection (alt)", "panTimeline": "Panoramique de la timeline", "zoomTimeline": "Zoom de la timeline",