From c37a7dbf0b9e596115ed3b72265dae825d6c5898 Mon Sep 17 00:00:00 2001 From: Kayvan Sylvan Date: Sat, 14 Feb 2026 12:03:40 -0800 Subject: [PATCH] feat: add i18n translations for ollama, extensions, lmstudio, and spotify modules - Add i18n translation keys for Ollama `num_ctx` validation error messages - Add i18n strings for Ollama server chat endpoint and SSE streaming errors - Add i18n strings for extension registry, executor, and manager operations - Add i18n strings for LM Studio client error messages and response handling - Add i18n strings for Spotify API client error messages - Add OpenAI image generation model compatibility warning translations - Replace hardcoded English strings with `i18n.T()` calls across all modules - Add translations for all new keys in de, en, es, fa, fr, it, ja, pt-BR, pt-PT, and zh locales --- internal/i18n/locales/de.json | 119 +++++++++++++++++- internal/i18n/locales/en.json | 119 +++++++++++++++++- internal/i18n/locales/es.json | 119 +++++++++++++++++- internal/i18n/locales/fa.json | 119 +++++++++++++++++- internal/i18n/locales/fr.json | 119 +++++++++++++++++- internal/i18n/locales/it.json | 119 +++++++++++++++++- internal/i18n/locales/ja.json | 119 +++++++++++++++++- internal/i18n/locales/pt-BR.json | 119 +++++++++++++++++- internal/i18n/locales/pt-PT.json | 119 +++++++++++++++++- internal/i18n/locales/zh.json | 119 +++++++++++++++++- internal/plugins/ai/lmstudio/lmstudio.go | 63 +++++----- internal/plugins/ai/openai/openai.go | 7 +- .../plugins/template/extension_executor.go | 30 ++--- .../plugins/template/extension_manager.go | 59 ++++----- .../plugins/template/extension_registry.go | 63 +++++----- internal/server/ollama.go | 101 +++++++-------- internal/tools/spotify/spotify.go | 28 ++--- 17 files changed, 1359 insertions(+), 182 deletions(-) diff --git a/internal/i18n/locales/de.json b/internal/i18n/locales/de.json index b29864f9..0ff15aaa 100644 --- a/internal/i18n/locales/de.json +++ b/internal/i18n/locales/de.json @@ -42,6 +42,8 @@ "openai_unexpected_status_code_read_error_partial": "unerwarteter Statuscode: %d von Anbieter %s (Fehler beim Lesen: %v), teilweise Antwort: %s", "openai_unexpected_status_code_read_error": "unerwarteter Statuscode: %d von Anbieter %s (Fehler beim Lesen der Antwort: %v)", "openai_unable_to_parse_models_response": "Modell-Antwort konnte nicht geparst werden; rohe Antwort: %s", + "openai_warning_model_no_image_generation": "Warnung: Modell '%s' unterstützt keine Bildgenerierung. Unterstützte Modelle: %s. Erwägen Sie die Verwendung von -m gpt-5.2 für Bildgenerierung.\n", + "openai_model_no_image_generation": "Modell '%s' unterstützt keine Bildgenerierung. Unterstützte Modelle: %s", "scraping_not_configured": "Scraping-Funktionalität ist nicht konfiguriert. Bitte richte Jina ein, um Scraping zu aktivieren", "could_not_determine_home_dir": "konnte Benutzer-Home-Verzeichnis nicht bestimmen: %w", "could_not_stat_env_file": "konnte .env-Datei nicht überprüfen: %w", @@ -293,5 +295,120 @@ "plugin_question_optional": "%v%v (leer lassen zum Überspringen):", "plugin_invalid_boolean_value": "Ungültiger Boolescher Wert: %v", "plugin_setting_not_valid": "%v=%v ist nicht gültig", - "plugin_invalid_bool": "Ungültiger boolescher Wert: %q" + "plugin_invalid_bool": "Ungültiger boolescher Wert: %q", + "ollama_num_ctx_must_be_finite": "num_ctx muss eine endliche Zahl sein", + "ollama_num_ctx_must_be_integer": "num_ctx muss eine Ganzzahl sein, erhielt Float mit Nachkommastellen", + "ollama_num_ctx_value_out_of_range": "num_ctx Wert außerhalb des Bereichs", + "ollama_num_ctx_must_be_positive": "num_ctx muss positiv sein, erhalten: %d", + "ollama_num_ctx_value_too_large": "num_ctx Wert zu groß: %d", + "ollama_num_ctx_must_be_valid_number": "num_ctx muss eine gültige Zahl sein", + "ollama_num_ctx_must_be_valid_number_got": "num_ctx muss eine gültige Zahl sein, erhalten: %s", + "ollama_num_ctx_invalid_type": "num_ctx muss eine Zahl sein, ungültiger Typ erhalten", + "ollama_num_ctx_exceeds_maximum": "num_ctx überschreitet den maximal zulässigen Wert von %d", + "ollama_error_reading_body": "Fehler beim Lesen des Bodys: %v", + "ollama_error_endpoint": "Endpunkt wird getestet", + "ollama_error_unmarshalling_body": "Fehler beim Unmarshalling des Bodys: %v", + "ollama_invalid_num_ctx_in_request": "Ungültiger num_ctx in Anfrage: %v", + "ollama_warning_parse_variables": "Warnung: Fehler beim Parsen von options.variables als JSON: %v", + "ollama_error_marshalling_body": "Fehler beim Marshalling des Bodys: %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_failed_create_request": "Fehler beim Erstellen der Anfrage", + "ollama_error_getting_chat_body": "Fehler beim Abrufen des /chat Bodys: %v", + "ollama_upstream_non_2xx_body_unreadable": "Upstream Fabric Server hat nicht-2xx Status %d zurückgegeben und Body konnte nicht gelesen werden: %v", + "ollama_upstream_non_2xx": "Upstream Fabric Server hat nicht-2xx Status %d zurückgegeben: %s", + "ollama_upstream_returned_status": "Upstream Fabric Server hat Status %d zurückgegeben", + "ollama_error_prefix": "Fehler: %s", + "ollama_error_parse_upstream_response": "Fehler: Upstream-Antwort konnte nicht geparst werden", + "ollama_failed_unmarshal_fabric_response": "Fehler beim Unmarshalling der Fabric Antwort", + "ollama_error_writing_response": "Fehler beim Schreiben der Antwort: %v", + "ollama_error_scanning_body": "Fehler beim Scannen des Bodys: %v", + "ollama_failed_scan_sse_stream": "Fehler beim Scannen des SSE Response Streams: %v", + "ollama_sse_buffer_limit": "SSE Zeile überschreitet 1MB Puffer-Limit - Datenzeile zu groß", + "ollama_warning_no_content": "Warnung: Kein Inhalt vom Upstream Fabric Server erhalten", + "ollama_no_content_from_upstream": "Kein Inhalt vom Upstream Fabric Server erhalten", + "ollama_empty_address": "Leere Adresse", + "ollama_invalid_address": "Ungültige Adresse: %w", + "ollama_invalid_address_missing_host": "Ungültige Adresse: Host fehlt", + "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", + "extension_registry_not_initialized": "Erweiterungsregistrierung nicht initialisiert", + "extension_name_label": "Erweiterung: %s\n", + "extension_status_disabled": " Status: DEAKTIVIERT - Hash-Überprüfung fehlgeschlagen: %v\n", + "extension_config_path_label": " Konfigurationspfad: %s\n\n", + "extension_status_enabled": " Status: AKTIVIERT\n", + "extension_executable_label": " Programmdatei: %s\n", + "extension_type_label": " Typ: %s\n", + "extension_timeout_label": " Zeitlimit: %s\n", + "extension_description_label": " Beschreibung: %s\n", + "extension_version_label": " Version: %s\n", + "extension_operations_label": " Operationen:\n", + "extension_command_template_label": " Befehlsvorlage: %s\n", + "extension_file_config_label": " Dateikonfiguration:\n", + "extension_invalid_config_path": "ungültiger Konfigurationspfad: %w", + "extension_failed_read_config": "Konfigurationsdatei konnte nicht gelesen werden: %w", + "extension_failed_parse_config": "Konfigurationsdatei konnte nicht geparst werden: %w", + "extension_failed_register": "Erweiterung konnte nicht registriert werden: %w", + "extension_invalid_timeout": "ungültiger Zeitlimit-Wert '%s': muss eine Dauer wie '30s' oder '1m' sein: %w", + "extension_registered_success": "Erweiterung erfolgreich registriert:\n", + "extension_name_detail_label": "Name: %s\n", + "extension_failed_remove": "Erweiterung konnte nicht entfernt werden: %w", + "lmstudio_api_url_question": "Geben Sie Ihre %v URL ein (zur Erinnerung, sie ist normalerweise %v)", + "lmstudio_failed_create_request": "Anfrage konnte nicht erstellt werden: %w", + "lmstudio_failed_send_request": "Anfrage konnte nicht gesendet werden: %w", + "lmstudio_unexpected_status_code": "Unerwarteter Statuscode: %d", + "lmstudio_failed_decode_response": "Antwort konnte nicht dekodiert werden: %w", + "lmstudio_failed_marshal_payload": "Nutzlast konnte nicht serialisiert werden: %w", + "lmstudio_error_reading_response": "Fehler beim Lesen der Antwort: %w", + "lmstudio_invalid_response_missing_choices": "Ungültiges Antwortformat: Auswahlmöglichkeiten fehlen oder sind leer", + "lmstudio_invalid_response_missing_message": "Ungültiges Antwortformat: Nachricht in der ersten Auswahl fehlt", + "lmstudio_invalid_response_missing_content": "Ungültiges Antwortformat: Inhalt in der Nachricht fehlt oder ist kein String", + "lmstudio_invalid_response_missing_text": "Ungültiges Antwortformat: Text in der ersten Auswahl fehlt oder ist kein String", + "lmstudio_no_embeddings_returned": "Keine Einbettungen zurückgegeben", + "extension_warning_load_registry": "Warnung: Erweiterungsregistrierung konnte nicht geladen werden: %v\n", + "extension_name_empty": "Erweiterungsname darf nicht leer sein", + "extension_name_contains_spaces": "Erweiterungsname '%s' enthält Leerzeichen - Namen dürfen keine Leerzeichen enthalten", + "extension_executable_not_found": "Programmdatei nicht gefunden: %w", + "extension_failed_get_absolute_path": "absoluter Pfad konnte nicht ermittelt werden: %w", + "extension_failed_hash_executable": "Programmdatei-Hash konnte nicht berechnet werden: %w", + "extension_invalid_definition": "ungültige Erweiterungsdefinition: %w", + "extension_name_required": "Erweiterungsname ist erforderlich", + "extension_executable_required": "Pfad zur Programmdatei ist erforderlich", + "extension_type_required": "Erweiterungstyp ist erforderlich", + "extension_invalid_timeout_format": "ungültiges Zeitlimit-Format: %w", + "extension_operation_required": "mindestens eine Operation muss definiert sein", + "extension_cmd_template_required": "Befehlsvorlage ist für Operation %s erforderlich", + "extension_not_found": "Erweiterung %s nicht gefunden", + "extension_config_hash_mismatch": "Hash-Abweichung der Konfigurationsdatei für %s", + "extension_failed_verify_executable": "Programmdatei konnte nicht verifiziert werden: %w", + "extension_executable_hash_mismatch": "Hash-Abweichung der Programmdatei für %s", + "extension_failed_marshal_registry": "Erweiterungsregistrierung konnte nicht serialisiert werden: %w", + "extension_failed_read_registry": "Erweiterungsregistrierung konnte nicht gelesen werden: %w", + "extension_failed_parse_registry": "Erweiterungsregistrierung konnte nicht geparst werden: %w", + "extension_failed_get_extension": "Erweiterung konnte nicht abgerufen werden: %w", + "extension_failed_format_command": "Befehl konnte nicht formatiert werden: %w", + "extension_empty_command": "Leerer Befehl nach Formatierung", + "extension_operation_not_found": "Operation %s für Erweiterung %s nicht gefunden", + "extension_executing_command": "Befehl wird ausgeführt: %s\n", + "extension_execution_failed_stderr": "Ausführung fehlgeschlagen: %w\nstderr: %s", + "extension_no_file_config": "Keine Dateikonfiguration gefunden", + "extension_no_output_file": "Keine Ausgabedatei in der Konfiguration angegeben", + "extension_execution_timed_out": "Ausführung nach %v abgelaufen", + "extension_execution_failed_err": "Ausführung fehlgeschlagen: %w\nerr: %s", + "extension_failed_read_output_file": "Ausgabedatei konnte nicht gelesen werden: %w", + "extension_failed_get_output_path": "Ausgabepfad konnte nicht ermittelt werden: %w\nerr: %s", + "spotify_failed_create_token_request": "Token-Anfrage konnte nicht erstellt werden: %w", + "spotify_failed_request_access_token": "Zugriffstoken konnte nicht angefordert werden: %w", + "spotify_failed_get_access_token": "Zugriffstoken konnte nicht abgerufen werden: Status %d, Antwort: %s", + "spotify_failed_decode_token_response": "Token-Antwort konnte nicht dekodiert werden: %w", + "spotify_failed_create_request": "Anfrage konnte nicht erstellt werden: %w", + "spotify_failed_execute_request": "Anfrage konnte nicht ausgeführt werden: %w", + "spotify_failed_read_response_body": "Antwortinhalt konnte nicht gelesen werden: %w", + "spotify_api_request_failed": "API-Anfrage fehlgeschlagen: Status %d, Antwort: %s", + "spotify_failed_parse_show_metadata": "Show-Metadaten konnten nicht analysiert werden: %w", + "spotify_failed_parse_episode_metadata": "Episoden-Metadaten konnten nicht analysiert werden: %w", + "spotify_search_failed": "Suche fehlgeschlagen: %w", + "spotify_failed_parse_search_results": "Suchergebnisse konnten nicht analysiert werden: %w", + "spotify_failed_get_show_episodes": "Show-Episoden konnten nicht abgerufen werden: %w", + "spotify_failed_parse_episodes": "Episoden konnten nicht analysiert werden: %w" } diff --git a/internal/i18n/locales/en.json b/internal/i18n/locales/en.json index 5ba624af..f524b108 100644 --- a/internal/i18n/locales/en.json +++ b/internal/i18n/locales/en.json @@ -42,6 +42,8 @@ "openai_unexpected_status_code_read_error_partial": "unexpected status code: %d from provider %s (error reading body: %v), partial response: %s", "openai_unexpected_status_code_read_error": "unexpected status code: %d from provider %s (failed to read response body: %v)", "openai_unable_to_parse_models_response": "unable to parse models response; raw response: %s", + "openai_warning_model_no_image_generation": "Warning: Model '%s' does not support image generation. Supported models: %s. Consider using -m gpt-5.2 for image generation.\n", + "openai_model_no_image_generation": "model '%s' does not support image generation. Supported models: %s", "scraping_not_configured": "scraping functionality is not configured. Please set up Jina to enable scraping", "could_not_determine_home_dir": "could not determine user home directory: %w", "could_not_stat_env_file": "could not stat .env file: %w", @@ -293,5 +295,120 @@ "plugin_question_optional": "%v%v (leave empty to skip):", "plugin_invalid_boolean_value": "invalid boolean value: %v", "plugin_setting_not_valid": "%v=%v, is not valid", - "plugin_invalid_bool": "invalid bool: %q" + "plugin_invalid_bool": "invalid bool: %q", + "ollama_num_ctx_must_be_finite": "num_ctx must be a finite number", + "ollama_num_ctx_must_be_integer": "num_ctx must be an integer, got float with fractional part", + "ollama_num_ctx_value_out_of_range": "num_ctx value out of range", + "ollama_num_ctx_must_be_positive": "num_ctx must be positive, got: %d", + "ollama_num_ctx_value_too_large": "num_ctx value too large: %d", + "ollama_num_ctx_must_be_valid_number": "num_ctx must be a valid number", + "ollama_num_ctx_must_be_valid_number_got": "num_ctx must be a valid number, got: %s", + "ollama_num_ctx_invalid_type": "num_ctx must be a number, got invalid type", + "ollama_num_ctx_exceeds_maximum": "num_ctx exceeds maximum allowed value of %d", + "ollama_error_reading_body": "Error reading body: %v", + "ollama_error_endpoint": "testing endpoint", + "ollama_error_unmarshalling_body": "Error unmarshalling body: %v", + "ollama_invalid_num_ctx_in_request": "Invalid num_ctx in request: %v", + "ollama_warning_parse_variables": "Warning: failed to parse options.variables as JSON: %v", + "ollama_error_marshalling_body": "Error marshalling body: %v", + "ollama_error_building_chat_url": "Error building /chat URL: %v", + "ollama_error_creating_chat_request": "Error creating /chat request: %v", + "ollama_failed_create_request": "failed to create request", + "ollama_error_getting_chat_body": "Error getting /chat body: %v", + "ollama_upstream_non_2xx_body_unreadable": "Upstream Fabric server returned non-2xx status %d and body could not be read: %v", + "ollama_upstream_non_2xx": "Upstream Fabric server returned non-2xx status %d: %s", + "ollama_upstream_returned_status": "upstream Fabric server returned status %d", + "ollama_error_prefix": "Error: %s", + "ollama_error_parse_upstream_response": "Error: failed to parse upstream response", + "ollama_failed_unmarshal_fabric_response": "failed to unmarshal Fabric response", + "ollama_error_writing_response": "Error writing response: %v", + "ollama_error_scanning_body": "Error scanning body: %v", + "ollama_failed_scan_sse_stream": "failed to scan SSE response stream: %v", + "ollama_sse_buffer_limit": "SSE line exceeds 1MB buffer limit - data line too large", + "ollama_warning_no_content": "Warning: no content received from upstream Fabric server", + "ollama_no_content_from_upstream": "no content received from upstream Fabric server", + "ollama_empty_address": "empty address", + "ollama_invalid_address": "invalid address: %w", + "ollama_invalid_address_missing_host": "invalid address: missing host", + "ollama_invalid_address_missing_hostname": "invalid address: missing hostname", + "ollama_invalid_address_path_not_allowed": "invalid address: path component not allowed in bare address", + "extension_registry_not_initialized": "extension registry not initialized", + "extension_name_label": "Extension: %s\n", + "extension_status_disabled": " Status: DISABLED - Hash verification failed: %v\n", + "extension_config_path_label": " Config Path: %s\n\n", + "extension_status_enabled": " Status: ENABLED\n", + "extension_executable_label": " Executable: %s\n", + "extension_type_label": " Type: %s\n", + "extension_timeout_label": " Timeout: %s\n", + "extension_description_label": " Description: %s\n", + "extension_version_label": " Version: %s\n", + "extension_operations_label": " Operations:\n", + "extension_command_template_label": " Command Template: %s\n", + "extension_file_config_label": " File Configuration:\n", + "extension_invalid_config_path": "invalid config path: %w", + "extension_failed_read_config": "failed to read config file: %w", + "extension_failed_parse_config": "failed to parse config file: %w", + "extension_failed_register": "failed to register extension: %w", + "extension_invalid_timeout": "invalid timeout value '%s': must be a duration like '30s' or '1m': %w", + "extension_registered_success": "Successfully registered extension:\n", + "extension_name_detail_label": "Name: %s\n", + "extension_failed_remove": "failed to remove extension: %w", + "lmstudio_api_url_question": "Enter your %v URL (as a reminder, it is usually %v)", + "lmstudio_failed_create_request": "failed to create request: %w", + "lmstudio_failed_send_request": "failed to send request: %w", + "lmstudio_unexpected_status_code": "unexpected status code: %d", + "lmstudio_failed_decode_response": "failed to decode response: %w", + "lmstudio_failed_marshal_payload": "failed to marshal payload: %w", + "lmstudio_error_reading_response": "error reading response: %w", + "lmstudio_invalid_response_missing_choices": "invalid response format: missing or empty choices", + "lmstudio_invalid_response_missing_message": "invalid response format: missing message in first choice", + "lmstudio_invalid_response_missing_content": "invalid response format: missing or non-string content in message", + "lmstudio_invalid_response_missing_text": "invalid response format: missing or non-string text in first choice", + "lmstudio_no_embeddings_returned": "no embeddings returned", + "extension_warning_load_registry": "Warning: could not load extension registry: %v\n", + "extension_name_empty": "extension name cannot be empty", + "extension_name_contains_spaces": "extension name '%s' contains spaces - names must not contain spaces", + "extension_executable_not_found": "executable not found: %w", + "extension_failed_get_absolute_path": "failed to get absolute path: %w", + "extension_failed_hash_executable": "failed to hash executable: %w", + "extension_invalid_definition": "invalid extension definition: %w", + "extension_name_required": "extension name is required", + "extension_executable_required": "executable path is required", + "extension_type_required": "extension type is required", + "extension_invalid_timeout_format": "invalid timeout format: %w", + "extension_operation_required": "at least one operation must be defined", + "extension_cmd_template_required": "command template is required for operation %s", + "extension_not_found": "extension %s not found", + "extension_config_hash_mismatch": "config file hash mismatch for %s", + "extension_failed_verify_executable": "failed to verify executable: %w", + "extension_executable_hash_mismatch": "executable hash mismatch for %s", + "extension_failed_marshal_registry": "failed to marshal extension registry: %w", + "extension_failed_read_registry": "failed to read extension registry: %w", + "extension_failed_parse_registry": "failed to parse extension registry: %w", + "extension_failed_get_extension": "failed to get extension: %w", + "extension_failed_format_command": "failed to format command: %w", + "extension_empty_command": "empty command after formatting", + "extension_operation_not_found": "operation %s not found for extension %s", + "extension_executing_command": "Executing command: %s\n", + "extension_execution_failed_stderr": "execution failed: %w\nstderr: %s", + "extension_no_file_config": "no file configuration found", + "extension_no_output_file": "no output file specified in configuration", + "extension_execution_timed_out": "execution timed out after %v", + "extension_execution_failed_err": "execution failed: %w\nerr: %s", + "extension_failed_read_output_file": "failed to read output file: %w", + "extension_failed_get_output_path": "failed to get output path: %w\nerr: %s", + "spotify_failed_create_token_request": "failed to create token request: %w", + "spotify_failed_request_access_token": "failed to request access token: %w", + "spotify_failed_get_access_token": "failed to get access token: status %d, body: %s", + "spotify_failed_decode_token_response": "failed to decode token response: %w", + "spotify_failed_create_request": "failed to create request: %w", + "spotify_failed_execute_request": "failed to execute request: %w", + "spotify_failed_read_response_body": "failed to read response body: %w", + "spotify_api_request_failed": "API request failed: status %d, body: %s", + "spotify_failed_parse_show_metadata": "failed to parse show metadata: %w", + "spotify_failed_parse_episode_metadata": "failed to parse episode metadata: %w", + "spotify_search_failed": "search failed: %w", + "spotify_failed_parse_search_results": "failed to parse search results: %w", + "spotify_failed_get_show_episodes": "failed to get show episodes: %w", + "spotify_failed_parse_episodes": "failed to parse episodes: %w" } diff --git a/internal/i18n/locales/es.json b/internal/i18n/locales/es.json index 6afd6ec8..d10d1134 100644 --- a/internal/i18n/locales/es.json +++ b/internal/i18n/locales/es.json @@ -42,6 +42,8 @@ "openai_unexpected_status_code_read_error_partial": "código de estado inesperado: %d del proveedor %s (error al leer cuerpo: %v), respuesta parcial: %s", "openai_unexpected_status_code_read_error": "código de estado inesperado: %d del proveedor %s (error al leer cuerpo de respuesta: %v)", "openai_unable_to_parse_models_response": "no se pudo analizar la respuesta de modelos; respuesta cruda: %s", + "openai_warning_model_no_image_generation": "Advertencia: El modelo '%s' no soporta generación de imágenes. Modelos soportados: %s. Considere usar -m gpt-5.2 para generación de imágenes.\n", + "openai_model_no_image_generation": "el modelo '%s' no soporta generación de imágenes. Modelos soportados: %s", "scraping_not_configured": "la funcionalidad de extracción no está configurada. Por favor configura Jina para habilitar la extracción", "could_not_determine_home_dir": "no se pudo determinar el directorio home del usuario: %w", "could_not_stat_env_file": "no se pudo verificar el archivo .env: %w", @@ -293,5 +295,120 @@ "plugin_question_optional": "%v%v (deja vacío para omitir):", "plugin_invalid_boolean_value": "valor booleano no válido: %v", "plugin_setting_not_valid": "%v=%v no es válido", - "plugin_invalid_bool": "bool no válido: %q" + "plugin_invalid_bool": "bool no válido: %q", + "ollama_num_ctx_must_be_finite": "num_ctx debe ser un número finito", + "ollama_num_ctx_must_be_integer": "num_ctx debe ser un entero, se obtuvo float con parte fraccionaria", + "ollama_num_ctx_value_out_of_range": "valor num_ctx fuera de rango", + "ollama_num_ctx_must_be_positive": "num_ctx debe ser positivo, se obtuvo: %d", + "ollama_num_ctx_value_too_large": "valor num_ctx demasiado grande: %d", + "ollama_num_ctx_must_be_valid_number": "num_ctx debe ser un número válido", + "ollama_num_ctx_must_be_valid_number_got": "num_ctx debe ser un número válido, se obtuvo: %s", + "ollama_num_ctx_invalid_type": "num_ctx debe ser un número, se obtuvo tipo inválido", + "ollama_num_ctx_exceeds_maximum": "num_ctx excede el valor máximo permitido de %d", + "ollama_error_reading_body": "Error al leer el cuerpo: %v", + "ollama_error_endpoint": "probando endpoint", + "ollama_error_unmarshalling_body": "Error al deserializar el cuerpo: %v", + "ollama_invalid_num_ctx_in_request": "num_ctx inválido en la solicitud: %v", + "ollama_warning_parse_variables": "Advertencia: error al analizar options.variables como JSON: %v", + "ollama_error_marshalling_body": "Error al serializar el cuerpo: %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_failed_create_request": "error al crear la solicitud", + "ollama_error_getting_chat_body": "Error al obtener el cuerpo /chat: %v", + "ollama_upstream_non_2xx_body_unreadable": "El servidor Fabric upstream devolvió estado no-2xx %d y el cuerpo no se pudo leer: %v", + "ollama_upstream_non_2xx": "El servidor Fabric upstream devolvió estado no-2xx %d: %s", + "ollama_upstream_returned_status": "el servidor Fabric upstream devolvió estado %d", + "ollama_error_prefix": "Error: %s", + "ollama_error_parse_upstream_response": "Error: no se pudo analizar la respuesta upstream", + "ollama_failed_unmarshal_fabric_response": "error al deserializar la respuesta Fabric", + "ollama_error_writing_response": "Error al escribir la respuesta: %v", + "ollama_error_scanning_body": "Error al escanear el cuerpo: %v", + "ollama_failed_scan_sse_stream": "error al escanear el flujo de respuesta SSE: %v", + "ollama_sse_buffer_limit": "Línea SSE excede el límite de búfer de 1MB - línea de datos demasiado grande", + "ollama_warning_no_content": "Advertencia: no se recibió contenido del servidor Fabric upstream", + "ollama_no_content_from_upstream": "no se recibió contenido del servidor Fabric upstream", + "ollama_empty_address": "dirección vacía", + "ollama_invalid_address": "dirección inválida: %w", + "ollama_invalid_address_missing_host": "dirección inválida: falta el host", + "ollama_invalid_address_missing_hostname": "dirección inválida: falta el nombre de host", + "ollama_invalid_address_path_not_allowed": "dirección inválida: componente de ruta no permitido en dirección simple", + "extension_registry_not_initialized": "registro de extensiones no inicializado", + "extension_name_label": "Extensión: %s\n", + "extension_status_disabled": " Estado: DESHABILITADA - Verificación de hash fallida: %v\n", + "extension_config_path_label": " Ruta de configuración: %s\n\n", + "extension_status_enabled": " Estado: HABILITADA\n", + "extension_executable_label": " Ejecutable: %s\n", + "extension_type_label": " Tipo: %s\n", + "extension_timeout_label": " Tiempo límite: %s\n", + "extension_description_label": " Descripción: %s\n", + "extension_version_label": " Versión: %s\n", + "extension_operations_label": " Operaciones:\n", + "extension_command_template_label": " Plantilla de comando: %s\n", + "extension_file_config_label": " Configuración de archivo:\n", + "extension_invalid_config_path": "ruta de configuración inválida: %w", + "extension_failed_read_config": "error al leer el archivo de configuración: %w", + "extension_failed_parse_config": "error al analizar el archivo de configuración: %w", + "extension_failed_register": "error al registrar la extensión: %w", + "extension_invalid_timeout": "valor de tiempo límite inválido '%s': debe ser una duración como '30s' o '1m': %w", + "extension_registered_success": "Extensión registrada exitosamente:\n", + "extension_name_detail_label": "Nombre: %s\n", + "extension_failed_remove": "error al eliminar la extensión: %w", + "lmstudio_api_url_question": "Introduzca su URL de %v (como recordatorio, generalmente es %v)", + "lmstudio_failed_create_request": "error al crear la solicitud: %w", + "lmstudio_failed_send_request": "error al enviar la solicitud: %w", + "lmstudio_unexpected_status_code": "código de estado inesperado: %d", + "lmstudio_failed_decode_response": "error al decodificar la respuesta: %w", + "lmstudio_failed_marshal_payload": "error al serializar la carga útil: %w", + "lmstudio_error_reading_response": "error al leer la respuesta: %w", + "lmstudio_invalid_response_missing_choices": "formato de respuesta inválido: opciones ausentes o vacías", + "lmstudio_invalid_response_missing_message": "formato de respuesta inválido: mensaje ausente en la primera opción", + "lmstudio_invalid_response_missing_content": "formato de respuesta inválido: contenido ausente o no es una cadena en el mensaje", + "lmstudio_invalid_response_missing_text": "formato de respuesta inválido: texto ausente o no es una cadena en la primera opción", + "lmstudio_no_embeddings_returned": "no se devolvieron incrustaciones", + "extension_warning_load_registry": "Advertencia: no se pudo cargar el registro de extensiones: %v\n", + "extension_name_empty": "el nombre de la extensión no puede estar vacío", + "extension_name_contains_spaces": "el nombre de la extensión '%s' contiene espacios - los nombres no deben contener espacios", + "extension_executable_not_found": "ejecutable no encontrado: %w", + "extension_failed_get_absolute_path": "no se pudo obtener la ruta absoluta: %w", + "extension_failed_hash_executable": "no se pudo calcular el hash del ejecutable: %w", + "extension_invalid_definition": "definición de extensión inválida: %w", + "extension_name_required": "el nombre de la extensión es obligatorio", + "extension_executable_required": "la ruta del ejecutable es obligatoria", + "extension_type_required": "el tipo de extensión es obligatorio", + "extension_invalid_timeout_format": "formato de tiempo límite inválido: %w", + "extension_operation_required": "se debe definir al menos una operación", + "extension_cmd_template_required": "la plantilla de comando es obligatoria para la operación %s", + "extension_not_found": "extensión %s no encontrada", + "extension_config_hash_mismatch": "discrepancia de hash del archivo de configuración para %s", + "extension_failed_verify_executable": "no se pudo verificar el ejecutable: %w", + "extension_executable_hash_mismatch": "discrepancia de hash del ejecutable para %s", + "extension_failed_marshal_registry": "no se pudo serializar el registro de extensiones: %w", + "extension_failed_read_registry": "no se pudo leer el registro de extensiones: %w", + "extension_failed_parse_registry": "no se pudo analizar el registro de extensiones: %w", + "extension_failed_get_extension": "no se pudo obtener la extensión: %w", + "extension_failed_format_command": "no se pudo formatear el comando: %w", + "extension_empty_command": "comando vacío después del formateo", + "extension_operation_not_found": "operación %s no encontrada para la extensión %s", + "extension_executing_command": "Ejecutando comando: %s\n", + "extension_execution_failed_stderr": "la ejecución falló: %w\nstderr: %s", + "extension_no_file_config": "no se encontró configuración de archivo", + "extension_no_output_file": "no se especificó archivo de salida en la configuración", + "extension_execution_timed_out": "la ejecución expiró después de %v", + "extension_execution_failed_err": "la ejecución falló: %w\nerr: %s", + "extension_failed_read_output_file": "no se pudo leer el archivo de salida: %w", + "extension_failed_get_output_path": "no se pudo obtener la ruta de salida: %w\nerr: %s", + "spotify_failed_create_token_request": "no se pudo crear la solicitud de token: %w", + "spotify_failed_request_access_token": "no se pudo solicitar el token de acceso: %w", + "spotify_failed_get_access_token": "no se pudo obtener el token de acceso: estado %d, respuesta: %s", + "spotify_failed_decode_token_response": "no se pudo decodificar la respuesta del token: %w", + "spotify_failed_create_request": "no se pudo crear la solicitud: %w", + "spotify_failed_execute_request": "no se pudo ejecutar la solicitud: %w", + "spotify_failed_read_response_body": "no se pudo leer el cuerpo de la respuesta: %w", + "spotify_api_request_failed": "la solicitud a la API falló: estado %d, respuesta: %s", + "spotify_failed_parse_show_metadata": "no se pudieron analizar los metadatos del programa: %w", + "spotify_failed_parse_episode_metadata": "no se pudieron analizar los metadatos del episodio: %w", + "spotify_search_failed": "la búsqueda falló: %w", + "spotify_failed_parse_search_results": "no se pudieron analizar los resultados de búsqueda: %w", + "spotify_failed_get_show_episodes": "no se pudieron obtener los episodios del programa: %w", + "spotify_failed_parse_episodes": "no se pudieron analizar los episodios: %w" } diff --git a/internal/i18n/locales/fa.json b/internal/i18n/locales/fa.json index caa71761..7c44f8af 100644 --- a/internal/i18n/locales/fa.json +++ b/internal/i18n/locales/fa.json @@ -34,6 +34,8 @@ "openai_unexpected_status_code_read_error_partial": "کد وضعیت غیرمنتظره: %d از ارائه‌دهنده %s (خطا در خواندن: %v)، پاسخ جزئی: %s", "openai_unexpected_status_code_read_error": "کد وضعیت غیرمنتظره: %d از ارائه‌دهنده %s (خطا در خواندن پاسخ: %v)", "openai_unable_to_parse_models_response": "تجزیه پاسخ مدل‌ها ناموفق بود; پاسخ خام: %s", + "openai_warning_model_no_image_generation": "هشدار: مدل '%s' از تولید تصویر پشتیبانی نمی‌کند. مدل‌های پشتیبانی شده: %s. استفاده از -m gpt-5.2 برای تولید تصویر را در نظر بگیرید.\n", + "openai_model_no_image_generation": "مدل '%s' از تولید تصویر پشتیبانی نمی‌کند. مدل‌های پشتیبانی شده: %s", "scraping_not_configured": "قابلیت استخراج داده پیکربندی نشده است. لطفاً Jina را برای فعال‌سازی استخراج تنظیم کنید", "could_not_determine_home_dir": "نتوانست دایرکتوری خانه کاربر را تعیین کند: %w", "could_not_stat_env_file": "نتوانست وضعیت فایل .env را بررسی کند: %w", @@ -285,5 +287,120 @@ "plugin_question_optional": "%v%v (برای رد کردن خالی بگذارید):", "plugin_invalid_boolean_value": "مقدار بولی نامعتبر: %v", "plugin_setting_not_valid": "%v=%v معتبر نیست", - "plugin_invalid_bool": "مقدار bool نامعتبر: %q" + "plugin_invalid_bool": "مقدار bool نامعتبر: %q", + "ollama_num_ctx_must_be_finite": "num_ctx باید یک عدد محدود باشد", + "ollama_num_ctx_must_be_integer": "num_ctx باید یک عدد صحیح باشد، عدد اعشاری با قسمت کسری دریافت شد", + "ollama_num_ctx_value_out_of_range": "مقدار num_ctx خارج از محدوده است", + "ollama_num_ctx_must_be_positive": "num_ctx باید مثبت باشد، دریافت شده: %d", + "ollama_num_ctx_value_too_large": "مقدار num_ctx بیش از حد بزرگ است: %d", + "ollama_num_ctx_must_be_valid_number": "num_ctx باید یک عدد معتبر باشد", + "ollama_num_ctx_must_be_valid_number_got": "num_ctx باید یک عدد معتبر باشد، دریافت شده: %s", + "ollama_num_ctx_invalid_type": "num_ctx باید یک عدد باشد، نوع نامعتبر دریافت شد", + "ollama_num_ctx_exceeds_maximum": "num_ctx از حداکثر مقدار مجاز %d فراتر رفته است", + "ollama_error_reading_body": "خطا در خواندن بدنه: %v", + "ollama_error_endpoint": "در حال آزمایش نقطه پایانی", + "ollama_error_unmarshalling_body": "خطا در تجزیه بدنه: %v", + "ollama_invalid_num_ctx_in_request": "num_ctx نامعتبر در درخواست: %v", + "ollama_warning_parse_variables": "هشدار: شکست در تجزیه options.variables به عنوان JSON: %v", + "ollama_error_marshalling_body": "خطا در سریال‌سازی بدنه: %v", + "ollama_error_building_chat_url": "خطا در ساخت URL چت /: %v", + "ollama_error_creating_chat_request": "خطا در ایجاد درخواست /chat: %v", + "ollama_failed_create_request": "ایجاد درخواست ناموفق بود", + "ollama_error_getting_chat_body": "خطا در دریافت بدنه /chat: %v", + "ollama_upstream_non_2xx_body_unreadable": "سرور Fabric بالادستی وضعیت غیر-2xx %d را برگرداند و بدنه قابل خواندن نبود: %v", + "ollama_upstream_non_2xx": "سرور Fabric بالادستی وضعیت غیر-2xx %d را برگرداند: %s", + "ollama_upstream_returned_status": "سرور Fabric بالادستی وضعیت %d را برگرداند", + "ollama_error_prefix": "خطا: %s", + "ollama_error_parse_upstream_response": "خطا: شکست در تجزیه پاسخ بالادستی", + "ollama_failed_unmarshal_fabric_response": "شکست در تجزیه پاسخ Fabric", + "ollama_error_writing_response": "خطا در نوشتن پاسخ: %v", + "ollama_error_scanning_body": "خطا در اسکن بدنه: %v", + "ollama_failed_scan_sse_stream": "شکست در اسکن جریان پاسخ SSE: %v", + "ollama_sse_buffer_limit": "خط SSE از حد بافر 1MB فراتر رفت - خط داده بیش از حد بزرگ است", + "ollama_warning_no_content": "هشدار: هیچ محتوایی از سرور Fabric بالادستی دریافت نشد", + "ollama_no_content_from_upstream": "هیچ محتوایی از سرور Fabric بالادستی دریافت نشد", + "ollama_empty_address": "آدرس خالی", + "ollama_invalid_address": "آدرس نامعتبر: %w", + "ollama_invalid_address_missing_host": "آدرس نامعتبر: میزبان وجود ندارد", + "ollama_invalid_address_missing_hostname": "آدرس نامعتبر: نام میزبان وجود ندارد", + "ollama_invalid_address_path_not_allowed": "آدرس نامعتبر: مؤلفه مسیر در آدرس ساده مجاز نیست", + "extension_registry_not_initialized": "رجیستری افزونه‌ها مقداردهی نشده است", + "extension_name_label": "افزونه: %s\n", + "extension_status_disabled": " وضعیت: غیرفعال - تأیید هش ناموفق: %v\n", + "extension_config_path_label": " مسیر پیکربندی: %s\n\n", + "extension_status_enabled": " وضعیت: فعال\n", + "extension_executable_label": " فایل اجرایی: %s\n", + "extension_type_label": " نوع: %s\n", + "extension_timeout_label": " مهلت زمانی: %s\n", + "extension_description_label": " توضیحات: %s\n", + "extension_version_label": " نسخه: %s\n", + "extension_operations_label": " عملیات‌ها:\n", + "extension_command_template_label": " الگوی دستور: %s\n", + "extension_file_config_label": " پیکربندی فایل:\n", + "extension_invalid_config_path": "مسیر پیکربندی نامعتبر: %w", + "extension_failed_read_config": "خواندن فایل پیکربندی ناموفق بود: %w", + "extension_failed_parse_config": "تجزیه فایل پیکربندی ناموفق بود: %w", + "extension_failed_register": "ثبت افزونه ناموفق بود: %w", + "extension_invalid_timeout": "مقدار مهلت زمانی نامعتبر '%s': باید مدت‌زمانی مانند '30s' یا '1m' باشد: %w", + "extension_registered_success": "افزونه با موفقیت ثبت شد:\n", + "extension_name_detail_label": "نام: %s\n", + "extension_failed_remove": "حذف افزونه ناموفق بود: %w", + "lmstudio_api_url_question": "آدرس URL %v خود را وارد کنید (به عنوان یادآوری، معمولاً %v است)", + "lmstudio_failed_create_request": "ایجاد درخواست ناموفق بود: %w", + "lmstudio_failed_send_request": "ارسال درخواست ناموفق بود: %w", + "lmstudio_unexpected_status_code": "کد وضعیت غیرمنتظره: %d", + "lmstudio_failed_decode_response": "رمزگشایی پاسخ ناموفق بود: %w", + "lmstudio_failed_marshal_payload": "سریال‌سازی بار داده ناموفق بود: %w", + "lmstudio_error_reading_response": "خطا در خواندن پاسخ: %w", + "lmstudio_invalid_response_missing_choices": "فرمت پاسخ نامعتبر: گزینه‌ها وجود ندارند یا خالی هستند", + "lmstudio_invalid_response_missing_message": "فرمت پاسخ نامعتبر: پیام در اولین گزینه وجود ندارد", + "lmstudio_invalid_response_missing_content": "فرمت پاسخ نامعتبر: محتوا در پیام وجود ندارد یا رشته نیست", + "lmstudio_invalid_response_missing_text": "فرمت پاسخ نامعتبر: متن در اولین گزینه وجود ندارد یا رشته نیست", + "lmstudio_no_embeddings_returned": "هیچ بردار جاسازی بازگردانده نشد", + "extension_warning_load_registry": "هشدار: بارگذاری رجیستری افزونه‌ها ممکن نبود: %v\n", + "extension_name_empty": "نام افزونه نمی‌تواند خالی باشد", + "extension_name_contains_spaces": "نام افزونه '%s' حاوی فاصله است - نام‌ها نباید فاصله داشته باشند", + "extension_executable_not_found": "فایل اجرایی یافت نشد: %w", + "extension_failed_get_absolute_path": "دریافت مسیر مطلق ناموفق بود: %w", + "extension_failed_hash_executable": "محاسبه هش فایل اجرایی ناموفق بود: %w", + "extension_invalid_definition": "تعریف افزونه نامعتبر: %w", + "extension_name_required": "نام افزونه الزامی است", + "extension_executable_required": "مسیر فایل اجرایی الزامی است", + "extension_type_required": "نوع افزونه الزامی است", + "extension_invalid_timeout_format": "فرمت مهلت زمانی نامعتبر: %w", + "extension_operation_required": "حداقل یک عملیات باید تعریف شود", + "extension_cmd_template_required": "الگوی دستور برای عملیات %s الزامی است", + "extension_not_found": "افزونه %s یافت نشد", + "extension_config_hash_mismatch": "عدم تطابق هش فایل پیکربندی برای %s", + "extension_failed_verify_executable": "تأیید فایل اجرایی ناموفق بود: %w", + "extension_executable_hash_mismatch": "عدم تطابق هش فایل اجرایی برای %s", + "extension_failed_marshal_registry": "سریال‌سازی رجیستری افزونه‌ها ناموفق بود: %w", + "extension_failed_read_registry": "خواندن رجیستری افزونه‌ها ناموفق بود: %w", + "extension_failed_parse_registry": "تجزیه رجیستری افزونه‌ها ناموفق بود: %w", + "extension_failed_get_extension": "دریافت افزونه ناموفق بود: %w", + "extension_failed_format_command": "قالب‌بندی دستور ناموفق بود: %w", + "extension_empty_command": "دستور خالی پس از قالب‌بندی", + "extension_operation_not_found": "عملیات %s برای افزونه %s یافت نشد", + "extension_executing_command": "در حال اجرای دستور: %s\n", + "extension_execution_failed_stderr": "اجرا ناموفق بود: %w\nstderr: %s", + "extension_no_file_config": "پیکربندی فایل یافت نشد", + "extension_no_output_file": "فایل خروجی در پیکربندی مشخص نشده است", + "extension_execution_timed_out": "اجرا پس از %v منقضی شد", + "extension_execution_failed_err": "اجرا ناموفق بود: %w\nerr: %s", + "extension_failed_read_output_file": "خواندن فایل خروجی ناموفق بود: %w", + "extension_failed_get_output_path": "دریافت مسیر خروجی ناموفق بود: %w\nerr: %s", + "spotify_failed_create_token_request": "ایجاد درخواست توکن ناموفق بود: %w", + "spotify_failed_request_access_token": "درخواست توکن دسترسی ناموفق بود: %w", + "spotify_failed_get_access_token": "دریافت توکن دسترسی ناموفق بود: وضعیت %d، پاسخ: %s", + "spotify_failed_decode_token_response": "رمزگشایی پاسخ توکن ناموفق بود: %w", + "spotify_failed_create_request": "ایجاد درخواست ناموفق بود: %w", + "spotify_failed_execute_request": "اجرای درخواست ناموفق بود: %w", + "spotify_failed_read_response_body": "خواندن متن پاسخ ناموفق بود: %w", + "spotify_api_request_failed": "درخواست API ناموفق بود: وضعیت %d، پاسخ: %s", + "spotify_failed_parse_show_metadata": "تجزیه فراداده برنامه ناموفق بود: %w", + "spotify_failed_parse_episode_metadata": "تجزیه فراداده قسمت ناموفق بود: %w", + "spotify_search_failed": "جستجو ناموفق بود: %w", + "spotify_failed_parse_search_results": "تجزیه نتایج جستجو ناموفق بود: %w", + "spotify_failed_get_show_episodes": "دریافت قسمت‌های برنامه ناموفق بود: %w", + "spotify_failed_parse_episodes": "تجزیه قسمت‌ها ناموفق بود: %w" } diff --git a/internal/i18n/locales/fr.json b/internal/i18n/locales/fr.json index 8bc765b9..7cffa14b 100644 --- a/internal/i18n/locales/fr.json +++ b/internal/i18n/locales/fr.json @@ -42,6 +42,8 @@ "openai_unexpected_status_code_read_error_partial": "code d'état inattendu : %d du fournisseur %s (erreur de lecture : %v), réponse partielle : %s", "openai_unexpected_status_code_read_error": "code d'état inattendu : %d du fournisseur %s (échec de lecture du corps de réponse : %v)", "openai_unable_to_parse_models_response": "impossible d'analyser la réponse des modèles ; réponse brute : %s", + "openai_warning_model_no_image_generation": "Avertissement : Le modèle '%s' ne prend pas en charge la génération d'images. Modèles pris en charge : %s. Envisagez d'utiliser -m gpt-5.2 pour la génération d'images.\n", + "openai_model_no_image_generation": "le modèle '%s' ne prend pas en charge la génération d'images. Modèles pris en charge : %s", "scraping_not_configured": "la fonctionnalité de scraping n'est pas configurée. Veuillez configurer Jina pour activer le scraping", "could_not_determine_home_dir": "impossible de déterminer le répertoire home de l'utilisateur : %w", "could_not_stat_env_file": "impossible de vérifier le fichier .env : %w", @@ -293,5 +295,120 @@ "plugin_question_optional": "%v%v (laissez vide pour passer) :", "plugin_invalid_boolean_value": "valeur booléenne invalide : %v", "plugin_setting_not_valid": "%v=%v n'est pas valide", - "plugin_invalid_bool": "booléen invalide : %q" + "plugin_invalid_bool": "booléen invalide : %q", + "ollama_num_ctx_must_be_finite": "num_ctx doit être un nombre fini", + "ollama_num_ctx_must_be_integer": "num_ctx doit être un entier, float avec partie fractionnaire reçu", + "ollama_num_ctx_value_out_of_range": "valeur num_ctx hors limites", + "ollama_num_ctx_must_be_positive": "num_ctx doit être positif, reçu : %d", + "ollama_num_ctx_value_too_large": "valeur num_ctx trop grande : %d", + "ollama_num_ctx_must_be_valid_number": "num_ctx doit être un nombre valide", + "ollama_num_ctx_must_be_valid_number_got": "num_ctx doit être un nombre valide, reçu : %s", + "ollama_num_ctx_invalid_type": "num_ctx doit être un nombre, type invalide reçu", + "ollama_num_ctx_exceeds_maximum": "num_ctx dépasse la valeur maximale autorisée de %d", + "ollama_error_reading_body": "Erreur lors de la lecture du corps : %v", + "ollama_error_endpoint": "test du point de terminaison", + "ollama_error_unmarshalling_body": "Erreur lors du décodage du corps : %v", + "ollama_invalid_num_ctx_in_request": "num_ctx invalide dans la requête : %v", + "ollama_warning_parse_variables": "Attention : échec de l'analyse de options.variables en JSON : %v", + "ollama_error_marshalling_body": "Erreur lors de l'encodage du corps : %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_failed_create_request": "échec de création de la requête", + "ollama_error_getting_chat_body": "Erreur lors de l'obtention du corps /chat : %v", + "ollama_upstream_non_2xx_body_unreadable": "Le serveur Fabric en amont a renvoyé un statut non-2xx %d et le corps n'a pas pu être lu : %v", + "ollama_upstream_non_2xx": "Le serveur Fabric en amont a renvoyé un statut non-2xx %d : %s", + "ollama_upstream_returned_status": "le serveur Fabric en amont a renvoyé le statut %d", + "ollama_error_prefix": "Erreur : %s", + "ollama_error_parse_upstream_response": "Erreur : échec de l'analyse de la réponse en amont", + "ollama_failed_unmarshal_fabric_response": "échec du décodage de la réponse Fabric", + "ollama_error_writing_response": "Erreur lors de l'écriture de la réponse : %v", + "ollama_error_scanning_body": "Erreur lors de l'analyse du corps : %v", + "ollama_failed_scan_sse_stream": "échec de l'analyse du flux de réponse SSE : %v", + "ollama_sse_buffer_limit": "La ligne SSE dépasse la limite de tampon de 1 Mo - ligne de données trop grande", + "ollama_warning_no_content": "Attention : aucun contenu reçu du serveur Fabric en amont", + "ollama_no_content_from_upstream": "aucun contenu reçu du serveur Fabric en amont", + "ollama_empty_address": "adresse vide", + "ollama_invalid_address": "adresse invalide : %w", + "ollama_invalid_address_missing_host": "adresse invalide : hôte manquant", + "ollama_invalid_address_missing_hostname": "adresse invalide : nom d'hôte manquant", + "ollama_invalid_address_path_not_allowed": "adresse invalide : composant de chemin non autorisé dans l'adresse simple", + "extension_registry_not_initialized": "registre d'extensions non initialisé", + "extension_name_label": "Extension : %s\n", + "extension_status_disabled": " Statut : DÉSACTIVÉE - Vérification du hash échouée : %v\n", + "extension_config_path_label": " Chemin de configuration : %s\n\n", + "extension_status_enabled": " Statut : ACTIVÉE\n", + "extension_executable_label": " Exécutable : %s\n", + "extension_type_label": " Type : %s\n", + "extension_timeout_label": " Délai d'expiration : %s\n", + "extension_description_label": " Description : %s\n", + "extension_version_label": " Version : %s\n", + "extension_operations_label": " Opérations :\n", + "extension_command_template_label": " Modèle de commande : %s\n", + "extension_file_config_label": " Configuration de fichier :\n", + "extension_invalid_config_path": "chemin de configuration invalide : %w", + "extension_failed_read_config": "échec de lecture du fichier de configuration : %w", + "extension_failed_parse_config": "échec de l'analyse du fichier de configuration : %w", + "extension_failed_register": "échec de l'enregistrement de l'extension : %w", + "extension_invalid_timeout": "valeur de délai invalide '%s' : doit être une durée comme '30s' ou '1m' : %w", + "extension_registered_success": "Extension enregistrée avec succès :\n", + "extension_name_detail_label": "Nom : %s\n", + "extension_failed_remove": "échec de la suppression de l'extension : %w", + "lmstudio_api_url_question": "Entrez votre URL %v (pour rappel, elle est généralement %v)", + "lmstudio_failed_create_request": "échec de la création de la requête : %w", + "lmstudio_failed_send_request": "échec de l'envoi de la requête : %w", + "lmstudio_unexpected_status_code": "code de statut inattendu : %d", + "lmstudio_failed_decode_response": "échec du décodage de la réponse : %w", + "lmstudio_failed_marshal_payload": "échec de la sérialisation des données : %w", + "lmstudio_error_reading_response": "erreur lors de la lecture de la réponse : %w", + "lmstudio_invalid_response_missing_choices": "format de réponse invalide : choix manquants ou vides", + "lmstudio_invalid_response_missing_message": "format de réponse invalide : message manquant dans le premier choix", + "lmstudio_invalid_response_missing_content": "format de réponse invalide : contenu manquant ou non-chaîne dans le message", + "lmstudio_invalid_response_missing_text": "format de réponse invalide : texte manquant ou non-chaîne dans le premier choix", + "lmstudio_no_embeddings_returned": "aucun embedding retourné", + "extension_warning_load_registry": "Attention : impossible de charger le registre d'extensions : %v\n", + "extension_name_empty": "le nom de l'extension ne peut pas être vide", + "extension_name_contains_spaces": "le nom de l'extension '%s' contient des espaces - les noms ne doivent pas contenir d'espaces", + "extension_executable_not_found": "exécutable introuvable : %w", + "extension_failed_get_absolute_path": "impossible d'obtenir le chemin absolu : %w", + "extension_failed_hash_executable": "impossible de calculer le hash de l'exécutable : %w", + "extension_invalid_definition": "définition d'extension invalide : %w", + "extension_name_required": "le nom de l'extension est requis", + "extension_executable_required": "le chemin de l'exécutable est requis", + "extension_type_required": "le type d'extension est requis", + "extension_invalid_timeout_format": "format de délai invalide : %w", + "extension_operation_required": "au moins une opération doit être définie", + "extension_cmd_template_required": "le modèle de commande est requis pour l'opération %s", + "extension_not_found": "extension %s introuvable", + "extension_config_hash_mismatch": "discordance de hash du fichier de configuration pour %s", + "extension_failed_verify_executable": "impossible de vérifier l'exécutable : %w", + "extension_executable_hash_mismatch": "discordance de hash de l'exécutable pour %s", + "extension_failed_marshal_registry": "impossible de sérialiser le registre d'extensions : %w", + "extension_failed_read_registry": "impossible de lire le registre d'extensions : %w", + "extension_failed_parse_registry": "impossible d'analyser le registre d'extensions : %w", + "extension_failed_get_extension": "impossible d'obtenir l'extension : %w", + "extension_failed_format_command": "impossible de formater la commande : %w", + "extension_empty_command": "commande vide après le formatage", + "extension_operation_not_found": "opération %s introuvable pour l'extension %s", + "extension_executing_command": "Exécution de la commande : %s\n", + "extension_execution_failed_stderr": "l'exécution a échoué : %w\nstderr : %s", + "extension_no_file_config": "aucune configuration de fichier trouvée", + "extension_no_output_file": "aucun fichier de sortie spécifié dans la configuration", + "extension_execution_timed_out": "l'exécution a expiré après %v", + "extension_execution_failed_err": "l'exécution a échoué : %w\nerr : %s", + "extension_failed_read_output_file": "impossible de lire le fichier de sortie : %w", + "extension_failed_get_output_path": "impossible d'obtenir le chemin de sortie : %w\nerr : %s", + "spotify_failed_create_token_request": "impossible de créer la requête de token : %w", + "spotify_failed_request_access_token": "impossible de demander le jeton d'accès : %w", + "spotify_failed_get_access_token": "impossible d'obtenir le jeton d'accès : statut %d, réponse : %s", + "spotify_failed_decode_token_response": "impossible de décoder la réponse du token : %w", + "spotify_failed_create_request": "impossible de créer la requête : %w", + "spotify_failed_execute_request": "impossible d'exécuter la requête : %w", + "spotify_failed_read_response_body": "impossible de lire le corps de la réponse : %w", + "spotify_api_request_failed": "la requête API a échoué : statut %d, réponse : %s", + "spotify_failed_parse_show_metadata": "impossible d'analyser les métadonnées de l'émission : %w", + "spotify_failed_parse_episode_metadata": "impossible d'analyser les métadonnées de l'épisode : %w", + "spotify_search_failed": "la recherche a échoué : %w", + "spotify_failed_parse_search_results": "impossible d'analyser les résultats de recherche : %w", + "spotify_failed_get_show_episodes": "impossible de récupérer les épisodes de l'émission : %w", + "spotify_failed_parse_episodes": "impossible d'analyser les épisodes : %w" } diff --git a/internal/i18n/locales/it.json b/internal/i18n/locales/it.json index d53d3797..1bb1cab8 100644 --- a/internal/i18n/locales/it.json +++ b/internal/i18n/locales/it.json @@ -42,6 +42,8 @@ "openai_unexpected_status_code_read_error_partial": "codice di stato imprevisto: %d dal provider %s (errore lettura corpo: %v), risposta parziale: %s", "openai_unexpected_status_code_read_error": "codice di stato imprevisto: %d dal provider %s (errore lettura corpo risposta: %v)", "openai_unable_to_parse_models_response": "impossibile analizzare risposta modelli; risposta grezza: %s", + "openai_warning_model_no_image_generation": "Avviso: Il modello '%s' non supporta la generazione di immagini. Modelli supportati: %s. Considera di usare -m gpt-5.2 per la generazione di immagini.\n", + "openai_model_no_image_generation": "il modello '%s' non supporta la generazione di immagini. Modelli supportati: %s", "scraping_not_configured": "la funzionalità di scraping non è configurata. Per favore configura Jina per abilitare lo scraping", "could_not_determine_home_dir": "impossibile determinare la directory home dell'utente: %w", "could_not_stat_env_file": "impossibile verificare il file .env: %w", @@ -293,5 +295,120 @@ "plugin_question_optional": "%v%v (lascia vuoto per saltare):", "plugin_invalid_boolean_value": "valore booleano non valido: %v", "plugin_setting_not_valid": "%v=%v non è valido", - "plugin_invalid_bool": "bool non valido: %q" + "plugin_invalid_bool": "bool non valido: %q", + "ollama_num_ctx_must_be_finite": "num_ctx deve essere un numero finito", + "ollama_num_ctx_must_be_integer": "num_ctx deve essere un intero, ricevuto float con parte frazionaria", + "ollama_num_ctx_value_out_of_range": "valore num_ctx fuori intervallo", + "ollama_num_ctx_must_be_positive": "num_ctx deve essere positivo, ricevuto: %d", + "ollama_num_ctx_value_too_large": "valore num_ctx troppo grande: %d", + "ollama_num_ctx_must_be_valid_number": "num_ctx deve essere un numero valido", + "ollama_num_ctx_must_be_valid_number_got": "num_ctx deve essere un numero valido, ricevuto: %s", + "ollama_num_ctx_invalid_type": "num_ctx deve essere un numero, ricevuto tipo non valido", + "ollama_num_ctx_exceeds_maximum": "num_ctx supera il valore massimo consentito di %d", + "ollama_error_reading_body": "Errore nella lettura del corpo: %v", + "ollama_error_endpoint": "test dell'endpoint", + "ollama_error_unmarshalling_body": "Errore nella deserializzazione del corpo: %v", + "ollama_invalid_num_ctx_in_request": "num_ctx non valido nella richiesta: %v", + "ollama_warning_parse_variables": "Avviso: impossibile analizzare options.variables come JSON: %v", + "ollama_error_marshalling_body": "Errore nella serializzazione del corpo: %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_failed_create_request": "impossibile creare la richiesta", + "ollama_error_getting_chat_body": "Errore nell'ottenere il corpo /chat: %v", + "ollama_upstream_non_2xx_body_unreadable": "Il server Fabric upstream ha restituito stato non-2xx %d e il corpo non è stato leggibile: %v", + "ollama_upstream_non_2xx": "Il server Fabric upstream ha restituito stato non-2xx %d: %s", + "ollama_upstream_returned_status": "il server Fabric upstream ha restituito stato %d", + "ollama_error_prefix": "Errore: %s", + "ollama_error_parse_upstream_response": "Errore: impossibile analizzare la risposta upstream", + "ollama_failed_unmarshal_fabric_response": "impossibile deserializzare la risposta Fabric", + "ollama_error_writing_response": "Errore nella scrittura della risposta: %v", + "ollama_error_scanning_body": "Errore nella scansione del corpo: %v", + "ollama_failed_scan_sse_stream": "impossibile scansionare il flusso di risposta SSE: %v", + "ollama_sse_buffer_limit": "La riga SSE supera il limite del buffer di 1MB - riga di dati troppo grande", + "ollama_warning_no_content": "Avviso: nessun contenuto ricevuto dal server Fabric upstream", + "ollama_no_content_from_upstream": "nessun contenuto ricevuto dal server Fabric upstream", + "ollama_empty_address": "indirizzo vuoto", + "ollama_invalid_address": "indirizzo non valido: %w", + "ollama_invalid_address_missing_host": "indirizzo non valido: host mancante", + "ollama_invalid_address_missing_hostname": "indirizzo non valido: nome host mancante", + "ollama_invalid_address_path_not_allowed": "indirizzo non valido: componente percorso non consentito nell'indirizzo semplice", + "extension_registry_not_initialized": "registro estensioni non inizializzato", + "extension_name_label": "Estensione: %s\n", + "extension_status_disabled": " Stato: DISABILITATA - Verifica hash fallita: %v\n", + "extension_config_path_label": " Percorso configurazione: %s\n\n", + "extension_status_enabled": " Stato: ABILITATA\n", + "extension_executable_label": " Eseguibile: %s\n", + "extension_type_label": " Tipo: %s\n", + "extension_timeout_label": " Timeout: %s\n", + "extension_description_label": " Descrizione: %s\n", + "extension_version_label": " Versione: %s\n", + "extension_operations_label": " Operazioni:\n", + "extension_command_template_label": " Modello di comando: %s\n", + "extension_file_config_label": " Configurazione file:\n", + "extension_invalid_config_path": "percorso di configurazione non valido: %w", + "extension_failed_read_config": "impossibile leggere il file di configurazione: %w", + "extension_failed_parse_config": "impossibile analizzare il file di configurazione: %w", + "extension_failed_register": "impossibile registrare l'estensione: %w", + "extension_invalid_timeout": "valore di timeout non valido '%s': deve essere una durata come '30s' o '1m': %w", + "extension_registered_success": "Estensione registrata con successo:\n", + "extension_name_detail_label": "Nome: %s\n", + "extension_failed_remove": "impossibile rimuovere l'estensione: %w", + "lmstudio_api_url_question": "Inserisci il tuo URL %v (come promemoria, di solito è %v)", + "lmstudio_failed_create_request": "impossibile creare la richiesta: %w", + "lmstudio_failed_send_request": "impossibile inviare la richiesta: %w", + "lmstudio_unexpected_status_code": "codice di stato imprevisto: %d", + "lmstudio_failed_decode_response": "impossibile decodificare la risposta: %w", + "lmstudio_failed_marshal_payload": "impossibile serializzare il payload: %w", + "lmstudio_error_reading_response": "errore durante la lettura della risposta: %w", + "lmstudio_invalid_response_missing_choices": "formato di risposta non valido: scelte mancanti o vuote", + "lmstudio_invalid_response_missing_message": "formato di risposta non valido: messaggio mancante nella prima scelta", + "lmstudio_invalid_response_missing_content": "formato di risposta non valido: contenuto mancante o non stringa nel messaggio", + "lmstudio_invalid_response_missing_text": "formato di risposta non valido: testo mancante o non stringa nella prima scelta", + "lmstudio_no_embeddings_returned": "nessun embedding restituito", + "extension_warning_load_registry": "Attenzione: impossibile caricare il registro estensioni: %v\n", + "extension_name_empty": "il nome dell'estensione non può essere vuoto", + "extension_name_contains_spaces": "il nome dell'estensione '%s' contiene spazi - i nomi non devono contenere spazi", + "extension_executable_not_found": "eseguibile non trovato: %w", + "extension_failed_get_absolute_path": "impossibile ottenere il percorso assoluto: %w", + "extension_failed_hash_executable": "impossibile calcolare l'hash dell'eseguibile: %w", + "extension_invalid_definition": "definizione estensione non valida: %w", + "extension_name_required": "il nome dell'estensione è obbligatorio", + "extension_executable_required": "il percorso dell'eseguibile è obbligatorio", + "extension_type_required": "il tipo di estensione è obbligatorio", + "extension_invalid_timeout_format": "formato timeout non valido: %w", + "extension_operation_required": "deve essere definita almeno un'operazione", + "extension_cmd_template_required": "il modello di comando è obbligatorio per l'operazione %s", + "extension_not_found": "estensione %s non trovata", + "extension_config_hash_mismatch": "discrepanza hash del file di configurazione per %s", + "extension_failed_verify_executable": "impossibile verificare l'eseguibile: %w", + "extension_executable_hash_mismatch": "discrepanza hash dell'eseguibile per %s", + "extension_failed_marshal_registry": "impossibile serializzare il registro estensioni: %w", + "extension_failed_read_registry": "impossibile leggere il registro estensioni: %w", + "extension_failed_parse_registry": "impossibile analizzare il registro estensioni: %w", + "extension_failed_get_extension": "impossibile ottenere l'estensione: %w", + "extension_failed_format_command": "impossibile formattare il comando: %w", + "extension_empty_command": "comando vuoto dopo la formattazione", + "extension_operation_not_found": "operazione %s non trovata per l'estensione %s", + "extension_executing_command": "Esecuzione comando: %s\n", + "extension_execution_failed_stderr": "esecuzione fallita: %w\nstderr: %s", + "extension_no_file_config": "nessuna configurazione file trovata", + "extension_no_output_file": "nessun file di output specificato nella configurazione", + "extension_execution_timed_out": "esecuzione scaduta dopo %v", + "extension_execution_failed_err": "esecuzione fallita: %w\nerr: %s", + "extension_failed_read_output_file": "impossibile leggere il file di output: %w", + "extension_failed_get_output_path": "impossibile ottenere il percorso di output: %w\nerr: %s", + "spotify_failed_create_token_request": "impossibile creare la richiesta del token: %w", + "spotify_failed_request_access_token": "impossibile richiedere il token di accesso: %w", + "spotify_failed_get_access_token": "impossibile ottenere il token di accesso: stato %d, risposta: %s", + "spotify_failed_decode_token_response": "impossibile decodificare la risposta del token: %w", + "spotify_failed_create_request": "impossibile creare la richiesta: %w", + "spotify_failed_execute_request": "impossibile eseguire la richiesta: %w", + "spotify_failed_read_response_body": "impossibile leggere il corpo della risposta: %w", + "spotify_api_request_failed": "richiesta API fallita: stato %d, risposta: %s", + "spotify_failed_parse_show_metadata": "impossibile analizzare i metadati dello show: %w", + "spotify_failed_parse_episode_metadata": "impossibile analizzare i metadati dell'episodio: %w", + "spotify_search_failed": "ricerca fallita: %w", + "spotify_failed_parse_search_results": "impossibile analizzare i risultati della ricerca: %w", + "spotify_failed_get_show_episodes": "impossibile ottenere gli episodi dello show: %w", + "spotify_failed_parse_episodes": "impossibile analizzare gli episodi: %w" } diff --git a/internal/i18n/locales/ja.json b/internal/i18n/locales/ja.json index b1319cd3..0b9d63b5 100644 --- a/internal/i18n/locales/ja.json +++ b/internal/i18n/locales/ja.json @@ -42,6 +42,8 @@ "openai_unexpected_status_code_read_error_partial": "予期しないステータスコード: プロバイダー %s から %d (本文読み取りエラー: %v)、部分的なレスポンス: %s", "openai_unexpected_status_code_read_error": "予期しないステータスコード: プロバイダー %s から %d (レスポンス本文の読み取りに失敗: %v)", "openai_unable_to_parse_models_response": "モデルレスポンスの解析に失敗しました; 生のレスポンス: %s", + "openai_warning_model_no_image_generation": "警告: モデル '%s' は画像生成をサポートしていません。サポートされているモデル: %s。画像生成には -m gpt-5.2 の使用を検討してください。\n", + "openai_model_no_image_generation": "モデル '%s' は画像生成をサポートしていません。サポートされているモデル: %s", "scraping_not_configured": "スクレイピング機能が設定されていません。スクレイピングを有効にするためにJinaを設定してください", "could_not_determine_home_dir": "ユーザーのホームディレクトリを特定できませんでした: %w", "could_not_stat_env_file": ".envファイルの状態を確認できませんでした: %w", @@ -293,5 +295,120 @@ "plugin_question_optional": "%v%v (スキップするには空欄のまま):", "plugin_invalid_boolean_value": "無効なブール値です: %v", "plugin_setting_not_valid": "%v=%v は無効です", - "plugin_invalid_bool": "無効な bool です: %q" + "plugin_invalid_bool": "無効な bool です: %q", + "ollama_num_ctx_must_be_finite": "num_ctx は有限数である必要があります", + "ollama_num_ctx_must_be_integer": "num_ctx は整数である必要があります。小数部分を持つ浮動小数点数を受け取りました", + "ollama_num_ctx_value_out_of_range": "num_ctx の値が範囲外です", + "ollama_num_ctx_must_be_positive": "num_ctx は正の値である必要があります。受け取った値: %d", + "ollama_num_ctx_value_too_large": "num_ctx の値が大きすぎます: %d", + "ollama_num_ctx_must_be_valid_number": "num_ctx は有効な数値である必要があります", + "ollama_num_ctx_must_be_valid_number_got": "num_ctx は有効な数値である必要があります。受け取った値: %s", + "ollama_num_ctx_invalid_type": "num_ctx は数値である必要があります。無効な型を受け取りました", + "ollama_num_ctx_exceeds_maximum": "num_ctx が許可される最大値 %d を超えています", + "ollama_error_reading_body": "ボディの読み取りエラー: %v", + "ollama_error_endpoint": "エンドポイントをテスト中", + "ollama_error_unmarshalling_body": "ボディのアンマーシャリングエラー: %v", + "ollama_invalid_num_ctx_in_request": "リクエストに無効な num_ctx があります: %v", + "ollama_warning_parse_variables": "警告: options.variables を JSON として解析できませんでした: %v", + "ollama_error_marshalling_body": "ボディのマーシャリングエラー: %v", + "ollama_error_building_chat_url": "/chat URL の構築エラー: %v", + "ollama_error_creating_chat_request": "/chat リクエストの作成エラー: %v", + "ollama_failed_create_request": "リクエストの作成に失敗しました", + "ollama_error_getting_chat_body": "/chat ボディの取得エラー: %v", + "ollama_upstream_non_2xx_body_unreadable": "アップストリームの Fabric サーバーが非 2xx ステータス %d を返し、ボディを読み取れませんでした: %v", + "ollama_upstream_non_2xx": "アップストリームの Fabric サーバーが非 2xx ステータス %d を返しました: %s", + "ollama_upstream_returned_status": "アップストリームの Fabric サーバーがステータス %d を返しました", + "ollama_error_prefix": "エラー: %s", + "ollama_error_parse_upstream_response": "エラー: アップストリームのレスポンスを解析できませんでした", + "ollama_failed_unmarshal_fabric_response": "Fabric レスポンスのアンマーシャルに失敗しました", + "ollama_error_writing_response": "レスポンスの書き込みエラー: %v", + "ollama_error_scanning_body": "ボディのスキャンエラー: %v", + "ollama_failed_scan_sse_stream": "SSE レスポンスストリームのスキャンに失敗しました: %v", + "ollama_sse_buffer_limit": "SSE 行が 1MB バッファー制限を超えています - データ行が大きすぎます", + "ollama_warning_no_content": "警告: アップストリームの Fabric サーバーからコンテンツを受信しませんでした", + "ollama_no_content_from_upstream": "アップストリームの Fabric サーバーからコンテンツを受信しませんでした", + "ollama_empty_address": "空のアドレス", + "ollama_invalid_address": "無効なアドレス: %w", + "ollama_invalid_address_missing_host": "無効なアドレス: ホストが不足しています", + "ollama_invalid_address_missing_hostname": "無効なアドレス: ホスト名が不足しています", + "ollama_invalid_address_path_not_allowed": "無効なアドレス: 単純なアドレスにパスコンポーネントは許可されていません", + "extension_registry_not_initialized": "拡張機能レジストリが初期化されていません", + "extension_name_label": "拡張機能: %s\n", + "extension_status_disabled": " ステータス: 無効 - ハッシュ検証失敗: %v\n", + "extension_config_path_label": " 設定パス: %s\n\n", + "extension_status_enabled": " ステータス: 有効\n", + "extension_executable_label": " 実行ファイル: %s\n", + "extension_type_label": " タイプ: %s\n", + "extension_timeout_label": " タイムアウト: %s\n", + "extension_description_label": " 説明: %s\n", + "extension_version_label": " バージョン: %s\n", + "extension_operations_label": " 操作:\n", + "extension_command_template_label": " コマンドテンプレート: %s\n", + "extension_file_config_label": " ファイル設定:\n", + "extension_invalid_config_path": "無効な設定パス: %w", + "extension_failed_read_config": "設定ファイルの読み込みに失敗しました: %w", + "extension_failed_parse_config": "設定ファイルの解析に失敗しました: %w", + "extension_failed_register": "拡張機能の登録に失敗しました: %w", + "extension_invalid_timeout": "無効なタイムアウト値 '%s': '30s' や '1m' のような期間である必要があります: %w", + "extension_registered_success": "拡張機能が正常に登録されました:\n", + "extension_name_detail_label": "名前: %s\n", + "extension_failed_remove": "拡張機能の削除に失敗しました: %w", + "lmstudio_api_url_question": "%v の URL を入力してください(通常は %v です)", + "lmstudio_failed_create_request": "リクエストの作成に失敗しました: %w", + "lmstudio_failed_send_request": "リクエストの送信に失敗しました: %w", + "lmstudio_unexpected_status_code": "予期しないステータスコード: %d", + "lmstudio_failed_decode_response": "レスポンスのデコードに失敗しました: %w", + "lmstudio_failed_marshal_payload": "ペイロードのシリアル化に失敗しました: %w", + "lmstudio_error_reading_response": "レスポンスの読み取りエラー: %w", + "lmstudio_invalid_response_missing_choices": "無効なレスポンス形式: 選択肢がないか空です", + "lmstudio_invalid_response_missing_message": "無効なレスポンス形式: 最初の選択肢にメッセージがありません", + "lmstudio_invalid_response_missing_content": "無効なレスポンス形式: メッセージにコンテンツがないか文字列ではありません", + "lmstudio_invalid_response_missing_text": "無効なレスポンス形式: 最初の選択肢にテキストがないか文字列ではありません", + "lmstudio_no_embeddings_returned": "埋め込みが返されませんでした", + "extension_warning_load_registry": "警告: 拡張機能レジストリを読み込めませんでした: %v\n", + "extension_name_empty": "拡張機能名は空にできません", + "extension_name_contains_spaces": "拡張機能名 '%s' にスペースが含まれています - 名前にスペースは使用できません", + "extension_executable_not_found": "実行ファイルが見つかりません: %w", + "extension_failed_get_absolute_path": "絶対パスの取得に失敗しました: %w", + "extension_failed_hash_executable": "実行ファイルのハッシュ計算に失敗しました: %w", + "extension_invalid_definition": "無効な拡張機能定義: %w", + "extension_name_required": "拡張機能名は必須です", + "extension_executable_required": "実行ファイルパスは必須です", + "extension_type_required": "拡張機能タイプは必須です", + "extension_invalid_timeout_format": "無効なタイムアウト形式: %w", + "extension_operation_required": "少なくとも1つの操作を定義する必要があります", + "extension_cmd_template_required": "操作 %s にはコマンドテンプレートが必要です", + "extension_not_found": "拡張機能 %s が見つかりません", + "extension_config_hash_mismatch": "%s の設定ファイルのハッシュが一致しません", + "extension_failed_verify_executable": "実行ファイルの検証に失敗しました: %w", + "extension_executable_hash_mismatch": "%s の実行ファイルのハッシュが一致しません", + "extension_failed_marshal_registry": "拡張機能レジストリのシリアル化に失敗しました: %w", + "extension_failed_read_registry": "拡張機能レジストリの読み込みに失敗しました: %w", + "extension_failed_parse_registry": "拡張機能レジストリの解析に失敗しました: %w", + "extension_failed_get_extension": "拡張機能の取得に失敗しました: %w", + "extension_failed_format_command": "コマンドのフォーマットに失敗しました: %w", + "extension_empty_command": "フォーマット後のコマンドが空です", + "extension_operation_not_found": "拡張機能 %s にオペレーション %s が見つかりません", + "extension_executing_command": "コマンドを実行中: %s\n", + "extension_execution_failed_stderr": "実行に失敗しました: %w\nstderr: %s", + "extension_no_file_config": "ファイル設定が見つかりません", + "extension_no_output_file": "設定に出力ファイルが指定されていません", + "extension_execution_timed_out": "%v 後に実行がタイムアウトしました", + "extension_execution_failed_err": "実行に失敗しました: %w\nerr: %s", + "extension_failed_read_output_file": "出力ファイルの読み取りに失敗しました: %w", + "extension_failed_get_output_path": "出力パスの取得に失敗しました: %w\nerr: %s", + "spotify_failed_create_token_request": "トークンリクエストの作成に失敗しました: %w", + "spotify_failed_request_access_token": "アクセストークンのリクエストに失敗しました: %w", + "spotify_failed_get_access_token": "アクセストークンの取得に失敗しました: ステータス %d、レスポンス: %s", + "spotify_failed_decode_token_response": "トークンレスポンスのデコードに失敗しました: %w", + "spotify_failed_create_request": "リクエストの作成に失敗しました: %w", + "spotify_failed_execute_request": "リクエストの実行に失敗しました: %w", + "spotify_failed_read_response_body": "レスポンスボディの読み取りに失敗しました: %w", + "spotify_api_request_failed": "APIリクエストが失敗しました: ステータス %d、レスポンス: %s", + "spotify_failed_parse_show_metadata": "番組メタデータの解析に失敗しました: %w", + "spotify_failed_parse_episode_metadata": "エピソードメタデータの解析に失敗しました: %w", + "spotify_search_failed": "検索に失敗しました: %w", + "spotify_failed_parse_search_results": "検索結果の解析に失敗しました: %w", + "spotify_failed_get_show_episodes": "番組エピソードの取得に失敗しました: %w", + "spotify_failed_parse_episodes": "エピソードの解析に失敗しました: %w" } diff --git a/internal/i18n/locales/pt-BR.json b/internal/i18n/locales/pt-BR.json index f21afb96..46df06bc 100644 --- a/internal/i18n/locales/pt-BR.json +++ b/internal/i18n/locales/pt-BR.json @@ -42,6 +42,8 @@ "openai_unexpected_status_code_read_error_partial": "código de status inesperado: %d do provedor %s (erro ao ler corpo: %v), resposta parcial: %s", "openai_unexpected_status_code_read_error": "código de status inesperado: %d do provedor %s (falha ao ler corpo da resposta: %v)", "openai_unable_to_parse_models_response": "não foi possível analisar a resposta de modelos; resposta bruta: %s", + "openai_warning_model_no_image_generation": "Aviso: O modelo '%s' não suporta geração de imagens. Modelos suportados: %s. Considere usar -m gpt-5.2 para geração de imagens.\n", + "openai_model_no_image_generation": "o modelo '%s' não suporta geração de imagens. Modelos suportados: %s", "scraping_not_configured": "funcionalidade de scraping não está configurada. Por favor configure o Jina para ativar o scraping", "could_not_determine_home_dir": "não foi possível determinar o diretório home do usuário: %w", "could_not_stat_env_file": "não foi possível verificar o arquivo .env: %w", @@ -293,5 +295,120 @@ "plugin_question_optional": "%v%v (deixe em branco para pular):", "plugin_invalid_boolean_value": "valor booleano inválido: %v", "plugin_setting_not_valid": "%v=%v não é válido", - "plugin_invalid_bool": "bool inválido: %q" + "plugin_invalid_bool": "bool inválido: %q", + "ollama_num_ctx_must_be_finite": "num_ctx deve ser um número finito", + "ollama_num_ctx_must_be_integer": "num_ctx deve ser um inteiro, recebeu float com parte fracionária", + "ollama_num_ctx_value_out_of_range": "valor num_ctx fora do intervalo", + "ollama_num_ctx_must_be_positive": "num_ctx deve ser positivo, recebeu: %d", + "ollama_num_ctx_value_too_large": "valor num_ctx muito grande: %d", + "ollama_num_ctx_must_be_valid_number": "num_ctx deve ser um número válido", + "ollama_num_ctx_must_be_valid_number_got": "num_ctx deve ser um número válido, recebeu: %s", + "ollama_num_ctx_invalid_type": "num_ctx deve ser um número, recebeu tipo inválido", + "ollama_num_ctx_exceeds_maximum": "num_ctx excede o valor máximo permitido de %d", + "ollama_error_reading_body": "Erro ao ler o corpo: %v", + "ollama_error_endpoint": "testando endpoint", + "ollama_error_unmarshalling_body": "Erro ao desserializar o corpo: %v", + "ollama_invalid_num_ctx_in_request": "num_ctx inválido na requisição: %v", + "ollama_warning_parse_variables": "Aviso: falha ao analisar options.variables como JSON: %v", + "ollama_error_marshalling_body": "Erro ao serializar o corpo: %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_failed_create_request": "falha ao criar a requisição", + "ollama_error_getting_chat_body": "Erro ao obter o corpo /chat: %v", + "ollama_upstream_non_2xx_body_unreadable": "Servidor Fabric upstream retornou status não-2xx %d e o corpo não pôde ser lido: %v", + "ollama_upstream_non_2xx": "Servidor Fabric upstream retornou status não-2xx %d: %s", + "ollama_upstream_returned_status": "servidor Fabric upstream retornou status %d", + "ollama_error_prefix": "Erro: %s", + "ollama_error_parse_upstream_response": "Erro: falha ao analisar resposta upstream", + "ollama_failed_unmarshal_fabric_response": "falha ao desserializar resposta Fabric", + "ollama_error_writing_response": "Erro ao escrever resposta: %v", + "ollama_error_scanning_body": "Erro ao escanear o corpo: %v", + "ollama_failed_scan_sse_stream": "falha ao escanear fluxo de resposta SSE: %v", + "ollama_sse_buffer_limit": "Linha SSE excede limite de buffer de 1MB - linha de dados muito grande", + "ollama_warning_no_content": "Aviso: nenhum conteúdo recebido do servidor Fabric upstream", + "ollama_no_content_from_upstream": "nenhum conteúdo recebido do servidor Fabric upstream", + "ollama_empty_address": "endereço vazio", + "ollama_invalid_address": "endereço inválido: %w", + "ollama_invalid_address_missing_host": "endereço inválido: host ausente", + "ollama_invalid_address_missing_hostname": "endereço inválido: nome do host ausente", + "ollama_invalid_address_path_not_allowed": "endereço inválido: componente de caminho não permitido em endereço simples", + "extension_registry_not_initialized": "registro de extensões não inicializado", + "extension_name_label": "Extensão: %s\n", + "extension_status_disabled": " Status: DESABILITADA - Verificação de hash falhou: %v\n", + "extension_config_path_label": " Caminho de configuração: %s\n\n", + "extension_status_enabled": " Status: HABILITADA\n", + "extension_executable_label": " Executável: %s\n", + "extension_type_label": " Tipo: %s\n", + "extension_timeout_label": " Tempo limite: %s\n", + "extension_description_label": " Descrição: %s\n", + "extension_version_label": " Versão: %s\n", + "extension_operations_label": " Operações:\n", + "extension_command_template_label": " Modelo de comando: %s\n", + "extension_file_config_label": " Configuração de arquivo:\n", + "extension_invalid_config_path": "caminho de configuração inválido: %w", + "extension_failed_read_config": "falha ao ler o arquivo de configuração: %w", + "extension_failed_parse_config": "falha ao analisar o arquivo de configuração: %w", + "extension_failed_register": "falha ao registrar a extensão: %w", + "extension_invalid_timeout": "valor de tempo limite inválido '%s': deve ser uma duração como '30s' ou '1m': %w", + "extension_registered_success": "Extensão registrada com sucesso:\n", + "extension_name_detail_label": "Nome: %s\n", + "extension_failed_remove": "falha ao remover a extensão: %w", + "lmstudio_api_url_question": "Digite sua URL %v (como lembrete, geralmente é %v)", + "lmstudio_failed_create_request": "falha ao criar a requisição: %w", + "lmstudio_failed_send_request": "falha ao enviar a requisição: %w", + "lmstudio_unexpected_status_code": "código de status inesperado: %d", + "lmstudio_failed_decode_response": "falha ao decodificar a resposta: %w", + "lmstudio_failed_marshal_payload": "falha ao serializar o payload: %w", + "lmstudio_error_reading_response": "erro ao ler a resposta: %w", + "lmstudio_invalid_response_missing_choices": "formato de resposta inválido: escolhas ausentes ou vazias", + "lmstudio_invalid_response_missing_message": "formato de resposta inválido: mensagem ausente na primeira escolha", + "lmstudio_invalid_response_missing_content": "formato de resposta inválido: conteúdo ausente ou não é uma string na mensagem", + "lmstudio_invalid_response_missing_text": "formato de resposta inválido: texto ausente ou não é uma string na primeira escolha", + "lmstudio_no_embeddings_returned": "nenhum embedding retornado", + "extension_warning_load_registry": "Aviso: não foi possível carregar o registro de extensões: %v\n", + "extension_name_empty": "o nome da extensão não pode estar vazio", + "extension_name_contains_spaces": "o nome da extensão '%s' contém espaços - nomes não devem conter espaços", + "extension_executable_not_found": "executável não encontrado: %w", + "extension_failed_get_absolute_path": "falha ao obter o caminho absoluto: %w", + "extension_failed_hash_executable": "falha ao calcular o hash do executável: %w", + "extension_invalid_definition": "definição de extensão inválida: %w", + "extension_name_required": "o nome da extensão é obrigatório", + "extension_executable_required": "o caminho do executável é obrigatório", + "extension_type_required": "o tipo da extensão é obrigatório", + "extension_invalid_timeout_format": "formato de tempo limite inválido: %w", + "extension_operation_required": "pelo menos uma operação deve ser definida", + "extension_cmd_template_required": "o modelo de comando é obrigatório para a operação %s", + "extension_not_found": "extensão %s não encontrada", + "extension_config_hash_mismatch": "discrepância de hash do arquivo de configuração para %s", + "extension_failed_verify_executable": "falha ao verificar o executável: %w", + "extension_executable_hash_mismatch": "discrepância de hash do executável para %s", + "extension_failed_marshal_registry": "falha ao serializar o registro de extensões: %w", + "extension_failed_read_registry": "falha ao ler o registro de extensões: %w", + "extension_failed_parse_registry": "falha ao analisar o registro de extensões: %w", + "extension_failed_get_extension": "falha ao obter a extensão: %w", + "extension_failed_format_command": "falha ao formatar o comando: %w", + "extension_empty_command": "comando vazio após a formatação", + "extension_operation_not_found": "operação %s não encontrada para a extensão %s", + "extension_executing_command": "Executando comando: %s\n", + "extension_execution_failed_stderr": "execução falhou: %w\nstderr: %s", + "extension_no_file_config": "nenhuma configuração de arquivo encontrada", + "extension_no_output_file": "nenhum arquivo de saída especificado na configuração", + "extension_execution_timed_out": "execução expirou após %v", + "extension_execution_failed_err": "execução falhou: %w\nerr: %s", + "extension_failed_read_output_file": "falha ao ler o arquivo de saída: %w", + "extension_failed_get_output_path": "falha ao obter o caminho de saída: %w\nerr: %s", + "spotify_failed_create_token_request": "falha ao criar a solicitação de token: %w", + "spotify_failed_request_access_token": "falha ao solicitar o token de acesso: %w", + "spotify_failed_get_access_token": "falha ao obter o token de acesso: status %d, resposta: %s", + "spotify_failed_decode_token_response": "falha ao decodificar a resposta do token: %w", + "spotify_failed_create_request": "falha ao criar a solicitação: %w", + "spotify_failed_execute_request": "falha ao executar a solicitação: %w", + "spotify_failed_read_response_body": "falha ao ler o corpo da resposta: %w", + "spotify_api_request_failed": "a solicitação da API falhou: status %d, resposta: %s", + "spotify_failed_parse_show_metadata": "falha ao analisar os metadados do programa: %w", + "spotify_failed_parse_episode_metadata": "falha ao analisar os metadados do episódio: %w", + "spotify_search_failed": "a pesquisa falhou: %w", + "spotify_failed_parse_search_results": "falha ao analisar os resultados da pesquisa: %w", + "spotify_failed_get_show_episodes": "falha ao obter os episódios do programa: %w", + "spotify_failed_parse_episodes": "falha ao analisar os episódios: %w" } diff --git a/internal/i18n/locales/pt-PT.json b/internal/i18n/locales/pt-PT.json index 1a5a3b1d..618806f8 100644 --- a/internal/i18n/locales/pt-PT.json +++ b/internal/i18n/locales/pt-PT.json @@ -42,6 +42,8 @@ "openai_unexpected_status_code_read_error_partial": "código de estado inesperado: %d do fornecedor %s (erro ao ler corpo: %v), resposta parcial: %s", "openai_unexpected_status_code_read_error": "código de estado inesperado: %d do fornecedor %s (falha ao ler corpo da resposta: %v)", "openai_unable_to_parse_models_response": "não foi possível analisar a resposta de modelos; resposta bruta: %s", + "openai_warning_model_no_image_generation": "Aviso: O modelo '%s' não suporta geração de imagens. Modelos suportados: %s. Considere usar -m gpt-5.2 para geração de imagens.\n", + "openai_model_no_image_generation": "o modelo '%s' não suporta geração de imagens. Modelos suportados: %s", "scraping_not_configured": "funcionalidade de scraping não está configurada. Por favor configure o Jina para ativar o scraping", "could_not_determine_home_dir": "não foi possível determinar o diretório home do utilizador: %w", "could_not_stat_env_file": "não foi possível verificar o ficheiro .env: %w", @@ -293,5 +295,120 @@ "plugin_question_optional": "%v%v (deixe em branco para ignorar):", "plugin_invalid_boolean_value": "valor booleano inválido: %v", "plugin_setting_not_valid": "%v=%v não é válido", - "plugin_invalid_bool": "bool inválido: %q" + "plugin_invalid_bool": "bool inválido: %q", + "ollama_num_ctx_must_be_finite": "num_ctx deve ser um número finito", + "ollama_num_ctx_must_be_integer": "num_ctx deve ser um inteiro, recebeu float com parte fracionária", + "ollama_num_ctx_value_out_of_range": "valor num_ctx fora do intervalo", + "ollama_num_ctx_must_be_positive": "num_ctx deve ser positivo, recebeu: %d", + "ollama_num_ctx_value_too_large": "valor num_ctx demasiado grande: %d", + "ollama_num_ctx_must_be_valid_number": "num_ctx deve ser um número válido", + "ollama_num_ctx_must_be_valid_number_got": "num_ctx deve ser um número válido, recebeu: %s", + "ollama_num_ctx_invalid_type": "num_ctx deve ser um número, recebeu tipo inválido", + "ollama_num_ctx_exceeds_maximum": "num_ctx excede o valor máximo permitido de %d", + "ollama_error_reading_body": "Erro ao ler o corpo: %v", + "ollama_error_endpoint": "a testar endpoint", + "ollama_error_unmarshalling_body": "Erro ao desserializar o corpo: %v", + "ollama_invalid_num_ctx_in_request": "num_ctx inválido no pedido: %v", + "ollama_warning_parse_variables": "Aviso: falha ao analisar options.variables como JSON: %v", + "ollama_error_marshalling_body": "Erro ao serializar o corpo: %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_failed_create_request": "falha ao criar o pedido", + "ollama_error_getting_chat_body": "Erro ao obter o corpo /chat: %v", + "ollama_upstream_non_2xx_body_unreadable": "Servidor Fabric upstream retornou estado não-2xx %d e o corpo não pôde ser lido: %v", + "ollama_upstream_non_2xx": "Servidor Fabric upstream retornou estado não-2xx %d: %s", + "ollama_upstream_returned_status": "servidor Fabric upstream retornou estado %d", + "ollama_error_prefix": "Erro: %s", + "ollama_error_parse_upstream_response": "Erro: falha ao analisar resposta upstream", + "ollama_failed_unmarshal_fabric_response": "falha ao desserializar resposta Fabric", + "ollama_error_writing_response": "Erro ao escrever resposta: %v", + "ollama_error_scanning_body": "Erro ao analisar o corpo: %v", + "ollama_failed_scan_sse_stream": "falha ao analisar fluxo de resposta SSE: %v", + "ollama_sse_buffer_limit": "Linha SSE excede limite de buffer de 1MB - linha de dados demasiado grande", + "ollama_warning_no_content": "Aviso: nenhum conteúdo recebido do servidor Fabric upstream", + "ollama_no_content_from_upstream": "nenhum conteúdo recebido do servidor Fabric upstream", + "ollama_empty_address": "endereço vazio", + "ollama_invalid_address": "endereço inválido: %w", + "ollama_invalid_address_missing_host": "endereço inválido: host ausente", + "ollama_invalid_address_missing_hostname": "endereço inválido: nome do host ausente", + "ollama_invalid_address_path_not_allowed": "endereço inválido: componente de caminho não permitido em endereço simples", + "extension_registry_not_initialized": "registo de extensões não inicializado", + "extension_name_label": "Extensão: %s\n", + "extension_status_disabled": " Estado: DESATIVADA - Verificação de hash falhou: %v\n", + "extension_config_path_label": " Caminho de configuração: %s\n\n", + "extension_status_enabled": " Estado: ATIVADA\n", + "extension_executable_label": " Executável: %s\n", + "extension_type_label": " Tipo: %s\n", + "extension_timeout_label": " Tempo limite: %s\n", + "extension_description_label": " Descrição: %s\n", + "extension_version_label": " Versão: %s\n", + "extension_operations_label": " Operações:\n", + "extension_command_template_label": " Modelo de comando: %s\n", + "extension_file_config_label": " Configuração de ficheiro:\n", + "extension_invalid_config_path": "caminho de configuração inválido: %w", + "extension_failed_read_config": "falha ao ler o ficheiro de configuração: %w", + "extension_failed_parse_config": "falha ao analisar o ficheiro de configuração: %w", + "extension_failed_register": "falha ao registar a extensão: %w", + "extension_invalid_timeout": "valor de tempo limite inválido '%s': deve ser uma duração como '30s' ou '1m': %w", + "extension_registered_success": "Extensão registada com sucesso:\n", + "extension_name_detail_label": "Nome: %s\n", + "extension_failed_remove": "falha ao remover a extensão: %w", + "lmstudio_api_url_question": "Introduza o seu URL %v (como lembrete, geralmente é %v)", + "lmstudio_failed_create_request": "falha ao criar o pedido: %w", + "lmstudio_failed_send_request": "falha ao enviar o pedido: %w", + "lmstudio_unexpected_status_code": "código de estado inesperado: %d", + "lmstudio_failed_decode_response": "falha ao descodificar a resposta: %w", + "lmstudio_failed_marshal_payload": "falha ao serializar os dados: %w", + "lmstudio_error_reading_response": "erro ao ler a resposta: %w", + "lmstudio_invalid_response_missing_choices": "formato de resposta inválido: escolhas ausentes ou vazias", + "lmstudio_invalid_response_missing_message": "formato de resposta inválido: mensagem ausente na primeira escolha", + "lmstudio_invalid_response_missing_content": "formato de resposta inválido: conteúdo ausente ou não é uma string na mensagem", + "lmstudio_invalid_response_missing_text": "formato de resposta inválido: texto ausente ou não é uma string na primeira escolha", + "lmstudio_no_embeddings_returned": "nenhum embedding retornado", + "extension_warning_load_registry": "Aviso: não foi possível carregar o registo de extensões: %v\n", + "extension_name_empty": "o nome da extensão não pode estar vazio", + "extension_name_contains_spaces": "o nome da extensão '%s' contém espaços - os nomes não devem conter espaços", + "extension_executable_not_found": "executável não encontrado: %w", + "extension_failed_get_absolute_path": "falha ao obter o caminho absoluto: %w", + "extension_failed_hash_executable": "falha ao calcular o hash do executável: %w", + "extension_invalid_definition": "definição de extensão inválida: %w", + "extension_name_required": "o nome da extensão é obrigatório", + "extension_executable_required": "o caminho do executável é obrigatório", + "extension_type_required": "o tipo da extensão é obrigatório", + "extension_invalid_timeout_format": "formato de tempo limite inválido: %w", + "extension_operation_required": "deve ser definida pelo menos uma operação", + "extension_cmd_template_required": "o modelo de comando é obrigatório para a operação %s", + "extension_not_found": "extensão %s não encontrada", + "extension_config_hash_mismatch": "discrepância de hash do ficheiro de configuração para %s", + "extension_failed_verify_executable": "falha ao verificar o executável: %w", + "extension_executable_hash_mismatch": "discrepância de hash do executável para %s", + "extension_failed_marshal_registry": "falha ao serializar o registo de extensões: %w", + "extension_failed_read_registry": "falha ao ler o registo de extensões: %w", + "extension_failed_parse_registry": "falha ao analisar o registo de extensões: %w", + "extension_failed_get_extension": "falha ao obter a extensão: %w", + "extension_failed_format_command": "falha ao formatar o comando: %w", + "extension_empty_command": "comando vazio após a formatação", + "extension_operation_not_found": "operação %s não encontrada para a extensão %s", + "extension_executing_command": "A executar comando: %s\n", + "extension_execution_failed_stderr": "execução falhou: %w\nstderr: %s", + "extension_no_file_config": "nenhuma configuração de ficheiro encontrada", + "extension_no_output_file": "nenhum ficheiro de saída especificado na configuração", + "extension_execution_timed_out": "execução expirou após %v", + "extension_execution_failed_err": "execução falhou: %w\nerr: %s", + "extension_failed_read_output_file": "falha ao ler o ficheiro de saída: %w", + "extension_failed_get_output_path": "falha ao obter o caminho de saída: %w\nerr: %s", + "spotify_failed_create_token_request": "falha ao criar o pedido de token: %w", + "spotify_failed_request_access_token": "falha ao solicitar o token de acesso: %w", + "spotify_failed_get_access_token": "falha ao obter o token de acesso: estado %d, resposta: %s", + "spotify_failed_decode_token_response": "falha ao descodificar a resposta do token: %w", + "spotify_failed_create_request": "falha ao criar o pedido: %w", + "spotify_failed_execute_request": "falha ao executar o pedido: %w", + "spotify_failed_read_response_body": "falha ao ler o corpo da resposta: %w", + "spotify_api_request_failed": "o pedido à API falhou: estado %d, resposta: %s", + "spotify_failed_parse_show_metadata": "falha ao analisar os metadados do programa: %w", + "spotify_failed_parse_episode_metadata": "falha ao analisar os metadados do episódio: %w", + "spotify_search_failed": "a pesquisa falhou: %w", + "spotify_failed_parse_search_results": "falha ao analisar os resultados da pesquisa: %w", + "spotify_failed_get_show_episodes": "falha ao obter os episódios do programa: %w", + "spotify_failed_parse_episodes": "falha ao analisar os episódios: %w" } diff --git a/internal/i18n/locales/zh.json b/internal/i18n/locales/zh.json index b21ca8d3..4f03cefb 100644 --- a/internal/i18n/locales/zh.json +++ b/internal/i18n/locales/zh.json @@ -42,6 +42,8 @@ "openai_unexpected_status_code_read_error_partial": "意外的状态码:来自提供商 %s 的 %d(读取主体错误:%v),部分响应:%s", "openai_unexpected_status_code_read_error": "意外的状态码:来自提供商 %s 的 %d(读取响应主体失败:%v)", "openai_unable_to_parse_models_response": "无法解析模型响应;原始响应:%s", + "openai_warning_model_no_image_generation": "警告:模型 '%s' 不支持图像生成。支持的模型:%s。请考虑使用 -m gpt-5.2 进行图像生成。\n", + "openai_model_no_image_generation": "模型 '%s' 不支持图像生成。支持的模型:%s", "scraping_not_configured": "抓取功能未配置。请设置 Jina 以启用抓取功能", "could_not_determine_home_dir": "无法确定用户主目录: %w", "could_not_stat_env_file": "无法获取 .env 文件状态: %w", @@ -293,5 +295,120 @@ "plugin_question_optional": "%v%v(留空以跳过):", "plugin_invalid_boolean_value": "无效的布尔值:%v", "plugin_setting_not_valid": "%v=%v 无效", - "plugin_invalid_bool": "无效的 bool:%q" + "plugin_invalid_bool": "无效的 bool:%q", + "ollama_num_ctx_must_be_finite": "num_ctx 必须是有限数", + "ollama_num_ctx_must_be_integer": "num_ctx 必须是整数,收到带小数部分的浮点数", + "ollama_num_ctx_value_out_of_range": "num_ctx 值超出范围", + "ollama_num_ctx_must_be_positive": "num_ctx 必须为正数,收到:%d", + "ollama_num_ctx_value_too_large": "num_ctx 值过大:%d", + "ollama_num_ctx_must_be_valid_number": "num_ctx 必须是有效的数字", + "ollama_num_ctx_must_be_valid_number_got": "num_ctx 必须是有效的数字,收到:%s", + "ollama_num_ctx_invalid_type": "num_ctx 必须是数字,收到无效类型", + "ollama_num_ctx_exceeds_maximum": "num_ctx 超过允许的最大值 %d", + "ollama_error_reading_body": "读取正文时出错:%v", + "ollama_error_endpoint": "正在测试端点", + "ollama_error_unmarshalling_body": "解析正文时出错:%v", + "ollama_invalid_num_ctx_in_request": "请求中的 num_ctx 无效:%v", + "ollama_warning_parse_variables": "警告:无法将 options.variables 解析为 JSON:%v", + "ollama_error_marshalling_body": "序列化正文时出错:%v", + "ollama_error_building_chat_url": "构建 /chat URL 时出错:%v", + "ollama_error_creating_chat_request": "创建 /chat 请求时出错:%v", + "ollama_failed_create_request": "创建请求失败", + "ollama_error_getting_chat_body": "获取 /chat 正文时出错:%v", + "ollama_upstream_non_2xx_body_unreadable": "上游 Fabric 服务器返回非 2xx 状态 %d 且无法读取正文:%v", + "ollama_upstream_non_2xx": "上游 Fabric 服务器返回非 2xx 状态 %d:%s", + "ollama_upstream_returned_status": "上游 Fabric 服务器返回状态 %d", + "ollama_error_prefix": "错误:%s", + "ollama_error_parse_upstream_response": "错误:无法解析上游响应", + "ollama_failed_unmarshal_fabric_response": "解析 Fabric 响应失败", + "ollama_error_writing_response": "写入响应时出错:%v", + "ollama_error_scanning_body": "扫描正文时出错:%v", + "ollama_failed_scan_sse_stream": "扫描 SSE 响应流失败:%v", + "ollama_sse_buffer_limit": "SSE 行超过 1MB 缓冲区限制 - 数据行过大", + "ollama_warning_no_content": "警告:未从上游 Fabric 服务器收到内容", + "ollama_no_content_from_upstream": "未从上游 Fabric 服务器收到内容", + "ollama_empty_address": "地址为空", + "ollama_invalid_address": "无效地址:%w", + "ollama_invalid_address_missing_host": "无效地址:缺少主机", + "ollama_invalid_address_missing_hostname": "无效地址:缺少主机名", + "ollama_invalid_address_path_not_allowed": "无效地址:简单地址中不允许路径组件", + "extension_registry_not_initialized": "扩展注册表未初始化", + "extension_name_label": "扩展:%s\n", + "extension_status_disabled": " 状态:已禁用 - 哈希验证失败:%v\n", + "extension_config_path_label": " 配置路径:%s\n\n", + "extension_status_enabled": " 状态:已启用\n", + "extension_executable_label": " 可执行文件:%s\n", + "extension_type_label": " 类型:%s\n", + "extension_timeout_label": " 超时:%s\n", + "extension_description_label": " 描述:%s\n", + "extension_version_label": " 版本:%s\n", + "extension_operations_label": " 操作:\n", + "extension_command_template_label": " 命令模板:%s\n", + "extension_file_config_label": " 文件配置:\n", + "extension_invalid_config_path": "无效的配置路径:%w", + "extension_failed_read_config": "读取配置文件失败:%w", + "extension_failed_parse_config": "解析配置文件失败:%w", + "extension_failed_register": "注册扩展失败:%w", + "extension_invalid_timeout": "无效的超时值 '%s':必须是 '30s' 或 '1m' 等时间格式:%w", + "extension_registered_success": "扩展注册成功:\n", + "extension_name_detail_label": "名称:%s\n", + "extension_failed_remove": "删除扩展失败:%w", + "lmstudio_api_url_question": "请输入您的 %v URL(提醒一下,通常是 %v)", + "lmstudio_failed_create_request": "创建请求失败:%w", + "lmstudio_failed_send_request": "发送请求失败:%w", + "lmstudio_unexpected_status_code": "意外的状态码:%d", + "lmstudio_failed_decode_response": "解码响应失败:%w", + "lmstudio_failed_marshal_payload": "序列化负载失败:%w", + "lmstudio_error_reading_response": "读取响应时出错:%w", + "lmstudio_invalid_response_missing_choices": "无效的响应格式:选项缺失或为空", + "lmstudio_invalid_response_missing_message": "无效的响应格式:第一个选项中缺少消息", + "lmstudio_invalid_response_missing_content": "无效的响应格式:消息中的内容缺失或不是字符串", + "lmstudio_invalid_response_missing_text": "无效的响应格式:第一个选项中的文本缺失或不是字符串", + "lmstudio_no_embeddings_returned": "未返回嵌入向量", + "extension_warning_load_registry": "警告:无法加载扩展注册表:%v\n", + "extension_name_empty": "扩展名称不能为空", + "extension_name_contains_spaces": "扩展名称 '%s' 包含空格 - 名称不能包含空格", + "extension_executable_not_found": "未找到可执行文件:%w", + "extension_failed_get_absolute_path": "获取绝对路径失败:%w", + "extension_failed_hash_executable": "计算可执行文件哈希失败:%w", + "extension_invalid_definition": "无效的扩展定义:%w", + "extension_name_required": "扩展名称为必填项", + "extension_executable_required": "可执行文件路径为必填项", + "extension_type_required": "扩展类型为必填项", + "extension_invalid_timeout_format": "无效的超时格式:%w", + "extension_operation_required": "必须定义至少一个操作", + "extension_cmd_template_required": "操作 %s 需要命令模板", + "extension_not_found": "未找到扩展 %s", + "extension_config_hash_mismatch": "%s 的配置文件哈希不匹配", + "extension_failed_verify_executable": "验证可执行文件失败:%w", + "extension_executable_hash_mismatch": "%s 的可执行文件哈希不匹配", + "extension_failed_marshal_registry": "序列化扩展注册表失败:%w", + "extension_failed_read_registry": "读取扩展注册表失败:%w", + "extension_failed_parse_registry": "解析扩展注册表失败:%w", + "extension_failed_get_extension": "获取扩展失败:%w", + "extension_failed_format_command": "格式化命令失败:%w", + "extension_empty_command": "格式化后命令为空", + "extension_operation_not_found": "扩展 %s 中未找到操作 %s", + "extension_executing_command": "正在执行命令:%s\n", + "extension_execution_failed_stderr": "执行失败:%w\nstderr:%s", + "extension_no_file_config": "未找到文件配置", + "extension_no_output_file": "配置中未指定输出文件", + "extension_execution_timed_out": "执行在 %v 后超时", + "extension_execution_failed_err": "执行失败:%w\nerr:%s", + "extension_failed_read_output_file": "读取输出文件失败:%w", + "extension_failed_get_output_path": "获取输出路径失败:%w\nerr:%s", + "spotify_failed_create_token_request": "创建令牌请求失败:%w", + "spotify_failed_request_access_token": "请求访问令牌失败:%w", + "spotify_failed_get_access_token": "获取访问令牌失败:状态 %d,响应:%s", + "spotify_failed_decode_token_response": "解码令牌响应失败:%w", + "spotify_failed_create_request": "创建请求失败:%w", + "spotify_failed_execute_request": "执行请求失败:%w", + "spotify_failed_read_response_body": "读取响应正文失败:%w", + "spotify_api_request_failed": "API 请求失败:状态 %d,响应:%s", + "spotify_failed_parse_show_metadata": "解析节目元数据失败:%w", + "spotify_failed_parse_episode_metadata": "解析剧集元数据失败:%w", + "spotify_search_failed": "搜索失败:%w", + "spotify_failed_parse_search_results": "解析搜索结果失败:%w", + "spotify_failed_get_show_episodes": "获取节目剧集失败:%w", + "spotify_failed_parse_episodes": "解析剧集失败:%w" } diff --git a/internal/plugins/ai/lmstudio/lmstudio.go b/internal/plugins/ai/lmstudio/lmstudio.go index d4d2fa89..169f7672 100644 --- a/internal/plugins/ai/lmstudio/lmstudio.go +++ b/internal/plugins/ai/lmstudio/lmstudio.go @@ -12,6 +12,7 @@ import ( "github.com/danielmiessler/fabric/internal/chat" "github.com/danielmiessler/fabric/internal/domain" + "github.com/danielmiessler/fabric/internal/i18n" "github.com/danielmiessler/fabric/internal/plugins" ) @@ -29,7 +30,7 @@ func NewClientCompatible(vendorName string, defaultBaseUrl string, configureCust } ret.PluginBase = plugins.NewVendorPluginBase(vendorName, configureCustom) ret.ApiUrl = ret.AddSetupQuestionCustom("API URL", true, - fmt.Sprintf("Enter your %v URL (as a reminder, it is usually %v')", vendorName, defaultBaseUrl)) + fmt.Sprintf(i18n.T("lmstudio_api_url_question"), vendorName, defaultBaseUrl)) return } @@ -52,17 +53,17 @@ func (c *Client) ListModels() ([]string, error) { req, err := http.NewRequest("GET", url, nil) if err != nil { - return nil, fmt.Errorf("failed to create request: %w", err) + return nil, fmt.Errorf(i18n.T("lmstudio_failed_create_request"), err) } resp, err := c.HttpClient.Do(req) if err != nil { - return nil, fmt.Errorf("failed to send request: %w", err) + return nil, fmt.Errorf(i18n.T("lmstudio_failed_send_request"), err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode) + return nil, fmt.Errorf(i18n.T("lmstudio_unexpected_status_code"), resp.StatusCode) } var result struct { @@ -72,7 +73,7 @@ func (c *Client) ListModels() ([]string, error) { } if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { - return nil, fmt.Errorf("failed to decode response: %w", err) + return nil, fmt.Errorf(i18n.T("lmstudio_failed_decode_response"), err) } models := make([]string, len(result.Data)) @@ -97,13 +98,13 @@ func (c *Client) SendStream(msgs []*chat.ChatCompletionMessage, opts *domain.Cha var jsonPayload []byte if jsonPayload, err = json.Marshal(payload); err != nil { - err = fmt.Errorf("failed to marshal payload: %w", err) + err = fmt.Errorf(i18n.T("lmstudio_failed_marshal_payload"), err) return } var req *http.Request if req, err = http.NewRequest("POST", url, bytes.NewBuffer(jsonPayload)); err != nil { - err = fmt.Errorf("failed to create request: %w", err) + err = fmt.Errorf(i18n.T("lmstudio_failed_create_request"), err) return } @@ -111,13 +112,13 @@ func (c *Client) SendStream(msgs []*chat.ChatCompletionMessage, opts *domain.Cha var resp *http.Response if resp, err = c.HttpClient.Do(req); err != nil { - err = fmt.Errorf("failed to send request: %w", err) + err = fmt.Errorf(i18n.T("lmstudio_failed_send_request"), err) return } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - err = fmt.Errorf("unexpected status code: %d", resp.StatusCode) + err = fmt.Errorf(i18n.T("lmstudio_unexpected_status_code"), resp.StatusCode) return } @@ -131,7 +132,7 @@ func (c *Client) SendStream(msgs []*chat.ChatCompletionMessage, opts *domain.Cha err = nil break } - err = fmt.Errorf("error reading response: %w", err) + err = fmt.Errorf(i18n.T("lmstudio_error_reading_response"), err) return } @@ -204,13 +205,13 @@ func (c *Client) Send(ctx context.Context, msgs []*chat.ChatCompletionMessage, o var jsonPayload []byte if jsonPayload, err = json.Marshal(payload); err != nil { - err = fmt.Errorf("failed to marshal payload: %w", err) + err = fmt.Errorf(i18n.T("lmstudio_failed_marshal_payload"), err) return } var req *http.Request if req, err = http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer(jsonPayload)); err != nil { - err = fmt.Errorf("failed to create request: %w", err) + err = fmt.Errorf(i18n.T("lmstudio_failed_create_request"), err) return } @@ -218,37 +219,37 @@ func (c *Client) Send(ctx context.Context, msgs []*chat.ChatCompletionMessage, o var resp *http.Response if resp, err = c.HttpClient.Do(req); err != nil { - err = fmt.Errorf("failed to send request: %w", err) + err = fmt.Errorf(i18n.T("lmstudio_failed_send_request"), err) return } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - err = fmt.Errorf("unexpected status code: %d", resp.StatusCode) + err = fmt.Errorf(i18n.T("lmstudio_unexpected_status_code"), resp.StatusCode) return } var result map[string]any if err = json.NewDecoder(resp.Body).Decode(&result); err != nil { - err = fmt.Errorf("failed to decode response: %w", err) + err = fmt.Errorf(i18n.T("lmstudio_failed_decode_response"), err) return } var choices []any var ok bool if choices, ok = result["choices"].([]any); !ok || len(choices) == 0 { - err = fmt.Errorf("invalid response format: missing or empty choices") + err = fmt.Errorf("%s", 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("invalid response format: missing message in first choice") + err = fmt.Errorf("%s", i18n.T("lmstudio_invalid_response_missing_message")) return } if content, ok = message["content"].(string); !ok { - err = fmt.Errorf("invalid response format: missing or non-string content in message") + err = fmt.Errorf("%s", i18n.T("lmstudio_invalid_response_missing_content")) return } @@ -266,13 +267,13 @@ func (c *Client) Complete(ctx context.Context, prompt string, opts *domain.ChatO var jsonPayload []byte if jsonPayload, err = json.Marshal(payload); err != nil { - err = fmt.Errorf("failed to marshal payload: %w", err) + err = fmt.Errorf(i18n.T("lmstudio_failed_marshal_payload"), err) return } var req *http.Request if req, err = http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer(jsonPayload)); err != nil { - err = fmt.Errorf("failed to create request: %w", err) + err = fmt.Errorf(i18n.T("lmstudio_failed_create_request"), err) return } @@ -280,31 +281,31 @@ func (c *Client) Complete(ctx context.Context, prompt string, opts *domain.ChatO var resp *http.Response if resp, err = c.HttpClient.Do(req); err != nil { - err = fmt.Errorf("failed to send request: %w", err) + err = fmt.Errorf(i18n.T("lmstudio_failed_send_request"), err) return } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - err = fmt.Errorf("unexpected status code: %d", resp.StatusCode) + err = fmt.Errorf(i18n.T("lmstudio_unexpected_status_code"), resp.StatusCode) return } var result map[string]any if err = json.NewDecoder(resp.Body).Decode(&result); err != nil { - err = fmt.Errorf("failed to decode response: %w", err) + err = fmt.Errorf(i18n.T("lmstudio_failed_decode_response"), err) return } var choices []any var ok bool if choices, ok = result["choices"].([]any); !ok || len(choices) == 0 { - err = fmt.Errorf("invalid response format: missing or empty choices") + err = fmt.Errorf("%s", i18n.T("lmstudio_invalid_response_missing_choices")) return } if text, ok = choices[0].(map[string]any)["text"].(string); !ok { - err = fmt.Errorf("invalid response format: missing or non-string text in first choice") + err = fmt.Errorf("%s", i18n.T("lmstudio_invalid_response_missing_text")) return } @@ -322,13 +323,13 @@ func (c *Client) GetEmbeddings(ctx context.Context, input string, opts *domain.C var jsonPayload []byte if jsonPayload, err = json.Marshal(payload); err != nil { - err = fmt.Errorf("failed to marshal payload: %w", err) + err = fmt.Errorf(i18n.T("lmstudio_failed_marshal_payload"), err) return } var req *http.Request if req, err = http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer(jsonPayload)); err != nil { - err = fmt.Errorf("failed to create request: %w", err) + err = fmt.Errorf(i18n.T("lmstudio_failed_create_request"), err) return } @@ -336,13 +337,13 @@ func (c *Client) GetEmbeddings(ctx context.Context, input string, opts *domain.C var resp *http.Response if resp, err = c.HttpClient.Do(req); err != nil { - err = fmt.Errorf("failed to send request: %w", err) + err = fmt.Errorf(i18n.T("lmstudio_failed_send_request"), err) return } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - err = fmt.Errorf("unexpected status code: %d", resp.StatusCode) + err = fmt.Errorf(i18n.T("lmstudio_unexpected_status_code"), resp.StatusCode) return } @@ -353,12 +354,12 @@ func (c *Client) GetEmbeddings(ctx context.Context, input string, opts *domain.C } if err = json.NewDecoder(resp.Body).Decode(&result); err != nil { - err = fmt.Errorf("failed to decode response: %w", err) + err = fmt.Errorf(i18n.T("lmstudio_failed_decode_response"), err) return } if len(result.Data) == 0 { - err = fmt.Errorf("no embeddings returned") + err = fmt.Errorf("%s", i18n.T("lmstudio_no_embeddings_returned")) return } diff --git a/internal/plugins/ai/openai/openai.go b/internal/plugins/ai/openai/openai.go index 85d848dc..a49efc35 100644 --- a/internal/plugins/ai/openai/openai.go +++ b/internal/plugins/ai/openai/openai.go @@ -11,6 +11,7 @@ import ( "github.com/danielmiessler/fabric/internal/chat" "github.com/danielmiessler/fabric/internal/domain" + "github.com/danielmiessler/fabric/internal/i18n" debuglog "github.com/danielmiessler/fabric/internal/log" "github.com/danielmiessler/fabric/internal/plugins" openai "github.com/openai/openai-go" @@ -75,8 +76,8 @@ func (o *Client) SetResponsesAPIEnabled(enabled bool) { // checkImageGenerationCompatibility warns if the model doesn't support image generation func checkImageGenerationCompatibility(model string) { if !supportsImageGeneration(model) { - fmt.Fprintf(os.Stderr, "Warning: Model '%s' does not support image generation. Supported models: %s. Consider using -m gpt-5.2 for image generation.\n", - model, strings.Join(ImageGenerationSupportedModels, ", ")) + fmt.Fprintf(os.Stderr, "%s", fmt.Sprintf(i18n.T("openai_warning_model_no_image_generation"), + model, strings.Join(ImageGenerationSupportedModels, ", "))) } } @@ -170,7 +171,7 @@ func (o *Client) sendResponses(ctx context.Context, msgs []*chat.ChatCompletionM // Validate model supports image generation if image file is specified if opts.ImageFile != "" && !supportsImageGeneration(opts.Model) { - return "", fmt.Errorf("model '%s' does not support image generation. Supported models: %s", opts.Model, strings.Join(ImageGenerationSupportedModels, ", ")) + return "", fmt.Errorf("%s", fmt.Sprintf(i18n.T("openai_model_no_image_generation"), opts.Model, strings.Join(ImageGenerationSupportedModels, ", "))) } req := o.buildResponseParams(msgs, opts) diff --git a/internal/plugins/template/extension_executor.go b/internal/plugins/template/extension_executor.go index b0ab51ff..68c71fcf 100644 --- a/internal/plugins/template/extension_executor.go +++ b/internal/plugins/template/extension_executor.go @@ -9,6 +9,8 @@ import ( "path/filepath" "strings" "time" + + "github.com/danielmiessler/fabric/internal/i18n" ) // ExtensionExecutor handles the secure execution of extensions @@ -34,19 +36,19 @@ func (e *ExtensionExecutor) Execute(name, operation, value string) (string, erro // Get and verify extension from registry ext, err := e.registry.GetExtension(name) if err != nil { - return "", fmt.Errorf("failed to get extension: %w", err) + return "", fmt.Errorf(i18n.T("extension_failed_get_extension"), err) } // Format the command using our template system cmdStr, err := e.formatCommand(ext, operation, value) if err != nil { - return "", fmt.Errorf("failed to format command: %w", err) + return "", fmt.Errorf(i18n.T("extension_failed_format_command"), err) } // Split the command string into command and arguments cmdParts := strings.Fields(cmdStr) if len(cmdParts) < 1 { - return "", fmt.Errorf("empty command after formatting") + return "", fmt.Errorf("%s", i18n.T("extension_empty_command")) } // Create command with the Executable and formatted arguments @@ -72,7 +74,7 @@ func (e *ExtensionExecutor) formatCommand(ext *ExtensionDefinition, operation st // Get operation config opConfig, exists := ext.Operations[operation] if !exists { - return "", fmt.Errorf("operation %s not found for extension %s", operation, ext.Name) + return "", fmt.Errorf("%s", fmt.Sprintf(i18n.T("extension_operation_not_found"), operation, ext.Name)) } vars := make(map[string]string) @@ -97,10 +99,10 @@ func (e *ExtensionExecutor) executeStdout(cmd *exec.Cmd, ext *ExtensionDefinitio cmd.Stderr = &stderr //debug output - fmt.Printf("Executing command: %s\n", cmd.String()) + fmt.Printf(i18n.T("extension_executing_command"), cmd.String()) if err := cmd.Run(); err != nil { - return "", fmt.Errorf("execution failed: %w\nstderr: %s", err, stderr.String()) + return "", fmt.Errorf(i18n.T("extension_execution_failed_stderr"), err, stderr.String()) } return stdout.String(), nil @@ -111,7 +113,7 @@ func (e *ExtensionExecutor) executeWithFile(cmd *exec.Cmd, ext *ExtensionDefinit // Parse timeout - this is now a first-class field timeout, err := time.ParseDuration(ext.Timeout) if err != nil { - return "", fmt.Errorf("invalid timeout format: %w", err) + return "", fmt.Errorf(i18n.T("extension_invalid_timeout_format"), err) } // Create context with timeout @@ -126,7 +128,7 @@ func (e *ExtensionExecutor) executeWithFile(cmd *exec.Cmd, ext *ExtensionDefinit fileConfig := ext.GetFileConfig() if fileConfig == nil { - return "", fmt.Errorf("no file configuration found") + return "", fmt.Errorf("%s", i18n.T("extension_no_file_config")) } // Handle path from stdout case @@ -139,7 +141,7 @@ func (e *ExtensionExecutor) executeWithFile(cmd *exec.Cmd, ext *ExtensionDefinit outputFile, _ := fileConfig["output_file"].(string) if outputFile == "" { - return "", fmt.Errorf("no output file specified in configuration") + return "", fmt.Errorf("%s", i18n.T("extension_no_output_file")) } // Set working directory if specified @@ -152,9 +154,9 @@ func (e *ExtensionExecutor) executeWithFile(cmd *exec.Cmd, ext *ExtensionDefinit if err := cmd.Run(); err != nil { if ctx.Err() == context.DeadlineExceeded { - return "", fmt.Errorf("execution timed out after %v", timeout) + return "", fmt.Errorf("%s", fmt.Sprintf(i18n.T("extension_execution_timed_out"), timeout)) } - return "", fmt.Errorf("execution failed: %w\nerr: %s", err, stderr.String()) + return "", fmt.Errorf(i18n.T("extension_execution_failed_err"), err, stderr.String()) } // Construct full file path @@ -165,7 +167,7 @@ func (e *ExtensionExecutor) executeWithFile(cmd *exec.Cmd, ext *ExtensionDefinit content, err := os.ReadFile(outputPath) if err != nil { - return "", fmt.Errorf("failed to read output file: %w", err) + return "", fmt.Errorf(i18n.T("extension_failed_read_output_file"), err) } // Handle cleanup if enabled @@ -183,13 +185,13 @@ func (e *ExtensionExecutor) handlePathFromStdout(cmd *exec.Cmd, ext *ExtensionDe cmd.Stderr = &stderr if err := cmd.Run(); err != nil { - return "", fmt.Errorf("failed to get output path: %w\nerr: %s", err, stderr.String()) + return "", fmt.Errorf(i18n.T("extension_failed_get_output_path"), err, stderr.String()) } outputPath := strings.TrimSpace(stdout.String()) content, err := os.ReadFile(outputPath) if err != nil { - return "", fmt.Errorf("failed to read output file: %w", err) + return "", fmt.Errorf(i18n.T("extension_failed_read_output_file"), err) } if ext.IsCleanupEnabled() { diff --git a/internal/plugins/template/extension_manager.go b/internal/plugins/template/extension_manager.go index 5ae6f8a8..596579b1 100644 --- a/internal/plugins/template/extension_manager.go +++ b/internal/plugins/template/extension_manager.go @@ -6,6 +6,7 @@ import ( "path/filepath" "time" + "github.com/danielmiessler/fabric/internal/i18n" "gopkg.in/yaml.v3" ) @@ -29,36 +30,36 @@ 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("extension registry not initialized") + return fmt.Errorf("%s", i18n.T("extension_registry_not_initialized")) } for name, entry := range em.registry.registry.Extensions { - fmt.Printf("Extension: %s\n", name) + fmt.Printf(i18n.T("extension_name_label"), name) // Try to load extension details ext, err := em.registry.GetExtension(name) if err != nil { - fmt.Printf(" Status: DISABLED - Hash verification failed: %v\n", err) - fmt.Printf(" Config Path: %s\n\n", entry.ConfigPath) + fmt.Printf(i18n.T("extension_status_disabled"), err) + fmt.Printf(i18n.T("extension_config_path_label"), entry.ConfigPath) continue } // Print extension details if verification succeeded - fmt.Printf(" Status: ENABLED\n") - fmt.Printf(" Executable: %s\n", ext.Executable) - fmt.Printf(" Type: %s\n", ext.Type) - fmt.Printf(" Timeout: %s\n", ext.Timeout) - fmt.Printf(" Description: %s\n", ext.Description) - fmt.Printf(" Version: %s\n", ext.Version) + fmt.Printf("%s", i18n.T("extension_status_enabled")) + fmt.Printf(i18n.T("extension_executable_label"), ext.Executable) + fmt.Printf(i18n.T("extension_type_label"), ext.Type) + fmt.Printf(i18n.T("extension_timeout_label"), ext.Timeout) + fmt.Printf(i18n.T("extension_description_label"), ext.Description) + fmt.Printf(i18n.T("extension_version_label"), ext.Version) - fmt.Printf(" Operations:\n") + fmt.Printf("%s", i18n.T("extension_operations_label")) for opName, opConfig := range ext.Operations { fmt.Printf(" %s:\n", opName) - fmt.Printf(" Command Template: %s\n", opConfig.CmdTemplate) + fmt.Printf(i18n.T("extension_command_template_label"), opConfig.CmdTemplate) } if fileConfig := ext.GetFileConfig(); fileConfig != nil { - fmt.Printf(" File Configuration:\n") + fmt.Printf("%s", i18n.T("extension_file_config_label")) for k, v := range fileConfig { fmt.Printf(" %s: %v\n", k, v) } @@ -73,45 +74,45 @@ func (em *ExtensionManager) ListExtensions() error { func (em *ExtensionManager) RegisterExtension(configPath string) error { absPath, err := filepath.Abs(configPath) if err != nil { - return fmt.Errorf("invalid config path: %w", err) + return fmt.Errorf(i18n.T("extension_invalid_config_path"), err) } // Get extension name before registration for status message data, err := os.ReadFile(absPath) if err != nil { - return fmt.Errorf("failed to read config file: %w", err) + return fmt.Errorf(i18n.T("extension_failed_read_config"), err) } var ext ExtensionDefinition if err := yaml.Unmarshal(data, &ext); err != nil { - return fmt.Errorf("failed to parse config file: %w", err) + return fmt.Errorf(i18n.T("extension_failed_parse_config"), err) } if err := em.registry.Register(absPath); err != nil { - return fmt.Errorf("failed to register extension: %w", err) + return fmt.Errorf(i18n.T("extension_failed_register"), err) } if _, err := time.ParseDuration(ext.Timeout); err != nil { - return fmt.Errorf("invalid timeout value '%s': must be a duration like '30s' or '1m': %w", ext.Timeout, err) + return fmt.Errorf(i18n.T("extension_invalid_timeout"), ext.Timeout, err) } // Print success message with extension details - fmt.Printf("Successfully registered extension:\n") - fmt.Printf("Name: %s\n", ext.Name) - fmt.Printf(" Executable: %s\n", ext.Executable) - fmt.Printf(" Type: %s\n", ext.Type) - fmt.Printf(" Timeout: %s\n", ext.Timeout) - fmt.Printf(" Description: %s\n", ext.Description) - fmt.Printf(" Version: %s\n", ext.Version) + fmt.Printf("%s", i18n.T("extension_registered_success")) + fmt.Printf(i18n.T("extension_name_detail_label"), ext.Name) + fmt.Printf(i18n.T("extension_executable_label"), ext.Executable) + fmt.Printf(i18n.T("extension_type_label"), ext.Type) + fmt.Printf(i18n.T("extension_timeout_label"), ext.Timeout) + fmt.Printf(i18n.T("extension_description_label"), ext.Description) + fmt.Printf(i18n.T("extension_version_label"), ext.Version) - fmt.Printf(" Operations:\n") + fmt.Printf("%s", i18n.T("extension_operations_label")) for opName, opConfig := range ext.Operations { fmt.Printf(" %s:\n", opName) - fmt.Printf(" Command Template: %s\n", opConfig.CmdTemplate) + fmt.Printf(i18n.T("extension_command_template_label"), opConfig.CmdTemplate) } if fileConfig := ext.GetFileConfig(); fileConfig != nil { - fmt.Printf(" File Configuration:\n") + fmt.Printf("%s", i18n.T("extension_file_config_label")) for k, v := range fileConfig { fmt.Printf(" %s: %v\n", k, v) } @@ -123,7 +124,7 @@ func (em *ExtensionManager) RegisterExtension(configPath string) error { // RemoveExtension handles the rmextension flag action func (em *ExtensionManager) RemoveExtension(name string) error { if err := em.registry.Remove(name); err != nil { - return fmt.Errorf("failed to remove extension: %w", err) + return fmt.Errorf(i18n.T("extension_failed_remove"), err) } return nil diff --git a/internal/plugins/template/extension_registry.go b/internal/plugins/template/extension_registry.go index 38906427..2673d381 100644 --- a/internal/plugins/template/extension_registry.go +++ b/internal/plugins/template/extension_registry.go @@ -10,6 +10,7 @@ import ( "strings" "time" + "github.com/danielmiessler/fabric/internal/i18n" debuglog "github.com/danielmiessler/fabric/internal/log" "gopkg.in/yaml.v3" @@ -88,7 +89,7 @@ func NewExtensionRegistry(configDir string) *ExtensionRegistry { r.ensureConfigDir() if err := r.loadRegistry(); err != nil { - debuglog.Log("Warning: could not load extension registry: %v\n", err) + debuglog.Log(i18n.T("extension_warning_load_registry"), err) } return r @@ -105,44 +106,44 @@ func (r *ExtensionRegistry) Register(configPath string) error { // Read and parse the extension definition to verify it data, err := os.ReadFile(configPath) if err != nil { - return fmt.Errorf("failed to read config file: %w", err) + return fmt.Errorf(i18n.T("extension_failed_read_config"), err) } var ext ExtensionDefinition if err := yaml.Unmarshal(data, &ext); err != nil { - return fmt.Errorf("failed to parse config file: %w", err) + return fmt.Errorf(i18n.T("extension_failed_parse_config"), err) } // Validate extension name if ext.Name == "" { - return fmt.Errorf("extension name cannot be empty") + return fmt.Errorf("%s", i18n.T("extension_name_empty")) } if strings.Contains(ext.Name, " ") { - return fmt.Errorf("extension name '%s' contains spaces - names must not contain spaces", ext.Name) + return fmt.Errorf("%s", fmt.Sprintf(i18n.T("extension_name_contains_spaces"), ext.Name)) } // Verify executable exists if _, err := os.Stat(ext.Executable); err != nil { - return fmt.Errorf("executable not found: %w", err) + return fmt.Errorf(i18n.T("extension_executable_not_found"), err) } // Get absolute path to config absPath, err := filepath.Abs(configPath) if err != nil { - return fmt.Errorf("failed to get absolute path: %w", err) + return fmt.Errorf(i18n.T("extension_failed_get_absolute_path"), err) } // Calculate hashes configHash := ComputeStringHash(string(data)) executableHash, err := ComputeHash(ext.Executable) if err != nil { - return fmt.Errorf("failed to hash executable: %w", err) + return fmt.Errorf(i18n.T("extension_failed_hash_executable"), err) } // Validate full extension definition (ensures operations and cmd_template present) if err := r.validateExtensionDefinition(&ext); err != nil { - return fmt.Errorf("invalid extension definition: %w", err) + return fmt.Errorf(i18n.T("extension_invalid_definition"), err) } // Store entry @@ -158,29 +159,29 @@ func (r *ExtensionRegistry) Register(configPath string) error { func (r *ExtensionRegistry) validateExtensionDefinition(ext *ExtensionDefinition) error { // Validate required fields if ext.Name == "" { - return fmt.Errorf("extension name is required") + return fmt.Errorf("%s", i18n.T("extension_name_required")) } if ext.Executable == "" { - return fmt.Errorf("executable path is required") + return fmt.Errorf("%s", i18n.T("extension_executable_required")) } if ext.Type == "" { - return fmt.Errorf("extension type is required") + return fmt.Errorf("%s", i18n.T("extension_type_required")) } // Validate timeout format if ext.Timeout != "" { if _, err := time.ParseDuration(ext.Timeout); err != nil { - return fmt.Errorf("invalid timeout format: %w", err) + return fmt.Errorf(i18n.T("extension_invalid_timeout_format"), err) } } // Validate operations if len(ext.Operations) == 0 { - return fmt.Errorf("at least one operation must be defined") + return fmt.Errorf("%s", i18n.T("extension_operation_required")) } for name, op := range ext.Operations { if op.CmdTemplate == "" { - return fmt.Errorf("command template is required for operation %s", name) + return fmt.Errorf("%s", fmt.Sprintf(i18n.T("extension_cmd_template_required"), name)) } } @@ -189,7 +190,7 @@ func (r *ExtensionRegistry) validateExtensionDefinition(ext *ExtensionDefinition func (r *ExtensionRegistry) Remove(name string) error { if _, exists := r.registry.Extensions[name]; !exists { - return fmt.Errorf("extension %s not found", name) + return fmt.Errorf("%s", fmt.Sprintf(i18n.T("extension_not_found"), name)) } delete(r.registry.Extensions, name) @@ -201,35 +202,35 @@ func (r *ExtensionRegistry) Verify(name string) error { // Get the registry entry entry, exists := r.registry.Extensions[name] if !exists { - return fmt.Errorf("extension %s not found", name) + return fmt.Errorf("%s", fmt.Sprintf(i18n.T("extension_not_found"), name)) } // Load and parse the config file data, err := os.ReadFile(entry.ConfigPath) if err != nil { - return fmt.Errorf("failed to read config file: %w", err) + return fmt.Errorf(i18n.T("extension_failed_read_config"), err) } // Verify config hash currentConfigHash := ComputeStringHash(string(data)) if currentConfigHash != entry.ConfigHash { - return fmt.Errorf("config file hash mismatch for %s", name) + return fmt.Errorf("%s", fmt.Sprintf(i18n.T("extension_config_hash_mismatch"), name)) } // Parse to get executable path var ext ExtensionDefinition if err := yaml.Unmarshal(data, &ext); err != nil { - return fmt.Errorf("failed to parse config file: %w", err) + return fmt.Errorf(i18n.T("extension_failed_parse_config"), err) } // Verify executable hash currentExecutableHash, err := ComputeHash(ext.Executable) if err != nil { - return fmt.Errorf("failed to verify executable: %w", err) + return fmt.Errorf(i18n.T("extension_failed_verify_executable"), err) } if currentExecutableHash != entry.ExecutableHash { - return fmt.Errorf("executable hash mismatch for %s", name) + return fmt.Errorf("%s", fmt.Sprintf(i18n.T("extension_executable_hash_mismatch"), name)) } return nil @@ -238,35 +239,35 @@ func (r *ExtensionRegistry) Verify(name string) error { func (r *ExtensionRegistry) GetExtension(name string) (*ExtensionDefinition, error) { entry, exists := r.registry.Extensions[name] if !exists { - return nil, fmt.Errorf("extension %s not found", name) + return nil, fmt.Errorf("%s", fmt.Sprintf(i18n.T("extension_not_found"), name)) } // Read current config file data, err := os.ReadFile(entry.ConfigPath) if err != nil { - return nil, fmt.Errorf("failed to read config file: %w", err) + return nil, fmt.Errorf(i18n.T("extension_failed_read_config"), err) } // Verify config hash currentHash := ComputeStringHash(string(data)) if currentHash != entry.ConfigHash { - return nil, fmt.Errorf("config file hash mismatch for %s", name) + return nil, fmt.Errorf("%s", fmt.Sprintf(i18n.T("extension_config_hash_mismatch"), name)) } // Parse config var ext ExtensionDefinition if err := yaml.Unmarshal(data, &ext); err != nil { - return nil, fmt.Errorf("failed to parse config file: %w", err) + return nil, fmt.Errorf(i18n.T("extension_failed_parse_config"), err) } // Verify executable hash currentExecHash, err := ComputeHash(ext.Executable) if err != nil { - return nil, fmt.Errorf("failed to verify executable: %w", err) + return nil, fmt.Errorf(i18n.T("extension_failed_verify_executable"), err) } if currentExecHash != entry.ExecutableHash { - return nil, fmt.Errorf("executable hash mismatch for %s", name) + return nil, fmt.Errorf("%s", fmt.Sprintf(i18n.T("extension_executable_hash_mismatch"), name)) } return &ext, nil @@ -307,7 +308,7 @@ func (r *ExtensionRegistry) calculateFileHash(path string) (string, error) { func (r *ExtensionRegistry) saveRegistry() error { data, err := yaml.Marshal(r.registry) if err != nil { - return fmt.Errorf("failed to marshal extension registry: %w", err) + return fmt.Errorf(i18n.T("extension_failed_marshal_registry"), err) } registryPath := filepath.Join(r.configDir, "extensions", "extensions.yaml") @@ -321,12 +322,12 @@ func (r *ExtensionRegistry) loadRegistry() error { if os.IsNotExist(err) { return nil // New registry } - return fmt.Errorf("failed to read extension registry: %w", err) + return fmt.Errorf(i18n.T("extension_failed_read_registry"), err) } // Need to unmarshal the data into our registry if err := yaml.Unmarshal(data, &r.registry); err != nil { - return fmt.Errorf("failed to parse extension registry: %w", err) + return fmt.Errorf(i18n.T("extension_failed_parse_registry"), err) } return nil diff --git a/internal/server/ollama.go b/internal/server/ollama.go index 7448e8bc..b975b4db 100644 --- a/internal/server/ollama.go +++ b/internal/server/ollama.go @@ -15,6 +15,7 @@ import ( "time" "github.com/danielmiessler/fabric/internal/core" + "github.com/danielmiessler/fabric/internal/i18n" "github.com/gin-gonic/gin" ) @@ -108,28 +109,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("num_ctx must be a finite number") + return 0, fmt.Errorf("%s", i18n.T("ollama_num_ctx_must_be_finite")) } if math.Trunc(v) != v { - return 0, fmt.Errorf("num_ctx must be an integer, got float with fractional part") + return 0, fmt.Errorf("%s", 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("num_ctx value out of range") + return 0, fmt.Errorf("%s", 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("num_ctx must be a finite number") + return 0, fmt.Errorf("%s", i18n.T("ollama_num_ctx_must_be_finite")) } if math.Trunc(f64) != f64 { - return 0, fmt.Errorf("num_ctx must be an integer, got float with fractional part") + return 0, fmt.Errorf("%s", 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("num_ctx value out of range") + return 0, fmt.Errorf("%s", i18n.T("ollama_num_ctx_value_out_of_range")) } contextLength = int(v) @@ -138,23 +139,23 @@ func parseOllamaNumCtx(options map[string]any) (int, error) { case int64: if v < 0 { - return 0, fmt.Errorf("num_ctx must be positive, got: %d", v) + return 0, fmt.Errorf(i18n.T("ollama_num_ctx_must_be_positive"), v) } if v > maxInt { - return 0, fmt.Errorf("num_ctx value too large: %d", v) + return 0, fmt.Errorf(i18n.T("ollama_num_ctx_value_too_large"), v) } contextLength = int(v) case json.Number: i64, err := v.Int64() if err != nil { - return 0, fmt.Errorf("num_ctx must be a valid number") + return 0, fmt.Errorf("%s", i18n.T("ollama_num_ctx_must_be_valid_number")) } if i64 < 0 { - return 0, fmt.Errorf("num_ctx must be positive, got: %d", i64) + return 0, fmt.Errorf(i18n.T("ollama_num_ctx_must_be_positive"), i64) } if i64 > maxInt { - return 0, fmt.Errorf("num_ctx value too large: %d", i64) + return 0, fmt.Errorf(i18n.T("ollama_num_ctx_value_too_large"), i64) } contextLength = int(i64) @@ -166,21 +167,21 @@ func parseOllamaNumCtx(options map[string]any) (int, error) { if len(v) > 50 { errVal = v[:50] + "..." } - return 0, fmt.Errorf("num_ctx must be a valid number, got: %s", errVal) + return 0, fmt.Errorf(i18n.T("ollama_num_ctx_must_be_valid_number_got"), errVal) } contextLength = parsed default: - return 0, fmt.Errorf("num_ctx must be a number, got invalid type") + return 0, fmt.Errorf("%s", i18n.T("ollama_num_ctx_invalid_type")) } if contextLength <= 0 { - return 0, fmt.Errorf("num_ctx must be positive, got: %d", contextLength) + return 0, fmt.Errorf(i18n.T("ollama_num_ctx_must_be_positive"), contextLength) } const maxContextLength = 1000000 if contextLength > maxContextLength { - return 0, fmt.Errorf("num_ctx exceeds maximum allowed value of %d", maxContextLength) + return 0, fmt.Errorf(i18n.T("ollama_num_ctx_exceeds_maximum"), maxContextLength) } return contextLength, nil @@ -257,22 +258,22 @@ func (f APIConvert) ollamaTags(c *gin.Context) { func (f APIConvert) ollamaChat(c *gin.Context) { body, err := io.ReadAll(c.Request.Body) if err != nil { - log.Printf("Error reading body: %v", err) - c.JSON(http.StatusInternalServerError, gin.H{"error": "testing endpoint"}) + log.Printf(i18n.T("ollama_error_reading_body"), err) + c.JSON(http.StatusInternalServerError, gin.H{"error": i18n.T("ollama_error_endpoint")}) return } var prompt OllamaRequestBody err = json.Unmarshal(body, &prompt) if err != nil { - log.Printf("Error unmarshalling body: %v", err) - c.JSON(http.StatusInternalServerError, gin.H{"error": "testing endpoint"}) + log.Printf(i18n.T("ollama_error_unmarshalling_body"), err) + c.JSON(http.StatusInternalServerError, gin.H{"error": i18n.T("ollama_error_endpoint")}) return } // Extract and validate num_ctx from options numCtx, err := parseOllamaNumCtx(prompt.Options) if err != nil { - log.Printf("Invalid num_ctx in request: %v", err) + log.Printf(i18n.T("ollama_invalid_num_ctx_in_request"), err) c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } @@ -289,7 +290,7 @@ func (f APIConvert) ollamaChat(c *gin.Context) { case string: // Parse JSON string into map if err := json.Unmarshal([]byte(v), &variables); err != nil { - log.Printf("Warning: failed to parse options.variables as JSON: %v", err) + log.Printf(i18n.T("ollama_warning_parse_variables"), err) } case map[string]any: // Convert map[string]any to map[string]string @@ -332,21 +333,21 @@ func (f APIConvert) ollamaChat(c *gin.Context) { fabricChatReq, err := json.Marshal(chat) if err != nil { - log.Printf("Error marshalling body: %v", err) + log.Printf(i18n.T("ollama_error_marshalling_body"), err) c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } var req *http.Request baseURL, err := buildFabricChatURL(*f.addr) if err != nil { - log.Printf("Error building /chat URL: %v", err) + log.Printf(i18n.T("ollama_error_building_chat_url"), err) c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } req, err = http.NewRequest("POST", fmt.Sprintf("%s/chat", baseURL), bytes.NewBuffer(fabricChatReq)) if err != nil { - log.Printf("Error creating /chat request: %v", err) - c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create request"}) + log.Printf(i18n.T("ollama_error_creating_chat_request"), err) + c.JSON(http.StatusInternalServerError, gin.H{"error": i18n.T("ollama_failed_create_request")}) return } @@ -354,7 +355,7 @@ func (f APIConvert) ollamaChat(c *gin.Context) { fabricRes, err := http.DefaultClient.Do(req) if err != nil { - log.Printf("Error getting /chat body: %v", err) + log.Printf(i18n.T("ollama_error_getting_chat_body"), err) c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } @@ -363,14 +364,14 @@ func (f APIConvert) ollamaChat(c *gin.Context) { if fabricRes.StatusCode < http.StatusOK || fabricRes.StatusCode >= http.StatusMultipleChoices { bodyBytes, readErr := io.ReadAll(fabricRes.Body) if readErr != nil { - log.Printf("Upstream Fabric server returned non-2xx status %d and body could not be read: %v", fabricRes.StatusCode, readErr) + log.Printf(i18n.T("ollama_upstream_non_2xx_body_unreadable"), fabricRes.StatusCode, readErr) } else { - log.Printf("Upstream Fabric server returned non-2xx status %d: %s", fabricRes.StatusCode, string(bodyBytes)) + log.Printf(i18n.T("ollama_upstream_non_2xx"), fabricRes.StatusCode, string(bodyBytes)) } - errorMessage := fmt.Sprintf("upstream Fabric server returned status %d", fabricRes.StatusCode) + errorMessage := fmt.Sprintf(i18n.T("ollama_upstream_returned_status"), fabricRes.StatusCode) if prompt.Stream { - _ = writeOllamaResponse(c, prompt.Model, fmt.Sprintf("Error: %s", errorMessage), true) + _ = writeOllamaResponse(c, prompt.Model, fmt.Sprintf(i18n.T("ollama_error_prefix"), errorMessage), true) } else { c.JSON(fabricRes.StatusCode, gin.H{"error": errorMessage}) } @@ -392,19 +393,19 @@ func (f APIConvert) ollamaChat(c *gin.Context) { payload := strings.TrimPrefix(line, "data: ") var fabricResponse FabricResponseFormat if err := json.Unmarshal([]byte(payload), &fabricResponse); err != nil { - log.Printf("Error unmarshalling body: %v", err) + log.Printf(i18n.T("ollama_error_unmarshalling_body"), err) if prompt.Stream { // In streaming mode, send the error in the same streaming format - _ = writeOllamaResponse(c, prompt.Model, "Error: failed to parse upstream response", true) + _ = writeOllamaResponse(c, prompt.Model, i18n.T("ollama_error_parse_upstream_response"), true) } else { - c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to unmarshal Fabric response"}) + c.JSON(http.StatusInternalServerError, gin.H{"error": i18n.T("ollama_failed_unmarshal_fabric_response")}) } return } if fabricResponse.Type == "error" { if prompt.Stream { // In streaming mode, propagate the upstream error via a final streaming chunk - _ = writeOllamaResponse(c, prompt.Model, fmt.Sprintf("Error: %s", fabricResponse.Content), true) + _ = writeOllamaResponse(c, prompt.Model, fmt.Sprintf(i18n.T("ollama_error_prefix"), fabricResponse.Content), true) } else { c.JSON(http.StatusInternalServerError, gin.H{"error": fabricResponse.Content}) } @@ -416,21 +417,21 @@ func (f APIConvert) ollamaChat(c *gin.Context) { contentBuilder.WriteString(fabricResponse.Content) if prompt.Stream { if err := writeOllamaResponse(c, prompt.Model, fabricResponse.Content, false); err != nil { - log.Printf("Error writing response: %v", err) + log.Printf(i18n.T("ollama_error_writing_response"), err) return } } } if err := scanner.Err(); err != nil { - log.Printf("Error scanning body: %v", err) - errorMsg := fmt.Sprintf("failed to scan SSE response stream: %v", err) + log.Printf(i18n.T("ollama_error_scanning_body"), err) + errorMsg := fmt.Sprintf(i18n.T("ollama_failed_scan_sse_stream"), err) // Check for buffer size exceeded error if strings.Contains(err.Error(), "token too long") { - errorMsg = "SSE line exceeds 1MB buffer limit - data line too large" + errorMsg = i18n.T("ollama_sse_buffer_limit") } if prompt.Stream { // In streaming mode, send the error in the same streaming format - _ = writeOllamaResponse(c, prompt.Model, fmt.Sprintf("Error: %s", errorMsg), true) + _ = writeOllamaResponse(c, prompt.Model, fmt.Sprintf(i18n.T("ollama_error_prefix"), errorMsg), true) } else { c.JSON(http.StatusInternalServerError, gin.H{"error": errorMsg}) } @@ -442,10 +443,10 @@ func (f APIConvert) ollamaChat(c *gin.Context) { // Check if we received any content from upstream if contentBuilder.Len() == 0 { - log.Printf("Warning: no content received from upstream Fabric server") + log.Printf("%s", i18n.T("ollama_warning_no_content")) // In non-streaming mode, treat absence of content as an error if !prompt.Stream { - c.JSON(http.StatusBadGateway, gin.H{"error": "no content received from upstream Fabric server"}) + c.JSON(http.StatusBadGateway, gin.H{"error": i18n.T("ollama_no_content_from_upstream")}) return } } @@ -458,7 +459,7 @@ func (f APIConvert) ollamaChat(c *gin.Context) { finalResponse := buildFinalOllamaResponse(prompt.Model, "", duration) if err := writeOllamaResponseStruct(c, finalResponse); err != nil { - log.Printf("Error writing response: %v", err) + log.Printf(i18n.T("ollama_error_writing_response"), err) } } @@ -492,18 +493,18 @@ func buildFinalOllamaResponse(model string, content string, duration int64) Olla // contains a path component. func buildFabricChatURL(addr string) (string, error) { if addr == "" { - return "", fmt.Errorf("empty address") + return "", fmt.Errorf("%s", i18n.T("ollama_empty_address")) } if strings.HasPrefix(addr, "http://") || strings.HasPrefix(addr, "https://") { parsed, err := url.Parse(addr) if err != nil { - return "", fmt.Errorf("invalid address: %w", err) + return "", fmt.Errorf(i18n.T("ollama_invalid_address"), err) } if parsed.Host == "" { - return "", fmt.Errorf("invalid address: missing host") + return "", fmt.Errorf("%s", i18n.T("ollama_invalid_address_missing_host")) } if strings.HasPrefix(parsed.Host, ":") { - return "", fmt.Errorf("invalid address: missing hostname") + return "", fmt.Errorf("%s", i18n.T("ollama_invalid_address_missing_hostname")) } return strings.TrimRight(parsed.String(), "/"), nil } @@ -513,17 +514,17 @@ func buildFabricChatURL(addr string) (string, error) { // Validate bare addresses (without http/https prefix) parsed, err := url.Parse("http://" + addr) if err != nil { - return "", fmt.Errorf("invalid address: %w", err) + return "", fmt.Errorf(i18n.T("ollama_invalid_address"), err) } if parsed.Host == "" { - return "", fmt.Errorf("invalid address: missing host") + return "", fmt.Errorf("%s", i18n.T("ollama_invalid_address_missing_host")) } if strings.HasPrefix(parsed.Host, ":") { - return "", fmt.Errorf("invalid address: missing hostname") + return "", fmt.Errorf("%s", 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("invalid address: path component not allowed in bare address") + return "", fmt.Errorf("%s", i18n.T("ollama_invalid_address_path_not_allowed")) } return strings.TrimRight(parsed.String(), "/"), nil } diff --git a/internal/tools/spotify/spotify.go b/internal/tools/spotify/spotify.go index 15aba08e..00a7530d 100644 --- a/internal/tools/spotify/spotify.go +++ b/internal/tools/spotify/spotify.go @@ -98,7 +98,7 @@ func (s *Spotify) refreshAccessToken() error { req, err := http.NewRequest("POST", tokenURL, strings.NewReader(data.Encode())) if err != nil { - return fmt.Errorf("failed to create token request: %w", err) + return fmt.Errorf(i18n.T("spotify_failed_create_token_request"), err) } // Set Basic Auth header with Client ID and Secret @@ -108,13 +108,13 @@ func (s *Spotify) refreshAccessToken() error { resp, err := s.httpClient.Do(req) if err != nil { - return fmt.Errorf("failed to request access token: %w", err) + return fmt.Errorf(i18n.T("spotify_failed_request_access_token"), err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { body, _ := io.ReadAll(resp.Body) - return fmt.Errorf("failed to get access token: status %d, body: %s", resp.StatusCode, string(body)) + return fmt.Errorf(i18n.T("spotify_failed_get_access_token"), resp.StatusCode, string(body)) } var tokenResp struct { @@ -124,7 +124,7 @@ func (s *Spotify) refreshAccessToken() error { } if err := json.NewDecoder(resp.Body).Decode(&tokenResp); err != nil { - return fmt.Errorf("failed to decode token response: %w", err) + return fmt.Errorf(i18n.T("spotify_failed_decode_token_response"), err) } s.tokenMutex.Lock() @@ -145,7 +145,7 @@ func (s *Spotify) doRequest(method, endpoint string) ([]byte, error) { reqURL := apiBaseURL + endpoint req, err := http.NewRequest(method, reqURL, nil) if err != nil { - return nil, fmt.Errorf("failed to create request: %w", err) + return nil, fmt.Errorf(i18n.T("spotify_failed_create_request"), err) } s.tokenMutex.RLock() @@ -154,17 +154,17 @@ func (s *Spotify) doRequest(method, endpoint string) ([]byte, error) { resp, err := s.httpClient.Do(req) if err != nil { - return nil, fmt.Errorf("failed to execute request: %w", err) + return nil, fmt.Errorf(i18n.T("spotify_failed_execute_request"), err) } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { - return nil, fmt.Errorf("failed to read response body: %w", err) + return nil, fmt.Errorf(i18n.T("spotify_failed_read_response_body"), err) } if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("API request failed: status %d, body: %s", resp.StatusCode, string(body)) + return nil, fmt.Errorf(i18n.T("spotify_api_request_failed"), resp.StatusCode, string(body)) } return body, nil @@ -249,7 +249,7 @@ func (s *Spotify) GetShowMetadata(showId string) (*ShowMetadata, error) { } if err := json.Unmarshal(body, &resp); err != nil { - return nil, fmt.Errorf("failed to parse show metadata: %w", err) + return nil, fmt.Errorf(i18n.T("spotify_failed_parse_show_metadata"), err) } if resp.Id == "" { @@ -303,7 +303,7 @@ func (s *Spotify) GetEpisodeMetadata(episodeId string) (*EpisodeMetadata, error) } if err := json.Unmarshal(body, &resp); err != nil { - return nil, fmt.Errorf("failed to parse episode metadata: %w", err) + return nil, fmt.Errorf(i18n.T("spotify_failed_parse_episode_metadata"), err) } if resp.Id == "" { @@ -341,7 +341,7 @@ func (s *Spotify) SearchShows(query string, limit int) (*SearchResult, error) { endpoint := fmt.Sprintf("/search?q=%s&type=show&limit=%d", url.QueryEscape(query), limit) body, err := s.doRequest("GET", endpoint) if err != nil { - return nil, fmt.Errorf("search failed: %w", err) + return nil, fmt.Errorf(i18n.T("spotify_search_failed"), err) } var resp struct { @@ -365,7 +365,7 @@ func (s *Spotify) SearchShows(query string, limit int) (*SearchResult, error) { } if err := json.Unmarshal(body, &resp); err != nil { - return nil, fmt.Errorf("failed to parse search results: %w", err) + return nil, fmt.Errorf(i18n.T("spotify_failed_parse_search_results"), err) } result := &SearchResult{ @@ -401,7 +401,7 @@ func (s *Spotify) GetShowEpisodes(showId string, limit int) ([]EpisodeMetadata, endpoint := fmt.Sprintf("/shows/%s/episodes?limit=%d", showId, limit) body, err := s.doRequest("GET", endpoint) if err != nil { - return nil, fmt.Errorf("failed to get show episodes: %w", err) + return nil, fmt.Errorf(i18n.T("spotify_failed_get_show_episodes"), err) } var resp struct { @@ -424,7 +424,7 @@ func (s *Spotify) GetShowEpisodes(showId string, limit int) ([]EpisodeMetadata, } if err := json.Unmarshal(body, &resp); err != nil { - return nil, fmt.Errorf("failed to parse episodes: %w", err) + return nil, fmt.Errorf(i18n.T("spotify_failed_parse_episodes"), err) } episodes := make([]EpisodeMetadata, 0, len(resp.Items))