'gemini_key', 'openai' => 'openai_key', 'anthropic' => 'anthropic_key', 'grok' => 'grok_key', 'deepl' => 'deepl_key', 'google' => 'google_key', 'lecto' => 'lecto_key', 'microsoft' => 'microsoft_key', ); foreach ( $key_mappings as $engine => $key_field ) { if ( ! empty( $settings[ $key_field ] ) ) { return $engine; } } // 4. Truly empty credentials across the board -> fallback to sandbox safely return 'none'; } /** * Resolves target path matrices inside your isolated sandbox environment. */ private function resolve_po_file_path( $slug, $type, $filename ) { $slug = preg_replace( '/[^A-Za-z0-9_\-\/.]/', '', $slug ); $slug = str_replace( array( '..', '\\' ), '', $slug ); $type = in_array( $type, array( 'theme', 'themes' ), true ) ? 'themes' : 'plugins'; $filename = sanitize_file_name( $filename ); $upload_dir = wp_upload_dir(); $sandbox_dir = $upload_dir['basedir'] . '/af-translate/' . $type; if ( ! file_exists( $sandbox_dir ) ) { wp_mkdir_p( $sandbox_dir ); } $sandbox_target_path = $sandbox_dir . '/' . $filename; if ( file_exists( $sandbox_target_path ) ) { return $sandbox_target_path; } $fallback_structures = array(); if ( 'themes' === $type ) { $fallback_structures[] = WP_LANG_DIR . '/themes/' . $filename; $fallback_structures[] = get_theme_root() . '/' . $slug . '/languages/' . $filename; $fallback_structures[] = get_theme_root() . '/' . $slug . '/' . $filename; } else { $fallback_structures[] = WP_LANG_DIR . '/plugins/' . $filename; $fallback_structures[] = WP_PLUGIN_DIR . '/' . $slug . '/languages/' . $filename; $fallback_structures[] = WP_PLUGIN_DIR . '/' . $slug . '/' . $filename; } foreach ( $fallback_structures as $source_path ) { if ( file_exists( $source_path ) ) { if ( copy( $source_path, $sandbox_target_path ) ) { return $sandbox_target_path; } } } return $sandbox_target_path; } /** * Real Extraction Action: Connects directly to your actual PO Processor */ private function parse_po_file_to_array( $file_path ) { if ( class_exists( 'AF_Translate_PO_Processor' ) ) { return AF_Translate_PO_Processor::parse_file( $file_path ); } return array(); } /** * Real File Compilation Action: Commits updates and builds the runtime binary object */ private function write_array_to_po_file( $file_path, $catalog_data ) { if ( ! class_exists( 'AF_Translate_PO_Processor' ) ) { return false; } // Generate the clean formatted PO string template payload $raw_po_text = AF_Translate_PO_Processor::generate_po_string( $catalog_data ); $write_status = ( false !== file_put_contents( $file_path, $raw_po_text ) ); // Automation: If PO writes successfully, auto-compile matching .mo target for system runtime if ( $write_status && class_exists( 'AF_Translate_Compiler_Engine' ) ) { $mo_target_path = str_replace( '.po', '.mo', $file_path ); AF_Translate_Compiler_Engine::compile_po_to_mo( $file_path, $mo_target_path ); } return $write_status; } /** * 1. Action Handler: Bulk Processing Integration */ public function execute_bulk_ai_translation() { check_ajax_referer( 'af_translate_engine_nonce', 'nonce' ); $locale = isset( $_POST['locale'] ) ? sanitize_text_field( wp_unslash( $_POST['locale'] ) ) : ''; $slug = isset( $_POST['slug'] ) ? sanitize_text_field( wp_unslash( $_POST['slug'] ) ) : ''; $type = isset( $_POST['type'] ) ? sanitize_text_field( wp_unslash( $_POST['type'] ) ) : ''; $filename = isset( $_POST['filename'] ) ? sanitize_text_field( wp_unslash( $_POST['filename'] ) ) : ''; $posted_provider = isset( $_POST['provider'] ) ? sanitize_text_field( wp_unslash( $_POST['provider'] ) ) : ''; $process_mode = isset( $_POST['process_mode'] ) ? sanitize_text_field( wp_unslash( $_POST['process_mode'] ) ) : 'untranslated_only'; // Intelligently resolve routing path contexts $provider = $this->resolve_active_provider( $posted_provider ); if ( empty( $locale ) || empty( $slug ) || empty( $type ) || empty( $filename ) ) { wp_send_json_error( array( 'message' => __( 'Missing contextual localization matrices keys.', 'af-translate' ) ) ); } $file_path = $this->resolve_po_file_path( $slug, $type, $filename ); if ( ! file_exists( $file_path ) ) { wp_send_json_error( array( 'message' => __( 'Target PO source file could not be mirrored or found.', 'af-translate' ) ) ); } if ( ! class_exists( 'AF_Translate_PO_Processor' ) ) { wp_send_json_error( array( 'message' => __( 'Core system processor runtime framework class missing.', 'af-translate' ) ) ); } $catalog_data = $this->parse_po_file_to_array( $file_path ); if ( empty( $catalog_data ) ) { wp_send_json_error( array( 'message' => __( 'Target translation file structure is unreadable or structurally empty.', 'af-translate' ) ) ); } // Loop using your actual internal parser taxonomy nodes (msgid / msgstr) $strings_to_process = array(); foreach ( $catalog_data as $index => $node ) { if ( empty( $node['msgid'] ) ) { continue; } // Resolve standard singular definition strings cleanly $existing_translation = isset( $node['msgstr'][0] ) ? $node['msgstr'][0] : ''; if ( 'all' === $process_mode ) { $strings_to_process[ $index ] = $node['msgid']; } else { if ( empty( $existing_translation ) ) { $strings_to_process[ $index ] = $node['msgid']; } } } if ( empty( $strings_to_process ) ) { wp_send_json_error( array( 'message' => __( 'No eligible strings detected matching your target visibility filter inside this file.', 'af-translate' ) ) ); } if ( class_exists( 'AF_Translate_AI_Engine' ) ) { $translated_results = AF_Translate_AI_Engine::translate_batch( $strings_to_process, $locale, $provider ); if ( is_wp_error( $translated_results ) ) { wp_send_json_error( array( 'message' => $translated_results->get_error_message() ) ); } wp_send_json_success( array( 'staged_translations' => $translated_results, 'message' => __( 'Staging preview calculations finished.', 'af-translate' ) ) ); } wp_send_json_error( array( 'message' => __( 'Core translation runtime framework not discovered.', 'af-translate' ) ) ); } /** * 2. Action Handler: Single Workbench Suggestion Engine */ public function execute_single_ai_suggestion() { check_ajax_referer( 'af_translate_engine_nonce', 'nonce' ); $source = isset( $_POST['source'] ) ? sanitize_textarea_field( wp_unslash( $_POST['source'] ) ) : ''; $locale = isset( $_POST['locale'] ) ? sanitize_text_field( wp_unslash( $_POST['locale'] ) ) : ''; $posted_provider = isset( $_POST['provider'] ) ? sanitize_text_field( wp_unslash( $_POST['provider'] ) ) : ''; // Intelligently resolve routing path contexts $provider = $this->resolve_active_provider( $posted_provider ); if ( empty( $source ) || empty( $locale ) ) { wp_send_json_error( array( 'message' => __( 'Required evaluation string missing data context vectors.', 'af-translate' ) ) ); } if ( class_exists( 'AF_Translate_AI_Engine' ) ) { $translation_output = AF_Translate_AI_Engine::translate_single( $source, $locale, $provider ); if ( is_wp_error( $translation_output ) ) { wp_send_json_error( array( 'message' => $translation_output->get_error_message() ) ); } wp_send_json_success( array( 'translation' => $translation_output ) ); } wp_send_json_error( array( 'message' => __( 'AI system runtime profile failed to resolve processing.', 'af-translate' ) ) ); } /** * 3. Action Handler: Commit Staged Configuration States Securely Back to Disk File */ public function save_workbench_matrix_to_disk() { check_ajax_referer( 'af_translate_engine_nonce', 'nonce' ); $slug = isset( $_POST['slug'] ) ? sanitize_text_field( wp_unslash( $_POST['slug'] ) ) : ''; $type = isset( $_POST['type'] ) ? sanitize_text_field( wp_unslash( $_POST['type'] ) ) : ''; $filename = isset( $_POST['filename'] ) ? sanitize_text_field( wp_unslash( $_POST['filename'] ) ) : ''; $user_edits = isset( $_POST['user_edits'] ) && is_array( $_POST['user_edits'] ) ? map_deep( wp_unslash( $_POST['user_edits'] ), 'sanitize_textarea_field' ) : array(); if ( empty( $slug ) || empty( $type ) || empty( $filename ) || empty( $user_edits ) ) { wp_send_json_error( array( 'message' => __( 'Missing core compilation parameters or update payloads.', 'af-translate' ) ) ); } $file_path = $this->resolve_po_file_path( $slug, $type, $filename ); $catalog_data = $this->parse_po_file_to_array( $file_path ); if ( empty( $catalog_data ) ) { wp_send_json_error( array( 'message' => __( 'Base tracking data tree unavailable for compiler reconstruction routines.', 'af-translate' ) ) ); } // Safely map and merge incoming user edits into the actual translation index arrays $merged_count = 0; foreach ( $user_edits as $index => $text ) { $index = sanitize_text_field( $index ); if ( isset( $catalog_data[ $index ] ) ) { if ( ! isset( $catalog_data[ $index ]['msgstr'] ) || ! is_array( $catalog_data[ $index ]['msgstr'] ) ) { $catalog_data[ $index ]['msgstr'] = array(); } // Assign translation safely to the root singular target slot $catalog_data[ $index ]['msgstr'][0] = $text; $merged_count++; } } // Compiles clean PO formatting syntax and fires localized .mo production binary tracking down to disk $write_status = $this->write_array_to_po_file( $file_path, $catalog_data ); if ( ! $write_status ) { wp_send_json_error( array( 'message' => __( 'Disk compilation write operation failure occurred inside your sandbox.', 'af-translate' ) ) ); } wp_send_json_success( array( /* translators: %d: total count of processed translation strings */ 'message' => sprintf( __( 'Successfully updated and compiled %d strings to your sandbox target files.', 'af-translate' ), $merged_count ) ) ); } } new AF_Translate_AJAX_Handler();