diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 88fa1d1..71dde69 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -507,5 +507,9 @@ "message": "Number of times to retry failed requests" }, "showAdvancedSettings": { "message": "Show Advanced Settings" }, - "hideAdvancedSettings": { "message": "Hide Advanced Settings" } + "hideAdvancedSettings": { "message": "Hide Advanced Settings" }, + + "errTranslationApi": { "message": "[Translation API Error. Check settings or try another provider.]" }, + "errTranslationRequest": { "message": "[Translation Request Error. Please try again.]" }, + "errTranslationGeneric": { "message": "[Translation Failed. Please try again or check settings.]" } } diff --git a/_locales/es/messages.json b/_locales/es/messages.json index af2f976..66f5f67 100644 --- a/_locales/es/messages.json +++ b/_locales/es/messages.json @@ -573,5 +573,9 @@ "message": "Número de veces para reintentar solicitudes fallidas" }, "showAdvancedSettings": { "message": "Mostrar Configuración Avanzada" }, - "hideAdvancedSettings": { "message": "Ocultar Configuración Avanzada" } + "hideAdvancedSettings": { "message": "Ocultar Configuración Avanzada" }, + + "errTranslationApi": { "message": "[Error de API de Traducción. Revisa la configuración o prueba otro proveedor.]" }, + "errTranslationRequest": { "message": "[Error en la Solicitud de Traducción. Por favor, inténtalo de nuevo.]" }, + "errTranslationGeneric": { "message": "[Traducción Fallida. Por favor, inténtalo de nuevo o revisa la configuración.]" } } diff --git a/_locales/fr/messages.json b/_locales/fr/messages.json new file mode 100644 index 0000000..86aa857 --- /dev/null +++ b/_locales/fr/messages.json @@ -0,0 +1,462 @@ +{ + "appName": { "message": "DualSub" }, + "appDesc": { + "message": "Affiche des sous-titres bilingues sur les plateformes de streaming." + }, + "pageTitle": { "message": "Paramètres DualSub" }, + "h1Title": { "message": "DualSub" }, + "enableSubtitlesLabel": { "message": "Activer les sous-titres bilingues :" }, + "useNativeSubtitlesLabel": { "message": "Utiliser les sous-titres officiels :" }, + "originalLanguageLabel": { "message": "Langue d'origine :" }, + "translationSettingsLegend": { "message": "Paramètres de traduction" }, + "providerLabel": { "message": "Fournisseur :" }, + "targetLanguageLabel": { "message": "Traduire en :" }, + "batchSizeLabel": { "message": "Taille de lot :" }, + "requestDelayLabel": { "message": "Délai de requête (ms) :" }, + "subtitleAppearanceTimingLegend": { + "message": "Apparence et synchronisation des sous-titres" + }, + "displayOrderLabel": { "message": "Ordre d'affichage :" }, + "layoutLabel": { "message": "Disposition :" }, + "fontSizeLabel": { "message": "Taille de police :" }, + "verticalGapLabel": { "message": "Espacement vertical :" }, + "subtitleVerticalPositionLabel": { "message": "Position verticale :" }, + "timeOffsetLabel": { "message": "Décalage temporel (s) :" }, + "displayOrderOriginalFirst": { "message": "Original en premier" }, + "displayOrderTranslationFirst": { "message": "Traduction en premier" }, + "layoutTopBottom": { "message": "Haut / Bas" }, + "layoutLeftRight": { "message": "Gauche / Droite" }, + "uiLanguageLabel": { "message": "Langue :" }, + "openOptionsButton": { "message": "Paramètres avancés" }, + "statusLanguageSetTo": { "message": "Langue définie (actualiser la page) : " }, + "statusDualEnabled": { + "message": "Sous-titres bilingues activés. (Actualiser la page)" + }, + "statusDualDisabled": { + "message": "Sous-titres bilingues désactivés. (Actualiser la page)" + }, + "statusSmartTranslationEnabled": { + "message": "Traduction intelligente activée. (Actualiser la page)" + }, + "statusSmartTranslationDisabled": { + "message": "Traduction intelligente désactivée. (Actualiser la page)" + }, + "statusOriginalLanguage": { "message": "Langue définie (actualiser la page) : " }, + "statusTimeOffset": { "message": "Décalage temporel : " }, + "statusDisplayOrderUpdated": { "message": "Ordre d'affichage mis à jour." }, + "statusLayoutOrientationUpdated": { + "message": "Orientation de la disposition mise à jour." + }, + "statusFontSize": { "message": "Taille de police : " }, + "statusVerticalGap": { "message": "Espacement vertical : " }, + "statusVerticalPosition": { "message": "Position verticale : " }, + "statusInvalidOffset": { "message": "Décalage invalide, retour à la valeur précédente." }, + "statusSettingNotApplied": { + "message": "Paramètre non appliqué. Actualisez la page." + }, + + "optionsPageTitle": { "message": "Options DualSub" }, + "optionsH1Title": { "message": "DualSub" }, + "navGeneral": { "message": "Général" }, + "navTranslation": { "message": "Traduction" }, + "navProviders": { "message": "Fournisseurs" }, + "navAbout": { "message": "À propos" }, + "sectionGeneral": { "message": "Général" }, + "cardUILanguageTitle": { "message": "Langue de l'interface" }, + "cardUILanguageDesc": { + "message": "Choisissez la langue d'affichage de l'interface de l'extension." + }, + "cardHideOfficialSubtitlesTitle": { "message": "Masquer les sous-titres officiels" }, + "cardHideOfficialSubtitlesDesc": { + "message": "Masque les sous-titres officiels de la plateforme vidéo lorsque DualSub est actif." + }, + "hideOfficialSubtitlesLabel": { "message": "Masquer les sous-titres officiels :" }, + "sectionTranslation": { "message": "Traduction" }, + "cardTranslationEngineTitle": { "message": "Moteur de traduction" }, + "cardTranslationEngineDesc": { + "message": "Sélectionnez votre service de traduction préféré." + }, + "cardPerformanceTitle": { "message": "Performance" }, + "cardPerformanceDesc": { + "message": "Ajustez la gestion des requêtes de traduction pour équilibrer vitesse et stabilité." + }, + "sectionProviders": { "message": "Paramètres des fournisseurs" }, + "cardDeepLTitle": { "message": "DeepL" }, + "cardDeepLDesc": { + "message": "Saisissez votre clé API DeepL Translate. Choisissez entre les offres Free et Pro." + }, + "apiKeyLabel": { "message": "Clé API :" }, + "apiPlanLabel": { "message": "Offre API :" }, + "apiPlanFree": { "message": "DeepL API Free" }, + "apiPlanPro": { "message": "DeepL API Pro" }, + "sectionAbout": { "message": "À propos" }, + "cardAboutTitle": { "message": "DualSub" }, + "aboutVersion": { "message": "Version" }, + "aboutDescription": { + "message": "Cette extension vous aide à regarder des vidéos avec des sous-titres en deux langues sur diverses plateformes." + }, + "aboutDevelopment": { "message": "Développé par QuellaMC & 1jifang." }, + + "lang_en": { "message": "Anglais" }, + "lang_es": { "message": "Espagnol" }, + "lang_fr": { "message": "Français" }, + "lang_de": { "message": "Allemand" }, + "lang_it": { "message": "Italien" }, + "lang_pt": { "message": "Portugais" }, + "lang_ja": { "message": "Japonais" }, + "lang_ko": { "message": "Coréen" }, + "lang_zh_CN": { "message": "Chinois (simplifié)" }, + "lang_zh_TW": { "message": "Chinois (traditionnel)" }, + "lang_ru": { "message": "Russe" }, + "lang_ar": { "message": "Arabe" }, + "lang_hi": { "message": "Hindi" }, + + "testDeepLButton": { "message": "Tester la connexion DeepL" }, + "deeplApiKeyError": { "message": "Veuillez d'abord saisir votre clé API DeepL." }, + "deeplTestNeedsTesting": { "message": "⚠️ La clé API DeepL doit être testée." }, + "testingButton": { "message": "Test en cours..." }, + "testingConnection": { "message": "Test de la connexion à DeepL..." }, + "deeplTestSuccess": { + "message": "✅ Test de l'API DeepL réussi !" + }, + "deeplTestUnexpectedFormat": { + "message": "⚠️ L'API DeepL a répondu avec un format inattendu" + }, + "deeplTestInvalidKey": { + "message": "❌ La clé API DeepL est invalide ou a été rejetée." + }, + "deeplTestQuotaExceeded": { + "message": "❌ Quota de l'API DeepL dépassé. Veuillez vérifier vos limites d'utilisation." + }, + "deeplTestApiError": { "message": "❌ Erreur de l'API DeepL (%d) : %s" }, + "deeplTestNetworkError": { + "message": "❌ Erreur réseau : impossible de se connecter à l'API DeepL. Vérifiez votre connexion internet." + }, + "deeplTestGenericError": { "message": "❌ Échec du test : %s" }, + "deepLApiUnavailable": { "message": "API DeepL indisponible" }, + "deepLApiUnavailableTooltip": { + "message": "Échec du chargement du script DeepL" + }, + "deeplApiNotLoadedError": { + "message": "❌ Le script de l'API DeepL n'est pas disponible. Veuillez actualiser la page." + }, + "cardGoogleTitle": { "message": "Google Traduction" }, + "cardGoogleDesc": { + "message": "Service de traduction gratuit fourni par Google. Aucune configuration supplémentaire requise." + }, + "cardMicrosoftTitle": { "message": "Microsoft Traduction" }, + "cardMicrosoftDesc": { + "message": "Service de traduction gratuit fourni par Microsoft Edge. Aucune configuration supplémentaire requise." + }, + "cardDeepLFreeTitle": { "message": "DeepL Traduction (Gratuit)" }, + "cardDeepLFreeDesc": { + "message": "Service DeepL gratuit avec des résultats de haute qualité. Aucune clé API requise - utilise l'interface web de DeepL." + }, + "providerStatus": { "message": "Statut :" }, + "statusReady": { "message": "Prêt à l'emploi" }, + "providerFeatures": { "message": "Fonctionnalités :" }, + "featureFree": { "message": "Gratuit" }, + "featureNoApiKey": { "message": "Aucune clé API requise" }, + "featureWideLanguageSupport": { "message": "Large prise en charge des langues" }, + "featureFastTranslation": { "message": "Traduction rapide" }, + "featureHighQuality": { "message": "Traduction de haute qualité" }, + "featureGoodPerformance": { "message": "Bonnes performances" }, + "featureHighestQuality": { "message": "Qualité de traduction la plus élevée" }, + "featureApiKeyRequired": { "message": "Clé API requise" }, + "featureLimitedLanguages": { "message": "Prise en charge limitée des langues" }, + "featureUsageLimits": { "message": "Limites d'utilisation applicables" }, + "featureMultipleBackups": { "message": "Multiples méthodes de secours" }, + "providerNotes": { "message": "Remarques :" }, + "noteSlowForSecurity": { + "message": "Légèrement plus lent en raison de mesures de sécurité" + }, + "noteAutoFallback": { + "message": "Basculement automatique vers des services alternatifs" + }, + "noteRecommendedDefault": { "message": "Recommandé comme fournisseur par défaut" }, + "providerGoogleName": { "message": "Google Traduction (Gratuit)" }, + "providerMicrosoftName": { "message": "Microsoft Traduction (Gratuit)" }, + "providerDeepLName": { "message": "DeepL (clé API requise)" }, + "providerDeepLFreeName": { "message": "DeepL Traduction (Gratuit)" }, + "providerOpenAICompatibleName": { + "message": "Compatible OpenAI (clé API requise)" + }, + "providerVertexGeminiName": { + "message": "Vertex AI Gemini (clé API requise)" + }, + "cardOpenAICompatibleTitle": { + "message": "Compatible OpenAI (clé API requise)" + }, + "cardOpenAICompatibleDesc": { + "message": "Saisissez votre clé API et vos paramètres pour les services compatibles OpenAI comme Gemini." + }, + "cardVertexGeminiTitle": { + "message": "Vertex AI Gemini (clé API requise)" + }, + "cardVertexGeminiDesc": { + "message": "Saisissez votre jeton d'accès et les paramètres du projet Vertex." + }, + "vertexAccessTokenLabel": { "message": "Jeton d'accès :" }, + "vertexProjectIdLabel": { "message": "ID du projet :" }, + "vertexLocationLabel": { "message": "Emplacement :" }, + "vertexModelLabel": { "message": "Modèle :" }, + "vertexMissingConfig": { + "message": "Veuillez saisir le jeton d'accès et l'ID du projet." + }, + "vertexConnectionFailed": { "message": "Échec de la connexion : %s" }, + "vertexServiceAccountLabel": { "message": "JSON du compte de service :" }, + "vertexImportButton": { "message": "Importer un fichier JSON" }, + "vertexRefreshButton": { "message": "🔄 Actualiser le jeton" }, + "vertexImportHint": { "message": "Remplit automatiquement les informations d'identification ci-dessous" }, + "vertexImporting": { "message": "Importation..." }, + "vertexRefreshingToken": { "message": "Actualisation du jeton d'accès..." }, + "vertexGeneratingToken": { "message": "Génération du jeton d'accès..." }, + "vertexImportSuccess": { + "message": "Compte de service importé et jeton généré." + }, + "vertexImportFailed": { "message": "Échec de l'importation : %s" }, + "vertexTokenRefreshed": { + "message": "Jeton d'accès actualisé avec succès." + }, + "vertexRefreshFailed": { "message": "Échec de l'actualisation du jeton : %s" }, + "vertexTokenExpired": { + "message": "⚠️ Jeton d'accès expiré. Cliquez sur Actualiser pour le renouveler." + }, + "vertexTokenExpiringSoon": { + "message": "⚠️ Le jeton expire dans %s minutes. Pensez à l'actualiser." + }, + "vertexConfigured": { + "message": "⚠️ Vertex AI configuré. Veuillez tester la connexion." + }, + "vertexNotConfigured": { + "message": "Veuillez importer le JSON du compte de service ou saisir les identifiants." + }, + "featureVertexServiceAccount": { "message": "Import du JSON du compte de service" }, + "featureVertexAutoToken": { "message": "Génération automatique de jeton" }, + "featureVertexGemini": { "message": "Modèles Google Gemini via Vertex AI" }, + "providerNote": { "message": "Remarque :" }, + "vertexNote": { + "message": "Les jetons d'accès expirent après 1 heure. Votre compte de service est stocké en toute sécurité pour un renouvellement facile du jeton - cliquez simplement sur le bouton Actualiser le jeton si nécessaire." + }, + "baseUrlLabel": { "message": "URL de base :" }, + "modelLabel": { "message": "Modèle :" }, + "featureCustomizable": { "message": "Point de terminaison et modèle personnalisables" }, + "fetchModelsButton": { "message": "Récupérer les modèles" }, + "testConnectionButton": { "message": "Tester la connexion" }, + "openaiApiKeyPlaceholder": { "message": "Saisissez votre clé API compatible OpenAI" }, + "openaiBaseUrlPlaceholder": { "message": "ex. https://api.openai.com/v1" }, + "openaiApiKeyError": { "message": "Veuillez d'abord saisir votre clé API." }, + "openaiApiKeyNeedsTesting": { "message": "⚠️ La clé API doit être testée." }, + "openaiTestNeedsTesting": { "message": "⚠️ La clé API compatible OpenAI doit être testée." }, + "openaiTestingConnection": { "message": "Test de la connexion..." }, + "openaiConnectionSuccessful": { "message": "Connexion réussie !" }, + "openaiConnectionFailed": { "message": "Échec de la connexion : %s" }, + "openaieFetchingModels": { "message": "Récupération des modèles..." }, + "openaiModelsFetchedSuccessfully": { "message": "Modèles récupérés avec succès." }, + "openaiFailedToFetchModels": { "message": "Échec de la récupération des modèles : %s" }, + + "cardLoggingLevelTitle": { "message": "Niveau de journalisation" }, + "cardLoggingLevelDesc": { + "message": "Contrôlez la quantité d'informations de débogage affichées dans la console du navigateur. Les niveaux supérieurs incluent tous les niveaux inférieurs." + }, + "loggingLevelLabel": { "message": "Niveau de journalisation :" }, + "loggingLevelOff": { "message": "Désactivé" }, + "loggingLevelError": { "message": "Erreurs uniquement" }, + "loggingLevelWarn": { "message": "Avertissements et erreurs" }, + "loggingLevelInfo": { "message": "Infos et plus" }, + "loggingLevelDebug": { "message": "Débogage (Tout)" }, + + "cardBatchTranslationTitle": { "message": "Traduction par lots" }, + "cardBatchTranslationDesc": { + "message": "La traduction par lots traite plusieurs segments de sous-titres ensemble, réduisant de 80 à 90 % les appels à l'API et améliorant les performances. Configurez les paramètres optimaux pour votre fournisseur de traduction préféré." + }, + "batchingEnabledLabel": { "message": "Activer la traduction par lots :" }, + "batchingEnabledHelp": { + "message": "Regroupe plusieurs segments de sous-titres en requêtes de traduction uniques" + }, + "useProviderDefaultsLabel": { "message": "Utiliser les paramètres optimisés du fournisseur :" }, + "useProviderDefaultsHelp": { + "message": "Utilise automatiquement les tailles de lot optimales pour chaque fournisseur" + }, + "globalBatchSizeLabel": { "message": "Taille de lot globale :" }, + "globalBatchSizeHelp": { + "message": "Nombre de segments de sous-titres à traiter ensemble (1-15)" + }, + "smartBatchingLabel": { "message": "Optimisation intelligente des lots :" }, + "smartBatchingHelp": { + "message": "Priorise les segments de sous-titres selon la position de lecture" + }, + "maxConcurrentBatchesLabel": { "message": "Nombre maximal de lots simultanés :" }, + "maxConcurrentBatchesHelp": { + "message": "Nombre de lots de traduction traités simultanément" + }, + + "cardProviderBatchTitle": { "message": "Tailles de lot spécifiques au fournisseur" }, + "cardProviderBatchDesc": { + "message": "Configurez les tailles de lot optimales pour chaque fournisseur de traduction. Ces paramètres sont utilisés lorsque \"Utiliser les paramètres optimisés du fournisseur\" est activé." + }, + "openaieBatchSizeLabel": { "message": "Taille de lot OpenAI :" }, + "openaieBatchSizeHelp": { "message": "Recommandé : 5-10 segments (par défaut : 8)" }, + "googleBatchSizeLabel": { "message": "Taille de lot Google Traduction :" }, + "googleBatchSizeHelp": { "message": "Recommandé : 3-5 segments (par défaut : 4)" }, + "deeplBatchSizeLabel": { "message": "Taille de lot DeepL :" }, + "deeplBatchSizeHelp": { "message": "Recommandé : 2-3 segments (par défaut : 3)" }, + "microsoftBatchSizeLabel": { "message": "Taille de lot Microsoft Traduction :" }, + "microsoftBatchSizeHelp": { "message": "Recommandé : 3-5 segments (par défaut : 4)" }, + "vertexBatchSizeLabel": { "message": "Taille de lot Vertex AI :" }, + "vertexBatchSizeHelp": { "message": "Recommandé : 5-10 segments (par défaut : 8)" }, + + "cardProviderDelayTitle": { "message": "Délais de requête spécifiques au fournisseur" }, + "cardProviderDelayDesc": { + "message": "Configurez des délais obligatoires entre les requêtes de traduction pour éviter les blocages de compte. Ces délais s'appliquent même lorsque le traitement par lots est activé." + }, + "openaieDelayLabel": { "message": "Délai des requêtes OpenAI (ms) :" }, + "openaieDelayHelp": { "message": "Délai minimal entre les requêtes (par défaut : 100 ms)" }, + "googleDelayLabel": { "message": "Délai des requêtes Google Traduction (ms) :" }, + "googleDelayHelp": { "message": "Délai requis pour éviter les blocages temporaires (par défaut : 1500 ms)" }, + "deeplDelayLabel": { "message": "Délai des requêtes DeepL API (ms) :" }, + "deeplDelayHelp": { "message": "Délai pour les requêtes DeepL API (par défaut : 500 ms)" }, + "deeplFreeDelayLabel": { "message": "Délai des requêtes DeepL Free (ms) :" }, + "deeplFreeDelayHelp": { "message": "Délai conservateur pour l'offre gratuite (par défaut : 2000 ms)" }, + "microsoftDelayLabel": { "message": "Délai des requêtes Microsoft Traduction (ms) :" }, + "microsoftDelayHelp": { "message": "Délai pour respecter les limites de caractères (par défaut : 800 ms)" }, + "vertexDelayLabel": { "message": "Délai des requêtes Vertex AI (ms) :" }, + "vertexDelayHelp": { "message": "Délai minimal entre les requêtes (par défaut : 100 ms)" }, + + "aiContextModalTitle": { "message": "Analyse de contexte IA" }, + "aiContextSelectedWords": { "message": "Mots sélectionnés" }, + "aiContextNoWordsSelected": { "message": "Aucun mot sélectionné" }, + "aiContextClickHint": { "message": "💡 Cliquez sur un mot pour l'ajouter ou le retirer." }, + "aiContextStartAnalysis": { "message": "Démarrer l'analyse" }, + "aiContextPauseAnalysis": { "message": "⏸ Pause" }, + "aiContextPauseAnalysisTitle": { "message": "Mettre l'analyse en pause" }, + "aiContextInitialMessage": { + "message": "Sélectionnez des mots dans les sous-titres pour commencer l'analyse." + }, + "aiContextAnalyzing": { "message": "Analyse du contexte..." }, + "aiContextPauseNote": { "message": "Cliquez sur ⏸ pour mettre l'analyse en pause" }, + "aiContextAnalysisFailed": { "message": "Analyse échouée" }, + "aiContextNoContent": { "message": "Aucun contenu d'analyse" }, + "aiContextNoContentMessage": { + "message": "Analyse terminée mais aucun contenu n'a été renvoyé." + }, + "aiContextDefinition": { "message": "📖 Définition" }, + "aiContextCultural": { "message": "🌍 Contexte culturel" }, + "aiContextCulturalSignificance": { "message": "⭐ Importance culturelle" }, + "aiContextHistorical": { "message": "📜 Contexte historique" }, + "aiContextHistoricalSignificance": { "message": "📜 Importance historique" }, + "aiContextEvolution": { "message": "🔄 Évolution dans le temps" }, + "aiContextLinguistic": { "message": "🔤 Analyse linguistique" }, + "aiContextGrammar": { "message": "📝 Grammaire et sémantique" }, + "aiContextUsage": { "message": "💡 Usage et exemples" }, + "aiContextExamples": { "message": "Exemples :" }, + "aiContextLearningTips": { "message": "🎯 Conseils d'apprentissage" }, + "aiContextRelatedExpressions": { "message": "🔗 Expressions liées" }, + "aiContextKeyInsights": { "message": "🔑 Points clés" }, + "aiContextTypeCultural": { "message": "Culturel" }, + "aiContextTypeHistorical": { "message": "Historique" }, + "aiContextTypeLinguistic": { "message": "Linguistique" }, + "aiContextTypeComprehensive": { "message": "Complet" }, + "aiContextTypeGeneric": { "message": "Contexte" }, + "aiContextClose": { "message": "Fermer" }, + "aiContextAnalysisResults": { "message": "Résultats de l'analyse" }, + "aiContextRetrying": { "message": "L'analyse a échoué, régénération..." }, + "aiContextRetryNotification": { "message": "Analyse échouée, nouvelle tentative..." }, + "aiContextRetryButton": { "message": "Réessayer" }, + "aiContextMalformedResponse": { + "message": "Le service IA a renvoyé un format de réponse invalide. Cela peut être dû à des problèmes temporaires du service." + }, + "aiContextJsonCodeBlock": { + "message": "Le service IA a renvoyé du code JSON non traité au lieu de données structurées. Cela indique une erreur de formatage de la réponse." + }, + "aiContextCulturalContext": { "message": "Contexte culturel :" }, + "aiContextSocialUsage": { "message": "Usage social :" }, + "aiContextRegionalNotes": { "message": "Notes régionales :" }, + "aiContextOrigins": { "message": "Origines :" }, + "aiContextHistoricalContext": { "message": "Contexte historique :" }, + "aiContextHistoricalSignificance": { "message": "Importance historique :" }, + "aiContextEvolution": { "message": "Évolution :" }, + "aiContextEtymology": { "message": "Étymologie :" }, + "aiContextGrammarNotes": { "message": "Notes de grammaire :" }, + "aiContextTranslationNotes": { "message": "Notes de traduction :" }, + "aiContextLinguisticAnalysis": { "message": "Analyse linguistique :" }, + "aiContextGrammarSemantics": { "message": "Grammaire et sémantique :" }, + "aiContextUsageExamples": { "message": "Usage et exemples :" }, + + "navAIContext": { "message": "Contexte IA" }, + "sectionAIContext": { "message": "Assistant de contexte IA" }, + "cardAIContextToggleTitle": { "message": "Activer l'analyse de contexte IA" }, + "cardAIContextToggleDesc": { + "message": "Active l'analyse contextuelle culturelle, historique et linguistique assistée par IA pour le texte des sous-titres. Cliquez sur des mots ou expressions dans les sous-titres pour obtenir des explications détaillées." + }, + "aiContextEnabledLabel": { "message": "Activer le contexte IA :" }, + "cardAIContextProviderTitle": { "message": "Fournisseur IA" }, + "cardAIContextProviderDesc": { + "message": "Choisissez le fournisseur de service IA pour l'analyse de contexte. Les fournisseurs peuvent offrir des qualités et des délais différents." + }, + "aiContextProviderLabel": { "message": "Fournisseur :" }, + "cardOpenAIContextTitle": { "message": "Configuration OpenAI" }, + "cardOpenAIContextDesc": { + "message": "Configurez vos paramètres d'API OpenAI pour l'analyse de contexte. Vous avez besoin d'une clé API OpenAI valide." + }, + "openaiApiKeyLabel": { "message": "Clé API :" }, + "openaiBaseUrlLabel": { "message": "URL de base :" }, + "openaiModelLabel": { "message": "Modèle :" }, + "cardGeminiContextTitle": { "message": "Configuration Google Gemini" }, + "cardGeminiContextDesc": { + "message": "Configurez vos paramètres API Google Gemini pour l'analyse de contexte. Vous avez besoin d'une clé API Gemini valide." + }, + "geminiApiKeyLabel": { "message": "Clé API :" }, + "geminiModelLabel": { "message": "Modèle :" }, + "cardAIContextTypesTitle": { "message": "Types de contexte" }, + "cardAIContextTypesDesc": { + "message": "Activez les types d'analyse contextuelle que vous souhaitez utiliser. Vous pouvez en activer plusieurs." + }, + "contextTypeCulturalLabel": { "message": "Contexte culturel :" }, + "contextTypeCulturalHelp": { + "message": "Analyse les références culturelles, idiomes et contexte social" + }, + "contextTypeHistoricalLabel": { "message": "Contexte historique :" }, + "contextTypeHistoricalHelp": { + "message": "Fournit des informations historiques et de période" + }, + "contextTypeLinguisticLabel": { "message": "Analyse linguistique :" }, + "contextTypeLinguisticHelp": { + "message": "Explique la grammaire, l'étymologie et la structure de la langue" + }, + "cardAIContextPrivacyTitle": { "message": "Confidentialité et données" }, + "cardAIContextPrivacyDesc": { + "message": "Contrôlez la manière dont vos données sont traitées pendant l'analyse contextuelle." + }, + "aiContextUserConsentLabel": { + "message": "J'accepte d'envoyer le texte des sous-titres aux fournisseurs IA pour analyse :" + }, + "aiContextUserConsentHelp": { + "message": "Requis pour le fonctionnement de l'analyse de contexte IA" + }, + "aiContextDataSharingLabel": { "message": "Autoriser les analyses d'utilisation anonymes :" }, + "aiContextDataSharingHelp": { + "message": "Aidez à améliorer le service en partageant des données d'utilisation anonymes" + }, + "cardAIContextAdvancedTitle": { "message": "Paramètres avancés" }, + "cardAIContextAdvancedDesc": { + "message": "Configurez les options avancées du comportement de l'analyse de contexte IA." + }, + "aiContextTimeoutLabel": { "message": "Délai d'attente (ms) :" }, + "aiContextTimeoutHelp": { "message": "Temps d'attente maximal pour la réponse IA" }, + "aiContextRateLimitLabel": { "message": "Limite de débit (requêtes/min) :" }, + "aiContextRateLimitHelp": { "message": "Nombre maximal de requêtes par minute" }, + "aiContextCacheEnabledLabel": { "message": "Activer le cache :" }, + "aiContextCacheEnabledHelp": { "message": "Mettre en cache les résultats d'analyse pour réduire les appels API" }, + "aiContextRetryAttemptsLabel": { "message": "Tentatives de réessai :" }, + "aiContextRetryAttemptsHelp": { "message": "Nombre de tentatives pour réessayer les requêtes échouées" }, + "showAdvancedSettings": { "message": "Afficher les paramètres avancés" }, + "hideAdvancedSettings": { "message": "Masquer les paramètres avancés" }, + + "errTranslationApi": { "message": "[Erreur de l'API de traduction. Vérifiez les paramètres ou essayez un autre fournisseur.]" }, + "errTranslationRequest": { "message": "[Erreur de requête de traduction. Veuillez réessayer.]" }, + "errTranslationGeneric": { "message": "[Échec de la traduction. Veuillez réessayer ou vérifier les paramètres.]" } + +} + + diff --git a/_locales/ja/messages.json b/_locales/ja/messages.json index d3c64ed..39dc742 100644 --- a/_locales/ja/messages.json +++ b/_locales/ja/messages.json @@ -512,5 +512,9 @@ "message": "失敗したリクエストを再試行する回数" }, "showAdvancedSettings": { "message": "詳細設定を表示" }, - "hideAdvancedSettings": { "message": "詳細設定を非表示" } + "hideAdvancedSettings": { "message": "詳細設定を非表示" }, + + "errTranslationApi": { "message": "[翻訳API エラー。設定を確認するか、他のプロバイダーを試してください。]" }, + "errTranslationRequest": { "message": "[翻訳リクエスト エラー。もう一度お試しください。]" }, + "errTranslationGeneric": { "message": "[翻訳に失敗しました。もう一度試すか、設定を確認してください。]" } } diff --git a/_locales/ko/messages.json b/_locales/ko/messages.json index 8102b13..c462779 100644 --- a/_locales/ko/messages.json +++ b/_locales/ko/messages.json @@ -501,5 +501,9 @@ "message": "실패한 요청을 재시도하는 횟수" }, "showAdvancedSettings": { "message": "고급 설정 표시" }, - "hideAdvancedSettings": { "message": "고급 설정 숨기기" } + "hideAdvancedSettings": { "message": "고급 설정 숨기기" }, + + "errTranslationApi": { "message": "[번역 API 오류. 설정을 확인하거나 다른 제공업체를 시도해보세요.]" }, + "errTranslationRequest": { "message": "[번역 요청 오류. 다시 시도해주세요.]" }, + "errTranslationGeneric": { "message": "[번역에 실패했습니다. 다시 시도하거나 설정을 확인해주세요.]" } } diff --git a/_locales/zh_CN/messages.json b/_locales/zh_CN/messages.json index 440b560..3604ec5 100644 --- a/_locales/zh_CN/messages.json +++ b/_locales/zh_CN/messages.json @@ -429,5 +429,9 @@ "aiContextRetryAttemptsLabel": { "message": "重试次数:" }, "aiContextRetryAttemptsHelp": { "message": "重试失败请求的次数" }, "showAdvancedSettings": { "message": "显示高级设置" }, - "hideAdvancedSettings": { "message": "隐藏高级设置" } + "hideAdvancedSettings": { "message": "隐藏高级设置" }, + + "errTranslationApi": { "message": "[翻译API错误。请检查设置或尝试其他翻译源。]" }, + "errTranslationRequest": { "message": "[翻译请求错误。请重试。]" }, + "errTranslationGeneric": { "message": "[翻译失败。请重试或检查设置。]" } } diff --git a/_locales/zh_TW/messages.json b/_locales/zh_TW/messages.json index 4fe520b..36a256a 100644 --- a/_locales/zh_TW/messages.json +++ b/_locales/zh_TW/messages.json @@ -449,5 +449,9 @@ "aiContextRetryAttemptsLabel": { "message": "重試次數:" }, "aiContextRetryAttemptsHelp": { "message": "重試失敗請求的次數" }, "showAdvancedSettings": { "message": "顯示進階設定" }, - "hideAdvancedSettings": { "message": "隱藏進階設定" } + "hideAdvancedSettings": { "message": "隱藏進階設定" }, + + "errTranslationApi": { "message": "[翻譯API錯誤。請檢查設定或嘗試其他翻譯源。]" }, + "errTranslationRequest": { "message": "[翻譯請求錯誤。請重試。]" }, + "errTranslationGeneric": { "message": "[翻譯失敗。請重試或檢查設定。]" } } diff --git a/config/configSchema.js b/config/configSchema.js index 2160cbb..19e113c 100644 --- a/config/configSchema.js +++ b/config/configSchema.js @@ -25,6 +25,7 @@ function detectBrowserLanguage() { if (lang.startsWith('zh-tw')) return 'zh-TW'; if (lang.startsWith('zh')) return 'zh-CN'; if (lang.startsWith('es')) return 'es'; + if (lang.startsWith('fr')) return 'fr'; if (lang.startsWith('ja')) return 'ja'; if (lang.startsWith('ko')) return 'ko'; return 'en'; diff --git a/content_scripts/shared/subtitleUtilities.js b/content_scripts/shared/subtitleUtilities.js index 8912c5b..5808765 100644 --- a/content_scripts/shared/subtitleUtilities.js +++ b/content_scripts/shared/subtitleUtilities.js @@ -300,32 +300,6 @@ export function setInteractiveSubtitlesEnabled(enabled) { } logWithFallback('info', 'Subtitle utilities module loaded'); -const localizedErrorMessages = { - TRANSLATION_API_ERROR: { - en: '[Translation API Error. Check settings or try another provider.]', - es: '[Error de API de Traducción. Revisa la configuración o prueba otro proveedor.]', - ja: '[翻訳API エラー。設定を確認するか、他のプロバイダーを試してください。]', - ko: '[번역 API 오류. 설정을 확인하거나 다른 제공업체를 시도해보세요.]', - 'zh-CN': '[翻译API错误。请检查设置或尝试其他翻译源。]', - 'zh-TW': '[翻譯API錯誤。請檢查設定或嘗試其他翻譯源。]', - }, - TRANSLATION_REQUEST_ERROR: { - en: '[Translation Request Error. Please try again.]', - es: '[Error en la Solicitud de Traducción. Por favor, inténtalo de nuevo.]', - ja: '[翻訳リクエスト エラー。もう一度お試しください。]', - ko: '[번역 요청 오류. 다시 시도해주세요.]', - 'zh-CN': '[翻译请求错误。请重试。]', - 'zh-TW': '[翻譯請求錯誤。請重試。]', - }, - TRANSLATION_GENERIC_ERROR: { - en: '[Translation Failed. Please try again or check settings.]', - es: '[Traducción Fallida. Por favor, inténtalo de nuevo o revisa la configuración.]', - ja: '[翻訳に失敗しました。もう一度試すか、設定を確認してください。]', - ko: '[번역에 실패했습니다. 다시 시도하거나 설정을 확인해주세요.]', - 'zh-CN': '[翻译失败。请重试或检查设置。]', - 'zh-TW': '[翻譯失敗。請重試或檢查設定。]', - }, -}; export function getUILanguage() { const lang = ( @@ -337,24 +311,31 @@ export function getUILanguage() { if (lang.startsWith('zh-tw')) return 'zh-TW'; if (lang.startsWith('zh')) return 'zh-CN'; if (lang.startsWith('es')) return 'es'; + if (lang.startsWith('fr')) return 'fr'; if (lang.startsWith('ja')) return 'ja'; if (lang.startsWith('ko')) return 'ko'; return 'en'; } export function getLocalizedErrorMessage(errorTypeKey, details = '') { - const uiLang = getUILanguage(); - const messagesForType = localizedErrorMessages[errorTypeKey]; - if (messagesForType) { - return messagesForType[uiLang] || messagesForType['en']; + const keyMap = { + TRANSLATION_API_ERROR: 'errTranslationApi', + TRANSLATION_REQUEST_ERROR: 'errTranslationRequest', + TRANSLATION_GENERIC_ERROR: 'errTranslationGeneric', + }; + const messageKey = keyMap[errorTypeKey] || 'errTranslationGeneric'; + try { + if ( + typeof chrome !== 'undefined' && + chrome.i18n && + typeof chrome.i18n.getMessage === 'function' + ) { + return chrome.i18n.getMessage(messageKey); + } + } catch (e) { + // no-op; rely on default locale resolution elsewhere } - const fallbackMessages = - localizedErrorMessages['TRANSLATION_GENERIC_ERROR']; - return ( - fallbackMessages[uiLang] || - fallbackMessages['en'] || - '[Translation Error]' - ); + return errorTypeKey; } // Core state variables (these are NOT user preferences) diff --git a/options/components/sections/GeneralSection.jsx b/options/components/sections/GeneralSection.jsx index e7f021f..1e41807 100644 --- a/options/components/sections/GeneralSection.jsx +++ b/options/components/sections/GeneralSection.jsx @@ -27,6 +27,7 @@ export function GeneralSection({ t, settings, onSettingChange }) { > + diff --git a/translation_providers/geminiVertexTranslate.js b/translation_providers/geminiVertexTranslate.js index 405630d..59177fd 100644 --- a/translation_providers/geminiVertexTranslate.js +++ b/translation_providers/geminiVertexTranslate.js @@ -19,7 +19,6 @@ async function getConfig() { 'vertexLocation', 'vertexModel', ]); - // test gpg sign const model = config.vertexModel || 'gemini-2.5-flash';