admin_url('admin-ajax.php'), 'test_nonce' => wp_create_nonce('aitrfoel_test_api'), 'clear_cache_nonce' => wp_create_nonce('aitrfoel_clear_cache'), 'glossary_nonce' => wp_create_nonce('aitrfoel_glossary_nonce'), 'inventory_nonce' => wp_create_nonce('aitrfoel_inventory_nonce'), 'tm_nonce' => wp_create_nonce('aitrfoel_tm_nonce'), 'clear_cache_confirm' => __('Are you sure you want to clear all cached translations? This cannot be undone.', 'ai-translator-for-elementor-and-polylang'), 'clear_tm_confirm' => __('Are you sure you want to clear all Translation Memory? Cached phrases will need to be re-translated on future runs.', 'ai-translator-for-elementor-and-polylang'), 'clearing_text' => __('Clearing...', 'ai-translator-for-elementor-and-polylang'), 'clear_cache_text' => __('Clear Translation Cache', 'ai-translator-for-elementor-and-polylang'), 'recalculating_text' => __('Recalculating...', 'ai-translator-for-elementor-and-polylang'), 'recalculate_stats_text' => __('Recalculate Stats', 'ai-translator-for-elementor-and-polylang'), ]); } /** * Get word limit for plan * Based on Freemius plan IDs and token limits: * - Free: 40K tokens ≈ 7,200 words * - Starter (45513): 100K tokens ≈ 18,000 words * - Agency (45514): 500K tokens ≈ 89,000 words * - Enterprise (45515): 2M tokens ≈ 355,000 words */ public static function get_word_limit_for_plan($plan_name) { $limits = [ 'Free' => 7200, // 40,000 tokens 'free' => 7200, // lowercase variant 'Starter' => 18000, // 100,000 tokens (Plan ID: 45513) 'starter' => 18000, // lowercase variant 'Agency' => 89000, // 500,000 tokens (Plan ID: 45514) 'agency' => 89000, // lowercase variant 'Enterprise' => 355000, // 2,000,000 tokens (Plan ID: 45515) 'enterprise' => 355000 // lowercase variant ]; return isset($limits[$plan_name]) ? $limits[$plan_name] : $limits['Free']; } /** * Sanitize the custom string-translations setting. * Form submits parallel rows under ['rows'][i]['source'] and ['rows'][i]['trans'][lang]. * Stored as: [ ['source'=>..., 'translations'=>['en'=>...,'de'=>...]], ... ]. * * @param mixed $input * @return array */ public static function sanitize_string_translations($input) { $out = []; if (!is_array($input)) { return $out; } // Normalize to a flat list of rows. This runs both on the settings-form // submission (shape: ['rows'][i]['source'], ['rows'][i]['trans'][lang]) // AND on direct update_option() calls from the auto-translate tool // (shape: [ ['source'=>..., 'translations'=>[lang=>txt]], ... ]), because // register_setting's sanitize filter fires on both. $rows = []; if (isset($input['rows']) && is_array($input['rows'])) { foreach ($input['rows'] as $row) { if (!is_array($row)) { continue; } $rows[] = [ 'source' => isset($row['source']) ? $row['source'] : '', 'translations' => (!empty($row['trans']) && is_array($row['trans'])) ? $row['trans'] : [], ]; } } else { foreach ($input as $row) { if (!is_array($row)) { continue; } $rows[] = [ 'source' => isset($row['source']) ? $row['source'] : '', 'translations' => (!empty($row['translations']) && is_array($row['translations'])) ? $row['translations'] : [], ]; } } foreach ($rows as $row) { $source = trim(wp_kses_post((string) $row['source'])); if ($source === '') { continue; } $translations = []; foreach ((array) $row['translations'] as $lang => $txt) { $txt = trim(wp_kses_post((string) $txt)); if ($txt !== '') { $translations[sanitize_key($lang)] = $txt; } } if (!empty($translations)) { $out[] = ['source' => $source, 'translations' => $translations]; } } return $out; } public static function register_settings() { // License Settings (hosted OpenAI service) register_setting('aitrfoel_settings_group', 'aitrfoel_license_key', [ 'sanitize_callback' => 'sanitize_text_field' ]); // Cache Settings register_setting('aitrfoel_settings_group', 'aitrfoel_cache_enabled', [ 'sanitize_callback' => 'boolval', 'default' => true ]); register_setting('aitrfoel_settings_group', 'aitrfoel_cache_duration', [ 'sanitize_callback' => 'intval', 'default' => 30 ]); // Elementor async (submit→poll) translation. Opt-in / experimental: when enabled, // Elementor pages translate via the proxy async job + browser polling instead of // the synchronous path. Default OFF so existing behavior is unchanged. register_setting('aitrfoel_settings_group', 'aitrfoel_elementor_async_enabled', [ 'sanitize_callback' => 'boolval', 'default' => false ]); // Floating translation toolbar on WP List Screens (edit.php) register_setting('aitrfoel_settings_group', 'aitrfoel_list_toolbar_enabled', [ 'sanitize_callback' => 'boolval', 'default' => true ]); // Page Creation Settings register_setting('aitrfoel_settings_group', 'aitrfoel_translated_page_status', [ 'sanitize_callback' => 'sanitize_text_field', 'default' => 'publish' ]); // SEO Translation Settings register_setting('aitrfoel_settings_group', 'aitrfoel_seo_translation_enabled', [ 'sanitize_callback' => 'boolval', 'default' => true ]); register_setting('aitrfoel_settings_group', 'aitrfoel_seo_translate_og', [ 'sanitize_callback' => 'boolval', 'default' => true ]); register_setting('aitrfoel_settings_group', 'aitrfoel_seo_translate_twitter', [ 'sanitize_callback' => 'boolval', 'default' => true ]); // WooCommerce Settings register_setting('aitrfoel_settings_group', 'aitrfoel_woocommerce_support', [ 'sanitize_callback' => 'boolval', 'default' => true ]); // SureCart product translation toggle register_setting('aitrfoel_settings_group', 'aitrfoel_surecart_support', [ 'sanitize_callback' => 'boolval', 'default' => true ]); register_setting('aitrfoel_settings_group', 'aitrfoel_woo_translate_categories', [ 'sanitize_callback' => 'boolval', 'default' => true ]); register_setting('aitrfoel_settings_group', 'aitrfoel_woo_translate_tags', [ 'sanitize_callback' => 'boolval', 'default' => true ]); register_setting('aitrfoel_settings_group', 'aitrfoel_woo_translate_attributes', [ 'sanitize_callback' => 'boolval', 'default' => true ]); register_setting('aitrfoel_settings_group', 'aitrfoel_woo_translate_variations', [ 'sanitize_callback' => 'boolval', 'default' => true ]); register_setting('aitrfoel_settings_group', 'aitrfoel_woo_translate_gallery_alt', [ 'sanitize_callback' => 'boolval', 'default' => true ]); register_setting('aitrfoel_settings_group', 'aitrfoel_woo_product_fallback', [ 'sanitize_callback' => 'boolval', 'default' => true ]); // Taxonomy name translation (built-in language system) register_setting('aitrfoel_settings_group', 'aitrfoel_translate_taxonomy_names', [ 'sanitize_callback' => 'boolval', 'default' => true ]); // URL/slug translation for translated posts register_setting('aitrfoel_settings_group', 'aitrfoel_translate_url_slugs', [ 'sanitize_callback' => 'boolval', 'default' => true ]); // Preserve existing slug on retranslation register_setting('aitrfoel_settings_group', 'aitrfoel_preserve_slug_on_retranslate', [ 'sanitize_callback' => 'boolval', 'default' => true ]); // Localize absolute internal links (/fr/...) in content/widgets to the current language register_setting('aitrfoel_settings_group', 'aitrfoel_localize_internal_links', [ 'sanitize_callback' => 'boolval', 'default' => true ]); // Paths excluded from language routing (no prefix, no redirect) register_setting('aitrfoel_settings_group', 'aitrfoel_routing_excluded_paths', [ 'sanitize_callback' => 'sanitize_textarea_field', 'default' => '' ]); // Custom string translations (admin overrides for UI strings not in .mo files) register_setting('aitrfoel_settings_group', 'aitrfoel_string_translations', [ 'sanitize_callback' => [self::class, 'sanitize_string_translations'], 'default' => [] ]); // Default language URL prefix register_setting('aitrfoel_settings_group', 'aitrfoel_default_lang_prefix', [ 'sanitize_callback' => 'boolval', 'default' => false ]); // Theme Compatibility Settings register_setting('aitrfoel_settings_group', 'aitrfoel_custom_meta_fields', [ 'sanitize_callback' => 'sanitize_textarea_field', 'default' => '' ]); // Preserve Post Sorting Order register_setting('aitrfoel_settings_group', 'aitrfoel_preserve_post_order', [ 'sanitize_callback' => 'boolval', 'default' => true ]); // Synchronize Media, Galleries and Custom Fields register_setting('aitrfoel_settings_group', 'aitrfoel_copy_custom_media_fields', [ 'sanitize_callback' => 'boolval', 'default' => true ]); // Media Library Duplication (Optional attachment post creation per language) register_setting('aitrfoel_settings_group', 'aitrfoel_duplicate_media_attachments', [ 'sanitize_callback' => 'boolval', 'default' => false ]); // Brand Voice & Localization Tone (PRO Language Enhancer) register_setting('aitrfoel_settings_group', 'aitrfoel_brand_tone_preset', [ 'sanitize_callback' => function($value) { $allowed = ['hospitality', 'corporate', 'sales', 'casual', 'custom']; return in_array($value, $allowed, true) ? $value : 'corporate'; }, 'default' => 'corporate' ]); register_setting('aitrfoel_settings_group', 'aitrfoel_brand_tone_custom', [ 'sanitize_callback' => 'sanitize_textarea_field', 'default' => '' ]); register_setting('aitrfoel_settings_group', 'aitrfoel_formality_mode', [ 'sanitize_callback' => function($value) { $allowed = ['auto', 'formal', 'informal']; return in_array($value, $allowed, true) ? $value : 'auto'; }, 'default' => 'auto' ]); // Debug Settings register_setting('aitrfoel_settings_group', 'aitrfoel_debug_mode', [ 'sanitize_callback' => 'boolval', 'default' => false ]); // Language Switcher Settings register_setting('aitrfoel_settings_group', 'aitrfoel_switcher_enabled', [ 'sanitize_callback' => 'boolval', 'default' => true ]); register_setting('aitrfoel_settings_group', 'aitrfoel_switcher_style', [ 'sanitize_callback' => function($value) { $allowed = ['dropdown', 'list', 'flags', 'flags-text']; return in_array($value, $allowed, true) ? $value : 'flags-text'; }, 'default' => 'flags-text' ]); register_setting('aitrfoel_settings_group', 'aitrfoel_switcher_show_flags', [ 'sanitize_callback' => 'boolval', 'default' => true ]); register_setting('aitrfoel_settings_group', 'aitrfoel_switcher_theme', [ 'sanitize_callback' => function($value) { $allowed = ['default', 'minimal', 'light', 'pill']; return in_array($value, $allowed, true) ? $value : 'default'; }, 'default' => 'default' ]); register_setting('aitrfoel_settings_group', 'aitrfoel_switcher_dropdown_names', [ 'sanitize_callback' => 'boolval', 'default' => true ]); register_setting('aitrfoel_settings_group', 'aitrfoel_switcher_mobile_names', [ 'sanitize_callback' => 'boolval', 'default' => false ]); register_setting('aitrfoel_settings_group', 'aitrfoel_auto_translate_templates', [ 'sanitize_callback' => 'boolval', 'default' => false ]); register_setting('aitrfoel_settings_group', 'aitrfoel_extra_template_post_types', [ 'sanitize_callback' => function($value) { $slugs = preg_split('/[\s,]+/', (string) $value); $slugs = array_filter(array_map('sanitize_key', array_map('trim', $slugs))); return implode(', ', array_values(array_unique($slugs))); }, 'default' => '' ]); register_setting('aitrfoel_settings_group', 'aitrfoel_switcher_border_color', [ 'sanitize_callback' => 'sanitize_hex_color', 'default' => '' ]); register_setting('aitrfoel_settings_group', 'aitrfoel_switcher_flag_size', [ 'sanitize_callback' => function($value) { $v = absint($value); if ($v === 0) { return 0; } return max(8, min(128, $v)); }, 'default' => 0 ]); register_setting('aitrfoel_settings_group', 'aitrfoel_switcher_show_names', [ 'sanitize_callback' => 'boolval', 'default' => true ]); register_setting('aitrfoel_settings_group', 'aitrfoel_switcher_name_display', [ 'sanitize_callback' => function($value) { $allowed = ['native', 'english', 'code', 'code_lower']; return in_array($value, $allowed, true) ? $value : 'native'; }, 'default' => 'native' ]); register_setting('aitrfoel_settings_group', 'aitrfoel_switcher_custom_names', [ 'sanitize_callback' => function($value) { if (!is_array($value)) return []; return array_map('sanitize_text_field', $value); }, 'default' => [] ]); register_setting('aitrfoel_settings_group', 'aitrfoel_switcher_custom_css', [ 'sanitize_callback' => 'wp_strip_all_tags', 'default' => '' ]); // Nav menu switcher settings register_setting('aitrfoel_settings_group', 'aitrfoel_switcher_hide_untranslated', [ 'sanitize_callback' => 'boolval', 'default' => false ]); register_setting('aitrfoel_settings_group', 'aitrfoel_nav_menu_switcher', [ 'sanitize_callback' => 'boolval', 'default' => false ]); register_setting('aitrfoel_settings_group', 'aitrfoel_nav_menu_location', [ 'sanitize_callback' => 'sanitize_text_field', 'default' => '' ]); // Per-language menu assignments register_setting('aitrfoel_settings_group', 'aitrfoel_menu_assignments', [ 'sanitize_callback' => [self::class, 'sanitize_menu_assignments'], 'default' => [] ]); // Per-language widget visibility register_setting('aitrfoel_settings_group', 'aitrfoel_widget_languages', [ 'sanitize_callback' => [self::class, 'sanitize_widget_languages'], 'default' => [] ]); // Glossary settings register_setting('aitrfoel_settings_group', 'aitrfoel_glossary_terms', [ 'sanitize_callback' => [self::class, 'sanitize_glossary_terms'], 'default' => '' ]); // Exclude words settings register_setting('aitrfoel_settings_group', 'aitrfoel_exclude_words', [ 'sanitize_callback' => [self::class, 'sanitize_exclude_words'], 'default' => '' ]); // Translation Memory settings register_setting('aitrfoel_settings_group', 'aitrfoel_enable_translation_memory', [ 'sanitize_callback' => 'boolval', 'default' => true ]); // Background Auto-Sync settings register_setting('aitrfoel_settings_group', 'aitrfoel_enable_background_sync', [ 'sanitize_callback' => 'boolval', 'default' => true ]); // AI Translation Engine selection register_setting('aitrfoel_settings_group', 'aitrfoel_ai_engine', [ 'sanitize_callback' => function($val) { $allowed = ['auto', 'claude-3-5-sonnet', 'deepl', 'gpt-4o', 'gpt-4o-mini']; return in_array($val, $allowed, true) ? $val : 'auto'; }, 'default' => 'auto' ]); } /** * Get exclude words as array (synced from both Settings page and modal) */ public static function get_exclude_words() { // Check the modal-based setting first (stored by AJAX handler) $modal_settings = get_option('aitrfoel_translation_settings', []); $modal_words = isset($modal_settings['exclude_words']) ? $modal_settings['exclude_words'] : []; // Check the settings-page-based textarea $textarea_value = get_option('aitrfoel_exclude_words', ''); $textarea_words = array_filter(array_map('trim', explode("\n", $textarea_value))); // Merge both sources, remove duplicates return array_values(array_unique(array_merge($modal_words, $textarea_words))); } /** * Sanitize exclude words textarea and sync to modal option */ public static function sanitize_exclude_words($input) { $words = array_filter(array_map('sanitize_text_field', explode("\n", $input))); // Sync to the AJAX-based modal option so both stay in sync $settings = get_option('aitrfoel_translation_settings', []); $settings['exclude_words'] = array_values($words); $settings['updated_at'] = time(); update_option('aitrfoel_translation_settings', $settings); return implode("\n", $words); } /** * Get site-wide translatable content inventory * * @param bool $force_recount * @return array */ public static function get_site_content_inventory($force_recount = false) { $transient_key = 'aitrfoel_site_inventory_stats_v2'; if (!$force_recount) { $cached = get_transient($transient_key); if (is_array($cached) && !empty($cached)) { return $cached; } } $detector = class_exists('AITRFOEL_Content_Detector') ? AITRFOEL_Content_Detector::get_instance() : null; $manager = class_exists('AITRFOEL_Multilingual_Manager') ? AITRFOEL_Multilingual_Manager::get_instance() : null; $default_lang = ($manager && $manager->is_active()) ? $manager->get_default_language() : ''; $target_languages = ($manager && $manager->is_active()) ? $manager->get_available_languages() : []; $target_lang_count = max(1, count($target_languages) - ($default_lang ? 1 : 0)); // Content group definitions with crisp vector SVG icons $post_type_groups = [ 'pages' => [ 'label' => __('Pages', 'ai-translator-for-elementor-and-polylang'), 'icon' => '', 'post_types' => ['page'], ], 'posts' => [ 'label' => __('Posts', 'ai-translator-for-elementor-and-polylang'), 'icon' => '', 'post_types' => ['post'], ], 'templates' => [ 'label' => __('Templates & Headers/Footers', 'ai-translator-for-elementor-and-polylang'), 'icon' => '', 'post_types' => ['elementor_library', 'elementor-hf', 'jet-theme-core'], ], 'products' => [ 'label' => __('Products', 'ai-translator-for-elementor-and-polylang'), 'icon' => '', 'post_types' => ['product', 'sc_product'], ], ]; $breakdown = []; $total_items = 0; $total_source_words = 0; foreach ($post_type_groups as $group_key => $group) { $valid_types = array_filter($group['post_types'], 'post_type_exists'); if (empty($valid_types)) { continue; } $posts = get_posts([ 'post_type' => $valid_types, 'post_status' => 'publish', 'posts_per_page' => -1, 'fields' => 'ids', ]); // Filter to only source-language posts if multilingual is active if ($manager && $manager->is_active() && $default_lang) { $posts = array_filter($posts, function($pid) use ($manager, $default_lang) { $lang = $manager->get_post_language($pid); if (!empty($lang)) { return $lang === $default_lang; } $translations = $manager->get_post_translations($pid); if (!empty($translations)) { foreach ($translations as $l => $id) { if ((int)$id === $pid && $l !== $default_lang) { return false; } } } return true; }); $posts = array_values($posts); } $group_words = 0; foreach ($posts as $pid) { if ($detector) { $group_words += $detector->get_cached_word_count($pid, $force_recount); } } $item_count = count($posts); if ($item_count > 0 || in_array($group_key, ['pages', 'posts'])) { $breakdown[$group_key] = [ 'label' => $group['label'], 'icon' => $group['icon'], 'count' => $item_count, 'words' => $group_words, ]; $total_items += $item_count; $total_source_words += $group_words; } } $estimated_total_demand = $total_source_words * $target_lang_count; $recommended_plan = self::get_recommended_plan_for_words($estimated_total_demand); $result = [ 'total_items' => $total_items, 'total_source_words' => $total_source_words, 'target_lang_count' => $target_lang_count, 'estimated_total_demand' => $estimated_total_demand, 'breakdown' => $breakdown, 'recommended_plan' => $recommended_plan, ]; set_transient($transient_key, $result, HOUR_IN_SECONDS); return $result; } /** * Get recommended plan based on word demand * * @param int $demand_words * @return array */ public static function get_recommended_plan_for_words($demand_words) { if ($demand_words <= 18000) { $plan_name = 'Starter'; $plan_limit = 18000; $plan_id = 45513; $plan_slug = 'starter'; } elseif ($demand_words <= 89000) { $plan_name = 'Agency'; $plan_limit = 89000; $plan_id = 45514; $plan_slug = 'agency'; } else { $plan_name = 'Enterprise'; $plan_limit = 355000; $plan_id = 45515; $plan_slug = 'enterprise'; } $upgrade_url = 'https://meeptech.com/products/ai-translator-for-elementor-polylang/?utm_source=wp-plugin&utm_medium=plugin-settings&utm_campaign=pro-upgrade&plan=' . $plan_slug . '#pricing'; try { $is_pro = (defined('AITRFOEL_VERSION_TYPE') && AITRFOEL_VERSION_TYPE === 'pro'); if ($is_pro && function_exists('aitrfoel_fs') && is_object(aitrfoel_fs()) && method_exists(aitrfoel_fs(), 'is_registered') && aitrfoel_fs()->is_registered()) { $upgrade_url = aitrfoel_fs()->get_upgrade_url(); } elseif (function_exists('eatp_fs') && is_object(eatp_fs()) && method_exists(eatp_fs(), 'is_registered') && eatp_fs()->is_registered()) { $upgrade_url = eatp_fs()->get_upgrade_url(); } } catch (\Throwable $e) { // Keep default meeptech pricing URL fallback } return [ 'name' => $plan_name, 'limit' => $plan_limit, 'limit_k' => round($plan_limit / 1000), 'slug' => $plan_slug, 'upgrade_url' => $upgrade_url, ]; } /** * AJAX endpoint to recalculate inventory stats on demand */ public static function refresh_inventory_stats_ajax() { try { check_ajax_referer('aitrfoel_inventory_nonce', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => __('Insufficient permissions.', 'ai-translator-for-elementor-and-polylang')]); } $inventory = self::get_site_content_inventory(true); wp_send_json_success($inventory); } catch (\Throwable $e) { wp_send_json_error(['message' => $e->getMessage()]); } } /** * AJAX handler to clear Translation Memory */ public static function clear_translation_memory_ajax() { try { check_ajax_referer('aitrfoel_tm_nonce', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => __('Insufficient permissions.', 'ai-translator-for-elementor-and-polylang')]); } if (class_exists('AITRFOEL_Translation_Memory')) { AITRFOEL_Translation_Memory::get_instance()->clear_memory(); } wp_send_json_success([ 'message' => __('Translation Memory cleared successfully.', 'ai-translator-for-elementor-and-polylang'), 'stats' => [ 'total_entries' => 0, 'total_words_saved' => 0, 'total_hits' => 0, ], ]); } catch (\Throwable $e) { wp_send_json_error(['message' => $e->getMessage()]); } } public static function render_page() { $license_key = get_option('aitrfoel_license_key', ''); $license_status = 'Free'; if (function_exists('aitrfoel_fs') && aitrfoel_fs()->is_registered()) { $plan = aitrfoel_fs()->get_plan(); $license_status = $plan ? $plan->name : 'Free'; } // Get usage from server (not local cache) $words_used = 0; $word_limit = 7200; // Default free limit if (class_exists('AITRFOEL_License_Manager_Server')) { $license_manager = AITRFOEL_License_Manager_Server::get_instance(); $license_info = $license_manager->get_license_status(); $words_used = $license_info['words_used']; $word_limit = $license_info['word_limit']; } $usage_percent = null; if ($word_limit !== -1 && $word_limit > 0) { $usage_percent = (int) min(100, round(($words_used / $word_limit) * 100)); } $cache_enabled = get_option('aitrfoel_cache_enabled', true); $cache_duration = get_option('aitrfoel_cache_duration', 30); $translated_page_status = get_option('aitrfoel_translated_page_status', 'publish'); ?>
render_plugin_header(__('Settings', 'ai-translator-for-elementor-and-polylang')); ?>

/

is_registered() || (method_exists(eatp_fs(), 'is_free_plan') && eatp_fs()->is_free_plan()))): ?> is_registered()): ?> is_registered() && method_exists(eatp_fs(), 'is_free_plan') && !eatp_fs()->is_free_plan()): ?>

~
~

'', 'posts' => '', 'templates' => '', 'products' => '', ]; foreach ($inventory['breakdown'] as $group_key => $group): $row_icon = (isset($group['icon']) && strpos($group['icon'], '
~


/>

/>

get_default_language() ?: ''; $languages = $builtin->get_available_languages(); $lang_codes = array_keys($languages); } // Check for Polylang elseif (function_exists('pll_languages_list') && function_exists('pll_default_language')) { $has_plugin = true; $plugin_name = 'Polylang'; $is_polylang = true; $default_lang = pll_default_language('slug'); $lang_codes = pll_languages_list(['fields' => 'slug']); } // Check for WPML elseif (defined('ICL_SITEPRESS_VERSION')) { global $sitepress; if (isset($sitepress) && is_object($sitepress) && method_exists($sitepress, 'get_default_language')) { $has_plugin = true; $plugin_name = 'WPML'; $default_lang = $sitepress->get_default_language(); $active_langs = $sitepress->get_active_languages(); $lang_codes = is_array($active_langs) ? array_keys($active_langs) : []; } } if ($has_plugin && !empty($plugin_name)) { $lang_count = is_array($lang_codes) ? count($lang_codes) : 0; // Get the language settings URL for the active plugin $manage_languages_url = ''; if ($is_polylang) { $manage_languages_url = admin_url('admin.php?page=mlang'); } elseif (!$is_builtin) { // WPML languages page $manage_languages_url = admin_url('admin.php?page=sitepress-multilingual-cms/menu/languages.php'); } ?>


()





get_available_languages(); } if (!empty($switcher_languages)): ?>
code, 16); ?>

[aitrfoel_language_switcher style="" show_flags="" show_names=""]

  • ' . esc_html__('Appearance → Widgets', 'ai-translator-for-elementor-and-polylang') . '' ); ?>
  • <?php echo do_shortcode('[aitrfoel_language_switcher]'); ?>
render_language_switcher_shortcode(); if (!empty($preview_html)) { echo $preview_html; } else { echo '

' . esc_html__('No languages configured yet. Add languages in the Multilingual Plugin section above.', 'ai-translator-for-elementor-and-polylang') . '

'; } } elseif (function_exists('pll_languages_list')) { $lang_codes = pll_languages_list(['fields' => 'slug']); if (!empty($lang_codes)) { echo '
'; foreach ($lang_codes as $lc) { $flag = $show_flags ? AITRFOEL_Language_Mapper::get_flag_html($lc, 20, 'margin-right: 4px;') : ''; $pll_name = $show_names ? esc_html(strtoupper($lc)) : ''; if (!$show_flags && !$show_names) { $pll_name = esc_html(strtoupper($lc)); } echo ''; echo $flag . ($pll_name ? ' ' . $pll_name . '' : ''); echo ''; } echo '
'; } else { echo '

' . esc_html__('No languages configured yet.', 'ai-translator-for-elementor-and-polylang') . '

'; } } else { echo '

' . esc_html__('No multilingual plugin active. Configure one first.', 'ai-translator-for-elementor-and-polylang') . '

'; } ?>


.aitrfoel-language-switcher, .aitrfoel-switcher-list, .aitrfoel-switcher-flags, .aitrfoel-switcher-flags-text, .aitrfoel-switcher-dropdown, .aitrfoel-flag-link, .aitrfoel-flag-text-link, .aitrfoel-switcher-item, .active

Elementor

get_available_languages(); $et_default_lang = $et_builtin->get_default_language(); $et_secondary = array_filter($et_all_languages, function($l) use ($et_default_lang) { return $l->code !== $et_default_lang; }); // Get all default-language templates $et_templates = get_posts([ 'post_type' => 'elementor_library', 'post_status' => 'publish', 'posts_per_page' => -1, 'meta_query' => [ ['key' => '_elementor_data', 'compare' => 'EXISTS'], ], 'tax_query' => !empty($et_secondary) ? [ [ 'taxonomy' => 'aitrfoel_language', 'field' => 'slug', 'terms' => array_map(function($l) { return $l->code; }, $et_secondary), 'operator' => 'NOT IN', ], ] : [], ]); // Split into active (used on the site) and other. "Used" now also // includes templates that are embedded elsewhere (via the Template/ // Loop widgets or an [elementor-template] shortcode), so reusable // sections without their own display conditions aren't mis-filed as // optional. $et_active = []; $et_other = []; $active_types = ['header', 'footer', 'single', 'single-post', 'single-page', 'archive', 'product', 'product-archive', 'error-404', 'search-results']; $referenced_ids = []; if (class_exists('AITRFOEL_Translation_Engine')) { $et_engine = AITRFOEL_Translation_Engine::get_instance(); if (method_exists($et_engine, 'get_referenced_template_ids')) { $referenced_ids = array_flip($et_engine->get_referenced_template_ids()); } } foreach ($et_templates as $tpl) { $conditions = get_post_meta($tpl->ID, '_elementor_conditions', true); $tpl_type = get_post_meta($tpl->ID, '_elementor_template_type', true); // Active = has display conditions, is a header/footer/theme-builder // type, OR is embedded somewhere on the site. if (!empty($conditions) || in_array($tpl_type, ['header', 'footer', 'single', 'single-post', 'single-page', 'archive', 'product', 'product-archive', 'error-404', 'search-results'], true) || isset($referenced_ids[(int) $tpl->ID])) { $et_active[] = $tpl; } else { $et_other[] = $tpl; } } $type_labels = [ 'header' => __('Header', 'ai-translator-for-elementor-and-polylang'), 'footer' => __('Footer', 'ai-translator-for-elementor-and-polylang'), 'single' => __('Single Page', 'ai-translator-for-elementor-and-polylang'), 'single-post' => __('Blog Post', 'ai-translator-for-elementor-and-polylang'), 'single-page' => __('Page', 'ai-translator-for-elementor-and-polylang'), 'archive' => __('Archive', 'ai-translator-for-elementor-and-polylang'), 'product' => __('Product', 'ai-translator-for-elementor-and-polylang'), 'product-archive' => __('Shop Page', 'ai-translator-for-elementor-and-polylang'), 'error-404' => __('404 Page', 'ai-translator-for-elementor-and-polylang'), 'search-results' => __('Search Results', 'ai-translator-for-elementor-and-polylang'), 'section' => __('Section', 'ai-translator-for-elementor-and-polylang'), 'page' => __('Page', 'ai-translator-for-elementor-and-polylang'), 'popup' => __('Popup', 'ai-translator-for-elementor-and-polylang'), 'loop-item' => __('Loop Item', 'ai-translator-for-elementor-and-polylang'), 'kit' => __('Kit', 'ai-translator-for-elementor-and-polylang'), 'container' => __('Section', 'ai-translator-for-elementor-and-polylang'), ]; ?>

ID, '_elementor_template_type', true); $tpl_translations = $et_builtin->get_post_translations($tpl->ID); $type_label = isset($type_labels[$tpl_type]) ? $type_labels[$tpl_type] : ucfirst($tpl_type ?: 'Template'); $is_hf = in_array($tpl_type, ['header', 'footer'], true); ?>
post_title ?: __('(no title)', 'ai-translator-for-elementor-and-polylang')); ?> #ID); ?> code]) && $tpl_translations[$lang->code] != $tpl->ID; $tr_id = $has ? (int) $tpl_translations[$lang->code] : 0; $tpl_label = esc_attr($tpl->post_title ?: 'Template #' . $tpl->ID); ?> code, 14); ?> code)); ?> ✓ # ✗

ID, '_elementor_template_type', true); $tpl_translations = $et_builtin->get_post_translations($tpl->ID); $type_label = isset($type_labels[$tpl_type]) ? $type_labels[$tpl_type] : ucfirst($tpl_type ?: 'Template'); ?>
post_title ?: __('(no title)', 'ai-translator-for-elementor-and-polylang')); ?> #ID); ?> code]) && $tpl_translations[$lang->code] != $tpl->ID; $tr_id = $has ? (int) $tpl_translations[$lang->code] : 0; $tpl_label = esc_attr($tpl->post_title ?: 'Template #' . $tpl->ID); ?> code, 14); ?> code)); ?> ✓ # ✗

get_available_languages(); $mw_default_lang = $mw_builtin->get_default_language(); $mw_secondary = array_filter($mw_all_languages, function($l) use ($mw_default_lang) { return $l->code !== $mw_default_lang; }); ?>

', '' ); ?>

$loc_name): ?>
()

$widgets) { if ($sidebar_id === 'wp_inactive_widgets' || empty($widgets)) { continue; } $sidebar_name = isset($registered_sidebars[$sidebar_id]['name']) ? $registered_sidebars[$sidebar_id]['name'] : $sidebar_id; $has_widgets = true; ?>
widgets)) { foreach ($wp_widget_factory->widgets as $widget_obj) { if ($widget_obj->id_base === $widget_type) { $widget_title = $widget_obj->name; // Try to get instance title $all_instances = get_option('widget_' . $widget_type, []); if (isset($all_instances[$widget_number]['title']) && $all_instances[$widget_number]['title'] !== '') { $widget_title .= ': ' . $all_instances[$widget_number]['title']; } break; } } } } $selected_langs = isset($mw_widget_assignments[$widget_id]) ? (array) $mw_widget_assignments[$widget_id] : []; ?>
()
', '' ); ?>

is_builtin() && class_exists('AITRFOEL_Builtin_Integration')) : $st_builtin = AITRFOEL_Builtin_Integration::get_instance(); $st_default = $st_builtin->get_default_language(); $st_all_langs = $st_builtin->get_available_languages(); $st_secondary = array_values(array_filter($st_all_langs, function ($l) use ($st_default) { return $l->code !== $st_default; })); $st_entries = get_option('aitrfoel_string_translations', []); if (!is_array($st_entries)) { $st_entries = []; } // Map known source strings to a human "Used in" label so the admin // can see where each detected string comes from. $st_sources_map = []; $st_priv = get_option('woocommerce_checkout_privacy_policy_text', ''); if (is_string($st_priv) && trim($st_priv) !== '') { $st_sources_map[trim($st_priv)] = __('WooCommerce · Checkout privacy', 'ai-translator-for-elementor-and-polylang'); } if (function_exists('WC') && WC()->payment_gateways) { foreach (WC()->payment_gateways->payment_gateways() as $st_gw) { if (!empty($st_gw->title)) { $st_sources_map[trim((string) $st_gw->title)] = sprintf(__('WooCommerce · Payment title (%s)', 'ai-translator-for-elementor-and-polylang'), $st_gw->id); } if (!empty($st_gw->description)) { $st_sources_map[trim((string) $st_gw->description)] = __('WooCommerce · Payment description', 'ai-translator-for-elementor-and-polylang'); } } } // Label JetSmartFilters strings so detected filter labels are easy // to spot in the table after Auto-translate. if (post_type_exists('jet-smart-filters')) { $st_jsf_ids = get_posts([ 'post_type' => 'jet-smart-filters', 'post_status' => ['publish', 'draft', 'pending', 'private'], 'numberposts' => -1, 'fields' => 'ids', 'suppress_filters' => true, ]); foreach ($st_jsf_ids as $st_fid) { $st_all = get_post_meta($st_fid, '_all_option_label', true); if (is_string($st_all) && trim($st_all) !== '') { $st_sources_map[trim($st_all)] = __('JetSmartFilters · "Show all"', 'ai-translator-for-elementor-and-polylang'); } $st_flabel = get_post_meta($st_fid, '_filter_label', true); if (is_string($st_flabel) && trim($st_flabel) !== '') { $st_sources_map[trim($st_flabel)] = __('JetSmartFilters · Filter label', 'ai-translator-for-elementor-and-polylang'); } if (get_post_meta($st_fid, '_data_source', true) === 'taxonomies') { $st_tax = get_post_meta($st_fid, '_source_taxonomy', true); if (is_string($st_tax) && $st_tax !== '' && taxonomy_exists($st_tax)) { $st_terms = get_terms(['taxonomy' => $st_tax, 'hide_empty' => false]); if (is_array($st_terms)) { foreach ($st_terms as $st_term) { if (is_object($st_term) && !empty($st_term->name)) { $st_sources_map[trim($st_term->name)] = __('JetSmartFilters · Filter option', 'ai-translator-for-elementor-and-polylang'); } } } } } } } ?>

code]) ? $translations[$sl->code] : ''; ?> payment_gateways) { foreach (WC()->payment_gateways->payment_gateways() as $gw) { if (!empty($gw->title)) { $st_seed[] = trim((string) $gw->title); } if (!empty($gw->description)) { $st_seed[] = trim((string) $gw->description); } } } $st_seed = array_values(array_unique(array_filter($st_seed))); foreach ($st_seed as $seed_source) { $st_render_row($st_i, $seed_source, []); $st_i++; } } // Always render one empty row to start. $st_render_row($st_i, '', []); $st_next_index = $st_i + 1; ?>
code)); ?>

WooCommerce
/>

/>

/>

/>

/>

/>

/>

/>

is_builtin() && class_exists('WooCommerce')) : ?>

SureCart
/>

/>

/>

/>

is_builtin()) : ?>

get_stats() : ['total_entries' => 0, 'total_words_saved' => 0, 'total_hits' => 0]; ?>
~

PRO

PRO
→

prefix . 'aitrfoel_glossary'; $terms = []; if ($wpdb->get_var("SHOW TABLES LIKE '$g_table'") === $g_table) { $terms = $wpdb->get_results("SELECT * FROM $g_table ORDER BY id DESC", ARRAY_A); } if (empty($terms)): ?>
' . esc_html__('All Languages', 'ai-translator-for-elementor-and-polylang') . ''; } else { echo '' . esc_html(strtoupper($t['lang_code'])) . ''; } ?> →


__('Repair Translation Links', 'ai-translator-for-elementor-and-polylang'), 'desc' => __('Reconcile inconsistent translation links (dashboard list, switcher, cart/checkout swap).', 'ai-translator-for-elementor-and-polylang'), 'href' => '#aitrfoel-repair-links-wrap', ], [ 'title' => __('Repair Page Content Links', 'ai-translator-for-elementor-and-polylang'), 'desc' => __('Re-localize internal links inside translated page content and Elementor data.', 'ai-translator-for-elementor-and-polylang'), 'href' => '#aitrfoel-content-links-wrap', ], [ 'title' => __('Fix Elementor Template Shortcodes', 'ai-translator-for-elementor-and-polylang'), 'desc' => __('Rewrite [elementor-template id="X"] shortcodes in translated posts to use the per-language template.', 'ai-translator-for-elementor-and-polylang'), 'href' => '#aitrfoel-fix-shortcodes-wrap', ], [ 'title' => __('Retranslate SEO Titles', 'ai-translator-for-elementor-and-polylang'), 'desc' => __('Re-translate SEO title, description, OG/Twitter for existing translations.', 'ai-translator-for-elementor-and-polylang'), 'href' => '#aitrfoel-seo-patch-wrap', ], [ 'title' => __('Retranslate Short Descriptions', 'ai-translator-for-elementor-and-polylang'), 'desc' => __('Re-translate WooCommerce product short descriptions (post excerpt).', 'ai-translator-for-elementor-and-polylang'), 'href' => '#aitrfoel-excerpt-patch-wrap', ], [ 'title' => __('Switch Default Language', 'ai-translator-for-elementor-and-polylang'), 'desc' => __('Move root URLs and migrate translation groups when changing default language.', 'ai-translator-for-elementor-and-polylang'), 'href' => '#aitrfoel-sdl-wrap', ], [ 'title' => __('String Translations', 'ai-translator-for-elementor-and-polylang'), 'desc' => __('Override UI strings not covered automatically (gateway titles, privacy text, plugin strings).', 'ai-translator-for-elementor-and-polylang'), 'href' => '#section-strings', ], [ 'title' => __('Localize Existing Media Library', 'ai-translator-for-elementor-and-polylang'), 'desc' => __('Generate localized Media Library attachment records for all active target languages.', 'ai-translator-for-elementor-and-polylang'), 'href' => '#aitrfoel-media-localizer-wrap', ], [ 'title' => __('Exclude Paths from Language Routing', 'ai-translator-for-elementor-and-polylang'), 'desc' => __('Stop the router from prefixing custom plugin/API endpoints.', 'ai-translator-for-elementor-and-polylang'), 'href' => '#section-multilingual', ], ]; foreach ($aitrfoel_tools as $tool) : ?>

/>

/>

/>

/>



define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);

meeptech.com


.

$langs) { $location = sanitize_key($location); if (empty($location) || !is_array($langs)) { continue; } foreach ($langs as $lang_code => $menu_id) { $lang_code = sanitize_key($lang_code); $menu_id = intval($menu_id); if ($menu_id > 0) { $clean[$location][$lang_code] = $menu_id; } } } return $clean; } /** * Sanitize per-language widget visibility assignments */ public static function sanitize_widget_languages($input) { if (empty($input) || !is_array($input)) { return []; } $clean = []; foreach ($input as $widget_id => $data) { $widget_id = sanitize_text_field($widget_id); if (empty($widget_id) || !is_array($data)) { continue; } // If "all" mode is selected, store empty array (show for all) if (isset($data['_mode']) && $data['_mode'] === 'all') { continue; } // Collect checked language codes $langs = []; foreach ($data as $key => $val) { if ($key === '_mode') continue; $key = sanitize_key($key); if ($val && !empty($key)) { $langs[] = $key; } } if (!empty($langs)) { $clean[$widget_id] = $langs; } } return $clean; } /** * Sanitize glossary terms textarea */ public static function sanitize_glossary_terms($input) { if (empty($input)) { return ''; } // Keep line structure, sanitize each line $lines = explode("\n", $input); $clean = []; foreach ($lines as $line) { $line = sanitize_text_field(trim($line)); if (!empty($line)) { $clean[] = $line; } } return implode("\n", $clean); } /** * Test API connection AJAX handler */ public static function test_api_connection() { check_ajax_referer('aitrfoel_test_api', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error('Permission denied'); } $provider = sanitize_text_field($_POST['provider'] ?? ''); $api_key = sanitize_text_field($_POST['api_key'] ?? ''); if (empty($api_key)) { wp_send_json_error('No API key provided'); } if ($provider !== 'openai') { wp_send_json_error('Only OpenAI provider is supported'); } try { $api = AITRFOEL_OpenAI_API::get_instance(); // Temporarily set the API key for testing $original_key = get_option('aitrfoel_openai_api_key', ''); $api->set_api_key($api_key); // Test connection with a simple translation $start_time = microtime(true); $result = $api->test_connection(); $end_time = microtime(true); $response_time = round(($end_time - $start_time) * 1000, 2); // Restore original key $api->set_api_key($original_key); if ($result) { wp_send_json_success("OpenAI API connection successful! Response time: {$response_time}ms"); } else { wp_send_json_error("OpenAI API key is invalid or connection failed"); } } catch (Exception $e) { wp_send_json_error('Error testing API: ' . $e->getMessage()); } } /** * AJAX handler for theme compatibility diagnosis. */ public static function diagnose_theme_ajax_handler() { check_ajax_referer('aitrfoel_diagnose_theme_nonce', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error('Permission denied'); } if (!class_exists('AITRFOEL_Theme_Compatibility')) { wp_send_json_error('Compatibility class not found.'); return; } $theme_compat = new AITRFOEL_Theme_Compatibility(); $report = $theme_compat->diagnose_theme_compatibility(); // Invalidate cache so next page load gets fresh data delete_transient('aitrfoel_theme_status_report'); wp_send_json_success($report); } /** * Validate word count AJAX handler */ public static function validate_word_count() { check_ajax_referer('aitrfoel_validate_word_count', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error('Permission denied'); } $word_count = isset($_POST['word_count']) ? intval($_POST['word_count']) : 0; $domain = isset($_POST['domain']) ? sanitize_text_field($_POST['domain']) : ''; try { $license_manager = AITRFOEL_License_Manager_Server::get_instance(); // Check if translation is allowed if (!$license_manager->can_translate($word_count)) { $license_status = $license_manager->get_license_status(); wp_send_json_error([ 'message' => __('Word limit exceeded', 'ai-translator-for-elementor-and-polylang'), 'words_used' => $license_status['words_used'], 'word_limit' => $license_status['word_limit'], 'plan' => $license_status['plan'] ]); } wp_send_json_success([ 'message' => __('Word count validated', 'ai-translator-for-elementor-and-polylang'), 'word_count' => $word_count, 'domain' => $domain ]); } catch (Exception $e) { wp_send_json_error($e->getMessage()); } } /** * Save cooldown expiry AJAX handler */ public static function save_cooldown() { check_ajax_referer('aitrfoel_save_cooldown', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error('Permission denied'); } $expiry_time = isset($_POST['expiry_time']) ? intval($_POST['expiry_time']) : 0; if ($expiry_time > 0) { update_option('aitrfoel_cooldown_expiry', $expiry_time); wp_send_json_success(['message' => __('Cooldown saved', 'ai-translator-for-elementor-and-polylang')]); } else { wp_send_json_error('Invalid expiry time'); } } /** * Clear translation cache AJAX handler */ public static function clear_cache() { check_ajax_referer('aitrfoel_clear_cache', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error('Permission denied'); } try { global $wpdb; $table_cache = $wpdb->prefix . 'aitrfoel_translation_cache'; $table_tm = $wpdb->prefix . 'aitrfoel_translation_memory'; // Delete all cached translations from both cache and TM if ($wpdb->get_var("SHOW TABLES LIKE '$table_cache'") === $table_cache) { $wpdb->query("TRUNCATE TABLE {$table_cache}"); } if ($wpdb->get_var("SHOW TABLES LIKE '$table_tm'") === $table_tm) { $wpdb->query("TRUNCATE TABLE {$table_tm}"); } // Also clear any transients related to translations delete_transient('aitrfoel_theme_status_report'); wp_send_json_success([ 'message' => __('Translation cache and Translation Memory cleared successfully!', 'ai-translator-for-elementor-and-polylang') ]); } catch (Exception $e) { wp_send_json_error(__('Error clearing cache: ', 'ai-translator-for-elementor-and-polylang') . $e->getMessage()); } } /** * Reset plugin data AJAX handler * Resets built-in language system, onboarding state, and dependency cache. * Preserves: API key, translated pages, cache settings, debug mode. */ public static function reset_plugin_data() { check_ajax_referer('aitrfoel_reset_plugin_data', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error('Permission denied'); } try { // 1. Disable and clear built-in language system delete_option('aitrfoel_builtin_enabled'); delete_option('aitrfoel_builtin_languages'); delete_option('aitrfoel_builtin_default_language'); delete_option('aitrfoel_builtin_url_structure'); delete_option('aitrfoel_builtin_db_version'); // 2. Reset onboarding / dependency status (triggers welcome screen) delete_option('aitrfoel_dependencies_status'); // 3. Clear analytics opt-in pref delete_option('aitrfoel_analytics_optin'); delete_option('aitrfoel_analytics_enabled'); // 4. Clear any transients delete_transient('aitrfoel_theme_status_report'); // 5. Clean up built-in taxonomy terms $terms = get_terms([ 'taxonomy' => 'aitrfoel_language', 'hide_empty' => false, 'fields' => 'ids', ]); if (!is_wp_error($terms)) { foreach ($terms as $term_id) { wp_delete_term($term_id, 'aitrfoel_language'); } } // 6. Flush rewrite rules flush_rewrite_rules(); // 7. Clear the singleton instance so it re-detects on next load if (class_exists('AITRFOEL_Multilingual_Manager')) { $manager = AITRFOEL_Multilingual_Manager::get_instance(); if (method_exists($manager, 'refresh')) { $manager->refresh(); } } wp_send_json_success([ 'message' => __('Plugin data reset successfully! Redirecting to welcome screen...', 'ai-translator-for-elementor-and-polylang') ]); } catch (Exception $e) { wp_send_json_error(__('Error resetting plugin data: ', 'ai-translator-for-elementor-and-polylang') . $e->getMessage()); } } /** * Render built-in language management controls on the settings page. * * @param AITRFOEL_Builtin_Integration $builtin Instance of the built-in integration. */ private static function render_builtin_language_management($builtin) { $languages = $builtin->get_available_languages(); $default_lang = $builtin->get_default_language(); $all_mappings = AITRFOEL_Language_Mapper::get_all_mappings(); $nonce = wp_create_nonce('aitrfoel_ajax_nonce'); // Get raw stored data for debugging language code corruption $raw_stored = get_option('aitrfoel_builtin_languages', []); $stored_codes = array_column($raw_stored, 'code'); $lang_keys = array_keys($languages); // Check for corruption: codes not matching $code_mismatch = array_diff($stored_codes, $lang_keys); $show_debug = !empty($code_mismatch) || (count($stored_codes) > 0 && count($lang_keys) > 0 && array_values($stored_codes) !== array_values($lang_keys)); $debug_mode_enabled = (bool) get_option('aitrfoel_debug_mode', false); ?>

Debug: Stored Language Codes
Stored in Database: 0 ? esc_html(json_encode($stored_codes)) : '(empty)'; ?>
Loaded into UI: 0 ? esc_html(json_encode($lang_keys)) : '(empty)'; ?>
MISMATCH DETECTED: - These codes in database don't match UI!
Raw Data:
$lang): ?>
[$] code=, name=, is_default=
$lang_obj): $lang_name = is_object($lang_obj) ? $lang_obj->name : $lang_obj; ?>
= 2) : ?>

prefix . 'aitrfoel_glossary'; $inserted = $wpdb->insert( $table_name, [ 'source_text' => $source_text, 'target_text' => $target_text, 'lang_code' => $lang_code, 'action' => $action, 'created_at' => current_time('mysql'), ], ['%s', '%s', '%s', '%s', '%s'] ); if ($inserted === false) { wp_send_json_error(__('Failed to save term in database', 'ai-translator-for-elementor-and-polylang')); } $new_id = $wpdb->insert_id; wp_send_json_success([ 'id' => $new_id, 'source_text' => esc_html($source_text), 'target_text' => esc_html($target_text), 'lang_code' => esc_html($lang_code), 'action' => esc_html($action), 'message' => __('Term added successfully', 'ai-translator-for-elementor-and-polylang'), ]); } /** * AJAX handler to delete a glossary term */ public static function delete_glossary_term_ajax() { check_ajax_referer('aitrfoel_glossary_nonce', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(__('Unauthorized', 'ai-translator-for-elementor-and-polylang'), 403); } $id = isset($_POST['id']) ? intval($_POST['id']) : 0; if ($id <= 0) { wp_send_json_error(__('Invalid term ID', 'ai-translator-for-elementor-and-polylang')); } global $wpdb; $table_name = $wpdb->prefix . 'aitrfoel_glossary'; $deleted = $wpdb->delete($table_name, ['id' => $id], ['%d']); if ($deleted === false) { wp_send_json_error(__('Failed to delete term', 'ai-translator-for-elementor-and-polylang')); } wp_send_json_success(['message' => __('Term deleted', 'ai-translator-for-elementor-and-polylang')]); } /** * AJAX handler to export glossary as CSV */ public static function export_glossary_csv_ajax() { check_ajax_referer('aitrfoel_glossary_nonce', 'nonce'); if (!current_user_can('manage_options')) { wp_die(__('Unauthorized', 'ai-translator-for-elementor-and-polylang'), 403); } global $wpdb; $table_name = $wpdb->prefix . 'aitrfoel_glossary'; $terms = $wpdb->get_results("SELECT source_text, target_text, lang_code, action FROM $table_name ORDER BY id ASC", ARRAY_A); header('Content-Type: text/csv; charset=utf-8'); header('Content-Disposition: attachment; filename=meep-glossary-export-' . date('Y-m-d') . '.csv'); $output = fopen('php://output', 'w'); fputcsv($output, ['Source Text', 'Target Text', 'Language Code', 'Action']); if (!empty($terms)) { foreach ($terms as $row) { fputcsv($output, [ $row['source_text'], $row['target_text'], $row['lang_code'], $row['action'] ]); } } fclose($output); exit; } /** * AJAX handler to import glossary from CSV */ public static function import_glossary_csv_ajax() { check_ajax_referer('aitrfoel_glossary_nonce', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(__('Unauthorized', 'ai-translator-for-elementor-and-polylang'), 403); } if (empty($_FILES['file']['tmp_name'])) { wp_send_json_error(__('No CSV file uploaded', 'ai-translator-for-elementor-and-polylang')); } $file = $_FILES['file']['tmp_name']; $handle = fopen($file, 'r'); if (!$handle) { wp_send_json_error(__('Could not read uploaded file', 'ai-translator-for-elementor-and-polylang')); } global $wpdb; $table_name = $wpdb->prefix . 'aitrfoel_glossary'; $imported = 0; $header = fgetcsv($handle); // skip header row while (($data = fgetcsv($handle, 1000, ',')) !== false) { if (count($data) < 1 || empty(trim($data[0]))) { continue; } $source_text = sanitize_text_field($data[0]); $target_text = isset($data[1]) ? sanitize_text_field($data[1]) : ''; $lang_code = isset($data[2]) && !empty(trim($data[2])) ? sanitize_text_field(trim($data[2])) : 'all'; $action = isset($data[3]) && in_array(trim($data[3]), ['replace', 'do_not_translate']) ? trim($data[3]) : (!empty($target_text) ? 'replace' : 'do_not_translate'); $wpdb->insert( $table_name, [ 'source_text' => $source_text, 'target_text' => $target_text, 'lang_code' => $lang_code, 'action' => $action, 'created_at' => current_time('mysql'), ], ['%s', '%s', '%s', '%s', '%s'] ); $imported++; } fclose($handle); wp_send_json_success([ 'imported' => $imported, 'message' => sprintf(__('%d terms imported successfully', 'ai-translator-for-elementor-and-polylang'), $imported) ]); } }