settings = $settings; } /** * Filters the http_request_timeout value while an AI translation call is * in progress. Registered and de-registered by translate() around each call. * * @since 1.2.0 * * @param mixed $timeout Current timeout value passed by WordPress. * * @return int */ public function filter_timeout( $timeout ) { if ( null !== $this->active_timeout ) { return $this->active_timeout; } return is_numeric( $timeout ) ? (int) $timeout : 30; } /** * Indicates whether AI features are enabled for this WordPress installation. * * `wp_ai_client_prompt()` is a native WordPress 7.0 function — the API is always * present. This method checks `wp_supports_ai()`, which returns false only when the * `WP_AI_SUPPORT` constant is set to false or the `wp_supports_ai` filter disables it. * * @since 1.0.0 * * @return bool True when WordPress AI is enabled for this installation. */ public function is_available() { return wp_supports_ai(); } /** * Indicates whether the WordPress AI client supports text generation right now. * * The check uses a tiny dummy prompt so the AI client can answer whether at * least one configured provider can serve text generation requests. * * @since 1.0.0 * * @return bool True if text generation is supported. */ public function is_supported() { if ( ! $this->is_available() ) { return false; } try { $builder = wp_ai_client_prompt( 'test' ); return (bool) $builder->is_supported_for_text_generation(); } catch ( Exception $e ) { return false; } catch ( Throwable $e ) { return false; } } /** * Translates a single piece of text to the target locale. * * Applies the configured request_timeout via the http_request_timeout filter * for the duration of the AI call, then restores the previous timeout. * * @since 1.0.0 * * @param string $text Source text. Plain text or HTML; preserved as-is. * @param string $target_locale WordPress locale code (for example: es_ES, en_US, fr_FR). * * @return string|WP_Error Translated text, or WP_Error on failure. */ public function translate( $text, $target_locale ) { $text = (string) $text; if ( '' === trim( $text ) ) { return ''; } if ( ! $this->is_available() ) { return new WP_Error( 'ai_translator_disabled', __( 'AI features are disabled for this WordPress installation.', 'robotstxt-ai-translator' ) ); } if ( ! $this->is_supported() ) { return new WP_Error( 'ai_translator_unsupported', __( 'No AI provider is configured for text generation. Open Settings → AI to set one up.', 'robotstxt-ai-translator' ) ); } $language_name = $this->locale_to_language_name( $target_locale ); if ( '' === $language_name ) { return new WP_Error( 'ai_translator_invalid_locale', __( 'The target language is not installed on this site.', 'robotstxt-ai-translator' ) ); } $system_instruction = sprintf( /* translators: %s: target language name. */ __( 'You are a professional translator. Translate the user message into %s. Preserve any HTML, shortcodes, line breaks, and Markdown exactly as they appear. Return only the translated text, without explanations, prefaces, or quotation marks.', 'robotstxt-ai-translator' ), $language_name ); // Apply the configured request timeout around the AI call. $site_settings = $this->settings->get_settings(); $this->active_timeout = max( 30, (int) $site_settings['request_timeout'] ); add_filter( 'http_request_timeout', array( $this, 'filter_timeout' ), PHP_INT_MAX ); try { $result = wp_ai_client_prompt( $text ) ->using_system_instruction( $system_instruction ) ->generate_text(); } catch ( Exception $e ) { $result = new WP_Error( 'ai_translator_exception', $e->getMessage() ); } catch ( Throwable $e ) { $result = new WP_Error( 'ai_translator_exception', $e->getMessage() ); } remove_filter( 'http_request_timeout', array( $this, 'filter_timeout' ), PHP_INT_MAX ); $this->active_timeout = null; if ( is_wp_error( $result ) ) { return $result; } if ( ! is_string( $result ) ) { return new WP_Error( 'ai_translator_invalid_response', __( 'The AI service returned an unexpected response.', 'robotstxt-ai-translator' ) ); } return trim( $result ); } /** * Converts a WordPress locale code into a human-readable language name. * * Only locales installed on the site (en_US plus any installed .po files) are * accepted, so the editor cannot request translations to languages that the * site has not declared as available. * * @since 1.0.0 * * @param string $locale WordPress locale code. * * @return string Native language name, or empty string when the locale is not installed. */ public function locale_to_language_name( $locale ) { $locale = (string) $locale; if ( '' === $locale ) { return ''; } $installed = $this->get_installed_locales(); if ( ! in_array( $locale, $installed, true ) ) { return ''; } if ( 'en_US' === $locale ) { return 'English (United States)'; } if ( ! function_exists( 'wp_get_available_translations' ) ) { require_once ABSPATH . 'wp-admin/includes/translation-install.php'; } if ( function_exists( 'wp_get_available_translations' ) ) { $translations = wp_get_available_translations(); if ( isset( $translations[ $locale ]['native_name'] ) && '' !== $translations[ $locale ]['native_name'] ) { return (string) $translations[ $locale ]['native_name']; } if ( isset( $translations[ $locale ]['english_name'] ) && '' !== $translations[ $locale ]['english_name'] ) { return (string) $translations[ $locale ]['english_name']; } } return $locale; } /** * Returns the locales installed on the site, including en_US. * * @since 1.0.0 * * @return array List of locale codes. */ public function get_installed_locales() { $locales = array( 'en_US' ); if ( function_exists( 'get_available_languages' ) ) { $installed = get_available_languages(); if ( is_array( $installed ) ) { foreach ( $installed as $locale ) { $locales[] = (string) $locale; } } } return array_values( array_unique( $locales ) ); } } }