PluginProbe
AI Builder – Generate pages, blocks, images & translate with AI / 2.7.10
AI Builder – Generate pages, blocks, images & translate with AI v2.7.10
2.7.10 2.7.9 2.7.8 2.0.8 2.0.9 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.3.0 2.3.10 All 122 releases
← All changes | includes/class-translation-handler.php +120 -2 2.1.42.7.10 View file →
@@ -226,8 +226,29 @@
226 226 if (!empty($block['innerHTML'])) {
227 227 $text_map = array_merge($text_map, $this->extract_text_nodes($block['innerHTML'], $block_key . '_table'));
228 228 }
229 229 break;
230 +
231 + case 'core/navigation':
232 + // If navigation has a ref, we need to extract from the referenced wp_navigation post
233 + if (!empty($block['attrs']['ref'])) {
234 + $nav_post = get_post($block['attrs']['ref']);
235 + if ($nav_post && $nav_post->post_type === 'wp_navigation') {
236 + $nav_blocks = parse_blocks($nav_post->post_content);
237 + $nav_map = $this->extract_texts_from_blocks($nav_blocks, $block_key . '_nav');
238 + $text_map = array_merge($text_map, $nav_map);
239 + }
240 + }
241 + // Also process innerBlocks if present (inline navigation)
242 + break;
243 +
244 + case 'core/navigation-link':
245 + case 'core/navigation-submenu':
246 + // Extract the label from attrs
247 + if (!empty($block['attrs']['label'])) {
248 + $text_map[$block_key . '_navlabel'] = $block['attrs']['label'];
249 + }
250 + break;
230 251 }
231 252
232 253 // Recursively process inner blocks
233 254 if (!empty($block['innerBlocks']) && is_array($block['innerBlocks'])) {
@@ -441,8 +462,26 @@
441 462 $new_block['innerHTML'] = $updated_html;
442 463 $new_block['innerContent'] = array($updated_html);
443 464 }
444 465 break;
466 +
467 + case 'core/navigation':
468 + // If navigation has a ref, duplicate the wp_navigation post with translated content
469 + if (!empty($block['attrs']['ref'])) {
470 + $new_nav_id = $this->duplicate_and_translate_navigation($block['attrs']['ref'], $translated_map, $block_key . '_nav');
471 + if ($new_nav_id) {
472 + $new_block['attrs']['ref'] = $new_nav_id;
473 + }
474 + }
475 + break;
476 +
477 + case 'core/navigation-link':
478 + case 'core/navigation-submenu':
479 + // Replace the label in attrs
480 + if (isset($translated_map[$block_key . '_navlabel'])) {
481 + $new_block['attrs']['label'] = $translated_map[$block_key . '_navlabel'];
482 + }
483 + break;
445 484 }
446 485
447 486 // Recursively process inner blocks
448 487 if (!empty($block['innerBlocks']) && is_array($block['innerBlocks'])) {
@@ -456,8 +495,60 @@
456 495 return $reconstructed;
457 496 }
458 497
459 498 /**
499 + * Duplicate a wp_navigation post and translate its content
500 + */
501 + private function duplicate_and_translate_navigation($nav_id, $translated_map, $prefix)
502 + {
503 + $nav_post = get_post($nav_id);
504 + if (!$nav_post || $nav_post->post_type !== 'wp_navigation') {
505 + return null;
506 + }
507 +
508 + // Parse the navigation blocks
509 + $nav_blocks = parse_blocks($nav_post->post_content);
510 +
511 + // Reconstruct with translated labels
512 + $translated_nav_blocks = $this->reconstruct_blocks($nav_blocks, $translated_map, $prefix);
513 +
514 + // Serialize back to block content
515 + $translated_nav_content = serialize_blocks($translated_nav_blocks);
516 +
517 + // Get target language from the current translation context
518 + $target_lang = isset($_POST['target_lang']) ? sanitize_text_field($_POST['target_lang']) : 'translated';
519 +
520 + // Create a new wp_navigation post
521 + $new_nav_data = array(
522 + 'post_title' => $nav_post->post_title . ' (' . strtoupper($target_lang) . ')',
523 + 'post_content' => $translated_nav_content,
524 + 'post_status' => 'publish',
525 + 'post_type' => 'wp_navigation',
526 + 'post_author' => get_current_user_id(),
527 + );
528 +
529 + $new_nav_id = wp_insert_post($new_nav_data);
530 +
531 + if (is_wp_error($new_nav_id)) {
532 + return null;
533 + }
534 +
535 + // Copy language meta
536 + update_post_meta($new_nav_id, '_ai_lang', $target_lang);
537 + update_post_meta($new_nav_id, '_ai_translation_source', $nav_id);
538 +
539 + // Link to same translation group
540 + $translation_group = get_post_meta($nav_id, '_ai_translation_group', true);
541 + if (empty($translation_group)) {
542 + $translation_group = 'nav_' . time() . '_' . wp_generate_password(8, false);
543 + update_post_meta($nav_id, '_ai_translation_group', $translation_group);
544 + }
545 + update_post_meta($new_nav_id, '_ai_translation_group', $translation_group);
546 +
547 + return $new_nav_id;
548 + }
549 +
550 + /**
460 551 * Get meta description with fallback logic
461 552 */
462 553 private function get_meta_description($post_id)
463 554 {
@@ -550,9 +641,9 @@
550 641 // Call translation API
551 642 $api_url = 'https://api.wordpress-ai-builder.com/api' . '/ai-transform-page/translate-post';
552 643
553 644 $response = wp_remote_post($api_url, array(
554 - 'timeout' => 60,
645 + 'timeout' => 180, // 5 minutes - AI translation can take longer for large pages
555 646 'headers' => array(
556 647 'Authorization' => 'Bearer ' . $jwt_token,
557 648 'Content-Type' => 'application/json',
558 649 ),
@@ -631,12 +722,14 @@
631 722 if (is_wp_error($new_post_id)) {
632 723 wp_send_json_error(array('message' => 'Failed to create translated post: ' . $new_post_id->get_error_message()));
633 724 }
634 725
635 - // Save meta description (both keys)
726 + // Save meta description across all supported meta keys
636 727 if (!empty($translated_data['meta_desc'])) {
637 728 update_post_meta($new_post_id, '_ai_builder_seo_desc', $translated_data['meta_desc']);
638 729 update_post_meta($new_post_id, '_yoast_wpseo_metadesc', $translated_data['meta_desc']);
730 + update_post_meta($new_post_id, 'aibui_meta_description', $translated_data['meta_desc']);
731 + update_post_meta($new_post_id, '_ai_translation_meta_desc', $translated_data['meta_desc']);
639 732 }
640 733
641 734 // Copy custom CSS meta if present
642 735 $css_meta_keys = array(
@@ -651,8 +744,22 @@
651 744 update_post_meta($new_post_id, $css_key, $css_value);
652 745 }
653 746 }
654 747
748 + // Copy custom JS meta if present (so translated pages keep JS behavior)
749 + $js_meta_keys = array(
750 + 'ai_builder_page_js_content',
751 + 'ai_builder_block_js_content',
752 + 'ai_builder_js_content'
753 + );
754 +
755 + foreach ($js_meta_keys as $js_key) {
756 + $js_value = get_post_meta($post_id, $js_key, true);
757 + if (!empty($js_value)) {
758 + update_post_meta($new_post_id, $js_key, $js_value);
759 + }
760 + }
761 +
655 762 // Save language meta
656 763 update_post_meta($new_post_id, '_ai_lang', $target_lang);
657 764 update_post_meta($new_post_id, '_ai_translation_source', $post_id);
658 765 if (!get_post_meta($post_id, '_ai_translation_source', true)) {
@@ -691,8 +798,19 @@
691 798
692 799 // Preserve AI-created flag to keep hide-title CSS behavior
693 800 if (get_post_meta($post_id, '_aibui_created_by_ai', true) === '1') {
694 801 update_post_meta($new_post_id, '_aibui_created_by_ai', '1');
802 + }
803 +
804 + // If the original page is the front page, mark the translation as homepage for its language
805 + $front_page_id = get_option('page_on_front');
806 + if ($front_page_id && (int) $front_page_id === (int) $post_id) {
807 + update_post_meta($new_post_id, '_ai_is_homepage', '1');
808 + }
809 +
810 + // Also copy the homepage flag if it exists on the original
811 + if (get_post_meta($post_id, '_ai_is_homepage', true) === '1') {
812 + update_post_meta($new_post_id, '_ai_is_homepage', '1');
695 813 }
696 814
697 815 // Copy featured image if exists
698 816 $thumbnail_id = get_post_thumbnail_id($post_id);