true], 'names'); if (!empty($public_cpts)) { $post_types = array_unique(array_merge($post_types, array_values($public_cpts))); } foreach ($post_types as $post_type) { // Exclude attachments / internal types if (in_array($post_type, ['attachment', 'revision', 'nav_menu_item', 'custom_css', 'customize_changeset'], true)) { continue; } add_meta_box( 'aitrfoel-translation-metabox', __('Meep Translator', 'ai-translator-for-elementor-and-polylang'), [$this, 'render_metabox'], $post_type, 'side', 'high' // High priority places it at the very top of sidebar ); } } /** * Enqueue assets only on post/page edit screens */ public function enqueue_assets($hook) { if (!in_array($hook, ['post.php', 'post-new.php'], true)) { return; } wp_enqueue_style( 'aitrfoel-metabox', AITRFOEL_PLUGIN_URL . 'assets/css/metabox.css', [], AITRFOEL_VERSION ); wp_enqueue_script( 'aitrfoel-metabox', AITRFOEL_PLUGIN_URL . 'assets/js/metabox.js', ['jquery'], AITRFOEL_VERSION, true ); wp_localize_script('aitrfoel-metabox', 'aitrfoelMetabox', [ 'ajax_url' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('aitrfoel_ajax_nonce'), 'post_id' => get_the_ID(), 'strings' => [ 'translating' => __('Translating...', 'ai-translator-for-elementor-and-polylang'), 'translate' => __('Translate', 'ai-translator-for-elementor-and-polylang'), 'translation_complete' => __('Translation completed!', 'ai-translator-for-elementor-and-polylang'), 'translation_failed' => __('Translation failed.', 'ai-translator-for-elementor-and-polylang'), 'select_language' => __('Please select at least one language.', 'ai-translator-for-elementor-and-polylang'), 'confirm_translate' => __('Start translating to the selected languages?', 'ai-translator-for-elementor-and-polylang'), 'view' => __('View', 'ai-translator-for-elementor-and-polylang'), 'edit' => __('Edit', 'ai-translator-for-elementor-and-polylang'), 'draft' => __('Draft', 'ai-translator-for-elementor-and-polylang'), 'published' => __('Published', 'ai-translator-for-elementor-and-polylang'), 'pending' => __('Pending', 'ai-translator-for-elementor-and-polylang'), 'no_multilingual' => __('Set up a multilingual plugin to enable quick translations.', 'ai-translator-for-elementor-and-polylang'), 'no_content' => __('No translatable content detected.', 'ai-translator-for-elementor-and-polylang'), 'word_limit_reached' => __('Word limit reached. Please upgrade your plan.', 'ai-translator-for-elementor-and-polylang'), ], ]); } /** * Add Quick Translate / Retranslate node to WordPress Admin Bar * (Deprecated: unified into AITRFOEL_Admin_Bar under 'aitrfoel-admin-bar-hub') * * @param WP_Admin_Bar $wp_admin_bar */ public function add_admin_bar_translate_menu($wp_admin_bar) { // Unified into AITRFOEL_Admin_Bar return; } /** * Handle Admin Bar Translate / Retranslate GET requests */ public function handle_admin_bar_translate() { $post_id = isset($_GET['post_id']) ? absint($_GET['post_id']) : 0; $target_lang = isset($_GET['target_lang']) ? sanitize_text_field($_GET['target_lang']) : ''; $redirect_to = isset($_GET['redirect_to']) ? esc_url_raw(rawurldecode($_GET['redirect_to'])) : home_url('/'); if (!$post_id || empty($target_lang)) { wp_die(esc_html__('Invalid translation request parameters.', 'ai-translator-for-elementor-and-polylang')); } check_admin_referer('aitrfoel_ab_trans_' . $post_id); if (!current_user_can('edit_post', $post_id)) { wp_die(esc_html__('You do not have permission to translate this content.', 'ai-translator-for-elementor-and-polylang')); } if (!class_exists('AITRFOEL_Translation_Engine')) { wp_die(esc_html__('Translation engine is not available.', 'ai-translator-for-elementor-and-polylang')); } $engine = AITRFOEL_Translation_Engine::get_instance(); $result = $engine->translate_page($post_id, $target_lang, 'auto'); if (is_wp_error($result)) { $err_msg = $result->get_error_message(); $target_url = add_query_arg('aitrfoel_err', rawurlencode($err_msg), $redirect_to); wp_safe_redirect($target_url); exit; } // Successfully translated: redirect to the translated post permalink (or redirect_to with success notice) $translated_permalink = get_permalink($result); $final_url = $translated_permalink ?: $redirect_to; $final_url = add_query_arg('aitrfoel_msg', rawurlencode(sprintf(__('Page successfully translated to %s!', 'ai-translator-for-elementor-and-polylang'), strtoupper($target_lang))), $final_url); wp_safe_redirect($final_url); exit; } /** * Render toast banner when coming back from admin bar translation */ public function render_admin_bar_toast() { if (!empty($_GET['aitrfoel_msg'])) { $msg = sanitize_text_field(rawurldecode($_GET['aitrfoel_msg'])); echo '
'; echo ''; } elseif (!empty($_GET['aitrfoel_err'])) { $err = sanitize_text_field(rawurldecode($_GET['aitrfoel_err'])); echo ''; echo ''; } } /** * Render full-screen frosted glass overlay during translation requests */ public function render_admin_bar_loading_overlay() { if (!is_user_logged_in() || !is_admin_bar_showing()) { return; } ?> #wpadminbar #wp-admin-bar-aitrfoel-top-bar-translate > .ab-item { display: flex !important; align-items: center !important; height: 32px !important; color: #fff !important; } @media screen and (max-width: 782px) { #wpadminbar #wp-admin-bar-aitrfoel-top-bar-translate > .ab-item { height: 46px !important; } } #wpadminbar #wp-admin-bar-aitrfoel-top-bar-translate .ab-icon { float: none !important; display: inline-flex !important; align-items: center !important; justify-content: center !important; width: 15px !important; height: 15px !important; min-width: 15px !important; margin: 0 6px 0 0 !important; padding: 0 !important; line-height: 1 !important; } #wpadminbar #wp-admin-bar-aitrfoel-top-bar-translate .ab-icon svg { display: block !important; width: 15px !important; height: 15px !important; margin: 0 !important; padding: 0 !important; } #wpadminbar #wp-admin-bar-aitrfoel-top-bar-translate .ab-label { display: inline-block !important; height: auto !important; line-height: 1.2 !important; margin: 0 !important; padding: 0 !important; } #wpadminbar #wp-admin-bar-aitrfoel-top-bar-translate:hover > .ab-item { background: #1447e6 !important; color: #fff !important; } #wpadminbar #wp-admin-bar-aitrfoel-top-bar-translate .ab-submenu .ab-item { display: flex !important; align-items: center !important; gap: 6px !important; } '; } /** * Render the metabox content */ public function render_metabox($post) { $multilingual = class_exists('AITRFOEL_Multilingual_Manager') ? AITRFOEL_Multilingual_Manager::get_instance() : null; // No multilingual plugin if (!$multilingual || !$multilingual->is_active()) { $dashboard_url = admin_url('admin.php?page=ai-translator-for-elementor-and-polylang'); echo ''; printf( /* translators: %1$s: opening link tag, %2$s: closing link tag */ esc_html__('Set up your languages for quick translations. %1$sGo to Dashboard%2$s', 'ai-translator-for-elementor-and-polylang'), '', '' ); echo '
'; return; } // Languages must be configured if (!$multilingual->has_languages_configured()) { $detected = $multilingual->get_detected_plugin(); $setup_url = ($detected === 'wpml') ? admin_url('admin.php?page=sitepress-multilingual-cms/menu/languages.php') : admin_url('admin.php?page=mlang'); echo ''; printf( /* translators: %s: URL to language settings page */ esc_html__('Please %sconfigure your languages%s first.', 'ai-translator-for-elementor-and-polylang'), '', '' ); echo '
'; return; } $post_lang = $multilingual->get_post_language($post->ID); $translations = $multilingual->get_post_translations($post->ID); $available_langs = $multilingual->get_available_languages(); $default_lang = $multilingual->get_default_language(); // Detect content type $content_type = 'unknown'; if (class_exists('AITRFOEL_Content_Detector')) { $detector = AITRFOEL_Content_Detector::get_instance(); $content_type = $detector->detect($post->ID); } // Determine which is the source post for this translation group $source_id = $post->ID; if (!empty($translations)) { // If the default language version exists, that's the source if (isset($translations[$default_lang]) && $translations[$default_lang] != $post->ID) { $source_id = $translations[$default_lang]; } } $is_translation = ($source_id !== $post->ID); ?>