From e84edd935bcb628778bd900e879c7b5c3f98d433 Mon Sep 17 00:00:00 2001 From: Kayvan Sylvan Date: Sat, 21 Feb 2026 15:48:01 -0800 Subject: [PATCH] refactor: replace `fmt.Errorf("%s", ...)` with `errors.New()` and normalize i18n strings - Replace `fmt.Errorf("%s", ...)` with `errors.New()` across all packages - Add `errors` import where needed, remove unused `fmt` imports - Lowercase error message strings in i18n locale files for Go conventions - Add `plugin_registry_run_setup_select_defaults` i18n key for setup prompt - Add `plugin_registry_could_not_find_vendor` i18n key for vendor errors - Internationalize hardcoded English strings in `plugin_registry.go` - Update `db_error_loading_env_file` format verb from `%s` to `%w` for wrapping - Normalize error casing in en, de, es, fr, it, ja, pt-BR, pt-PT, zh, fa locales --- internal/cli/chat.go | 3 +- internal/cli/flags.go | 2 +- internal/cli/tools.go | 7 +-- internal/cli/transcribe.go | 3 +- internal/core/chatter.go | 4 +- internal/core/plugin_registry.go | 7 +-- internal/domain/attachment.go | 7 +-- internal/i18n/locales/de.json | 36 ++++++++------- internal/i18n/locales/en.json | 42 +++++++++-------- internal/i18n/locales/es.json | 46 ++++++++++--------- internal/i18n/locales/fa.json | 6 ++- internal/i18n/locales/fr.json | 40 ++++++++-------- internal/i18n/locales/it.json | 40 ++++++++-------- internal/i18n/locales/ja.json | 8 ++-- internal/i18n/locales/pt-BR.json | 40 ++++++++-------- internal/i18n/locales/pt-PT.json | 40 ++++++++-------- internal/i18n/locales/zh.json | 8 ++-- internal/plugins/ai/azure/azure.go | 8 ++-- .../plugins/ai/azure_entra/azure_entra.go | 5 +- .../plugins/ai/azurecommon/azurecommon.go | 5 +- internal/plugins/ai/copilot/copilot.go | 3 +- .../plugins/ai/digitalocean/digitalocean.go | 3 +- internal/plugins/ai/lmstudio/lmstudio.go | 13 +++--- internal/plugins/ai/ollama/ollama.go | 3 +- internal/plugins/ai/openai/openai_audio.go | 5 +- internal/plugins/ai/vendors.go | 3 +- internal/plugins/strategy/strategy.go | 3 +- internal/plugins/template/datetime.go | 5 +- .../plugins/template/extension_executor.go | 7 +-- .../plugins/template/extension_manager.go | 3 +- .../plugins/template/extension_registry.go | 11 +++-- internal/plugins/template/file.go | 7 +-- internal/plugins/template/sys.go | 3 +- internal/plugins/template/template.go | 3 +- internal/server/ollama.go | 29 ++++++------ internal/tools/notifications/notifications.go | 4 +- internal/tools/spotify/spotify.go | 3 +- internal/tools/youtube/youtube.go | 19 ++++---- 38 files changed, 264 insertions(+), 220 deletions(-) diff --git a/internal/cli/chat.go b/internal/cli/chat.go index b88eeebb..277c0b03 100644 --- a/internal/cli/chat.go +++ b/internal/cli/chat.go @@ -1,6 +1,7 @@ package cli import ( + "errors" "fmt" "os" "os/exec" @@ -185,7 +186,7 @@ func sendNotification(options *domain.ChatOptions, patternName, result string) e // Use built-in notification system notificationManager := notifications.NewNotificationManager() if !notificationManager.IsAvailable() { - return fmt.Errorf("%s", i18n.T("no_notification_system_available")) + return errors.New(i18n.T("no_notification_system_available")) } return notificationManager.Send(title, message) diff --git a/internal/cli/flags.go b/internal/cli/flags.go index 51146a53..4bff3b26 100644 --- a/internal/cli/flags.go +++ b/internal/cli/flags.go @@ -373,7 +373,7 @@ func validateImageParameters(imagePath, size, quality, background string, compre if imagePath == "" { // Check if any image parameters are specified without --image-file if size != "" || quality != "" || background != "" || compression != 0 { - return fmt.Errorf("%s", i18n.T("image_parameters_require_image_file")) + return errors.New(i18n.T("image_parameters_require_image_file")) } return nil } diff --git a/internal/cli/tools.go b/internal/cli/tools.go index c9ab2c05..5881226c 100644 --- a/internal/cli/tools.go +++ b/internal/cli/tools.go @@ -1,6 +1,7 @@ package cli import ( + "errors" "fmt" "github.com/danielmiessler/fabric/internal/core" @@ -12,7 +13,7 @@ import ( func handleToolProcessing(currentFlags *Flags, registry *core.PluginRegistry) (messageTools string, err error) { if currentFlags.YouTube != "" { if !registry.YouTube.IsConfigured() { - err = fmt.Errorf("%s", i18n.T("youtube_not_configured")) + err = errors.New(i18n.T("youtube_not_configured")) return } @@ -59,7 +60,7 @@ func handleToolProcessing(currentFlags *Flags, registry *core.PluginRegistry) (m if currentFlags.ScrapeURL != "" || currentFlags.ScrapeQuestion != "" { if !registry.Jina.IsConfigured() { - err = fmt.Errorf("%s", i18n.T("scraping_not_configured")) + err = errors.New(i18n.T("scraping_not_configured")) return } // Check if the scrape_url flag is set and call ScrapeURL @@ -90,7 +91,7 @@ func handleToolProcessing(currentFlags *Flags, registry *core.PluginRegistry) (m // Handle Spotify podcast/episode metadata if currentFlags.Spotify != "" { if !registry.Spotify.IsConfigured() { - err = fmt.Errorf("%s", i18n.T("spotify_not_configured")) + err = errors.New(i18n.T("spotify_not_configured")) return } diff --git a/internal/cli/transcribe.go b/internal/cli/transcribe.go index d8965182..16620b97 100644 --- a/internal/cli/transcribe.go +++ b/internal/cli/transcribe.go @@ -2,6 +2,7 @@ package cli import ( "context" + "errors" "fmt" "github.com/danielmiessler/fabric/internal/core" @@ -28,7 +29,7 @@ func handleTranscription(flags *Flags, registry *core.PluginRegistry) (message s } model := flags.TranscribeModel if model == "" { - return "", fmt.Errorf("%s", i18n.T("transcription_model_required")) + return "", errors.New(i18n.T("transcription_model_required")) } if message, err = tr.TranscribeFile(context.Background(), flags.TranscribeFile, model, flags.SplitMediaFile); err != nil { return diff --git a/internal/core/chatter.go b/internal/core/chatter.go index cc60033d..dd6ca02a 100644 --- a/internal/core/chatter.go +++ b/internal/core/chatter.go @@ -48,7 +48,7 @@ func (o *Chatter) Send(request *domain.ChatRequest, opts *domain.ChatOptions) (s return } } - err = fmt.Errorf("%s", i18n.T("chatter_error_no_messages_provided")) + err = errors.New(i18n.T("chatter_error_no_messages_provided")) return } @@ -136,7 +136,7 @@ func (o *Chatter) Send(request *domain.ChatRequest, opts *domain.ChatOptions) (s if message == "" { session = nil - err = fmt.Errorf("%s", i18n.T("chatter_error_empty_response")) + err = errors.New(i18n.T("chatter_error_empty_response")) return } diff --git a/internal/core/plugin_registry.go b/internal/core/plugin_registry.go index f6069b26..a5b7c014 100644 --- a/internal/core/plugin_registry.go +++ b/internal/core/plugin_registry.go @@ -2,6 +2,7 @@ package core import ( "bytes" + "errors" "fmt" "io" "os" @@ -300,7 +301,7 @@ func (o *PluginRegistry) runVendorSetup() (err error) { } if setupQuestion.Value == "" { - return fmt.Errorf("%s", i18n.T("setup_no_ai_provider_selected")) + return errors.New(i18n.T("setup_no_ai_provider_selected")) } number, parseErr := strconv.Atoi(setupQuestion.Value) @@ -576,9 +577,9 @@ func (o *PluginRegistry) GetChatter(model string, modelContextLength int, vendor if ret.vendor == nil { var errMsg string if defaultModel == "" || defaultVendor == "" { - errMsg = "Please run, fabric --setup, and select default model and vendor." + errMsg = i18n.T("plugin_registry_run_setup_select_defaults") } else { - errMsg = "could not find vendor." + errMsg = i18n.T("plugin_registry_could_not_find_vendor") } err = fmt.Errorf( " Requested Model = %s\n Default Model = %s\n Default Vendor = %s.\n\n%s", diff --git a/internal/domain/attachment.go b/internal/domain/attachment.go index 7e311ee0..0e637d39 100644 --- a/internal/domain/attachment.go +++ b/internal/domain/attachment.go @@ -5,6 +5,7 @@ import ( "crypto/sha256" "encoding/base64" "encoding/json" + "errors" "fmt" "io" "net/http" @@ -74,7 +75,7 @@ func (a *Attachment) ResolveType() (ret string, err error) { ret = mimetype.Detect(a.Content).String() return } - err = fmt.Errorf("%s", i18n.T("attachment_no_type_no_content")) + err = errors.New(i18n.T("attachment_no_type_no_content")) return } @@ -100,7 +101,7 @@ func (a *Attachment) ContentBytes() (ret []byte, err error) { } return } - err = fmt.Errorf("%s", i18n.T("attachment_no_content_available")) + err = errors.New(i18n.T("attachment_no_content_available")) return } @@ -154,7 +155,7 @@ func detectMimeTypeFromURL(url string) (string, error) { defer resp.Body.Close() mimeType := resp.Header.Get("Content-Type") if mimeType == "" { - return "", fmt.Errorf("%s", i18n.T("attachment_could_not_determine_mimetype")) + return "", errors.New(i18n.T("attachment_could_not_determine_mimetype")) } return mimeType, nil } diff --git a/internal/i18n/locales/de.json b/internal/i18n/locales/de.json index 8c168831..a4f911cd 100644 --- a/internal/i18n/locales/de.json +++ b/internal/i18n/locales/de.json @@ -20,9 +20,9 @@ "azure_api_version_question": "Geben Sie die Azure API-Version ein (leer lassen für Standard)", "azure_base_url_question": "API Basis-URL", "azure_base_url_required": "Azure Basis-URL ist erforderlich", - "azure_credential_failure": "Fehler beim Erstellen der Azure-Anmeldeinformationen", + "azure_credential_failure": "Azure-Anmeldeinformationen konnten nicht erstellt werden", "azure_deployments_question": "Geben Sie Ihre Azure-Bereitstellungsnamen ein (kommagetrennt)", - "azure_deployments_required": "Mindestens ein Azure-Bereitstellungsname ist erforderlich", + "azure_deployments_required": "mindestens ein Azure-Bereitstellungsname ist erforderlich", "azure_failed_extract_deployment": "Bereitstellungsname konnte nicht aus der Anfrage extrahiert werden", "azure_model_field_empty": "Modellfeld ist im Anfragekörper leer", "azure_request_body_nil": "Anfragekörper ist nil", @@ -88,7 +88,7 @@ "custom_patterns_label": "Benutzerdefinierte Patterns", "custom_patterns_setup_description": "Benutzerdefinierte Patterns - Verzeichnis für Ihre benutzerdefinierten Patterns festlegen", "custom_patterns_warning_create_directory": "Warnung: Benutzerdefiniertes Musterverzeichnis %s konnte nicht erstellt werden: %v\n", - "db_error_loading_env_file": "Fehler beim Laden der .env-Datei: %s", + "db_error_loading_env_file": "fehler beim Laden der .env-Datei: %w", "defaults_model_context_length_question": "Geben Sie die Kontextlänge des Modells ein", "defaults_model_question": "Geben Sie den Index oder den Namen Ihres Standardmodells ein", "defaults_setup_description": "Standard-KI-Anbieter und -Modell", @@ -208,8 +208,8 @@ "help_message": "Diese Hilfenachricht anzeigen", "help_options_header": "Hilfe-Optionen:", "html_readability_error": "verwende ursprüngliche Eingabe, da HTML-Lesbarkeit nicht angewendet werden kann", - "i18n_download_failed": "Fehler beim Herunterladen der Übersetzung für Sprache '%s': %v", - "i18n_load_failed": "Fehler beim Laden der Übersetzungsdatei: %v", + "i18n_download_failed": "fehler beim Herunterladen der Übersetzung für Sprache '%s': %v", + "i18n_load_failed": "fehler beim Laden der Übersetzungsdatei: %v", "image_compression_jpeg_webp_only": "Bildkomprimierung kann nur mit JPEG- und WebP-Formaten verwendet werden, nicht %s", "image_compression_range_error": "Bildkomprimierung muss zwischen 0 und 100 liegen, erhalten: %d", "image_dimensions_help": "Bildabmessungen: 1024x1024, 1536x1024, 1024x1536, auto (Standard: auto)", @@ -260,17 +260,17 @@ "ollama_cannot_parse_url": "URL '%s' kann nicht geparst werden: %v", "ollama_chat_request_failed": "Chat-Anfrage fehlgeschlagen: %v", "ollama_empty_address": "Leere Adresse", - "ollama_error_building_chat_url": "Fehler beim Erstellen der /chat URL: %v", - "ollama_error_creating_chat_request": "Fehler beim Erstellen der /chat Anfrage: %v", + "ollama_error_building_chat_url": "fehler beim Erstellen der /chat URL: %v", + "ollama_error_creating_chat_request": "fehler beim Erstellen der /chat Anfrage: %v", "ollama_error_endpoint": "Endpunkt wird getestet", - "ollama_error_getting_chat_body": "Fehler beim Abrufen des /chat Bodys: %v", - "ollama_error_marshalling_body": "Fehler beim Marshalling des Bodys: %v", + "ollama_error_getting_chat_body": "fehler beim Abrufen des /chat Bodys: %v", + "ollama_error_marshalling_body": "fehler beim Marshalling des Bodys: %v", "ollama_error_parse_upstream_response": "Fehler: Upstream-Antwort konnte nicht geparst werden", "ollama_error_prefix": "Fehler: %s", - "ollama_error_reading_body": "Fehler beim Lesen des Bodys: %v", - "ollama_error_scanning_body": "Fehler beim Scannen des Bodys: %v", - "ollama_error_unmarshalling_body": "Fehler beim Unmarshalling des Bodys: %v", - "ollama_error_writing_response": "Fehler beim Schreiben der Antwort: %v", + "ollama_error_reading_body": "fehler beim Lesen des Bodys: %v", + "ollama_error_scanning_body": "fehler beim Scannen des Bodys: %v", + "ollama_error_unmarshalling_body": "fehler beim Unmarshalling des Bodys: %v", + "ollama_error_writing_response": "fehler beim Schreiben der Antwort: %v", "ollama_failed_create_request": "Fehler beim Erstellen der Anfrage", "ollama_failed_decode_data_url": "Data-URL konnte nicht dekodiert werden: %v", "ollama_failed_fetch_image": "Bild konnte nicht von %s abgerufen werden: %s", @@ -282,7 +282,7 @@ "ollama_invalid_address_missing_hostname": "Ungültige Adresse: Hostname fehlt", "ollama_invalid_address_path_not_allowed": "Ungültige Adresse: Pfadkomponente in bloßer Adresse nicht zulässig", "ollama_invalid_data_url_format": "ungültiges Data-URL-Format", - "ollama_invalid_http_timeout_using_default": "Ungültiges HTTP-Zeitlimit '%s': %v, verwende Standardwert", + "ollama_invalid_http_timeout_using_default": "ungültiges HTTP-Zeitlimit '%s': %v, verwende Standardwert", "ollama_invalid_num_ctx_in_request": "Ungültiger num_ctx in Anfrage: %v", "ollama_no_content_from_upstream": "Kein Inhalt vom Upstream Fabric Server erhalten", "ollama_num_ctx_exceeds_maximum": "num_ctx überschreitet den maximal zulässigen Wert von %d", @@ -419,7 +419,7 @@ "server_chat_error": "Fehler: %v", "server_error_marshaling_response": "Fehler beim Serialisieren der Antwort: %v", "server_error_writing_response": "Fehler beim Schreiben der Antwort: %v", - "server_invalid_request_format": "Ungültiges Anfrageformat: %v", + "server_invalid_request_format": "ungültiges Anfrageformat: %v", "sessions_creating_new": "Erstelle neue Sitzung: %s\n", "set_debug_level": "Debug-Level festlegen (0=aus, 1=grundlegend, 2=detailliert, 3=Trace)", "set_frequency_penalty": "Häufigkeitsstrafe festlegen", @@ -650,5 +650,7 @@ "youtube_url_is_playlist_not_video": "URL ist eine Playlist, kein Video", "youtube_video_id_title_header": "VideoID: Titel", "youtube_ytdlp_not_found": "yt-dlp wurde nicht in PATH gefunden. Bitte installiere yt-dlp, um die YouTube-Transkript-Funktionalität zu nutzen", - "youtube_ytdlp_stderr_error": "Fehler beim Lesen von yt-dlp stderr" -} \ No newline at end of file + "youtube_ytdlp_stderr_error": "fehler beim Lesen von yt-dlp stderr", + "plugin_registry_run_setup_select_defaults": "bitte führen Sie 'fabric --setup' aus und wählen Sie Standardmodell und -anbieter", + "plugin_registry_could_not_find_vendor": "Anbieter konnte nicht gefunden werden" +} diff --git a/internal/i18n/locales/en.json b/internal/i18n/locales/en.json index ccfb74fe..119e6b87 100644 --- a/internal/i18n/locales/en.json +++ b/internal/i18n/locales/en.json @@ -20,9 +20,9 @@ "azure_api_version_question": "Enter the Azure API version (leave blank for default)", "azure_base_url_question": "API Base URL", "azure_base_url_required": "Azure base URL is required", - "azure_credential_failure": "Failed to create Azure credential", + "azure_credential_failure": "failed to create Azure credential", "azure_deployments_question": "Enter your Azure deployment names (comma-separated)", - "azure_deployments_required": "At least one Azure deployment name is required", + "azure_deployments_required": "at least one Azure deployment name is required", "azure_failed_extract_deployment": "failed to extract deployment name from request", "azure_model_field_empty": "model field is empty in request body", "azure_request_body_nil": "request body is nil", @@ -68,7 +68,7 @@ "config_file_not_found": "config file not found: %s", "convert_html_readability": "Convert HTML input into a clean, readable view", "copilot_debug_created_conversation": "Created Copilot conversation: %s", - "copilot_debug_failed_parse_sse_event": "Failed to parse SSE event: %v", + "copilot_debug_failed_parse_sse_event": "failed to parse SSE event: %v", "copilot_error_chat_request": "chat request failed: %s - %s", "copilot_error_create_conversation": "failed to create conversation: %s - %s", "copilot_error_reading_stream": "error reading stream: %w", @@ -88,7 +88,7 @@ "custom_patterns_label": "Custom Patterns", "custom_patterns_setup_description": "Custom Patterns - Set directory for your custom patterns", "custom_patterns_warning_create_directory": "Warning: Could not create custom patterns directory %s: %v\n", - "db_error_loading_env_file": "error loading .env file: %s", + "db_error_loading_env_file": "error loading .env file: %w", "defaults_model_context_length_question": "Enter model context length", "defaults_model_question": "Enter the index or the name of your default model", "defaults_setup_description": "Default AI Vendor and Model", @@ -208,8 +208,8 @@ "help_message": "Show this help message", "help_options_header": "Help Options:", "html_readability_error": "use original input, because can't apply html readability", - "i18n_download_failed": "Failed to download translation for language '%s': %v", - "i18n_load_failed": "Failed to load translation file: %v", + "i18n_download_failed": "failed to download translation for language '%s': %v", + "i18n_load_failed": "failed to load translation file: %v", "image_compression_jpeg_webp_only": "image compression can only be used with JPEG and WebP formats, not %s", "image_compression_range_error": "image compression must be between 0 and 100, got %d", "image_dimensions_help": "Image dimensions: 1024x1024, 1536x1024, 1024x1536, auto (default: auto)", @@ -257,20 +257,20 @@ "no_notification_system_available": "no notification system available", "notifications_no_provider_available": "no notification provider available", "number_of_latest_patterns": "Number of latest patterns to list", - "ollama_cannot_parse_url": "Cannot parse URL '%s': %v", + "ollama_cannot_parse_url": "cannot parse URL '%s': %v", "ollama_chat_request_failed": "Chat request failed: %v", "ollama_empty_address": "empty address", - "ollama_error_building_chat_url": "Error building /chat URL: %v", - "ollama_error_creating_chat_request": "Error creating /chat request: %v", + "ollama_error_building_chat_url": "error building /chat URL: %v", + "ollama_error_creating_chat_request": "error creating /chat request: %v", "ollama_error_endpoint": "testing endpoint", - "ollama_error_getting_chat_body": "Error getting /chat body: %v", - "ollama_error_marshalling_body": "Error marshalling body: %v", + "ollama_error_getting_chat_body": "error getting /chat body: %v", + "ollama_error_marshalling_body": "error marshalling body: %v", "ollama_error_parse_upstream_response": "Error: failed to parse upstream response", "ollama_error_prefix": "Error: %s", - "ollama_error_reading_body": "Error reading body: %v", - "ollama_error_scanning_body": "Error scanning body: %v", - "ollama_error_unmarshalling_body": "Error unmarshalling body: %v", - "ollama_error_writing_response": "Error writing response: %v", + "ollama_error_reading_body": "error reading body: %v", + "ollama_error_scanning_body": "error scanning body: %v", + "ollama_error_unmarshalling_body": "error unmarshalling body: %v", + "ollama_error_writing_response": "error writing response: %v", "ollama_failed_create_request": "failed to create request", "ollama_failed_decode_data_url": "failed to decode data URL: %v", "ollama_failed_fetch_image": "failed to fetch image from %s: %s", @@ -282,8 +282,8 @@ "ollama_invalid_address_missing_hostname": "invalid address: missing hostname", "ollama_invalid_address_path_not_allowed": "invalid address: path component not allowed in bare address", "ollama_invalid_data_url_format": "invalid data URL format", - "ollama_invalid_http_timeout_using_default": "Invalid HTTP timeout '%s': %v, using default", - "ollama_invalid_num_ctx_in_request": "Invalid num_ctx in request: %v", + "ollama_invalid_http_timeout_using_default": "invalid HTTP timeout '%s': %v, using default", + "ollama_invalid_num_ctx_in_request": "invalid num_ctx in request: %v", "ollama_no_content_from_upstream": "no content received from upstream Fabric server", "ollama_num_ctx_exceeds_maximum": "num_ctx exceeds maximum allowed value of %d", "ollama_num_ctx_invalid_type": "num_ctx must be a number, got invalid type", @@ -419,7 +419,7 @@ "server_chat_error": "Error: %v", "server_error_marshaling_response": "error marshaling response: %v", "server_error_writing_response": "error writing response: %v", - "server_invalid_request_format": "Invalid request format: %v", + "server_invalid_request_format": "invalid request format: %v", "sessions_creating_new": "Creating new session: %s\n", "set_debug_level": "Set debug level (0=off, 1=basic, 2=detailed, 3=trace)", "set_frequency_penalty": "Set frequency penalty", @@ -628,7 +628,7 @@ "youtube_error_parsing_duration": "error parsing video duration: %v", "youtube_error_saving_csv": "error saving videos to CSV: %v", "youtube_failed_create_temp_dir": "failed to create temp directory: %v", - "youtube_failed_fetch_comments": "Failed to fetch comments: %v", + "youtube_failed_fetch_comments": "failed to fetch comments: %v", "youtube_failed_walk_directory": "failed to walk directory: %v", "youtube_invalid_duration_string": "invalid duration string: %s", "youtube_invalid_seconds_format": "invalid seconds format %q: %w", @@ -650,5 +650,7 @@ "youtube_url_is_playlist_not_video": "URL is a playlist, not a video", "youtube_video_id_title_header": "VideoID: Title", "youtube_ytdlp_not_found": "yt-dlp not found in PATH. Please install yt-dlp to use YouTube transcript functionality", - "youtube_ytdlp_stderr_error": "Error reading yt-dlp stderr" + "youtube_ytdlp_stderr_error": "error reading yt-dlp stderr", + "plugin_registry_run_setup_select_defaults": "please run 'fabric --setup' and select default model and vendor", + "plugin_registry_could_not_find_vendor": "could not find vendor" } diff --git a/internal/i18n/locales/es.json b/internal/i18n/locales/es.json index 028f743d..5198c285 100644 --- a/internal/i18n/locales/es.json +++ b/internal/i18n/locales/es.json @@ -16,16 +16,16 @@ "available_models_header": "Modelos disponibles", "available_transcription_models": "Modelos de transcripción disponibles:", "available_vendors_header": "Proveedores Disponibles:", - "azure_api_key_required": "Se requiere la clave API de Azure", + "azure_api_key_required": "se requiere la clave API de Azure", "azure_api_version_question": "Ingrese la versión de la API de Azure (deje en blanco para el valor predeterminado)", "azure_base_url_question": "URL base de la API", - "azure_base_url_required": "Se requiere la URL base de Azure", + "azure_base_url_required": "se requiere la URL base de Azure", "azure_deployments_question": "Ingrese los nombres de sus implementaciones de Azure (separados por comas)", - "azure_deployments_required": "Se requiere al menos un nombre de implementación de Azure", + "azure_deployments_required": "se requiere al menos un nombre de implementación de Azure", "azure_failed_extract_deployment": "no se pudo extraer el nombre de la implementación de la solicitud", "azure_model_field_empty": "el campo de modelo está vacío en el cuerpo de la solicitud", "azure_request_body_nil": "el cuerpo de la solicitud es nil", - "azure_credential_failure": "Error al crear la credencial de Azure", + "azure_credential_failure": "no se pudo crear la credencial de Azure", "background_type_help": "Tipo de fondo: opaque, transparent (predeterminado: opaque, solo para PNG/WebP)", "bedrock_aws_region_label": "Región de AWS", "bedrock_converse_failed": "bedrock converse falló para el modelo %s: %w", @@ -68,7 +68,7 @@ "config_file_not_found": "archivo de configuración no encontrado: %s", "convert_html_readability": "Convertir entrada HTML en una vista limpia y legible", "copilot_debug_created_conversation": "Conversación de Copilot creada: %s", - "copilot_debug_failed_parse_sse_event": "Error al analizar el evento SSE: %v", + "copilot_debug_failed_parse_sse_event": "error al analizar el evento SSE: %v", "copilot_error_chat_request": "solicitud de chat fallida: %s - %s", "copilot_error_create_conversation": "error al crear la conversación: %s - %s", "copilot_error_reading_stream": "error al leer la transmisión: %w", @@ -88,12 +88,12 @@ "custom_patterns_label": "Patrones personalizados", "custom_patterns_setup_description": "Patrones personalizados - Establecer directorio para tus patrones personalizados", "custom_patterns_warning_create_directory": "Advertencia: No se pudo crear el directorio de patrones personalizados %s: %v\n", - "db_error_loading_env_file": "Error al cargar el archivo .env: %s", + "db_error_loading_env_file": "error al cargar el archivo .env: %w", "defaults_model_context_length_question": "Introduce la longitud del contexto del modelo", "defaults_model_question": "Introduce el índice o el nombre de tu modelo predeterminado", "defaults_setup_description": "Proveedor y modelo de IA predeterminados", "digitalocean_failed_parse_control_plane_url": "No se pudo analizar la URL del plano de control de DigitalOcean: %w", - "digitalocean_model_list_unavailable": "Lista de modelos de DigitalOcean no disponible. Configure DIGITALOCEAN_TOKEN para obtener modelos del plano de control", + "digitalocean_model_list_unavailable": "lista de modelos de DigitalOcean no disponible. Configure DIGITALOCEAN_TOKEN para obtener modelos del plano de control", "disable_openai_responses_api": "Deshabilitar API de Respuestas de OpenAI (predeterminado: false)", "disable_pattern_variable_replacement": "Deshabilitar reemplazo de variables de patrón", "enable_web_search_tool": "Habilitar herramienta de búsqueda web para modelos soportados (Anthropic, OpenAI, Gemini)", @@ -208,8 +208,8 @@ "help_message": "Mostrar este mensaje de ayuda", "help_options_header": "Opciones de Ayuda:", "html_readability_error": "usa la entrada original, porque no se puede aplicar la legibilidad de html", - "i18n_download_failed": "Error al descargar traducción para el idioma '%s': %v", - "i18n_load_failed": "Error al cargar archivo de traducción: %v", + "i18n_download_failed": "error al descargar traducción para el idioma '%s': %v", + "i18n_load_failed": "error al cargar archivo de traducción: %v", "image_compression_jpeg_webp_only": "la compresión de imagen solo puede usarse con formatos JPEG y WebP, no %s", "image_compression_range_error": "la compresión de imagen debe estar entre 0 y 100, se obtuvo %d", "image_dimensions_help": "Dimensiones de imagen: 1024x1024, 1536x1024, 1024x1536, auto (predeterminado: auto)", @@ -260,17 +260,17 @@ "ollama_cannot_parse_url": "No se puede analizar la URL '%s': %v", "ollama_chat_request_failed": "Solicitud de chat fallida: %v", "ollama_empty_address": "dirección vacía", - "ollama_error_building_chat_url": "Error al construir la URL /chat: %v", - "ollama_error_creating_chat_request": "Error al crear la solicitud /chat: %v", + "ollama_error_building_chat_url": "error al construir la URL /chat: %v", + "ollama_error_creating_chat_request": "error al crear la solicitud /chat: %v", "ollama_error_endpoint": "probando endpoint", - "ollama_error_getting_chat_body": "Error al obtener el cuerpo /chat: %v", - "ollama_error_marshalling_body": "Error al serializar el cuerpo: %v", + "ollama_error_getting_chat_body": "error al obtener el cuerpo /chat: %v", + "ollama_error_marshalling_body": "error al serializar el cuerpo: %v", "ollama_error_parse_upstream_response": "Error: no se pudo analizar la respuesta upstream", "ollama_error_prefix": "Error: %s", - "ollama_error_reading_body": "Error al leer el cuerpo: %v", - "ollama_error_scanning_body": "Error al escanear el cuerpo: %v", - "ollama_error_unmarshalling_body": "Error al deserializar el cuerpo: %v", - "ollama_error_writing_response": "Error al escribir la respuesta: %v", + "ollama_error_reading_body": "error al leer el cuerpo: %v", + "ollama_error_scanning_body": "error al escanear el cuerpo: %v", + "ollama_error_unmarshalling_body": "error al deserializar el cuerpo: %v", + "ollama_error_writing_response": "error al escribir la respuesta: %v", "ollama_failed_create_request": "error al crear la solicitud", "ollama_failed_decode_data_url": "no se pudo decodificar la URL de datos: %v", "ollama_failed_fetch_image": "no se pudo obtener la imagen de %s: %s", @@ -381,7 +381,7 @@ "patterns_unique_file_created": "📝 Archivo de patrones únicos creado con %d patrones\\n", "patterns_warning_custom_directory": "Advertencia: no se pudo leer el directorio de patrones personalizado %s: %v\\n", "patterns_warning_remove_test_folder": "Advertencia: no se pudo eliminar la carpeta temporal de prueba '%s': %v\\n", - "perplexity_api_key_not_configured": "Clave API no configurada para %s. Configure la variable de entorno %s o ejecute 'fabric --setup' para configurar %s", + "perplexity_api_key_not_configured": "clave API no configurada para %s. Configure la variable de entorno %s o ejecute 'fabric --setup' para configurar %s", "perplexity_api_request_failed": "solicitud a la API de Perplexity fallida: %w", "perplexity_citations_header": "\n\n**Citas:**\n", "perplexity_failed_configure": "no se pudo configurar Perplexity: %w", @@ -419,7 +419,7 @@ "server_chat_error": "Error: %v", "server_error_marshaling_response": "error al serializar la respuesta: %v", "server_error_writing_response": "error al escribir la respuesta: %v", - "server_invalid_request_format": "Formato de solicitud no válido: %v", + "server_invalid_request_format": "formato de solicitud no válido: %v", "sessions_creating_new": "Creando nueva sesión: %s\n", "set_debug_level": "Establecer nivel de depuración (0=apagado, 1=básico, 2=detallado, 3=rastreo)", "set_frequency_penalty": "Establecer penalización de frecuencia", @@ -619,7 +619,7 @@ "vertexai_stream_error": "Error: %v", "wipe_context": "Limpiar contexto", "wipe_session": "Limpiar sesión", - "youtube_api_key_required": "Se requiere clave API de YouTube para comentarios y metadatos. Ejecute 'fabric --setup' para configurar", + "youtube_api_key_required": "se requiere clave API de YouTube para comentarios y metadatos. Ejecute 'fabric --setup' para configurar", "youtube_auth_required_bot_detection": "YouTube requiere autenticación (detección de bot). Usa --yt-dlp-args='--cookies-from-browser BROWSER' donde BROWSER puede ser chrome, firefox, brave, etc.", "youtube_empty_seconds_string": "cadena de segundos vacía", "youtube_error_getting_comments": "error al obtener comentarios: %v", @@ -647,8 +647,10 @@ "youtube_rate_limit_exceeded": "Límite de tasa de YouTube excedido. Intenta de nuevo más tarde o usa diferentes argumentos de yt-dlp como '--sleep-requests 1' para ralentizar las solicitudes.", "youtube_setup_description": "YouTube - para obtener transcripciones de video (vía yt-dlp) y comentarios/metadatos (vía API de YouTube)", "youtube_url_help": "Video de YouTube o \"URL\" de lista de reproducción para obtener transcripción, comentarios y enviar al chat o imprimir en la consola y almacenar en el archivo de salida", - "youtube_url_is_playlist_not_video": "La URL es una lista de reproducción, no un video", + "youtube_url_is_playlist_not_video": "la URL es una lista de reproducción, no un video", "youtube_video_id_title_header": "VideoID: Título", "youtube_ytdlp_not_found": "yt-dlp no encontrado en PATH. Por favor instala yt-dlp para usar la funcionalidad de transcripción de YouTube", - "youtube_ytdlp_stderr_error": "Error al leer stderr de yt-dlp" + "youtube_ytdlp_stderr_error": "error al leer stderr de yt-dlp", + "plugin_registry_run_setup_select_defaults": "ejecute 'fabric --setup' y seleccione el modelo y proveedor predeterminados", + "plugin_registry_could_not_find_vendor": "no se pudo encontrar el proveedor" } diff --git a/internal/i18n/locales/fa.json b/internal/i18n/locales/fa.json index 9099d985..37a863e5 100644 --- a/internal/i18n/locales/fa.json +++ b/internal/i18n/locales/fa.json @@ -88,7 +88,7 @@ "custom_patterns_label": "الگوهای سفارشی", "custom_patterns_setup_description": "الگوهای سفارشی - تنظیم دایرکتوری برای الگوهای سفارشی شما", "custom_patterns_warning_create_directory": "هشدار: امکان ایجاد پوشه الگوهای سفارشی %s وجود ندارد: %v\n", - "db_error_loading_env_file": "خطا در بارگذاری فایل .env: %s", + "db_error_loading_env_file": "خطا در بارگذاری فایل .env: %w", "defaults_model_context_length_question": "طول زمینه مدل را وارد کنید", "defaults_model_question": "شاخص یا نام مدل پیش‌فرض خود را وارد کنید", "defaults_setup_description": "ارائه‌دهنده و مدل هوش مصنوعی پیش‌فرض", @@ -650,5 +650,7 @@ "youtube_url_is_playlist_not_video": "URL یک فهرست پخش است، نه یک ویدیو", "youtube_video_id_title_header": "شناسه ویدیو: عنوان", "youtube_ytdlp_not_found": "yt-dlp در PATH یافت نشد. لطفاً yt-dlp را نصب کنید تا از قابلیت رونویسی یوتیوب استفاده کنید", - "youtube_ytdlp_stderr_error": "خطا در خواندن stderr yt-dlp" + "youtube_ytdlp_stderr_error": "خطا در خواندن stderr yt-dlp", + "plugin_registry_run_setup_select_defaults": "لطفاً 'fabric --setup' را اجرا کنید و مدل و ارائه‌دهنده پیش‌فرض را انتخاب کنید", + "plugin_registry_could_not_find_vendor": "ارائه‌دهنده پیدا نشد" } diff --git a/internal/i18n/locales/fr.json b/internal/i18n/locales/fr.json index f1479a89..5b0df3f9 100644 --- a/internal/i18n/locales/fr.json +++ b/internal/i18n/locales/fr.json @@ -16,13 +16,13 @@ "available_models_header": "Modèles disponibles", "available_transcription_models": "Modèles de transcription disponibles :", "available_vendors_header": "Fournisseurs disponibles :", - "azure_api_key_required": "La clé API Azure est requise", + "azure_api_key_required": "la clé API Azure est requise", "azure_api_version_question": "Entrez la version de l'API Azure (laissez vide pour la valeur par défaut)", "azure_base_url_question": "URL de base de l'API", - "azure_base_url_required": "L'URL de base Azure est requise", - "azure_credential_failure": "Échec de la création des identifiants Azure", + "azure_base_url_required": "l'URL de base Azure est requise", + "azure_credential_failure": "échec de la création des identifiants Azure", "azure_deployments_question": "Entrez les noms de vos déploiements Azure (séparés par des virgules)", - "azure_deployments_required": "Au moins un nom de déploiement Azure est requis", + "azure_deployments_required": "au moins un nom de déploiement Azure est requis", "azure_failed_extract_deployment": "échec de l'extraction du nom de déploiement de la requête", "azure_model_field_empty": "le champ modèle est vide dans le corps de la requête", "azure_request_body_nil": "le corps de la requête est nil", @@ -88,12 +88,12 @@ "custom_patterns_label": "Patrons personnalisés", "custom_patterns_setup_description": "Patrons personnalisés - Définir le répertoire pour vos patrons personnalisés", "custom_patterns_warning_create_directory": "Avertissement : Impossible de créer le répertoire de modèles personnalisés %s : %v\n", - "db_error_loading_env_file": "Erreur lors du chargement du fichier .env : %s", + "db_error_loading_env_file": "erreur lors du chargement du fichier .env : %w", "defaults_model_context_length_question": "Saisissez la longueur du contexte du modèle", "defaults_model_question": "Saisissez l'index ou le nom de votre modèle par défaut", "defaults_setup_description": "Fournisseur et modèle d'IA par défaut", "digitalocean_failed_parse_control_plane_url": "Impossible d'analyser l'URL du plan de contrôle DigitalOcean : %w", - "digitalocean_model_list_unavailable": "Liste des modèles DigitalOcean non disponible. Définissez DIGITALOCEAN_TOKEN pour récupérer les modèles depuis le plan de contrôle", + "digitalocean_model_list_unavailable": "liste des modèles DigitalOcean non disponible. Définissez DIGITALOCEAN_TOKEN pour récupérer les modèles depuis le plan de contrôle", "disable_openai_responses_api": "Désactiver l'API OpenAI Responses (par défaut : false)", "disable_pattern_variable_replacement": "Désactiver le remplacement des variables de motif", "enable_web_search_tool": "Activer l'outil de recherche web pour les modèles pris en charge (Anthropic, OpenAI, Gemini)", @@ -260,17 +260,17 @@ "ollama_cannot_parse_url": "Impossible d'analyser l'URL '%s' : %v", "ollama_chat_request_failed": "Requête de chat échouée : %v", "ollama_empty_address": "adresse vide", - "ollama_error_building_chat_url": "Erreur lors de la construction de l'URL /chat : %v", - "ollama_error_creating_chat_request": "Erreur lors de la création de la requête /chat : %v", + "ollama_error_building_chat_url": "erreur lors de la construction de l'URL /chat : %v", + "ollama_error_creating_chat_request": "erreur lors de la création de la requête /chat : %v", "ollama_error_endpoint": "test du point de terminaison", - "ollama_error_getting_chat_body": "Erreur lors de l'obtention du corps /chat : %v", - "ollama_error_marshalling_body": "Erreur lors de l'encodage du corps : %v", + "ollama_error_getting_chat_body": "erreur lors de l'obtention du corps /chat : %v", + "ollama_error_marshalling_body": "erreur lors de l'encodage du corps : %v", "ollama_error_parse_upstream_response": "Erreur : échec de l'analyse de la réponse en amont", "ollama_error_prefix": "Erreur : %s", - "ollama_error_reading_body": "Erreur lors de la lecture du corps : %v", - "ollama_error_scanning_body": "Erreur lors de l'analyse du corps : %v", - "ollama_error_unmarshalling_body": "Erreur lors du décodage du corps : %v", - "ollama_error_writing_response": "Erreur lors de l'écriture de la réponse : %v", + "ollama_error_reading_body": "erreur lors de la lecture du corps : %v", + "ollama_error_scanning_body": "erreur lors de l'analyse du corps : %v", + "ollama_error_unmarshalling_body": "erreur lors du décodage du corps : %v", + "ollama_error_writing_response": "erreur lors de l'écriture de la réponse : %v", "ollama_failed_create_request": "échec de création de la requête", "ollama_failed_decode_data_url": "échec du décodage de l'URL de données : %v", "ollama_failed_fetch_image": "échec de la récupération de l'image depuis %s : %s", @@ -381,7 +381,7 @@ "patterns_unique_file_created": "📝 Fichier de patrons uniques créé avec %d patrons\\n", "patterns_warning_custom_directory": "Avertissement : impossible de lire le répertoire de patrons personnalisé %s : %v\\n", "patterns_warning_remove_test_folder": "Avertissement : impossible de supprimer le dossier temporaire de test '%s' : %v\\n", - "perplexity_api_key_not_configured": "Clé API non configurée pour %s. Définissez la variable d'environnement %s ou exécutez 'fabric --setup' pour configurer %s", + "perplexity_api_key_not_configured": "clé API non configurée pour %s. Définissez la variable d'environnement %s ou exécutez 'fabric --setup' pour configurer %s", "perplexity_api_request_failed": "requête API Perplexity échouée : %w", "perplexity_citations_header": "\n\n**Citations :**\n", "perplexity_failed_configure": "échec de la configuration de Perplexity : %w", @@ -419,7 +419,7 @@ "server_chat_error": "Erreur : %v", "server_error_marshaling_response": "erreur de sérialisation de la réponse : %v", "server_error_writing_response": "erreur d'écriture de la réponse : %v", - "server_invalid_request_format": "Format de requête invalide : %v", + "server_invalid_request_format": "format de requête invalide : %v", "sessions_creating_new": "Création d'une nouvelle session : %s\n", "set_debug_level": "Définir le niveau de débogage (0=désactivé, 1=basique, 2=détaillé, 3=trace)", "set_frequency_penalty": "Définir la pénalité de fréquence", @@ -619,7 +619,7 @@ "vertexai_stream_error": "Erreur : %v", "wipe_context": "Effacer le contexte", "wipe_session": "Effacer la session", - "youtube_api_key_required": "Clé API YouTube requise pour les commentaires et métadonnées. Exécutez 'fabric --setup' pour configurer", + "youtube_api_key_required": "clé API YouTube requise pour les commentaires et métadonnées. Exécutez 'fabric --setup' pour configurer", "youtube_auth_required_bot_detection": "YouTube nécessite une authentification (détection de bot). Utilisez --yt-dlp-args='--cookies-from-browser BROWSER' où BROWSER peut être chrome, firefox, brave, etc.", "youtube_empty_seconds_string": "chaîne de secondes vide", "youtube_error_getting_comments": "erreur lors de l'obtention des commentaires : %v", @@ -647,8 +647,10 @@ "youtube_rate_limit_exceeded": "Limite de taux YouTube dépassée. Réessayez plus tard ou utilisez différents arguments yt-dlp comme '--sleep-requests 1' pour ralentir les requêtes.", "youtube_setup_description": "YouTube - pour récupérer les transcriptions vidéo (via yt-dlp) et les commentaires/métadonnées (via l'API YouTube)", "youtube_url_help": "Vidéo YouTube ou \"URL\" de liste de lecture pour récupérer la transcription, les commentaires et envoyer au chat ou afficher dans la console et stocker dans le fichier de sortie", - "youtube_url_is_playlist_not_video": "L'URL est une liste de lecture, pas une vidéo", + "youtube_url_is_playlist_not_video": "l'URL est une liste de lecture, pas une vidéo", "youtube_video_id_title_header": "VideoID : Titre", "youtube_ytdlp_not_found": "yt-dlp introuvable dans PATH. Veuillez installer yt-dlp pour utiliser la fonctionnalité de transcription YouTube", - "youtube_ytdlp_stderr_error": "Erreur lors de la lecture du stderr de yt-dlp" + "youtube_ytdlp_stderr_error": "erreur lors de la lecture du stderr de yt-dlp", + "plugin_registry_run_setup_select_defaults": "veuillez exécuter 'fabric --setup' et sélectionner le modèle et le fournisseur par défaut", + "plugin_registry_could_not_find_vendor": "fournisseur introuvable" } diff --git a/internal/i18n/locales/it.json b/internal/i18n/locales/it.json index 31cdb2f1..01a63112 100644 --- a/internal/i18n/locales/it.json +++ b/internal/i18n/locales/it.json @@ -16,13 +16,13 @@ "available_models_header": "Modelli disponibili", "available_transcription_models": "Modelli di trascrizione disponibili:", "available_vendors_header": "Fornitori disponibili:", - "azure_api_key_required": "La chiave API di Azure è obbligatoria", + "azure_api_key_required": "la chiave API di Azure è obbligatoria", "azure_api_version_question": "Inserire la versione dell'API Azure (lasciare vuoto per il valore predefinito)", "azure_base_url_question": "URL base dell'API", - "azure_base_url_required": "L'URL base di Azure è obbligatorio", - "azure_credential_failure": "Impossibile creare le credenziali Azure", + "azure_base_url_required": "l'URL base di Azure è obbligatorio", + "azure_credential_failure": "impossibile creare le credenziali Azure", "azure_deployments_question": "Inserire i nomi delle distribuzioni Azure (separati da virgola)", - "azure_deployments_required": "È necessario almeno un nome di distribuzione Azure", + "azure_deployments_required": "è necessario almeno un nome di distribuzione Azure", "azure_failed_extract_deployment": "impossibile estrarre il nome della distribuzione dalla richiesta", "azure_model_field_empty": "il campo modello è vuoto nel corpo della richiesta", "azure_request_body_nil": "il corpo della richiesta è nil", @@ -88,12 +88,12 @@ "custom_patterns_label": "Pattern personalizzati", "custom_patterns_setup_description": "Pattern personalizzati - Imposta la directory per i tuoi pattern personalizzati", "custom_patterns_warning_create_directory": "Avviso: Impossibile creare la directory dei modelli personalizzati %s: %v\n", - "db_error_loading_env_file": "Errore nel caricamento del file .env: %s", + "db_error_loading_env_file": "errore nel caricamento del file .env: %w", "defaults_model_context_length_question": "Inserisci la lunghezza del contesto del modello", "defaults_model_question": "Inserisci l'indice o il nome del tuo modello predefinito", "defaults_setup_description": "Fornitore e modello AI predefiniti", "digitalocean_failed_parse_control_plane_url": "Impossibile analizzare l'URL del piano di controllo DigitalOcean: %w", - "digitalocean_model_list_unavailable": "Lista modelli DigitalOcean non disponibile. Impostare DIGITALOCEAN_TOKEN per recuperare i modelli dal piano di controllo", + "digitalocean_model_list_unavailable": "lista modelli DigitalOcean non disponibile. Impostare DIGITALOCEAN_TOKEN per recuperare i modelli dal piano di controllo", "disable_openai_responses_api": "Disabilita API OpenAI Responses (predefinito: false)", "disable_pattern_variable_replacement": "Disabilita sostituzione variabili pattern", "enable_web_search_tool": "Abilita strumento di ricerca web per modelli supportati (Anthropic, OpenAI, Gemini)", @@ -260,17 +260,17 @@ "ollama_cannot_parse_url": "Impossibile analizzare l'URL '%s': %v", "ollama_chat_request_failed": "Richiesta di chat fallita: %v", "ollama_empty_address": "indirizzo vuoto", - "ollama_error_building_chat_url": "Errore nella costruzione dell'URL /chat: %v", - "ollama_error_creating_chat_request": "Errore nella creazione della richiesta /chat: %v", + "ollama_error_building_chat_url": "errore nella costruzione dell'URL /chat: %v", + "ollama_error_creating_chat_request": "errore nella creazione della richiesta /chat: %v", "ollama_error_endpoint": "test dell'endpoint", - "ollama_error_getting_chat_body": "Errore nell'ottenere il corpo /chat: %v", - "ollama_error_marshalling_body": "Errore nella serializzazione del corpo: %v", + "ollama_error_getting_chat_body": "errore nell'ottenere il corpo /chat: %v", + "ollama_error_marshalling_body": "errore nella serializzazione del corpo: %v", "ollama_error_parse_upstream_response": "Errore: impossibile analizzare la risposta upstream", "ollama_error_prefix": "Errore: %s", - "ollama_error_reading_body": "Errore nella lettura del corpo: %v", - "ollama_error_scanning_body": "Errore nella scansione del corpo: %v", - "ollama_error_unmarshalling_body": "Errore nella deserializzazione del corpo: %v", - "ollama_error_writing_response": "Errore nella scrittura della risposta: %v", + "ollama_error_reading_body": "errore nella lettura del corpo: %v", + "ollama_error_scanning_body": "errore nella scansione del corpo: %v", + "ollama_error_unmarshalling_body": "errore nella deserializzazione del corpo: %v", + "ollama_error_writing_response": "errore nella scrittura della risposta: %v", "ollama_failed_create_request": "impossibile creare la richiesta", "ollama_failed_decode_data_url": "decodifica dell'URL dati fallita: %v", "ollama_failed_fetch_image": "recupero dell'immagine da %s fallito: %s", @@ -381,7 +381,7 @@ "patterns_unique_file_created": "📝 File dei pattern univoci creato con %d pattern\\n", "patterns_warning_custom_directory": "Avviso: impossibile leggere la directory dei pattern personalizzata %s: %v\\n", "patterns_warning_remove_test_folder": "Avviso: impossibile rimuovere la cartella temporanea di test '%s': %v\\n", - "perplexity_api_key_not_configured": "Chiave API non configurata per %s. Imposta la variabile d'ambiente %s o esegui 'fabric --setup' per configurare %s", + "perplexity_api_key_not_configured": "chiave API non configurata per %s. Imposta la variabile d'ambiente %s o esegui 'fabric --setup' per configurare %s", "perplexity_api_request_failed": "richiesta API Perplexity fallita: %w", "perplexity_citations_header": "\n\n**Citazioni:**\n", "perplexity_failed_configure": "configurazione di Perplexity fallita: %w", @@ -419,7 +419,7 @@ "server_chat_error": "Errore: %v", "server_error_marshaling_response": "errore nella serializzazione della risposta: %v", "server_error_writing_response": "errore nella scrittura della risposta: %v", - "server_invalid_request_format": "Formato della richiesta non valido: %v", + "server_invalid_request_format": "formato della richiesta non valido: %v", "sessions_creating_new": "Creazione nuova sessione: %s\n", "set_debug_level": "Imposta livello di debug (0=spento, 1=base, 2=dettagliato, 3=traccia)", "set_frequency_penalty": "Imposta penalità di frequenza", @@ -619,7 +619,7 @@ "vertexai_stream_error": "Errore: %v", "wipe_context": "Cancella contesto", "wipe_session": "Cancella sessione", - "youtube_api_key_required": "Chiave API YouTube richiesta per commenti e metadati. Eseguire 'fabric --setup' per configurare", + "youtube_api_key_required": "chiave API YouTube richiesta per commenti e metadati. Eseguire 'fabric --setup' per configurare", "youtube_auth_required_bot_detection": "YouTube richiede autenticazione (rilevamento bot). Usa --yt-dlp-args='--cookies-from-browser BROWSER' dove BROWSER può essere chrome, firefox, brave, ecc.", "youtube_empty_seconds_string": "stringa di secondi vuota", "youtube_error_getting_comments": "errore nell'ottenere i commenti: %v", @@ -647,8 +647,10 @@ "youtube_rate_limit_exceeded": "Limite di richieste YouTube superato. Riprova più tardi o usa argomenti yt-dlp diversi come '--sleep-requests 1' per rallentare le richieste.", "youtube_setup_description": "YouTube - per ottenere trascrizioni video (tramite yt-dlp) e commenti/metadati (tramite API YouTube)", "youtube_url_help": "Video YouTube o \"URL\" della playlist per ottenere trascrizioni, commenti e inviarli alla chat o stamparli sulla console e memorizzarli nel file di output", - "youtube_url_is_playlist_not_video": "L'URL è una playlist, non un video", + "youtube_url_is_playlist_not_video": "l'URL è una playlist, non un video", "youtube_video_id_title_header": "VideoID: Titolo", "youtube_ytdlp_not_found": "yt-dlp non trovato in PATH. Per favore installa yt-dlp per usare la funzionalità di trascrizione YouTube", - "youtube_ytdlp_stderr_error": "Errore durante la lettura dello stderr di yt-dlp" + "youtube_ytdlp_stderr_error": "errore durante la lettura dello stderr di yt-dlp", + "plugin_registry_run_setup_select_defaults": "eseguire 'fabric --setup' e selezionare modello e fornitore predefiniti", + "plugin_registry_could_not_find_vendor": "impossibile trovare il fornitore" } diff --git a/internal/i18n/locales/ja.json b/internal/i18n/locales/ja.json index 3c537cc4..c11860e4 100644 --- a/internal/i18n/locales/ja.json +++ b/internal/i18n/locales/ja.json @@ -20,7 +20,7 @@ "azure_api_version_question": "Azure APIバージョンを入力してください(デフォルトの場合は空白のまま)", "azure_base_url_question": "API ベース URL", "azure_base_url_required": "Azure ベースURLが必要です", - "azure_credential_failure": "Azure 認証情報の作成に失敗しました", + "azure_credential_failure": "Azure資格情報の作成に失敗しました", "azure_deployments_question": "Azureデプロイメント名を入力してください(カンマ区切り)", "azure_deployments_required": "少なくとも1つのAzureデプロイメント名が必要です", "azure_failed_extract_deployment": "リクエストからデプロイメント名を抽出できませんでした", @@ -88,7 +88,7 @@ "custom_patterns_label": "カスタムパターン", "custom_patterns_setup_description": "カスタムパターン - カスタムパターン用のディレクトリを設定", "custom_patterns_warning_create_directory": "警告: カスタムパターンディレクトリ%sを作成できませんでした: %v\n", - "db_error_loading_env_file": ".envファイルの読み込みエラー: %s", + "db_error_loading_env_file": ".envファイルの読み込みエラー: %w", "defaults_model_context_length_question": "モデルのコンテキスト長を入力してください", "defaults_model_question": "デフォルトモデルのインデックスまたは名前を入力してください", "defaults_setup_description": "デフォルトのAIプロバイダーとモデル", @@ -650,5 +650,7 @@ "youtube_url_is_playlist_not_video": "URLはプレイリストであり、動画ではありません", "youtube_video_id_title_header": "動画ID: タイトル", "youtube_ytdlp_not_found": "PATHにyt-dlpが見つかりません。YouTubeトランスクリプト機能を使用するにはyt-dlpをインストールしてください", - "youtube_ytdlp_stderr_error": "yt-dlp stderrの読み取りエラー" + "youtube_ytdlp_stderr_error": "yt-dlp stderrの読み取りエラー", + "plugin_registry_run_setup_select_defaults": "'fabric --setup' を実行して、デフォルトのモデルとベンダーを選択してください", + "plugin_registry_could_not_find_vendor": "ベンダーが見つかりません" } diff --git a/internal/i18n/locales/pt-BR.json b/internal/i18n/locales/pt-BR.json index a9c25c35..1f2c4e3f 100644 --- a/internal/i18n/locales/pt-BR.json +++ b/internal/i18n/locales/pt-BR.json @@ -16,13 +16,13 @@ "available_models_header": "Modelos disponíveis", "available_transcription_models": "Modelos de transcrição disponíveis:", "available_vendors_header": "Fornecedores disponíveis:", - "azure_api_key_required": "A chave API do Azure é obrigatória", + "azure_api_key_required": "a chave API do Azure é obrigatória", "azure_api_version_question": "Insira a versão da API do Azure (deixe em branco para o padrão)", "azure_base_url_question": "URL base da API", - "azure_base_url_required": "A URL base do Azure é obrigatória", - "azure_credential_failure": "Falha ao criar a credencial do Azure", + "azure_base_url_required": "a URL base do Azure é obrigatória", + "azure_credential_failure": "falha ao criar credencial do Azure", "azure_deployments_question": "Insira os nomes das implantações do Azure (separados por vírgula)", - "azure_deployments_required": "Pelo menos um nome de implantação do Azure é obrigatório", + "azure_deployments_required": "pelo menos um nome de implantação do Azure é obrigatório", "azure_failed_extract_deployment": "falha ao extrair o nome da implantação da requisição", "azure_model_field_empty": "o campo de modelo está vazio no corpo da requisição", "azure_request_body_nil": "o corpo da requisição é nil", @@ -88,12 +88,12 @@ "custom_patterns_label": "Padrões personalizados", "custom_patterns_setup_description": "Padrões personalizados - Definir diretório para seus padrões personalizados", "custom_patterns_warning_create_directory": "Aviso: Não foi possível criar o diretório de padrões personalizados %s: %v\n", - "db_error_loading_env_file": "Erro ao carregar o arquivo .env: %s", + "db_error_loading_env_file": "erro ao carregar o arquivo .env: %w", "defaults_model_context_length_question": "Informe o comprimento do contexto do modelo", "defaults_model_question": "Informe o índice ou o nome do seu modelo padrão", "defaults_setup_description": "Provedor e modelo de IA padrão", "digitalocean_failed_parse_control_plane_url": "Falha ao analisar a URL do plano de controle do DigitalOcean: %w", - "digitalocean_model_list_unavailable": "Lista de modelos do DigitalOcean indisponível. Defina DIGITALOCEAN_TOKEN para buscar modelos do plano de controle", + "digitalocean_model_list_unavailable": "lista de modelos do DigitalOcean indisponível. Defina DIGITALOCEAN_TOKEN para buscar modelos do plano de controle", "disable_openai_responses_api": "Desabilitar API OpenAI Responses (padrão: false)", "disable_pattern_variable_replacement": "Desabilitar substituição de variáveis de padrão", "enable_web_search_tool": "Habilitar ferramenta de busca web para modelos suportados (Anthropic, OpenAI, Gemini)", @@ -260,17 +260,17 @@ "ollama_cannot_parse_url": "Não é possível analisar a URL '%s': %v", "ollama_chat_request_failed": "Requisição de chat falhou: %v", "ollama_empty_address": "endereço vazio", - "ollama_error_building_chat_url": "Erro ao construir a URL /chat: %v", - "ollama_error_creating_chat_request": "Erro ao criar a requisição /chat: %v", + "ollama_error_building_chat_url": "erro ao construir a URL /chat: %v", + "ollama_error_creating_chat_request": "erro ao criar a requisição /chat: %v", "ollama_error_endpoint": "testando endpoint", - "ollama_error_getting_chat_body": "Erro ao obter o corpo /chat: %v", - "ollama_error_marshalling_body": "Erro ao serializar o corpo: %v", + "ollama_error_getting_chat_body": "erro ao obter o corpo /chat: %v", + "ollama_error_marshalling_body": "erro ao serializar o corpo: %v", "ollama_error_parse_upstream_response": "Erro: falha ao analisar resposta upstream", "ollama_error_prefix": "Erro: %s", - "ollama_error_reading_body": "Erro ao ler o corpo: %v", - "ollama_error_scanning_body": "Erro ao escanear o corpo: %v", - "ollama_error_unmarshalling_body": "Erro ao desserializar o corpo: %v", - "ollama_error_writing_response": "Erro ao escrever resposta: %v", + "ollama_error_reading_body": "erro ao ler o corpo: %v", + "ollama_error_scanning_body": "erro ao escanear o corpo: %v", + "ollama_error_unmarshalling_body": "erro ao desserializar o corpo: %v", + "ollama_error_writing_response": "erro ao escrever resposta: %v", "ollama_failed_create_request": "falha ao criar a requisição", "ollama_failed_decode_data_url": "falha ao decodificar URL de dados: %v", "ollama_failed_fetch_image": "falha ao buscar imagem de %s: %s", @@ -381,7 +381,7 @@ "patterns_unique_file_created": "📝 Arquivo de padrões únicos criado com %d padrões\\n", "patterns_warning_custom_directory": "Aviso: não foi possível ler o diretório de padrões personalizado %s: %v\\n", "patterns_warning_remove_test_folder": "Aviso: não foi possível remover a pasta temporária de teste '%s': %v\\n", - "perplexity_api_key_not_configured": "Chave API não configurada para %s. Defina a variável de ambiente %s ou execute 'fabric --setup' para configurar %s", + "perplexity_api_key_not_configured": "chave API não configurada para %s. Defina a variável de ambiente %s ou execute 'fabric --setup' para configurar %s", "perplexity_api_request_failed": "requisição à API Perplexity falhou: %w", "perplexity_citations_header": "\n\n**Citações:**\n", "perplexity_failed_configure": "falha ao configurar Perplexity: %w", @@ -419,7 +419,7 @@ "server_chat_error": "Erro: %v", "server_error_marshaling_response": "erro ao serializar resposta: %v", "server_error_writing_response": "erro ao escrever resposta: %v", - "server_invalid_request_format": "Formato de solicitação inválido: %v", + "server_invalid_request_format": "formato de solicitação inválido: %v", "sessions_creating_new": "Criando nova sessão: %s\n", "set_debug_level": "Definir nível de debug (0=desligado, 1=básico, 2=detalhado, 3=rastreamento)", "set_frequency_penalty": "Definir penalidade de frequência", @@ -619,7 +619,7 @@ "vertexai_stream_error": "Erro: %v", "wipe_context": "Limpar contexto", "wipe_session": "Limpar sessão", - "youtube_api_key_required": "Chave de API do YouTube necessária para comentários e metadados. Execute 'fabric --setup' para configurar", + "youtube_api_key_required": "chave de API do YouTube necessária para comentários e metadados. Execute 'fabric --setup' para configurar", "youtube_auth_required_bot_detection": "YouTube requer autenticação (detecção de bot). Use --yt-dlp-args='--cookies-from-browser BROWSER' onde BROWSER pode ser chrome, firefox, brave, etc.", "youtube_empty_seconds_string": "string de segundos vazia", "youtube_error_getting_comments": "erro ao obter comentários: %v", @@ -647,8 +647,10 @@ "youtube_rate_limit_exceeded": "Limite de taxa do YouTube excedido. Tente novamente mais tarde ou use argumentos diferentes do yt-dlp como '--sleep-requests 1' para desacelerar as requisições.", "youtube_setup_description": "YouTube - para obter transcrições de vídeo (via yt-dlp) e comentários/metadados (via API do YouTube)", "youtube_url_help": "Vídeo do YouTube ou URL da playlist para obter transcrição, comentários e enviar ao chat ou imprimir no console e armazenar no arquivo de saída", - "youtube_url_is_playlist_not_video": "A URL é uma playlist, não um vídeo", + "youtube_url_is_playlist_not_video": "a URL é uma playlist, não um vídeo", "youtube_video_id_title_header": "VideoID: Título", "youtube_ytdlp_not_found": "yt-dlp não encontrado no PATH. Por favor instale o yt-dlp para usar a funcionalidade de transcrição do YouTube", - "youtube_ytdlp_stderr_error": "Erro ao ler stderr do yt-dlp" + "youtube_ytdlp_stderr_error": "erro ao ler stderr do yt-dlp", + "plugin_registry_run_setup_select_defaults": "execute 'fabric --setup' e selecione o modelo e fornecedor padrão", + "plugin_registry_could_not_find_vendor": "não foi possível encontrar o fornecedor" } diff --git a/internal/i18n/locales/pt-PT.json b/internal/i18n/locales/pt-PT.json index 808deda3..8e6b9bc5 100644 --- a/internal/i18n/locales/pt-PT.json +++ b/internal/i18n/locales/pt-PT.json @@ -16,13 +16,13 @@ "available_models_header": "Modelos disponíveis", "available_transcription_models": "Modelos de transcrição disponíveis:", "available_vendors_header": "Fornecedores disponíveis:", - "azure_api_key_required": "A chave API do Azure é obrigatória", + "azure_api_key_required": "a chave API do Azure é obrigatória", "azure_api_version_question": "Introduza a versão da API do Azure (deixe vazio para o padrão)", "azure_base_url_question": "URL base da API", - "azure_base_url_required": "O URL base do Azure é obrigatório", - "azure_credential_failure": "Falha ao criar a credencial do Azure", + "azure_base_url_required": "o URL base do Azure é obrigatório", + "azure_credential_failure": "falha ao criar credencial do Azure", "azure_deployments_question": "Introduza os nomes das implementações do Azure (separados por vírgula)", - "azure_deployments_required": "É necessário pelo menos um nome de implementação do Azure", + "azure_deployments_required": "é necessário pelo menos um nome de implementação do Azure", "azure_failed_extract_deployment": "falha ao extrair o nome da implementação do pedido", "azure_model_field_empty": "o campo de modelo está vazio no corpo do pedido", "azure_request_body_nil": "o corpo do pedido é nil", @@ -88,12 +88,12 @@ "custom_patterns_label": "Padrões personalizados", "custom_patterns_setup_description": "Padrões personalizados - Definir diretório para os seus padrões personalizados", "custom_patterns_warning_create_directory": "Aviso: Não foi possível criar o diretório de padrões personalizados %s: %v\n", - "db_error_loading_env_file": "Erro ao carregar o ficheiro .env: %s", + "db_error_loading_env_file": "erro ao carregar o ficheiro .env: %w", "defaults_model_context_length_question": "Indique o comprimento do contexto do modelo", "defaults_model_question": "Indique o índice ou o nome do seu modelo padrão", "defaults_setup_description": "Fornecedor e modelo de IA padrão", "digitalocean_failed_parse_control_plane_url": "Falha ao analisar o URL do plano de controlo do DigitalOcean: %w", - "digitalocean_model_list_unavailable": "Lista de modelos do DigitalOcean indisponível. Defina DIGITALOCEAN_TOKEN para obter modelos do plano de controlo", + "digitalocean_model_list_unavailable": "lista de modelos do DigitalOcean indisponível. Defina DIGITALOCEAN_TOKEN para obter modelos do plano de controlo", "disable_openai_responses_api": "Desabilitar API OpenAI Responses (por omissão: false)", "disable_pattern_variable_replacement": "Desabilitar substituição de variáveis de padrão", "enable_web_search_tool": "Habilitar ferramenta de pesquisa web para modelos suportados (Anthropic, OpenAI, Gemini)", @@ -260,17 +260,17 @@ "ollama_cannot_parse_url": "Não é possível analisar o URL '%s': %v", "ollama_chat_request_failed": "Pedido de chat falhou: %v", "ollama_empty_address": "endereço vazio", - "ollama_error_building_chat_url": "Erro ao construir a URL /chat: %v", - "ollama_error_creating_chat_request": "Erro ao criar o pedido /chat: %v", + "ollama_error_building_chat_url": "erro ao construir a URL /chat: %v", + "ollama_error_creating_chat_request": "erro ao criar o pedido /chat: %v", "ollama_error_endpoint": "a testar endpoint", - "ollama_error_getting_chat_body": "Erro ao obter o corpo /chat: %v", - "ollama_error_marshalling_body": "Erro ao serializar o corpo: %v", + "ollama_error_getting_chat_body": "erro ao obter o corpo /chat: %v", + "ollama_error_marshalling_body": "erro ao serializar o corpo: %v", "ollama_error_parse_upstream_response": "Erro: falha ao analisar resposta upstream", "ollama_error_prefix": "Erro: %s", - "ollama_error_reading_body": "Erro ao ler o corpo: %v", - "ollama_error_scanning_body": "Erro ao analisar o corpo: %v", - "ollama_error_unmarshalling_body": "Erro ao desserializar o corpo: %v", - "ollama_error_writing_response": "Erro ao escrever resposta: %v", + "ollama_error_reading_body": "erro ao ler o corpo: %v", + "ollama_error_scanning_body": "erro ao analisar o corpo: %v", + "ollama_error_unmarshalling_body": "erro ao desserializar o corpo: %v", + "ollama_error_writing_response": "erro ao escrever resposta: %v", "ollama_failed_create_request": "falha ao criar o pedido", "ollama_failed_decode_data_url": "falha ao descodificar URL de dados: %v", "ollama_failed_fetch_image": "falha ao obter imagem de %s: %s", @@ -381,7 +381,7 @@ "patterns_unique_file_created": "📝 Ficheiro de padrões únicos criado com %d padrões\\n", "patterns_warning_custom_directory": "Aviso: não foi possível ler o directório de padrões personalizado %s: %v\\n", "patterns_warning_remove_test_folder": "Aviso: não foi possível remover a pasta temporária de teste '%s': %v\\n", - "perplexity_api_key_not_configured": "Chave API não configurada para %s. Defina a variável de ambiente %s ou execute 'fabric --setup' para configurar %s", + "perplexity_api_key_not_configured": "chave API não configurada para %s. Defina a variável de ambiente %s ou execute 'fabric --setup' para configurar %s", "perplexity_api_request_failed": "pedido à API Perplexity falhou: %w", "perplexity_citations_header": "\n\n**Citações:**\n", "perplexity_failed_configure": "falha ao configurar Perplexity: %w", @@ -419,7 +419,7 @@ "server_chat_error": "Erro: %v", "server_error_marshaling_response": "erro ao serializar resposta: %v", "server_error_writing_response": "erro ao escrever resposta: %v", - "server_invalid_request_format": "Formato de pedido inválido: %v", + "server_invalid_request_format": "formato de pedido inválido: %v", "sessions_creating_new": "A criar nova sessão: %s\n", "set_debug_level": "Definir nível de debug (0=desligado, 1=básico, 2=detalhado, 3=rastreio)", "set_frequency_penalty": "Definir penalidade de frequência", @@ -619,7 +619,7 @@ "vertexai_stream_error": "Erro: %v", "wipe_context": "Limpar contexto", "wipe_session": "Limpar sessão", - "youtube_api_key_required": "Chave de API do YouTube necessária para comentários e metadados. Execute 'fabric --setup' para configurar", + "youtube_api_key_required": "chave de API do YouTube necessária para comentários e metadados. Execute 'fabric --setup' para configurar", "youtube_auth_required_bot_detection": "YouTube requer autenticação (deteção de bot). Use --yt-dlp-args='--cookies-from-browser BROWSER' onde BROWSER pode ser chrome, firefox, brave, etc.", "youtube_empty_seconds_string": "cadeia de segundos vazia", "youtube_error_getting_comments": "erro ao obter comentários: %v", @@ -647,8 +647,10 @@ "youtube_rate_limit_exceeded": "Limite de taxa do YouTube excedido. Tente novamente mais tarde ou utilize argumentos diferentes do yt-dlp como '--sleep-requests 1' para desacelerar os pedidos.", "youtube_setup_description": "YouTube - para obter transcrições de vídeo (via yt-dlp) e comentários/metadados (via API do YouTube)", "youtube_url_help": "Vídeo do YouTube ou \"URL\" de playlist para obter transcrição, comentários e enviar ao chat ou imprimir na consola e armazenar no ficheiro de saída", - "youtube_url_is_playlist_not_video": "O URL é uma lista de reprodução, não um vídeo", + "youtube_url_is_playlist_not_video": "o URL é uma lista de reprodução, não um vídeo", "youtube_video_id_title_header": "VideoID: Título", "youtube_ytdlp_not_found": "yt-dlp não encontrado no PATH. Por favor instale o yt-dlp para usar a funcionalidade de transcrição do YouTube", - "youtube_ytdlp_stderr_error": "Erro ao ler stderr do yt-dlp" + "youtube_ytdlp_stderr_error": "erro ao ler stderr do yt-dlp", + "plugin_registry_run_setup_select_defaults": "execute 'fabric --setup' e selecione o modelo e fornecedor padrão", + "plugin_registry_could_not_find_vendor": "não foi possível encontrar o fornecedor" } diff --git a/internal/i18n/locales/zh.json b/internal/i18n/locales/zh.json index 9c585d41..e0873182 100644 --- a/internal/i18n/locales/zh.json +++ b/internal/i18n/locales/zh.json @@ -20,7 +20,7 @@ "azure_api_version_question": "请输入 Azure API 版本(留空使用默认值)", "azure_base_url_question": "API 基础 URL", "azure_base_url_required": "Azure 基础 URL 是必需的", - "azure_credential_failure": "创建 Azure 凭据失败", + "azure_credential_failure": "Azure凭据创建失败", "azure_deployments_question": "请输入您的 Azure 部署名称(用逗号分隔)", "azure_deployments_required": "至少需要一个 Azure 部署名称", "azure_failed_extract_deployment": "无法从请求中提取部署名称", @@ -88,7 +88,7 @@ "custom_patterns_label": "自定义模式", "custom_patterns_setup_description": "自定义模式 - 设置您的自定义模式目录", "custom_patterns_warning_create_directory": "警告:无法创建自定义模式目录%s:%v\n", - "db_error_loading_env_file": "加载.env文件错误:%s", + "db_error_loading_env_file": "加载.env文件错误:%w", "defaults_model_context_length_question": "请输入模型上下文长度", "defaults_model_question": "请输入您的默认模型的索引或名称", "defaults_setup_description": "默认 AI 提供商和模型", @@ -650,5 +650,7 @@ "youtube_url_is_playlist_not_video": "URL 是播放列表,而不是视频", "youtube_video_id_title_header": "视频ID: 标题", "youtube_ytdlp_not_found": "在 PATH 中未找到 yt-dlp。请安装 yt-dlp 以使用 YouTube 转录功能", - "youtube_ytdlp_stderr_error": "读取 yt-dlp stderr 时出错" + "youtube_ytdlp_stderr_error": "读取 yt-dlp stderr 时出错", + "plugin_registry_run_setup_select_defaults": "请运行 'fabric --setup' 并选择默认模型和供应商", + "plugin_registry_could_not_find_vendor": "找不到供应商" } diff --git a/internal/plugins/ai/azure/azure.go b/internal/plugins/ai/azure/azure.go index f029ae5d..b2e78da5 100644 --- a/internal/plugins/ai/azure/azure.go +++ b/internal/plugins/ai/azure/azure.go @@ -1,7 +1,7 @@ package azure import ( - "fmt" + "errors" "strings" "github.com/danielmiessler/fabric/internal/i18n" @@ -35,17 +35,17 @@ type Client struct { func (oi *Client) configure() error { oi.apiDeployments = azurecommon.ParseDeployments(oi.ApiDeployments.Value) if len(oi.apiDeployments) == 0 { - return fmt.Errorf("%s", i18n.T("azure_deployments_required")) + return errors.New(i18n.T("azure_deployments_required")) } apiKey := strings.TrimSpace(oi.ApiKey.Value) if apiKey == "" { - return fmt.Errorf("%s", i18n.T("azure_api_key_required")) + return errors.New(i18n.T("azure_api_key_required")) } baseURL := strings.TrimSpace(oi.ApiBaseURL.Value) if baseURL == "" { - return fmt.Errorf("%s", i18n.T("azure_base_url_required")) + return errors.New(i18n.T("azure_base_url_required")) } apiVersion := strings.TrimSpace(oi.ApiVersion.Value) diff --git a/internal/plugins/ai/azure_entra/azure_entra.go b/internal/plugins/ai/azure_entra/azure_entra.go index af61ff27..2ee2d591 100644 --- a/internal/plugins/ai/azure_entra/azure_entra.go +++ b/internal/plugins/ai/azure_entra/azure_entra.go @@ -1,6 +1,7 @@ package azure_entra import ( + "errors" "fmt" "strings" @@ -39,12 +40,12 @@ type Client struct { func (c *Client) configure() error { c.apiDeployments = azurecommon.ParseDeployments(c.ApiDeployments.Value) if len(c.apiDeployments) == 0 { - return fmt.Errorf("%s", i18n.T("azure_deployments_required")) + return errors.New(i18n.T("azure_deployments_required")) } baseURL := strings.TrimSpace(c.ApiBaseURL.Value) if baseURL == "" { - return fmt.Errorf("%s", i18n.T("azure_base_url_required")) + return errors.New(i18n.T("azure_base_url_required")) } apiVersion := strings.TrimSpace(c.ApiVersion.Value) diff --git a/internal/plugins/ai/azurecommon/azurecommon.go b/internal/plugins/ai/azurecommon/azurecommon.go index 70144c64..63b42188 100644 --- a/internal/plugins/ai/azurecommon/azurecommon.go +++ b/internal/plugins/ai/azurecommon/azurecommon.go @@ -3,6 +3,7 @@ package azurecommon import ( "bytes" "encoding/json" + "errors" "fmt" "io" "net/http" @@ -78,7 +79,7 @@ func AzureDeploymentMiddleware(req *http.Request, next option.MiddlewareNext) (* // and restores the body for subsequent use. func ExtractDeploymentFromBody(req *http.Request) (string, error) { if req.Body == nil { - return "", fmt.Errorf("%s", i18n.T("azure_request_body_nil")) + return "", errors.New(i18n.T("azure_request_body_nil")) } bodyBytes, err := io.ReadAll(req.Body) @@ -96,7 +97,7 @@ func ExtractDeploymentFromBody(req *http.Request) (string, error) { } if payload.Model == "" { - return "", fmt.Errorf("%s", i18n.T("azure_model_field_empty")) + return "", errors.New(i18n.T("azure_model_field_empty")) } return payload.Model, nil diff --git a/internal/plugins/ai/copilot/copilot.go b/internal/plugins/ai/copilot/copilot.go index b89d67a3..60d9ce67 100644 --- a/internal/plugins/ai/copilot/copilot.go +++ b/internal/plugins/ai/copilot/copilot.go @@ -16,6 +16,7 @@ import ( "bytes" "context" "encoding/json" + "errors" "fmt" "io" "net/http" @@ -107,7 +108,7 @@ type Client struct { // configure initializes the client with OAuth2 configuration. func (c *Client) configure() error { if c.TenantID.Value == "" || c.ClientID.Value == "" { - return fmt.Errorf("%s", i18n.T("copilot_tenant_client_id_required")) + return errors.New(i18n.T("copilot_tenant_client_id_required")) } // Build OAuth2 configuration diff --git a/internal/plugins/ai/digitalocean/digitalocean.go b/internal/plugins/ai/digitalocean/digitalocean.go index cd2fd943..9aea9bfb 100644 --- a/internal/plugins/ai/digitalocean/digitalocean.go +++ b/internal/plugins/ai/digitalocean/digitalocean.go @@ -3,6 +3,7 @@ package digitalocean import ( "context" "encoding/json" + "errors" "fmt" "io" "net/http" @@ -63,7 +64,7 @@ func (c *Client) ListModels() ([]string, error) { err, ) } - return nil, fmt.Errorf("%s", i18n.T("digitalocean_model_list_unavailable")) + return nil, errors.New(i18n.T("digitalocean_model_list_unavailable")) } return c.fetchModelsFromControlPlane(context.Background()) } diff --git a/internal/plugins/ai/lmstudio/lmstudio.go b/internal/plugins/ai/lmstudio/lmstudio.go index 0b73e7c8..d12e6ec6 100644 --- a/internal/plugins/ai/lmstudio/lmstudio.go +++ b/internal/plugins/ai/lmstudio/lmstudio.go @@ -5,6 +5,7 @@ import ( "bytes" "context" "encoding/json" + "errors" "fmt" "io" "net/http" @@ -244,18 +245,18 @@ func (c *Client) Send(ctx context.Context, msgs []*chat.ChatCompletionMessage, o var choices []any var ok bool if choices, ok = result["choices"].([]any); !ok || len(choices) == 0 { - err = fmt.Errorf("%s", i18n.T("lmstudio_invalid_response_missing_choices")) + err = errors.New(i18n.T("lmstudio_invalid_response_missing_choices")) return } var message map[string]any if message, ok = choices[0].(map[string]any)["message"].(map[string]any); !ok { - err = fmt.Errorf("%s", i18n.T("lmstudio_invalid_response_missing_message")) + err = errors.New(i18n.T("lmstudio_invalid_response_missing_message")) return } if content, ok = message["content"].(string); !ok { - err = fmt.Errorf("%s", i18n.T("lmstudio_invalid_response_missing_content")) + err = errors.New(i18n.T("lmstudio_invalid_response_missing_content")) return } @@ -307,12 +308,12 @@ func (c *Client) Complete(ctx context.Context, prompt string, opts *domain.ChatO var choices []any var ok bool if choices, ok = result["choices"].([]any); !ok || len(choices) == 0 { - err = fmt.Errorf("%s", i18n.T("lmstudio_invalid_response_missing_choices")) + err = errors.New(i18n.T("lmstudio_invalid_response_missing_choices")) return } if text, ok = choices[0].(map[string]any)["text"].(string); !ok { - err = fmt.Errorf("%s", i18n.T("lmstudio_invalid_response_missing_text")) + err = errors.New(i18n.T("lmstudio_invalid_response_missing_text")) return } @@ -367,7 +368,7 @@ func (c *Client) GetEmbeddings(ctx context.Context, input string, opts *domain.C } if len(result.Data) == 0 { - err = fmt.Errorf("%s", i18n.T("lmstudio_no_embeddings_returned")) + err = errors.New(i18n.T("lmstudio_no_embeddings_returned")) return } diff --git a/internal/plugins/ai/ollama/ollama.go b/internal/plugins/ai/ollama/ollama.go index 631ee64a..b0d59fb9 100644 --- a/internal/plugins/ai/ollama/ollama.go +++ b/internal/plugins/ai/ollama/ollama.go @@ -3,6 +3,7 @@ package ollama import ( "context" "encoding/base64" + "errors" "fmt" "io" "net/http" @@ -226,7 +227,7 @@ func (o *Client) loadImageBytes(ctx context.Context, imageURL string) (ret []byt if strings.HasPrefix(imageURL, "data:") { parts := strings.SplitN(imageURL, ",", 2) if len(parts) != 2 { - err = fmt.Errorf("%s", i18n.T("ollama_invalid_data_url_format")) + err = errors.New(i18n.T("ollama_invalid_data_url_format")) return } if ret, err = base64.StdEncoding.DecodeString(parts[1]); err != nil { diff --git a/internal/plugins/ai/openai/openai_audio.go b/internal/plugins/ai/openai/openai_audio.go index 492ab935..5d307bb7 100644 --- a/internal/plugins/ai/openai/openai_audio.go +++ b/internal/plugins/ai/openai/openai_audio.go @@ -3,6 +3,7 @@ package openai import ( "bytes" "context" + "errors" "fmt" "os" "os/exec" @@ -141,7 +142,7 @@ func (o *Client) TranscribeFile(ctx context.Context, filePath, model string, spl // It returns the list of chunk file paths and a cleanup function. func splitAudioFile(src, ext string, maxSize int64) (files []string, cleanup func(), err error) { if _, err = exec.LookPath("ffmpeg"); err != nil { - return nil, nil, fmt.Errorf("%s", i18n.T("openai_audio_ffmpeg_not_found_install")) + return nil, nil, errors.New(i18n.T("openai_audio_ffmpeg_not_found_install")) } var dir string @@ -184,7 +185,7 @@ func splitAudioFile(src, ext string, maxSize int64) (files []string, cleanup fun _ = os.Remove(f) } if segmentTime <= 1 { - return nil, cleanup, fmt.Errorf("%s", i18n.T("openai_audio_unable_to_split_acceptable_size_chunks")) + return nil, cleanup, errors.New(i18n.T("openai_audio_unable_to_split_acceptable_size_chunks")) } segmentTime /= 2 } diff --git a/internal/plugins/ai/vendors.go b/internal/plugins/ai/vendors.go index b3468b5a..796b7723 100644 --- a/internal/plugins/ai/vendors.go +++ b/internal/plugins/ai/vendors.go @@ -3,6 +3,7 @@ package ai import ( "bytes" "context" + "errors" "fmt" "sort" "strings" @@ -75,7 +76,7 @@ func (o *VendorsManager) FindByName(name string) Vendor { func (o *VendorsManager) readModels() (err error) { if len(o.Vendors) == 0 { - err = fmt.Errorf("%s", i18n.T("vendors_no_ai_vendors_configured_read_models")) + err = errors.New(i18n.T("vendors_no_ai_vendors_configured_read_models")) return } diff --git a/internal/plugins/strategy/strategy.go b/internal/plugins/strategy/strategy.go index c04a47ef..9fd173b9 100644 --- a/internal/plugins/strategy/strategy.go +++ b/internal/plugins/strategy/strategy.go @@ -2,6 +2,7 @@ package strategy import ( "encoding/json" + "errors" "fmt" "io/fs" "os" @@ -218,7 +219,7 @@ func LoadStrategy(filename string) (*Strategy, error) { // ListStrategies prints available strategies func (sm *StrategiesManager) ListStrategies(shellCompleteList bool) error { if len(sm.Strategies) == 0 { - return fmt.Errorf("%s", i18n.T("strategies_none_found")) + return errors.New(i18n.T("strategies_none_found")) } if !shellCompleteList { fmt.Print(i18n.T("strategies_available_header"), "\n\n") diff --git a/internal/plugins/template/datetime.go b/internal/plugins/template/datetime.go index 35efea0f..2bcd64bc 100644 --- a/internal/plugins/template/datetime.go +++ b/internal/plugins/template/datetime.go @@ -2,6 +2,7 @@ package template import ( + "errors" "fmt" "strconv" "time" @@ -104,7 +105,7 @@ func (p *DateTimePlugin) handleRelative(now time.Time, value string) (string, er debugf("DateTime: handling relative time value=%q", value) if value == "" { - return "", fmt.Errorf("%s", i18n.T("template_datetime_error_relative_requires_value")) + return "", errors.New(i18n.T("template_datetime_error_relative_requires_value")) } // Try standard duration first (hours, minutes) @@ -116,7 +117,7 @@ func (p *DateTimePlugin) handleRelative(now time.Time, value string) (string, er // Handle date units if len(value) < 2 { - return "", fmt.Errorf("%s", i18n.T("template_datetime_error_invalid_relative_format")) + return "", errors.New(i18n.T("template_datetime_error_invalid_relative_format")) } unit := value[len(value)-1:] diff --git a/internal/plugins/template/extension_executor.go b/internal/plugins/template/extension_executor.go index 68c71fcf..de4d6895 100644 --- a/internal/plugins/template/extension_executor.go +++ b/internal/plugins/template/extension_executor.go @@ -3,6 +3,7 @@ package template import ( "bytes" "context" + "errors" "fmt" "os" "os/exec" @@ -48,7 +49,7 @@ func (e *ExtensionExecutor) Execute(name, operation, value string) (string, erro // Split the command string into command and arguments cmdParts := strings.Fields(cmdStr) if len(cmdParts) < 1 { - return "", fmt.Errorf("%s", i18n.T("extension_empty_command")) + return "", errors.New(i18n.T("extension_empty_command")) } // Create command with the Executable and formatted arguments @@ -128,7 +129,7 @@ func (e *ExtensionExecutor) executeWithFile(cmd *exec.Cmd, ext *ExtensionDefinit fileConfig := ext.GetFileConfig() if fileConfig == nil { - return "", fmt.Errorf("%s", i18n.T("extension_no_file_config")) + return "", errors.New(i18n.T("extension_no_file_config")) } // Handle path from stdout case @@ -141,7 +142,7 @@ func (e *ExtensionExecutor) executeWithFile(cmd *exec.Cmd, ext *ExtensionDefinit outputFile, _ := fileConfig["output_file"].(string) if outputFile == "" { - return "", fmt.Errorf("%s", i18n.T("extension_no_output_file")) + return "", errors.New(i18n.T("extension_no_output_file")) } // Set working directory if specified diff --git a/internal/plugins/template/extension_manager.go b/internal/plugins/template/extension_manager.go index 596579b1..3cb3c23d 100644 --- a/internal/plugins/template/extension_manager.go +++ b/internal/plugins/template/extension_manager.go @@ -1,6 +1,7 @@ package template import ( + "errors" "fmt" "os" "path/filepath" @@ -30,7 +31,7 @@ func NewExtensionManager(configDir string) *ExtensionManager { // ListExtensions handles the listextensions flag action func (em *ExtensionManager) ListExtensions() error { if em.registry == nil || em.registry.registry.Extensions == nil { - return fmt.Errorf("%s", i18n.T("extension_registry_not_initialized")) + return errors.New(i18n.T("extension_registry_not_initialized")) } for name, entry := range em.registry.registry.Extensions { diff --git a/internal/plugins/template/extension_registry.go b/internal/plugins/template/extension_registry.go index 2673d381..9a7a413a 100644 --- a/internal/plugins/template/extension_registry.go +++ b/internal/plugins/template/extension_registry.go @@ -3,6 +3,7 @@ package template import ( "crypto/sha256" "encoding/hex" + "errors" "fmt" "io" "os" @@ -116,7 +117,7 @@ func (r *ExtensionRegistry) Register(configPath string) error { // Validate extension name if ext.Name == "" { - return fmt.Errorf("%s", i18n.T("extension_name_empty")) + return errors.New(i18n.T("extension_name_empty")) } if strings.Contains(ext.Name, " ") { @@ -159,13 +160,13 @@ func (r *ExtensionRegistry) Register(configPath string) error { func (r *ExtensionRegistry) validateExtensionDefinition(ext *ExtensionDefinition) error { // Validate required fields if ext.Name == "" { - return fmt.Errorf("%s", i18n.T("extension_name_required")) + return errors.New(i18n.T("extension_name_required")) } if ext.Executable == "" { - return fmt.Errorf("%s", i18n.T("extension_executable_required")) + return errors.New(i18n.T("extension_executable_required")) } if ext.Type == "" { - return fmt.Errorf("%s", i18n.T("extension_type_required")) + return errors.New(i18n.T("extension_type_required")) } // Validate timeout format @@ -177,7 +178,7 @@ func (r *ExtensionRegistry) validateExtensionDefinition(ext *ExtensionDefinition // Validate operations if len(ext.Operations) == 0 { - return fmt.Errorf("%s", i18n.T("extension_operation_required")) + return errors.New(i18n.T("extension_operation_required")) } for name, op := range ext.Operations { if op.CmdTemplate == "" { diff --git a/internal/plugins/template/file.go b/internal/plugins/template/file.go index 5173ff77..abdb0282 100644 --- a/internal/plugins/template/file.go +++ b/internal/plugins/template/file.go @@ -5,6 +5,7 @@ package template import ( "bufio" + "errors" "fmt" "os" "path/filepath" @@ -30,7 +31,7 @@ func (p *FilePlugin) safePath(path string) (string, error) { // Basic security check - no path traversal if strings.Contains(path, "..") { - return "", fmt.Errorf("%s", i18n.T("template_file_error_path_contains_parent_ref")) + return "", errors.New(i18n.T("template_file_error_path_contains_parent_ref")) } // Expand home directory if needed @@ -61,7 +62,7 @@ func (p *FilePlugin) Apply(operation string, value string) (string, error) { case "tail": parts := strings.Split(value, "|") if len(parts) != 2 { - return "", fmt.Errorf("%s", i18n.T("template_file_error_tail_requires_path_lines")) + return "", errors.New(i18n.T("template_file_error_tail_requires_path_lines")) } path, err := p.safePath(parts[0]) @@ -75,7 +76,7 @@ func (p *FilePlugin) Apply(operation string, value string) (string, error) { } if n < 1 { - return "", fmt.Errorf("%s", i18n.T("template_file_error_line_count_positive")) + return "", errors.New(i18n.T("template_file_error_line_count_positive")) } lines, err := p.lastNLines(path, n) diff --git a/internal/plugins/template/sys.go b/internal/plugins/template/sys.go index c04e5866..3daf298d 100644 --- a/internal/plugins/template/sys.go +++ b/internal/plugins/template/sys.go @@ -2,6 +2,7 @@ package template import ( + "errors" "fmt" "os" "os/user" @@ -58,7 +59,7 @@ func (p *SysPlugin) Apply(operation string, value string) (string, error) { case "env": if value == "" { debugf("Sys: env error: missing variable name") - return "", fmt.Errorf("%s", i18n.T("template_sys_error_env_requires_var")) + return "", errors.New(i18n.T("template_sys_error_env_requires_var")) } result := os.Getenv(value) debugf("Sys: env %q=%q", value, result) diff --git a/internal/plugins/template/template.go b/internal/plugins/template/template.go index d9ee61a4..e4f4b27f 100644 --- a/internal/plugins/template/template.go +++ b/internal/plugins/template/template.go @@ -1,6 +1,7 @@ package template import ( + "errors" "fmt" "os" "path/filepath" @@ -143,7 +144,7 @@ func ApplyTemplate(content string, variables map[string]string, input string) (s } if !progress { - return "", fmt.Errorf("%s", i18n.T("template_processing_stuck")) + return "", errors.New(i18n.T("template_processing_stuck")) } } diff --git a/internal/server/ollama.go b/internal/server/ollama.go index b975b4db..7b414d10 100644 --- a/internal/server/ollama.go +++ b/internal/server/ollama.go @@ -4,6 +4,7 @@ import ( "bufio" "bytes" "encoding/json" + "errors" "fmt" "io" "log" @@ -109,28 +110,28 @@ func parseOllamaNumCtx(options map[string]any) (int, error) { switch v := val.(type) { case float64: if math.IsNaN(v) || math.IsInf(v, 0) { - return 0, fmt.Errorf("%s", i18n.T("ollama_num_ctx_must_be_finite")) + return 0, errors.New(i18n.T("ollama_num_ctx_must_be_finite")) } if math.Trunc(v) != v { - return 0, fmt.Errorf("%s", i18n.T("ollama_num_ctx_must_be_integer")) + return 0, errors.New(i18n.T("ollama_num_ctx_must_be_integer")) } // Check for overflow on 32-bit systems (negative values handled by validation at line 166) if v > float64(maxInt) { - return 0, fmt.Errorf("%s", i18n.T("ollama_num_ctx_value_out_of_range")) + return 0, errors.New(i18n.T("ollama_num_ctx_value_out_of_range")) } contextLength = int(v) case float32: f64 := float64(v) if math.IsNaN(f64) || math.IsInf(f64, 0) { - return 0, fmt.Errorf("%s", i18n.T("ollama_num_ctx_must_be_finite")) + return 0, errors.New(i18n.T("ollama_num_ctx_must_be_finite")) } if math.Trunc(f64) != f64 { - return 0, fmt.Errorf("%s", i18n.T("ollama_num_ctx_must_be_integer")) + return 0, errors.New(i18n.T("ollama_num_ctx_must_be_integer")) } // Check for overflow on 32-bit systems (negative values handled by validation at line 177) if f64 > float64(maxInt) { - return 0, fmt.Errorf("%s", i18n.T("ollama_num_ctx_value_out_of_range")) + return 0, errors.New(i18n.T("ollama_num_ctx_value_out_of_range")) } contextLength = int(v) @@ -149,7 +150,7 @@ func parseOllamaNumCtx(options map[string]any) (int, error) { case json.Number: i64, err := v.Int64() if err != nil { - return 0, fmt.Errorf("%s", i18n.T("ollama_num_ctx_must_be_valid_number")) + return 0, errors.New(i18n.T("ollama_num_ctx_must_be_valid_number")) } if i64 < 0 { return 0, fmt.Errorf(i18n.T("ollama_num_ctx_must_be_positive"), i64) @@ -172,7 +173,7 @@ func parseOllamaNumCtx(options map[string]any) (int, error) { contextLength = parsed default: - return 0, fmt.Errorf("%s", i18n.T("ollama_num_ctx_invalid_type")) + return 0, errors.New(i18n.T("ollama_num_ctx_invalid_type")) } if contextLength <= 0 { @@ -493,7 +494,7 @@ func buildFinalOllamaResponse(model string, content string, duration int64) Olla // contains a path component. func buildFabricChatURL(addr string) (string, error) { if addr == "" { - return "", fmt.Errorf("%s", i18n.T("ollama_empty_address")) + return "", errors.New(i18n.T("ollama_empty_address")) } if strings.HasPrefix(addr, "http://") || strings.HasPrefix(addr, "https://") { parsed, err := url.Parse(addr) @@ -501,10 +502,10 @@ func buildFabricChatURL(addr string) (string, error) { return "", fmt.Errorf(i18n.T("ollama_invalid_address"), err) } if parsed.Host == "" { - return "", fmt.Errorf("%s", i18n.T("ollama_invalid_address_missing_host")) + return "", errors.New(i18n.T("ollama_invalid_address_missing_host")) } if strings.HasPrefix(parsed.Host, ":") { - return "", fmt.Errorf("%s", i18n.T("ollama_invalid_address_missing_hostname")) + return "", errors.New(i18n.T("ollama_invalid_address_missing_hostname")) } return strings.TrimRight(parsed.String(), "/"), nil } @@ -517,14 +518,14 @@ func buildFabricChatURL(addr string) (string, error) { return "", fmt.Errorf(i18n.T("ollama_invalid_address"), err) } if parsed.Host == "" { - return "", fmt.Errorf("%s", i18n.T("ollama_invalid_address_missing_host")) + return "", errors.New(i18n.T("ollama_invalid_address_missing_host")) } if strings.HasPrefix(parsed.Host, ":") { - return "", fmt.Errorf("%s", i18n.T("ollama_invalid_address_missing_hostname")) + return "", errors.New(i18n.T("ollama_invalid_address_missing_hostname")) } // Bare addresses should be host[:port] only - reject path components if parsed.Path != "" && parsed.Path != "/" { - return "", fmt.Errorf("%s", i18n.T("ollama_invalid_address_path_not_allowed")) + return "", errors.New(i18n.T("ollama_invalid_address_path_not_allowed")) } return strings.TrimRight(parsed.String(), "/"), nil } diff --git a/internal/tools/notifications/notifications.go b/internal/tools/notifications/notifications.go index 48c4175a..1a8009b8 100644 --- a/internal/tools/notifications/notifications.go +++ b/internal/tools/notifications/notifications.go @@ -1,7 +1,7 @@ package notifications import ( - "fmt" + "errors" "os" "os/exec" "runtime" @@ -45,7 +45,7 @@ func NewNotificationManager() *NotificationManager { // Send sends a notification using the configured provider func (nm *NotificationManager) Send(title, message string) error { if nm.provider == nil { - return fmt.Errorf("%s", i18n.T("notifications_no_provider_available")) + return errors.New(i18n.T("notifications_no_provider_available")) } return nm.provider.Send(title, message) } diff --git a/internal/tools/spotify/spotify.go b/internal/tools/spotify/spotify.go index 59b36914..9e60b6aa 100644 --- a/internal/tools/spotify/spotify.go +++ b/internal/tools/spotify/spotify.go @@ -13,6 +13,7 @@ package spotify import ( "encoding/base64" "encoding/json" + "errors" "fmt" "io" "net/http" @@ -89,7 +90,7 @@ func (s *Spotify) initClient() error { // refreshAccessToken obtains a new access token using Client Credentials flow. func (s *Spotify) refreshAccessToken() error { if s.ClientId.Value == "" || s.ClientSecret.Value == "" { - return fmt.Errorf("%s", i18n.T("spotify_not_configured")) + return errors.New(i18n.T("spotify_not_configured")) } // Prepare the token request diff --git a/internal/tools/youtube/youtube.go b/internal/tools/youtube/youtube.go index 19e3abe6..c3824fc2 100644 --- a/internal/tools/youtube/youtube.go +++ b/internal/tools/youtube/youtube.go @@ -15,6 +15,7 @@ import ( "context" "encoding/csv" "flag" + "errors" "fmt" "io" "log" @@ -86,7 +87,7 @@ type YouTube struct { func (o *YouTube) initService() (err error) { if o.service == nil { if o.ApiKey.Value == "" { - err = fmt.Errorf("%s", i18n.T("youtube_api_key_required")) + err = errors.New(i18n.T("youtube_api_key_required")) return } o.normalizeRegex = regexp.MustCompile(`[^a-zA-Z0-9]+`) @@ -124,10 +125,10 @@ func (o *YouTube) extractAndValidateVideoId(url string) (videoId string, err err return "", err } if videoId == "" && playlistId != "" { - return "", fmt.Errorf("%s", i18n.T("youtube_url_is_playlist_not_video")) + return "", errors.New(i18n.T("youtube_url_is_playlist_not_video")) } if videoId == "" { - return "", fmt.Errorf("%s", i18n.T("youtube_no_video_id_found")) + return "", errors.New(i18n.T("youtube_no_video_id_found")) } return videoId, nil } @@ -192,7 +193,7 @@ func detectError(ytOutput io.Reader) error { } } if err := scanner.Err(); err != nil { - return fmt.Errorf("%s", i18n.T("youtube_ytdlp_stderr_error")) + return errors.New(i18n.T("youtube_ytdlp_stderr_error")) } return nil } @@ -218,7 +219,7 @@ func noLangs(args []string) []string { func (o *YouTube) tryMethodYtDlpInternal(videoId string, language string, additionalArgs string, processVTTFileFunc func(filename string) (string, error)) (ret string, err error) { // Check if yt-dlp is available if _, err = exec.LookPath("yt-dlp"); err != nil { - err = fmt.Errorf("%s", i18n.T("youtube_ytdlp_not_found")) + err = errors.New(i18n.T("youtube_ytdlp_not_found")) return } @@ -328,7 +329,7 @@ func (o *YouTube) readAndCleanVTTFile(filename string) (ret string, err error) { ret = strings.TrimSpace(textBuilder.String()) if ret == "" { - err = fmt.Errorf("%s", i18n.T("youtube_no_transcript_content")) + err = errors.New(i18n.T("youtube_no_transcript_content")) } return } @@ -398,7 +399,7 @@ func (o *YouTube) readAndFormatVTTWithTimestamps(filename string) (ret string, e ret = strings.TrimSpace(textBuilder.String()) if ret == "" { - err = fmt.Errorf("%s", i18n.T("youtube_no_transcript_content")) + err = errors.New(i18n.T("youtube_no_transcript_content")) } return } @@ -476,7 +477,7 @@ func parseTimestampToSeconds(timestamp string) (int, error) { func parseSeconds(secondsStr string) (int, error) { if secondsStr == "" { - return 0, fmt.Errorf("%s", i18n.T("youtube_empty_seconds_string")) + return 0, errors.New(i18n.T("youtube_empty_seconds_string")) } // Extract integer part (before decimal point if present) @@ -723,7 +724,7 @@ func (o *YouTube) findVTTFilesWithFallback(dir, requestedLanguage string) ([]str } if len(vttFiles) == 0 { - return nil, fmt.Errorf("%s", i18n.T("youtube_no_vtt_files_found")) + return nil, errors.New(i18n.T("youtube_no_vtt_files_found")) } // If no specific language requested, return the first file