Przeglądaj źródła

Add French (Canada) localization and language picker support.

Includes fr-CA strings, Google product naming in French, and project registration for the new locale.

Co-authored-by: Cursor <cursoragent@cursor.com>
AhtashamShahzad1 1 miesiąc temu
rodzic
commit
92e8ae1a01

+ 1 - 0
Info.plist

@@ -12,6 +12,7 @@
 	<array>
 		<string>en</string>
 		<string>fr</string>
+		<string>fr-CA</string>
 		<string>zh-Hans</string>
 		<string>zh-Hant</string>
 	</array>

+ 1 - 0
meetings_app.xcodeproj/project.pbxproj

@@ -98,6 +98,7 @@
 				en,
 				Base,
 				fr,
+				"fr-CA",
 				"zh-Hans",
 				"zh-Hant",
 			);

+ 3 - 1
meetings_app/Localization/AppLanguageManager.swift

@@ -8,7 +8,7 @@ enum AppLanguageManager {
     private static let defaultsKey = "settings.appLanguageCode"
 
     /// UI languages shown in Settings → Language (order preserved).
-    private static let supportedLanguageCodes = ["en", "fr", "zh-Hans", "zh-Hant"]
+    private static let supportedLanguageCodes = ["en", "fr", "fr-CA", "zh-Hans", "zh-Hant"]
 
     /// `nil` follows the system language; otherwise uses the selected `.lproj` code.
     static var selectedLanguageCode: String? {
@@ -59,6 +59,8 @@ enum AppLanguageManager {
             return "English"
         case "fr":
             return "Français"
+        case "fr-CA":
+            return "Français (Canada)"
         case "zh-Hans":
             return "简体中文"
         case "zh-Hant":

+ 4 - 0
meetings_app/fr-CA.lproj/InfoPlist.strings

@@ -0,0 +1,4 @@
+"CFBundleDisplayName" = "Assistant IA pour Meet";
+"CFBundleName" = "Assistant IA pour Meet";
+"NSMicrophoneUsageDescription" = "Avec votre consentement, l'application enregistrera l'audio de la réunion sur ce Mac et l'affichera dans l'Assistant IA après la réunion.";
+"NSSpeechRecognitionUsageDescription" = "Cette application convertit l'audio de vos réunions enregistrées en transcriptions textuelles pour l'Assistant IA.";

+ 452 - 0
meetings_app/fr-CA.lproj/Localizable.strings

@@ -0,0 +1,452 @@
+/* AppBrand.name ("Meetings") intentionnellement omis — le nom de l'application reste inchangé dans le code. */
+"AI Companion for Meet" = "Assistant IA pour Meet";
+
+/* MARK: - Général */
+"OK" = "OK";
+"Cancel" = "Annuler";
+"Continue" = "Continuer";
+"Apply" = "Appliquer";
+"Reset" = "Réinitialiser";
+"Save" = "Enregistrer";
+"Copy" = "Copier";
+"Copied" = "Copié";
+"Close" = "Fermer";
+"Join" = "Rejoindre";
+"None" = "Aucun";
+"Link" = "Lien";
+"Stop" = "Arrêter";
+"Address" = "Adresse";
+"Title" = "Titre";
+"Notes" = "Notes";
+"Start" = "Démarrer";
+"Premium" = "Premium";
+"Popular" = "Populaire";
+"Browser" = "Navigateur";
+"Meeting" = "Réunion";
+"Error" = "Erreur";
+"Success" = "Succès";
+
+/* MARK: - Barre d'état */
+"Meetings status menu" = "Menu d'état des réunions";
+"Open App" = "Ouvrir l'application";
+"Join Meetings" = "Rejoindre des réunions";
+"Schedule" = "Planifier";
+"Calendar" = "Calendrier";
+"Widgets" = "Widgets";
+"AI Companion" = "Assistant IA";
+"Settings" = "Paramètres";
+"Top meetings" = "Réunions en tête";
+"No upcoming meetings" = "Aucune réunion à venir";
+"Sign Out" = "Déconnexion";
+"Sign In" = "Connexion";
+"Quit" = "Quitter";
+
+/* MARK: - Écran de démarrage */
+"Preparing your meetings workspace" = "Préparation de votre espace de travail pour réunions";
+"CONNECTING SERVICES" = "CONNEXION DES SERVICES";
+"READY" = "PRÊT";
+"Plan smart, achieve more" = "Planifiez intelligemment, accomplissez plus";
+
+/* MARK: - Widgets */
+"Add Widget to Desktop" = "Ajouter un widget au bureau";
+"Choose a size and add your Google Meet widget." = "Choisissez une taille et ajoutez votre widget Meet de Google.";
+"Tip: Right-click a desktop widget to remove it." = "Conseil : faites un clic droit sur un widget du bureau pour le supprimer.";
+"Removed %@ widget from desktop." = "Widget %@ supprimé du bureau.";
+"Added %@ widget from desktop." = "Widget %@ ajouté à partir du bureau.";
+"Widgets are Premium only. Upgrade to add widgets." = "Les widgets sont disponibles uniquement avec Premium. Passez à Premium pour ajouter des widgets.";
+"Remove Widget" = "Supprimer le widget";
+"Small" = "Petit";
+"Medium" = "Moyen";
+"Quick actions" = "Actions rapides";
+"Open Meet" = "Ouvrir Meet";
+"Upcoming meetings" = "Réunions à venir";
+"Sign in to see upcoming meetings" = "Connectez-vous pour voir les réunions à venir";
+"Sign in with Google" = "Se connecter à Google";
+"Google Meet" = "Meet de Google";
+"Open and join quickly" = "Ouvrir et rejoindre rapidement";
+"Refresh" = "Actualiser";
+
+/* MARK: - Barre latérale et navigation */
+"Planning" = "Planification";
+"Additional" = "Supplémentaire";
+"Get Premium" = "Obtenir Premium";
+"Manage Subscription" = "Gérer l'abonnement";
+"App for Google Meet" = "Application Meet de Google";
+
+/* MARK: - Rejoindre une réunion */
+"New Instant Meet" = "Nouveau Meet instantané";
+"Start instant Meet in more section with\nGoogle Meet meet." = "Commencez un Meet instantané dans la section plus\navec Meet de Google.";
+"Join with Link" = "Rejoindre avec un lien";
+"Code or meet.google.com/…" = "Code ou meet.google.com/…";
+"Join with ID" = "Rejoindre avec un ID";
+"Join with URL" = "Rejoindre avec une URL";
+"Meeting URL" = "URL de la réunion";
+"Enter meeting URL..." = "Entrez l'URL de la réunion…";
+"Quick Join Meeting" = "Rejoindre rapidement une réunion";
+"Invalid Meet link" = "Lien Meet invalide";
+"Enter a valid Google Meet link or meeting code (for example nkd-grps-duv, meet.google.com/nkd-grps-duv, or https://meet.google.com/nkd-grps-duv)." = "Entrez un lien Meet de Google ou un code de réunion valide (par exemple nkd-grps-duv, meet.google.com/nkd-grps-duv, ou https://meet.google.com/nkd-grps-duv).";
+"Instant Meeting" = "Réunion instantanée";
+"Connect Google to start Instant Meeting" = "Connectez-vous à Google pour démarrer une réunion instantanée";
+"To auto-stop recording when the meeting ends, connect your Google account first." = "Pour arrêter automatiquement l'enregistrement lorsque la réunion se termine, connectez d'abord votre compte Google.";
+"Could not create Instant Meeting" = "Impossible de créer la réunion instantanée";
+"Free trial used. Upgrade to continue." = "Période d'essai utilisée. Passez à Premium pour continuer.";
+
+/* MARK: - Naviguer sur le Web */
+"Browse the web" = "Naviguer sur le Web";
+"Enter a web address (for example meet.google.com)." = "Entrez une adresse Web (par exemple meet.google.com).";
+"Invalid address" = "Adresse invalide";
+"Enter a valid http or https URL." = "Entrez une URL http ou https valide.";
+"Browse" = "Naviguer";
+"Quick links" = "Liens rapides";
+"Zoom help" = "Aide Zoom";
+"https://example.com or example.com" = "https://example.com ou example.com";
+"Enter a valid web address, for example https://example.com" = "Entrez une adresse Web valide, par exemple https://example.com";
+"Address not allowed" = "Adresse non autorisée";
+"This URL is not permitted with the current in-app browser policy (whitelist)." = "Cette URL n'est pas autorisée par la politique actuelle du navigateur intégré (liste blanche).";
+"Unable to load page" = "Impossible de charger la page";
+"Could not load this page in the in-app browser.\n\n%@" = "Impossible de charger cette page dans le navigateur intégré.\n\n%@";
+"Page stopped loading" = "Le chargement de la page s'est arrêté";
+"Unable to open browser" = "Impossible d'ouvrir le navigateur";
+"Unable to Open Safari" = "Impossible d'ouvrir Safari";
+
+/* MARK: - Planification et calendrier */
+"All" = "Tout";
+"Today" = "Aujourd'hui";
+"This week" = "Cette semaine";
+"This month" = "Ce mois-ci";
+"Previous week" = "Semaine précédente";
+"Previous month" = "Mois précédent";
+"Custom range" = "Plage personnalisée";
+"No meetings match the selected filters." = "Aucune réunion ne correspond aux filtres sélectionnés.";
+"Adjust the date range and tap Apply." = "Ajustez la plage de dates et appuyez sur Appliquer.";
+"Premium required. Get Premium now to unlock Calendar." = "Premium requis. Obtenez Premium maintenant pour débloquer le calendrier.";
+"Premium required. Click to open paywall." = "Premium requis. Cliquez pour ouvrir l'écran d'achat.";
+"Get Premium to see events" = "Obtenez Premium pour voir les événements";
+"Connect Google" = "Se connecter à Google";
+"Sign in with Google first to schedule a meeting from Calendar." = "Connectez-vous d'abord à Google pour planifier une réunion à partir du calendrier.";
+"Select date" = "Sélectionner la date";
+"Choose a date to schedule your meeting." = "Choisissez une date pour planifier votre réunion.";
+"Google schedule" = "Agenda Google";
+"To show scheduled meetings, connect your Google account. You’ll need a Google OAuth client ID (Desktop) and a redirect URI matching the app’s callback scheme." = "Pour afficher les réunions planifiées, connectez votre compte Google. Vous aurez besoin d'un ID client Google OAuth (Desktop) et d'un URI de redirection correspondant au schéma de rappel de l'application.";
+"Actions" = "Actions";
+"Schedule meeting" = "Planifier une réunion";
+"Team sync" = "Synchronisation d'équipe";
+"Duration (min)" = "Durée (min)";
+"Refresh saved meetings" = "Actualiser les réunions enregistrées";
+
+/* MARK: - Paramètres */
+"Manage appearance, account, and app options." = "Gérer l'apparence, le compte et les options de l'application.";
+"Appearance" = "Apparence";
+"Account" = "Compte";
+"App" = "Application";
+"Notifications" = "Notifications";
+"Help & Legal" = "Aide et mentions légales";
+"Dark Mode" = "Mode sombre";
+"Language" = "Langue";
+"System Language" = "Langue du système";
+"Rate Us" = "Évaluer l'application";
+"Share App" = "Partager l'application";
+"Upgrade" = "Mettre à niveau";
+"Privacy Policy" = "Politique de confidentialité";
+"Support" = "Assistance";
+"Terms of Services" = "Conditions d'utilisation";
+"AI Language 1" = "Langue IA 1";
+"AI Language 2" = "Langue IA 2";
+"Google account connected" = "Compte Google connecté";
+"Google account not connected" = "Compte Google non connecté";
+"Signed in" = "Connecté";
+"Sign in to sync your meetings and calendar." = "Connectez-vous pour synchroniser vos réunions et votre calendrier.";
+"Log out" = "Déconnexion";
+"Check out %@ for managing and joining meetings." = "Découvrez %@ pour gérer et rejoindre des réunions.";
+"Meetings App" = "Application Réunions";
+
+/* MARK: - Consentement pour l'enregistrement */
+"Record this meeting locally?" = "Enregistrer cette réunion localement ?";
+"With your consent, the app will record meeting audio on this Mac and show it in AI Companion after the meeting." = "Avec votre consentement, l'application enregistrera l'audio de la réunion sur ce Mac et l'affichera dans l'Assistant IA après la réunion.";
+"Allow and Continue" = "Autoriser et continuer";
+"Continue Without Recording" = "Continuer sans enregistrement";
+"Transcription Languages" = "Langues de transcription";
+"Preferred Language 1" = "Langue préférée 1";
+"Preferred Language 2" = "Langue préférée 2";
+"Microphone permission denied" = "Autorisation du microphone refusée";
+"Microphone permission required" = "Autorisation du microphone requise";
+"Enable microphone access in System Settings to record meetings locally." = "Activez l'accès au microphone dans les Réglages Système pour enregistrer les réunions localement.";
+"Could not start recording" = "Impossible de démarrer l'enregistrement";
+"Recording will start when the meeting starts" = "L'enregistrement démarrera au début de la réunion";
+"Meeting recording started (meeting + microphone)" = "Enregistrement de la réunion démarré (réunion + microphone)";
+"System audio unavailable. Recording microphone only." = "Audio système indisponible. Enregistrement du microphone uniquement.";
+"Meeting recording started (microphone only)" = "Enregistrement de la réunion démarré (microphone uniquement)";
+"Meeting recording saved" = "Enregistrement de la réunion sauvegardé";
+
+/* MARK: - Assistant IA */
+"Get notes from your previous meetings" = "Obtenez les notes de vos réunions précédentes";
+"Connect your Google account to see your AI Companion meeting history." = "Connectez votre compte Google pour voir l'historique de vos réunions dans l'Assistant IA.";
+"No previous meetings yet for %@. End a meeting to save it here and generate notes." = "Aucune réunion précédente pour %@. Terminez une réunion pour l'enregistrer ici et générer des notes.";
+"this account" = "ce compte";
+"Previous meetings" = "Réunions précédentes";
+"Recording in progress: %@" = "Enregistrement en cours : %@";
+"Started: %@" = "Commencé : %@";
+"Stop Recording" = "Arrêter l'enregistrement";
+"Date: %@" = "Date : %@";
+"Saved time: %@" = "Sauvegardé le : %@";
+"Get Notes" = "Obtenir les notes";
+"Premium required. Click to unlock AI Companion notes." = "Premium requis. Cliquez pour débloquer les notes de l'Assistant IA.";
+"Play Audio" = "Lire l'audio";
+"Pause Audio" = "Pause audio";
+"Stopped" = "Arrêté";
+"Speaking..." = "Parle…";
+"Paused" = "En pause";
+"Playing..." = "Lecture…";
+"Not playing" = "Ne joue pas";
+"Checking audio..." = "Vérification de l'audio…";
+"Audio file missing" = "Fichier audio manquant";
+"Could not play recording" = "Impossible de lire l'enregistrement";
+"Failed: %@" = "Échec : %@";
+"Failed to play audio" = "Échec de la lecture audio";
+"decode error" = "erreur de décodage";
+"Loading..." = "Chargement…";
+"Buffering..." = "Tamponnage…";
+"Finished" = "Terminé";
+"Transcript not requested" = "Transcription non demandée";
+"Transcript processing..." = "Traitement de la transcription…";
+"Transcript ready" = "Transcription prête";
+"Transcript unavailable (tap to retry)" = "Transcription indisponible (appuyez pour réessayer)";
+"Notes not requested" = "Notes non demandées";
+"Preparing notes, please wait..." = "Préparation des notes, veuillez patienter…";
+"Notes ready" = "Notes prêtes";
+"Notes unavailable (tap to retry)" = "Notes indisponibles (appuyez pour réessayer)";
+"Transcript unavailable" = "Transcription indisponible";
+"Notes unavailable" = "Notes indisponibles";
+"Could not find recording details for this meeting." = "Impossible de trouver les détails d'enregistrement pour cette réunion.";
+"Transcript - %@" = "Transcription - %@";
+"Notes - %@" = "Notes - %@";
+"Meeting Notes" = "Notes de réunion";
+"Transcript processing..." = "Traitement de la transcription…";
+
+/* MARK: - Paywall et StoreKit */
+"Meetings Pro" = "Réunions Pro";
+"PREMIUM EXPERIENCE" = "EXPÉRIENCE PREMIUM";
+"Elevate your meetings workflow" = "Améliorez votre flux de travail de réunions";
+"Unlock automation, premium meeting tools, and priority support for every session." = "Débloquez l'automatisation, les outils de réunion premium et le soutien prioritaire pour chaque session.";
+"Manage meetings" = "Gérer les réunions";
+"Smart scheduling" = "Planification intelligente";
+"Faster workflow" = "Flux de travail plus rapide";
+"Reminder notifications" = "Notifications de rappel";
+"Monthly" = "Mensuel";
+"Bi Yearly" = "Semestriel";
+"Yearly" = "Annuel";
+"Most Popular" = "Le plus populaire";
+"%@ • Most Popular" = "%@ • Le plus populaire";
+"3 days free trial" = "3 jours d'essai gratuit";
+"Cancel anytime" = "Annulez à tout moment";
+"No lock-in" = "Sans engagement";
+"Instant access" = "Accès instantané";
+"Unlock all tools" = "Débloquez tous les outils";
+"Secure billing" = "Facturation sécurisée";
+"Handled by Apple" = "Géré par Apple";
+"Secured by Apple. Cancel anytime." = "Sécurisé par Apple. Annulez à tout moment.";
+"Restore Purchase" = "Restaurer l'achat";
+"Continue with free plan" = "Continuer avec le forfait gratuit";
+"Already Premium" = "Déjà Premium";
+"You are already premium. Do you want to continue with this purchase?" = "Vous êtes déjà Premium. Voulez-vous continuer cet achat ?";
+"Current plan: %@. Choose a different package to switch." = "Forfait actuel : %@. Choisissez une autre formule pour changer.";
+"Premium is active on this Apple ID." = "Premium est actif sur cet Apple ID.";
+"Free for %@, then %@" = "Gratuit pendant %@, puis %@";
+"Billed via App Store" = "Facturé via l'App Store";
+"%@  •  %@" = "%@  •  %@";
+"Save 0%" = "Économisez 0 %";
+"Save %@%%" = "Économisez %@ %%";
+"month" = "mois";
+"year" = "an";
+"%@ free trial" = "Essai gratuit de %@";
+"Processing..." = "Traitement…";
+"Premium Active" = "Premium actif";
+"Continue with 3 day trail" = "Continuer avec l'essai de 3 jours";
+"Please Wait" = "Veuillez patienter";
+"A purchase is already being processed." = "Un achat est déjà en cours de traitement.";
+"This Apple ID already has premium access." = "Cet Apple ID a déjà accès à Premium.";
+"Purchase Complete" = "Achat terminé";
+"Premium has been unlocked successfully." = "Premium a été débloqué avec succès.";
+"Purchase Pending" = "Achat en attente";
+"Your purchase is pending approval. You can continue once it completes." = "Votre achat est en attente d'approbation. Vous pouvez continuer une fois qu'il est terminé.";
+"Product Not Available" = "Produit indisponible";
+"Unable to load this product. Check your StoreKit configuration and product IDs." = "Impossible de charger ce produit. Vérifiez votre configuration StoreKit et vos IDs de produit.";
+"Already Purchased" = "Déjà acheté";
+"This plan is already active on your Apple ID." = "Ce forfait est déjà actif sur votre Apple ID.";
+"Purchase Failed" = "Échec de l'achat";
+"Restore Purchases" = "Restaurer les achats";
+"Purchases restored successfully." = "Achats restaurés avec succès.";
+"No previous premium purchase was found for this Apple ID." = "Aucun achat Premium antérieur n'a été trouvé pour cet Apple ID.";
+"Restore failed: %@" = "La restauration a échoué : %@";
+"Unable to Open Subscriptions" = "Impossible d'ouvrir les abonnements";
+"The subscriptions URL is invalid." = "L'URL des abonnements est invalide.";
+"%@ tapped." = "%@ tapé.";
+
+/* MARK: - Authentification */
+"Google Sign-In" = "Connexion à Google";
+
+/* MARK: - Erreurs de reconnaissance vocale / transcription */
+"Speech recognition permission denied. Enable it in System Settings and try again." = "Autorisation de reconnaissance vocale refusée. Activez-la dans les Réglages Système et réessayez.";
+"Speech recognition is restricted on this Mac." = "La reconnaissance vocale est restreinte sur ce Mac.";
+"Speech recognition authorization is unavailable." = "L'autorisation de reconnaissance vocale est indisponible.";
+"Speech recognizer is unavailable for the current locale." = "Le reconnaisseur vocal est indisponible pour la langue et la région actuelles.";
+"Speech recognizer is unavailable for %@." = "Le reconnaisseur vocal est indisponible pour %@.";
+"No audio was available to transcribe." = "Aucun audio n'était disponible pour la transcription.";
+
+/* MARK: - Erreurs de transaction StoreKit */
+"Unable to load products: %@" = "Impossible de charger les produits : %@";
+"Product ID not found in StoreKit response. Requested: %@. Loaded IDs: %@" = "ID de produit non trouvé dans la réponse StoreKit. Demandé : %@. IDs chargés : %@";
+"Purchase verification failed." = "La vérification de l'achat a échoué.";
+"Unknown purchase state." = "État de l'achat inconnu.";
+"none" = "aucun";
+
+/* MARK: - Textes de prix du paywall */
+"%@ one-time purchase" = "Achat unique de %@";
+"%@/month" = "%@/mois";
+"%@/6 months" = "%@/6 mois";
+"%@/year (3 days free trial)" = "%@/an (3 jours d'essai gratuit)";
+"day" = "jour";
+"week" = "semaine";
+"period" = "période";
+"1 %@" = "1 %@";
+"%d %@s" = "%d %@";
+
+/* MARK: - Toasts et retours rapides */
+"Meeting added successfully." = "Réunion ajoutée avec succès.";
+"An issue occurred. Please try again." = "Un problème est survenu. Veuillez réessayer.";
+"Failed to load transcript." = "Échec du chargement de la transcription.";
+"Failed to generate notes." = "Échec de la génération des notes.";
+
+/* MARK: - Alertes du navigateur */
+"Try Again" = "Réessayer";
+"The in-app browser closed this page unexpectedly. You can try loading it again in this same window." = "Le navigateur intégré a fermé cette page de manière inattendue. Vous pouvez essayer de la recharger dans cette même fenêtre.";
+
+/* MARK: - Erreurs de planification et de calendrier */
+"Couldn't load schedule." = "Impossible de charger l'horaire.";
+"Couldn't refresh schedule." = "Impossible d'actualiser l'horaire.";
+"Couldn't connect Google account." = "Impossible de connecter le compte Google.";
+"Couldn't logout Google account." = "Impossible de déconnecter le compte Google.";
+"Couldn't load schedule" = "Impossible de charger l'horaire";
+"Couldn't determine Meet meeting code from URL." = "Impossible de déterminer le code de réunion Meet à partir de l'URL.";
+"Refreshing…" = "Actualisation…";
+"Connect Google to see meetings" = "Connectez-vous à Google pour voir les réunions";
+"Connect to load schedule" = "Connectez-vous pour charger l'horaire";
+"Loading…" = "Chargement…";
+"Loading schedule…" = "Chargement de l'horaire…";
+"Start date must be on or before end date." = "La date de début doit être antérieure ou égale à la date de fin.";
+"Invalid custom date range" = "Plage de dates personnalisée invalide";
+"No meetings" = "Aucune réunion";
+"No meetings for selected filters" = "Aucune réunion pour les filtres sélectionnés";
+"No meetings on %@" = "Aucune réunion le %@";
+"1 meeting on %@" = "1 réunion le %@";
+"%d meetings on %@" = "%d réunions le %@";
+"All day" = "Toute la journée";
+"Duration: all day" = "Durée : toute la journée";
+"Duration: %dh %dm" = "Durée : %dh %dm";
+"Duration: %dh" = "Durée : %dh";
+"Duration: %dm" = "Durée : %dm";
+"%@ - %@" = "%@ - %@";
+"Scheduled Meeting" = "Réunion planifiée";
+
+/* MARK: - Popup de création de réunion */
+"Please enter a title." = "Veuillez entrer un titre.";
+"Duration must be a positive number of minutes." = "La durée doit être un nombre de minutes positif.";
+"Invalid start time." = "Heure de début invalide.";
+
+/* MARK: - Rappels et notifications */
+"Reminders" = "Rappels";
+"1 Day Before" = "1 jour avant";
+"12 Hours Before" = "12 heures avant";
+"1 Hour Before" = "1 heure avant";
+"Starts in 1 day" = "Commence dans 1 jour";
+"Starts in 12 hours" = "Commence dans 12 heures";
+"Starts in 1 hour" = "Commence dans 1 heure";
+
+/* MARK: - Erreurs OAuth Google */
+"Missing Google OAuth Client ID." = "ID client OAuth Google manquant.";
+"Missing Google OAuth Client Secret." = "Secret client OAuth Google manquant.";
+"Invalid OAuth callback URL." = "URL de rappel OAuth invalide.";
+"Google did not return an authorization code." = "Google n'a pas renvoyé de code d'autorisation.";
+"Token exchange failed: %@" = "Échange de jeton échoué : %@";
+"Could not open browser for Google sign-in." = "Impossible d'ouvrir le navigateur pour la connexion à Google.";
+"Google sign-in timed out." = "La connexion à Google a expiré.";
+"No stored Google tokens found." = "Aucun jeton Google stocké trouvé.";
+
+/* MARK: - Erreurs de l'Assistant IA / transcription */
+"Recording not found." = "Enregistrement non trouvé.";
+"Local meeting audio is missing." = "L'audio local de la réunion est manquant.";
+"Generated transcript was empty." = "La transcription générée était vide.";
+"(No transcript entries found.)" = "(Aucune entrée de transcription trouvée.)";
+"No conference record found for this meeting." = "Aucun enregistrement de conférence trouvé pour cette réunion.";
+"No transcript found for this meeting." = "Aucune transcription trouvée pour cette réunion.";
+"Unable to create audio composition tracks." = "Impossible de créer les pistes de composition audio.";
+"Unable to create audio export session." = "Impossible de créer la session d'exportation audio.";
+"Audio mix export failed." = "L'exportation du mixage audio a échoué.";
+"No display available for system audio capture." = "Aucun écran disponible pour la capture audio système.";
+"Cannot add audio writer input." = "Impossible d'ajouter l'entrée du writer audio.";
+
+/* MARK: - Erreurs API Google */
+"Google Meet returned an invalid response." = "Meet de Google a renvoyé une réponse invalide.";
+"Google Meet API error (%d): %@" = "Erreur d'API Meet de Google (%d) : %@";
+"Failed to parse Google Meet response: %@" = "Échec de l'analyse de la réponse Meet de Google : %@";
+"Google Calendar returned an invalid response." = "Agenda Google a renvoyé une réponse invalide.";
+"Google Calendar API error (%d): %@" = "Erreur d'API Agenda Google (%d) : %@";
+"Failed to parse Google Calendar events: %@" = "Échec de l'analyse des événements de l'Agenda Google : %@";
+
+/* MARK: - Erreurs OpenAI */
+"OpenAI API key is missing. Set OPENAI_API_KEY in the environment or app key vault." = "Clé API OpenAI manquante. Définissez OPENAI_API_KEY dans l'environnement ou le trousseau de clés de l'application.";
+"Notes generation returned an invalid response." = "La génération des notes a renvoyé une réponse invalide.";
+"Notes generation failed (%d): %@" = "La génération des notes a échoué (%d) : %@";
+"Notes generation returned empty text." = "La génération des notes a renvoyé un texte vide.";
+"OpenAI transcription returned an invalid response." = "La transcription OpenAI a renvoyé une réponse invalide.";
+"OpenAI transcription failed (%d): %@" = "La transcription OpenAI a échoué (%d) : %@";
+"OpenAI transcription returned no text." = "La transcription OpenAI n'a renvoyé aucun texte.";
+
+/* MARK: - Navigation et navigateur intégré */
+"Open sites in the in-app browser (back, forward, reload, address bar). OAuth and “Continue in browser” flows stay inside the app." = "Ouvrez des sites dans le navigateur intégré (précédent, suivant, recharger, barre d'adresse). Les flux OAuth et « Continuer dans le navigateur » restent dans l'application.";
+"Open in app browser" = "Ouvrir dans le navigateur de l'application";
+"Meet help" = "Aide Meet";
+"Go" = "Aller";
+"Back" = "Précédent";
+"Forward" = "Suivant";
+"Reload" = "Recharger";
+
+/* MARK: - Libellés du calendrier et de la planification */
+"Google Calendar" = "Agenda Google";
+"Untitled meeting" = "Réunion sans titre";
+"Created event missing start/end." = "L'événement créé n'a pas de début/fin.";
+"Created event missing Meet/HTML link." = "L'événement créé n'a pas de lien Meet/HTML.";
+
+/* MARK: - Compte Google */
+"Google account" = "Compte Google";
+"%@, Google account" = "%@, compte Google";
+"Google User" = "Utilisateur Google";
+
+/* MARK: - Rejoindre Zoom (parties non utilisées) */
+"Meeting ID" = "ID de réunion";
+"Enter meeting ID..." = "Entrez l'ID de la réunion…";
+"Meeting Passcode" = "Code d'accès de la réunion";
+"Enter meeting passcode..." = "Entrez le code d'accès de la réunion…";
+
+/* MARK: - Paywall (avantages alternatifs) */
+"Virtual backgrounds" = "Fonds d'écran virtuels";
+"Tools for productivity" = "Outils de productivité";
+"24/7 support" = "Soutien 24/7";
+
+/* MARK: - Intervenants de la transcription */
+"You" = "Vous";
+
+/* MARK: - Erreurs Trousseau (Keychain) */
+"Failed to read OAuth tokens from Keychain." = "Échec de la lecture des jetons OAuth depuis le Trousseau.";
+"Keychain returned an invalid token payload." = "Le Trousseau a renvoyé une charge utile de jeton invalide.";
+"Failed to update OAuth tokens in Keychain." = "Échec de la mise à jour des jetons OAuth dans le Trousseau.";
+"Failed to save OAuth tokens to Keychain." = "Échec de l'enregistrement des jetons OAuth dans le Trousseau.";
+"Failed to delete OAuth tokens from Keychain." = "Échec de la suppression des jetons OAuth du Trousseau.";
+"Unable to allocate audio buffer." = "Impossible d'allouer le tampon audio.";
+"Invalid revoke response." = "Réponse de révocation invalide.";
+"Token revoke failed with status %d." = "La révocation du jeton a échoué avec le statut %d.";
+
+/* MARK: - Diagnostics d'erreur */
+"Keychain: %d (%@)" = "Trousseau : %d (%@)";
+"OSStatus: %d" = "OSStatus : %d";
+"Item: service=%@, account=%@" = "Élément : service=%@, compte=%@";

+ 17 - 0
meetings_app/fr-CA.lproj/Main.strings

@@ -0,0 +1,17 @@
+/* Class = "NSMenuItem"; title = "AI Companion for Meet"; ObjectID = "1Xt-HY-uBw"; */
+"1Xt-HY-uBw.title" = "Assistant IA pour Meet";
+
+/* Class = "NSMenu"; title = "AI Companion for Meet"; ObjectID = "uQy-DD-JDr"; */
+"uQy-DD-JDr.title" = "Assistant IA pour Meet";
+
+/* Class = "NSMenuItem"; title = "About AI Companion for Meet"; ObjectID = "5kV-Vb-QxS"; */
+"5kV-Vb-QxS.title" = "À propos d'Assistant IA pour Meet";
+
+/* Class = "NSMenuItem"; title = "Hide AI Companion for Meet"; ObjectID = "Olw-nP-bQN"; */
+"Olw-nP-bQN.title" = "Masquer Assistant IA pour Meet";
+
+/* Class = "NSMenuItem"; title = "Quit AI Companion for Meet"; ObjectID = "4sb-4s-VLi"; */
+"4sb-4s-VLi.title" = "Quitter Assistant IA pour Meet";
+
+/* Class = "NSMenuItem"; title = "AI Companion for Meet Help"; ObjectID = "FKE-Sm-Kum"; */
+"FKE-Sm-Kum.title" = "Aide Assistant IA pour Meet";