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
This commit is contained in:
Kayvan Sylvan
2026-02-14 12:03:40 -08:00
parent 66621c5fb2
commit c37a7dbf0b
17 changed files with 1359 additions and 182 deletions

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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
}

View File

@@ -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)

View File

@@ -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() {

View File

@@ -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

View File

@@ -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

View File

@@ -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
}

View File

@@ -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))