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