false,
'auto_translate' => false,
'advanced_cache' => false,
'freemius' => false
));
}
// Exit if accessed directly
if (!defined('ABSPATH')) {
exit;
}
// Activation redirect to dashboard
function eatp_activation_redirect( $plugin ) {
if ( $plugin == plugin_basename( __FILE__ ) ) {
// Set flag to show welcome popup on first visit
update_option( 'aitrfoel_show_welcome_popup', 'yes' );
// For PRO: Only redirect if Freemius opt-in is already completed
// For FREE: Always redirect (no Freemius opt-in screen)
if ( defined('AITRFOEL_VERSION_TYPE') && AITRFOEL_VERSION_TYPE === 'pro' ) {
// PRO version - let Freemius show opt-in screen if not registered
if ( eatp_fs()->is_registered() ) {
wp_safe_redirect( admin_url( 'admin.php?page=ai-translator-for-elementor-and-polylang&welcome=1' ) );
exit;
}
// Otherwise, Freemius will show its opt-in screen and redirect after
} else {
// FREE version - always redirect to plugin page
wp_safe_redirect( admin_url( 'admin.php?page=ai-translator-for-elementor-and-polylang&welcome=1' ) );
exit;
}
}
}
add_action( 'activated_plugin', 'eatp_activation_redirect' );
/**
* Main Plugin Class
*/
class AITRFOEL_Main {
/**
* Instance
*/
private static $instance = null;
/**
* Get instance
*/
public static function get_instance() {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Constructor
*/
private function __construct() {
$this->init_hooks();
}
/**
* Initialize hooks
*/
private function init_hooks() {
// Check dependencies
add_action('plugins_loaded', [$this, 'check_dependencies']);
// Load plugin
add_action('plugins_loaded', [$this, 'load_plugin']);
// Activation/Deactivation hooks
register_activation_hook(__FILE__, [$this, 'activate']);
register_deactivation_hook(__FILE__, [$this, 'deactivate']);
// Note: load_plugin_textdomain() removed - WordPress 4.6+ automatically loads
// translations for plugins hosted on WordPress.org
// Enqueue scripts
add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_scripts']);
add_action('admin_enqueue_scripts', [$this, 'enqueue_deactivation_survey']);
// Enqueue editor inline translations script (Elementor & Gutenberg)
add_action('admin_enqueue_scripts', [$this, 'enqueue_editor_inline_scripts']);
add_action('elementor/editor/after_enqueue_scripts', [$this, 'enqueue_editor_inline_scripts']);
add_action('enqueue_block_editor_assets', [$this, 'enqueue_editor_inline_scripts']);
// Plugin list row actions
add_filter('plugin_action_links_' . AITRFOEL_PLUGIN_BASENAME, [$this, 'add_plugin_action_links']);
// Hide Elementor admin elements on our plugin pages
add_action('admin_init', [$this, 'disable_elementor_admin_ui']);
// Welcome popup (shown once after activation)
add_action('admin_footer', [$this, 'render_welcome_popup']);
add_action('wp_ajax_aitrfoel_dismiss_welcome', [$this, 'dismiss_welcome_popup']);
// Auto-repair previously translated Elementor templates missing taxonomy terms
add_action('admin_init', [$this, 'repair_translated_elementor_templates']);
// Removed: Background processing (using direct processing now)
// add_action('aitrfoel_process_bulk_translation', [$this, 'process_bulk_translation_background'], 10, 4);
}
/**
* Self-healing repair: automatically assigns missing elementor_library_type taxonomy terms
* and regenerates CSS for any previously translated Elementor templates that were created
* without taxonomy terms (restoring them from Theme Builder back to Saved Templates).
*/
public function repair_translated_elementor_templates() {
if (!is_admin() || !post_type_exists('elementor_library')) {
return;
}
$repair_flag = 'aitrfoel_tpl_tax_repaired_v315';
if (get_option($repair_flag, false)) {
return;
}
if (!class_exists('AITRFOEL_Translation_Engine')) {
return;
}
$engine = AITRFOEL_Translation_Engine::get_instance();
$translated_templates = get_posts([
'post_type' => 'elementor_library',
'post_status' => 'any',
'posts_per_page' => -1,
'fields' => 'ids',
'meta_query' => [
[
'key' => '_aitrfoel_source_page',
'compare' => 'EXISTS',
],
],
]);
if (!empty($translated_templates)) {
foreach ($translated_templates as $tpl_id) {
$source_id = (int) get_post_meta($tpl_id, '_aitrfoel_source_page', true);
if ($source_id > 0 && $source_id !== (int) $tpl_id) {
$engine->sync_elementor_template_meta_and_taxonomies($source_id, $tpl_id);
} else {
$tpl_type = get_post_meta($tpl_id, '_elementor_template_type', true);
$existing_terms = wp_get_object_terms($tpl_id, 'elementor_library_type', ['fields' => 'slugs']);
if (empty($existing_terms) && !empty($tpl_type)) {
wp_set_object_terms($tpl_id, (string) $tpl_type, 'elementor_library_type');
}
}
}
}
update_option($repair_flag, true);
}
/**
* Add plugin row action links on the Plugins screen.
*
* @param array $links Existing action links.
* @return array
*/
public function add_plugin_action_links($links) {
$settings_link = '' . esc_html__('Settings', 'ai-translator-for-elementor-and-polylang') . '';
array_unshift($links, $settings_link);
return $links;
}
/**
* Check plugin dependencies
* @param bool $show_notices Whether to show admin notices
* @return array Array with 'status' (bool) and 'missing' (array of missing dependencies)
*/
public function check_dependencies($show_notices = true) {
$missing = [];
// Check PHP version (hard requirement)
if (version_compare(PHP_VERSION, '7.4', '<')) {
$missing[] = 'php_version';
if ($show_notices) {
add_action('admin_notices', [$this, 'admin_notice_php_version']);
}
}
return [
'status' => empty($missing),
'missing' => $missing
];
}
/**
* Load plugin components
*/
public function load_plugin() {
$deps = $this->check_dependencies(true);
// Store dependencies status early for the dashboard
update_option('aitrfoel_dependencies_status', $deps);
// Always load admin menu so plugin is visible, even when dependencies aren't met
if (is_admin()) {
// Load classes required for admin pages to work
require_once AITRFOEL_PLUGIN_DIR . 'includes/class-license-manager-server.php';
require_once AITRFOEL_PLUGIN_DIR . 'includes/class-license-manager.php';
require_once AITRFOEL_PLUGIN_DIR . 'admin/class-admin-menu.php';
require_once AITRFOEL_PLUGIN_DIR . 'admin/class-settings.php';
// Wizard AJAX must work even when deps are missing (it IS the onboarding path)
require_once AITRFOEL_PLUGIN_DIR . 'admin/class-wizard-controller.php';
// Initialize admin menu directly (not in a hook since we're already in plugins_loaded)
AITRFOEL_Admin_Menu::get_instance();
AITRFOEL_Wizard_Controller::get_instance();
}
// If dependencies are not met, don't load the rest of the plugin
if (!$deps['status']) {
return;
}
// Load core classes
$this->load_includes();
// Initialize components
$this->init_components();
}
/**
* Load include files
*/
private function load_includes() {
// Core includes - Load logger FIRST (used by many classes)
require_once AITRFOEL_PLUGIN_DIR . 'includes/class-logger.php';
// Load language mapper and widget parser
require_once AITRFOEL_PLUGIN_DIR . 'includes/class-language-mapper.php';
$widget_parser_file = AITRFOEL_PLUGIN_DIR . 'includes/class-widget-parser.php';
if (!file_exists($widget_parser_file)) {
error_log('AITRFOEL Fatal: Widget parser file not found at: ' . $widget_parser_file);
throw new Exception('Widget parser file not found');
}
require_once $widget_parser_file;
// Verify class was loaded
if (!class_exists('AITRFOEL_Widget_Parser')) {
error_log('AITRFOEL Fatal: AITRFOEL_Widget_Parser class not defined after loading file');
throw new Exception('AITRFOEL_Widget_Parser class not defined');
}
require_once AITRFOEL_PLUGIN_DIR . 'includes/class-translation-memory.php';
require_once AITRFOEL_PLUGIN_DIR . 'includes/class-openai-api.php';
require_once AITRFOEL_PLUGIN_DIR . 'includes/class-post-processor.php';
require_once AITRFOEL_PLUGIN_DIR . 'includes/class-gap-filler.php';
require_once AITRFOEL_PLUGIN_DIR . 'includes/class-translation-engine.php';
require_once AITRFOEL_PLUGIN_DIR . 'includes/class-theme-compatibility.php';
require_once AITRFOEL_PLUGIN_DIR . 'includes/class-cache-manager.php';
// Content type detection and parsers for different editors
require_once AITRFOEL_PLUGIN_DIR . 'includes/class-content-detector.php';
require_once AITRFOEL_PLUGIN_DIR . 'includes/class-classic-parser.php';
require_once AITRFOEL_PLUGIN_DIR . 'includes/class-gutenberg-parser.php';
// Load debug logger if it exists
if (file_exists(AITRFOEL_PLUGIN_DIR . 'includes/class-debug-logger.php')) {
require_once AITRFOEL_PLUGIN_DIR . 'includes/class-debug-logger.php';
}
require_once AITRFOEL_PLUGIN_DIR . 'includes/class-usage-tracker.php';
require_once AITRFOEL_PLUGIN_DIR . 'includes/class-bulk-translation-queue.php';
require_once AITRFOEL_PLUGIN_DIR . 'includes/class-license-manager-server.php';
require_once AITRFOEL_PLUGIN_DIR . 'includes/class-license-manager.php'; // Backward compatibility alias
require_once AITRFOEL_PLUGIN_DIR . 'includes/class-api-client.php';
require_once AITRFOEL_PLUGIN_DIR . 'includes/class-background-sync.php';
// Admin includes (may already be loaded in load_plugin for dependency-failed cases)
if (is_admin()) {
if (!class_exists('AITRFOEL_Admin_Menu')) {
require_once AITRFOEL_PLUGIN_DIR . 'admin/class-admin-menu.php';
}
if (!class_exists('AITRFOEL_Settings')) {
require_once AITRFOEL_PLUGIN_DIR . 'admin/class-settings.php';
}
if (!class_exists('AITRFOEL_Translate_All')) {
require_once AITRFOEL_PLUGIN_DIR . 'admin/class-translate-all.php';
}
if (!class_exists('AITRFOEL_Translation_History')) {
require_once AITRFOEL_PLUGIN_DIR . 'admin/class-translation-history.php';
}
if (!class_exists('AITRFOEL_Review_Editor')) {
require_once AITRFOEL_PLUGIN_DIR . 'admin/class-review-editor.php';
}
if (!class_exists('AITRFOEL_Wizard_Controller')) {
require_once AITRFOEL_PLUGIN_DIR . 'admin/class-wizard-controller.php';
}
}
// Translation Metabox & Admin Top Bar menu must be loaded for both admin and frontend
if (!class_exists('AITRFOEL_Translation_Metabox')) {
require_once AITRFOEL_PLUGIN_DIR . 'admin/class-translation-metabox.php';
}
if (!class_exists('AITRFOEL_Admin_Bar')) {
require_once AITRFOEL_PLUGIN_DIR . 'includes/class-admin-bar.php';
}
if (!class_exists('AITRFOEL_WP_List_Translator')) {
require_once AITRFOEL_PLUGIN_DIR . 'includes/class-wp-list-translator.php';
}
// Translate All must also be loaded for WP-Cron (non-admin context)
if (!is_admin() && !class_exists('AITRFOEL_Translate_All')) {
require_once AITRFOEL_PLUGIN_DIR . 'admin/class-translate-all.php';
}
// AJAX handler must be loaded for both admin and frontend AJAX requests
require_once AITRFOEL_PLUGIN_DIR . 'admin/class-ajax-handler.php';
// Load multilingual integrations (abstract interface, manager, and implementations)
require_once AITRFOEL_PLUGIN_DIR . 'includes/class-multilingual-interface.php';
require_once AITRFOEL_PLUGIN_DIR . 'includes/class-polylang-integration.php';
require_once AITRFOEL_PLUGIN_DIR . 'includes/class-wpml-integration.php';
require_once AITRFOEL_PLUGIN_DIR . 'includes/class-builtin-integration.php';
require_once AITRFOEL_PLUGIN_DIR . 'includes/class-multilingual-manager.php';
// Load analytics (PostHog integration)
require_once AITRFOEL_PLUGIN_DIR . 'includes/class-analytics.php';
// Debug tools removed - plugin simplified
}
/**
* Initialize plugin components
*/
private function init_components() {
try {
// Run database migration if version upgraded
$db_version = get_option('aitrfoel_db_version', '0.0.0');
if (version_compare($db_version, AITRFOEL_VERSION, '<')) {
$this->create_tables();
update_option('aitrfoel_db_version', AITRFOEL_VERSION);
}
// PERFORMANCE: Disable Freemius background sync during AJAX translation requests
$this->maybe_disable_freemius_sync();
// Initialize core components
AITRFOEL_Content_Detector::get_instance();
AITRFOEL_Widget_Parser::get_instance();
AITRFOEL_License_Manager_Server::get_instance(); // Initialize server-based license manager
AITRFOEL_OpenAI_API::get_instance();
AITRFOEL_Translation_Engine::get_instance();
AITRFOEL_Cache_Manager::get_instance();
AITRFOEL_Logger::get_instance();
// Initialize debug logger if available
if (class_exists('AITRFOEL_Debug_Logger')) {
AITRFOEL_Debug_Logger::get_instance();
}
AITRFOEL_Usage_Tracker::get_instance(); // Initialize usage tracker
AITRFOEL_Bulk_Translation_Queue::get_instance(); // Initialize bulk translation queue
AITRFOEL_Background_Sync::get_instance(); // Initialize background auto-sync manager
// Initialize translation metabox & admin bar menu (active on frontend & admin)
if (class_exists('AITRFOEL_Translation_Metabox')) {
AITRFOEL_Translation_Metabox::get_instance();
}
if (class_exists('AITRFOEL_Admin_Bar')) {
AITRFOEL_Admin_Bar::get_instance();
}
if (class_exists('AITRFOEL_WP_List_Translator')) {
AITRFOEL_WP_List_Translator::get_instance();
}
// Initialize admin with error checking
if (is_admin()) {
AITRFOEL_Admin_Menu::get_instance();
AITRFOEL_Settings::init();
// Initialize Translate All feature
if (class_exists('AITRFOEL_Translate_All')) {
AITRFOEL_Translate_All::get_instance();
}
}
// Register Translate All cron hook (must be available outside admin for WP-Cron)
add_action('aitrfoel_translate_all_cron_process', ['AITRFOEL_Translate_All', 'process_cron_batch']);
// Register custom cron interval (5 minutes)
add_filter('cron_schedules', function($schedules) {
if (!isset($schedules['aitrfoel_five_minutes'])) {
$schedules['aitrfoel_five_minutes'] = [
'interval' => 300,
'display' => __('Every 5 Minutes (Meep Translator)', 'ai-translator-for-elementor-and-polylang'),
];
}
return $schedules;
});
// Initialize AJAX handler (needed for both admin and frontend AJAX requests)
if (class_exists('AITRFOEL_Ajax_Handler')) {
AITRFOEL_Ajax_Handler::get_instance();
} else {
error_log('EAT Error: AITRFOEL_Ajax_Handler class not found - using fallback');
}
// Initialize Translation History (registers AJAX handler for clear history)
if (class_exists('AITRFOEL_Translation_History')) {
AITRFOEL_Translation_History::get_instance();
}
// Initialize Review Editor (hidden subpage + save-segment AJAX)
if (class_exists('AITRFOEL_Review_Editor')) {
AITRFOEL_Review_Editor::get_instance();
}
// Initialize Wizard Controller (wizard AJAX actions)
if (class_exists('AITRFOEL_Wizard_Controller')) {
AITRFOEL_Wizard_Controller::get_instance();
}
// Initialize multilingual integration manager (auto-detects Polylang or WPML)
AITRFOEL_Multilingual_Manager::get_instance();
// Register language switcher shortcode for all multilingual integrations
// (built-in integration registers its own, but Polylang/WPML users need this too)
add_action('init', function() {
if (!shortcode_exists('aitrfoel_language_switcher')) {
add_shortcode('aitrfoel_language_switcher', function($atts) {
if (!get_option('aitrfoel_switcher_enabled', true)) {
return '';
}
$multilingual = AITRFOEL_Multilingual_Manager::get_instance();
if (!$multilingual->is_active()) {
return '';
}
$atts = shortcode_atts([
'style' => get_option('aitrfoel_switcher_style', 'flags-text'),
'show_names' => get_option('aitrfoel_switcher_show_names', true) ? 'true' : 'false',
'show_flags' => get_option('aitrfoel_switcher_show_flags', true) ? 'true' : 'false',
], $atts);
$switcher_data = $multilingual->get_language_switcher_data();
if (empty($switcher_data)) {
return '';
}
// Use builtin renderer if available (it has the full rendering with CSS)
if (class_exists('AITRFOEL_Builtin_Integration') && method_exists(AITRFOEL_Builtin_Integration::get_instance(), 'render_language_switcher_shortcode')) {
return AITRFOEL_Builtin_Integration::get_instance()->render_language_switcher_shortcode($atts);
}
// Fallback generic renderer for Polylang/WPML
return aitrfoel_render_language_switcher($switcher_data, $atts);
});
}
});
// Initialize analytics (PostHog)
AITRFOEL_Analytics::get_instance();
} catch (Exception $e) {
error_log('EAT Fatal Error during component initialization: ' . $e->getMessage());
add_action('admin_notices', function() use ($e) {
echo '
Meep AI Translator Error: ' . esc_html($e->getMessage()) . '
';
});
} catch (Error $e) {
error_log('EAT Fatal PHP Error during component initialization: ' . $e->getMessage());
add_action('admin_notices', function() use ($e) {
echo 'Meep AI Translator Fatal Error: ' . esc_html($e->getMessage()) . '
';
});
}
}
/**
* Disable Elementor admin UI elements on our plugin pages
*/
public function disable_elementor_admin_ui() {
// Check if we're on one of our plugin pages
$screen = get_current_screen();
if (!$screen) {
return;
}
// Check if current page is ANY of our plugin pages (including Freemius pages)
$page = isset($_GET['page']) ? $_GET['page'] : '';
$is_our_page = (
strpos($screen->id, 'ai-translator-for-elementor-and-polylang') !== false ||
strpos($page, 'ai-translator-for-elementor-and-polylang') !== false ||
$screen->id === 'admin_page_aitrfoel-debug'
);
if ($is_our_page) {
// Add immediate CSS to prevent flashing
add_action('admin_head', [$this, 'add_immediate_elementor_hiding'], 1);
// Remove Elementor admin top bar actions
remove_action('admin_notices', '\Elementor\Core\Admin\Admin_Notices::admin_notices');
remove_action('admin_bar_menu', '\Elementor\Core\Admin\Menu\Admin_Menu_Manager::add_menu', 200);
// Remove Elementor footer text
add_filter('admin_footer_text', [$this, 'restore_admin_footer'], 1000);
// Prevent Elementor from loading its admin styles/scripts on our pages
remove_action('admin_enqueue_scripts', '\Elementor\Core\Admin\UI\Components\Form_Manager::enqueue_form_manager_scripts');
// More aggressive: Prevent Elementor admin completely on our pages
add_action('admin_enqueue_scripts', [$this, 'prevent_elementor_admin_scripts'], 1);
// Add custom CSS to hide any remaining Elementor elements
add_action('admin_head', [$this, 'hide_elementor_admin_elements']);
}
}
/**
* PERFORMANCE: Disable Freemius background sync during AJAX translation requests
*/
private function maybe_disable_freemius_sync() {
// Check if this is a translation AJAX request
if ( defined('DOING_AJAX') && DOING_AJAX && isset($_REQUEST['action']) ) {
$translation_actions = [
'aitrfoel_translate_page',
'aitrfoel_bulk_translate',
'aitrfoel_get_translation_preview',
'aitrfoel_detect_language',
'aitrfoel_get_page_strings'
];
if ( in_array($_REQUEST['action'], $translation_actions) ) {
// Disable Freemius background sync during translation
add_filter( 'fs_is_long_term_user', '__return_false', 999 );
add_filter( 'fs_is_submenu_visible', '__return_false', 999 );
// Prevent any Freemius API calls during translation
if ( function_exists('eatp_fs') && isset($GLOBALS['eatp_fs']) ) {
// Skip connectivity tests
add_filter( 'fs_skip_connectivity_test__20522', '__return_true', 999 );
}
}
}
}
/**
* Prevent Elementor from loading admin scripts on our pages
*/
public function prevent_elementor_admin_scripts() {
// Get current screen
$screen = get_current_screen();
$page = isset($_GET['page']) ? $_GET['page'] : '';
$is_our_page = (
strpos($screen->id, 'ai-translator-for-elementor-and-polylang') !== false ||
strpos($page, 'ai-translator-for-elementor-and-polylang') !== false ||
$screen->id === 'admin_page_aitrfoel-debug'
);
if ($is_our_page) {
// Dequeue Elementor admin scripts and styles
wp_dequeue_script('elementor-admin-top-bar');
wp_dequeue_script('elementor-admin');
wp_dequeue_script('elementor-admin-feedback');
wp_dequeue_script('elementor-admin-notifications');
wp_dequeue_style('elementor-admin-top-bar');
wp_dequeue_style('elementor-admin');
// Prevent Elementor admin bar from initializing
if (class_exists('\Elementor\Core\Admin\Admin_Notices')) {
remove_action('admin_notices', [\Elementor\Core\Admin\Admin_Notices::instance(), 'admin_notices']);
}
// Remove Elementor admin menu
if (class_exists('\Elementor\Core\Admin\Menu\Admin_Menu_Manager')) {
remove_action('admin_bar_menu', [\Elementor\Core\Admin\Menu\Admin_Menu_Manager::instance(), 'add_menu'], 200);
}
}
}
/**
* Add immediate CSS to hide Elementor elements before they render
*/
public function add_immediate_elementor_hiding() {
// Enqueue a minimal inline style early
wp_register_style('aitrfoel-elementor-hiding-early', false);
wp_enqueue_style('aitrfoel-elementor-hiding-early');
$css = '
/* Hide Elementor admin top bar immediately */
#e-admin-top-bar-root,
.e-admin-top-bar,
.e-admin-top-bar--active,
[id*="e-admin-top-bar"],
[class*="e-admin-top-bar"] {
display: none !important;
visibility: hidden !important;
opacity: 0 !important;
height: 0 !important;
overflow: hidden !important;
}
/* Hide Elementor admin notices */
.e-admin-notice {
display: none !important;
}
/* Prevent body class interference */
body.e-admin-top-bar--active {
padding-top: 0 !important;
}
';
wp_add_inline_style('aitrfoel-elementor-hiding-early', $css);
}
/**
* Hide Elementor admin elements with additional CSS
*/
public function hide_elementor_admin_elements() {
// Enqueue additional hiding rules
wp_register_style('aitrfoel-elementor-hiding-additional', false);
wp_enqueue_style('aitrfoel-elementor-hiding-additional');
$css = '
/* Additional Elementor hiding rules */
#e-admin-top-bar-root,
.e-admin-top-bar,
.e-admin-top-bar--active,
.e-admin-top-bar__container,
.e-admin-top-bar__main-area,
[id*="e-admin-top-bar"],
[class*="e-admin-top-bar"] {
display: none !important;
visibility: hidden !important;
opacity: 0 !important;
height: 0 !important;
width: 0 !important;
overflow: hidden !important;
position: absolute !important;
left: -9999px !important;
}
/* Hide Elementor admin notices and components */
.e-admin-notice,
.e-admin-components-wrapper,
.e-admin-feedback,
.e-admin-announcement {
display: none !important;
}
/* Reset body padding that Elementor might have added */
body {
padding-top: 0 !important;
}
/* Hide any Elementor React root elements */
[data-reactroot],
[data-react-helmet] {
display: none !important;
}
';
wp_add_inline_style('aitrfoel-elementor-hiding-additional', $css);
}
/**
* Restore admin footer text
*/
public function restore_admin_footer($text) {
$text = (string) $text;
// Remove Elementor's custom footer text
if (strpos($text, 'Elementor') !== false) {
return '';
}
return $text;
}
/**
* Enqueue admin scripts and styles
*/
public function enqueue_admin_scripts($hook) {
$hook = (string) $hook;
$page = isset($_GET['page']) ? (string) $_GET['page'] : '';
// Only load on our plugin pages - use NEW plugin slug
// Be more inclusive to catch Freemius pages and any related admin pages
$is_our_page = (
strpos($hook, 'ai-translator-for-elementor-and-polylang') !== false ||
strpos($hook, 'eatp') !== false || // Our plugin prefix
strpos($hook, 'freemius') !== false || // Freemius pages
$hook === 'admin_page_aitrfoel-debug' ||
// Also check for any admin page that might be related
strpos($page, 'ai-translator-for-elementor-and-polylang') !== false
);
if (!$is_our_page) {
return;
}
// Enqueue unified admin stylesheet (covers Elementor-bar hiding, design tokens, all page styles)
wp_enqueue_style(
'aitrfoel-meep-admin',
AITRFOEL_PLUGIN_URL . 'assets/css/meep-admin.css',
[],
AITRFOEL_VERSION
);
// Runtime DOM enhancements for Phase B UI (icons, eyebrows, hero, counters).
wp_enqueue_script(
'aitrfoel-meep-admin',
AITRFOEL_PLUGIN_URL . 'assets/js/meep-admin.js',
[],
AITRFOEL_VERSION,
true
);
wp_localize_script( 'aitrfoel-meep-admin', 'meepAdmin', [
'pluginName' => __( 'Meep AI Translator', 'ai-translator-for-elementor-and-polylang' ),
'logoUrl' => AITRFOEL_PLUGIN_URL . 'assets/img/meep-ai-translator.png',
] );
// Enqueue scripts
wp_enqueue_script(
'aitrfoel-debug-monitor',
AITRFOEL_PLUGIN_URL . 'assets/js/debug-monitor.js',
['jquery'],
AITRFOEL_VERSION,
true
);
wp_enqueue_script(
'aitrfoel-admin-script',
AITRFOEL_PLUGIN_URL . 'assets/js/admin.js',
['jquery', 'aitrfoel-debug-monitor'],
AITRFOEL_VERSION,
true
);
// Localize script
wp_localize_script('aitrfoel-admin-script', 'aitrfoel_ajax', [
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('aitrfoel_ajax_nonce'),
'diagnose_nonce' => wp_create_nonce('aitrfoel_diagnose_theme_nonce'),
'validate_word_count_nonce' => wp_create_nonce('aitrfoel_validate_word_count'),
'save_cooldown_nonce' => wp_create_nonce('aitrfoel_save_cooldown'),
'clear_usage_nonce' => wp_create_nonce('aitrfoel_clear_usage'),
'debug' => true, // Add debug flag
'strings' => [
// Basic translations
'plugin_title' => __('Meep AI Translator', 'ai-translator-for-elementor-and-polylang'),
'translating' => __('Translating...', 'ai-translator-for-elementor-and-polylang'),
'success' => __('Translation completed successfully!', 'ai-translator-for-elementor-and-polylang'),
'error' => __('Translation failed. Please try again.', 'ai-translator-for-elementor-and-polylang'),
'confirm_translate' => __('Are you sure you want to translate this content?', 'ai-translator-for-elementor-and-polylang'),
// Page loading
'loading_pages' => __('Loading pages...', 'ai-translator-for-elementor-and-polylang'),
'select_page' => __('Select a page to translate', 'ai-translator-for-elementor-and-polylang'),
'error_loading_pages' => __('Error loading pages', 'ai-translator-for-elementor-and-polylang'),
'select_page_first' => __('Please select a page first', 'ai-translator-for-elementor-and-polylang'),
// Cost estimation
'calculating' => __('Calculating...', 'ai-translator-for-elementor-and-polylang'),
'estimated_cost' => __('Estimated Cost', 'ai-translator-for-elementor-and-polylang'),
'words' => __('Words', 'ai-translator-for-elementor-and-polylang'),
'tokens' => __('Tokens', 'ai-translator-for-elementor-and-polylang'),
'provider' => __('Provider', 'ai-translator-for-elementor-and-polylang'),
'model' => __('Model', 'ai-translator-for-elementor-and-polylang'),
'large_page_warning' => __('This page contains a lot of content and may take longer to translate.', 'ai-translator-for-elementor-and-polylang'),
'error_calculating' => __('Error calculating cost', 'ai-translator-for-elementor-and-polylang'),
'estimate_cost' => __('Estimate Cost', 'ai-translator-for-elementor-and-polylang'),
// Translation process
'select_language' => __('Please select a target language', 'ai-translator-for-elementor-and-polylang'),
'time_taken' => __('Time taken', 'ai-translator-for-elementor-and-polylang'),
'words_translated' => __('Words translated', 'ai-translator-for-elementor-and-polylang'),
'buy_more_tokens' => __('Buy More Tokens', 'ai-translator-for-elementor-and-polylang'),
'buy_more' => __('Buy More', 'ai-translator-for-elementor-and-polylang'),
'buy_more_words' => __('Buy More Words', 'ai-translator-for-elementor-and-polylang'),
'upgrade_plan' => __('Upgrade Plan', 'ai-translator-for-elementor-and-polylang'),
'total_words' => __('Total Words', 'ai-translator-for-elementor-and-polylang'),
'words_left' => __('Words Left', 'ai-translator-for-elementor-and-polylang'),
'license' => __('License', 'ai-translator-for-elementor-and-polylang'),
'pages_left' => __('Pages Left', 'ai-translator-for-elementor-and-polylang'),
// Tooltip strings
/* translators: 1: Plan name, 2: Number of words */
'plan_tooltip' => __('%1$s Plan: %2$s words included', 'ai-translator-for-elementor-and-polylang'),
/* translators: 1: Words used, 2: Total words available */
'usage_tooltip' => __('Used: %1$s of %2$s words', 'ai-translator-for-elementor-and-polylang'),
'tokens_left' => __('Tokens Left', 'ai-translator-for-elementor-and-polylang'),
'actual_cost' => __('Actual cost', 'ai-translator-for-elementor-and-polylang'),
'preview_translation' => __('Preview Translation', 'ai-translator-for-elementor-and-polylang'),
'edit_translation' => __('Edit Translation', 'ai-translator-for-elementor-and-polylang'),
'publish_now' => __('Publish Now', 'ai-translator-for-elementor-and-polylang'),
'translate_page' => __('Translate Page', 'ai-translator-for-elementor-and-polylang'),
'confirm_publish' => __('Are you sure you want to publish this page?', 'ai-translator-for-elementor-and-polylang'),
'page_published' => __('Page published successfully!', 'ai-translator-for-elementor-and-polylang'),
// Errors
'timeout_error' => __('Request timed out. Please try again.', 'ai-translator-for-elementor-and-polylang'),
'network_error' => __('Network error:', 'ai-translator-for-elementor-and-polylang'),
// Cache management
'confirm_clear_cache' => __('Are you sure you want to clear the translation cache?', 'ai-translator-for-elementor-and-polylang'),
'clearing_cache' => __('Clearing translation cache...', 'ai-translator-for-elementor-and-polylang'),
'cache_error' => __('Failed to clear cache:', 'ai-translator-for-elementor-and-polylang'),
// UI messages
'found_strings' => __('Found', 'ai-translator-for-elementor-and-polylang'),
'translatable_strings' => __('translatable strings.', 'ai-translator-for-elementor-and-polylang'),
'no_content' => __('No translatable content found on this page.', 'ai-translator-for-elementor-and-polylang'),
'select_strings' => __('Please select at least one string to translate.', 'ai-translator-for-elementor-and-polylang'),
'ready' => __('Ready to translate another page.', 'ai-translator-for-elementor-and-polylang'),
'cost_complete' => __('Cost estimation completed successfully!', 'ai-translator-for-elementor-and-polylang'),
// Usage statistics
'pages_translated' => __('Pages Translated', 'ai-translator-for-elementor-and-polylang'),
'words_processed' => __('Words Processed', 'ai-translator-for-elementor-and-polylang'),
'total_cost' => __('Total Cost', 'ai-translator-for-elementor-and-polylang'),
'daily_average' => __('Daily Average', 'ai-translator-for-elementor-and-polylang'),
'breakdown_by_provider' => __('Breakdown by Provider', 'ai-translator-for-elementor-and-polylang'),
'uses' => __('Uses', 'ai-translator-for-elementor-and-polylang'),
'cost' => __('Cost', 'ai-translator-for-elementor-and-polylang'),
'error_loading_stats' => __('Error loading statistics', 'ai-translator-for-elementor-and-polylang'),
// Theme compatibility
'checking' => __('Checking...', 'ai-translator-for-elementor-and-polylang'),
'theme_check_complete' => __('Theme check complete', 'ai-translator-for-elementor-and-polylang'),
'recheck_compatibility' => __('Re-check Compatibility', 'ai-translator-for-elementor-and-polylang'),
'theme' => __('Theme', 'ai-translator-for-elementor-and-polylang'),
'status' => __('Status', 'ai-translator-for-elementor-and-polylang'),
'fields_found' => __('Fields found', 'ai-translator-for-elementor-and-polylang'),
'view_fields' => __('View fields', 'ai-translator-for-elementor-and-polylang'),
'detected_fields' => __('Detected Fields', 'ai-translator-for-elementor-and-polylang'),
'copy_all' => __('Copy all fields', 'ai-translator-for-elementor-and-polylang'),
'no_fields_found' => __('No fields found', 'ai-translator-for-elementor-and-polylang'),
'fields_copied' => __('Fields copied to clipboard', 'ai-translator-for-elementor-and-polylang'),
'click_to_check_compatibility' => __('Click the button to check theme compatibility.', 'ai-translator-for-elementor-and-polylang'),
],
'is_pro' => (defined('AITRFOEL_VERSION_TYPE') && AITRFOEL_VERSION_TYPE === 'pro'),
]);
// Add inline script for immediate Elementor removal
wp_add_inline_script('aitrfoel-admin-script', '
(function() {
// Immediate Elementor removal - runs before DOM ready
function removeElementor() {
var selectors = [
"#e-admin-top-bar-root",
".e-admin-top-bar",
".e-admin-top-bar--active",
"[id*=\"e-admin-top-bar\"]",
"[class*=\"e-admin-top-bar\"]"
];
selectors.forEach(function(selector) {
var elements = document.querySelectorAll(selector);
elements.forEach(function(el) {
if (el) el.remove();
});
});
// Remove body classes
document.body.classList.remove("e-admin-top-bar--active");
document.body.className = document.body.className.replace(/e-admin-top-bar[^\s]*/g, "");
}
// Run immediately
removeElementor();
// Run when DOM is ready
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", removeElementor);
} else {
removeElementor();
}
// Continue watching for changes
if (window.MutationObserver) {
var observer = new MutationObserver(function(mutations) {
var shouldRemove = false;
mutations.forEach(function(mutation) {
if (mutation.type === "childList") {
mutation.addedNodes.forEach(function(node) {
if (node.nodeType === 1) {
if ((node.id && node.id.includes("e-admin-top-bar")) ||
(node.className && node.className.includes && node.className.includes("e-admin-top-bar"))) {
shouldRemove = true;
}
}
});
}
});
if (shouldRemove) removeElementor();
});
observer.observe(document.body, {
childList: true,
subtree: true
});
}
})();
', 'before');
}
/**
* Enqueue deactivation survey script and style on plugins.php page
*/
public function enqueue_deactivation_survey($hook) {
if ($hook !== 'plugins.php') {
return;
}
// Only load in the FREE version (Freemius handles PRO version surveys)
if (defined('AITRFOEL_VERSION_TYPE') && AITRFOEL_VERSION_TYPE !== 'free') {
return;
}
wp_enqueue_style(
'aitrfoel-deactivation-survey-style',
AITRFOEL_PLUGIN_URL . 'assets/css/deactivation-survey.css',
[],
AITRFOEL_VERSION
);
wp_enqueue_script(
'aitrfoel-deactivation-survey',
AITRFOEL_PLUGIN_URL . 'assets/js/deactivation-survey.js',
['jquery'],
AITRFOEL_VERSION,
true
);
wp_localize_script('aitrfoel-deactivation-survey', 'aitrfoelDeactivation', [
'siteUrl' => esc_url(home_url()),
]);
}
/**
* Enqueue inline translation scripts in Gutenberg and Elementor editor sessions
*/
public function enqueue_editor_inline_scripts() {
global $pagenow;
$is_editor_page = ($pagenow === 'post.php' || $pagenow === 'post-new.php' || (isset($_GET['action']) && $_GET['action'] === 'elementor'));
$is_hook_call = current_action() === 'elementor/editor/after_enqueue_scripts' || current_action() === 'enqueue_block_editor_assets';
if ( ! $is_editor_page && ! $is_hook_call ) {
return;
}
wp_enqueue_script(
'aitrfoel-editor-inline-translation',
AITRFOEL_PLUGIN_URL . 'assets/js/editor-inline-translation.js',
['jquery'],
AITRFOEL_VERSION,
true
);
$post_id = 0;
if (isset($_GET['post'])) {
$post_id = intval($_GET['post']);
} elseif (isset($_POST['post_ID'])) {
$post_id = intval($_POST['post_ID']);
}
$current_lang = 'en';
if ($post_id > 0 && class_exists('AITRFOEL_Multilingual_Manager')) {
$current_lang = AITRFOEL_Multilingual_Manager::get_instance()->get_post_language($post_id);
}
if (empty($current_lang) && class_exists('AITRFOEL_Multilingual_Manager')) {
$current_lang = AITRFOEL_Multilingual_Manager::get_instance()->get_default_language();
}
if (empty($current_lang)) {
$current_lang = 'en';
}
$target_lang_name = 'English';
if (class_exists('AITRFOEL_Language_Mapper')) {
$target_lang_name = AITRFOEL_Language_Mapper::get_language_name($current_lang);
}
// Pass REST API information to the script
wp_localize_script('aitrfoel-editor-inline-translation', 'aitrfoelEditorInline', [
'rest_url' => esc_url_raw(rest_url('meep-translator/v1/translate-string')),
'nonce' => wp_create_nonce('wp_rest'),
'current_lang' => $current_lang,
'current_lang_name' => $target_lang_name,
'strings' => [
'translate' => sprintf(__('Translate to %s', 'ai-translator-for-elementor-and-polylang'), $target_lang_name),
'translating' => __('Translating...', 'ai-translator-for-elementor-and-polylang'),
'success' => __('Translated successfully!', 'ai-translator-for-elementor-and-polylang'),
'failed' => __('Translation failed', 'ai-translator-for-elementor-and-polylang'),
]
]);
}
/**
* Plugin activation
*/
public function activate() {
// Create database tables if needed
$this->create_tables();
// Set default options
$this->set_default_options();
// Create cache directory
$this->create_cache_directory();
// Log activation
if (class_exists('AITRFOEL_Logger')) {
AITRFOEL_Logger::log('Plugin activated', 'info');
}
// Immediately register activation with remote server
if (class_exists('AITRFOEL_License_Manager_Server')) {
try {
$license_server = AITRFOEL_License_Manager_Server::get_instance();
$license_key = $license_server->get_license_key();
if (empty($license_key)) {
$license_server->initialize_user_in_remote_db('Free', null);
}
} catch (Exception $e) {
// Silent fail on activation hook if network is unreachable
}
}
// Clear rewrite rules
flush_rewrite_rules();
}
/**
* Plugin deactivation
*/
public function deactivate() {
// Clean up scheduled tasks
wp_clear_scheduled_hook('aitrfoel_cleanup_cache');
// Log deactivation
if (class_exists('AITRFOEL_Logger')) {
AITRFOEL_Logger::log('Plugin deactivated', 'info');
}
// Clear rewrite rules
flush_rewrite_rules();
}
/**
* Create database tables
*/
private function create_tables() {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
// Translation cache table
$table_name = $wpdb->prefix . 'aitrfoel_translation_cache';
$sql = "CREATE TABLE IF NOT EXISTS $table_name (
id bigint(20) NOT NULL AUTO_INCREMENT,
source_hash varchar(32) NOT NULL,
source_text longtext NOT NULL,
translated_text longtext NOT NULL,
source_lang varchar(10) NOT NULL,
target_lang varchar(10) NOT NULL,
model varchar(50) NOT NULL,
created_at datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY source_hash_langs (source_hash, source_lang, target_lang),
KEY target_lang (target_lang),
KEY created_at (created_at)
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql);
// Usage statistics table
$table_name = $wpdb->prefix . 'aitrfoel_usage_stats';
$sql = "CREATE TABLE IF NOT EXISTS $table_name (
id bigint(20) NOT NULL AUTO_INCREMENT,
user_id bigint(20) NOT NULL,
provider varchar(20) NOT NULL,
model varchar(50) NOT NULL,
input_tokens int DEFAULT 0,
output_tokens int DEFAULT 0,
word_count int DEFAULT 0,
page_count int DEFAULT 0,
estimated_cost decimal(10,6) DEFAULT 0,
created_at datetime DEFAULT CURRENT_TIMESTAMP,
month_year varchar(7) NOT NULL,
PRIMARY KEY (id),
KEY user_month (user_id, month_year),
KEY provider_model (provider, model),
KEY created_at (created_at)
) $charset_collate;";
dbDelta($sql);
// Translation history table
$table_name = $wpdb->prefix . 'aitrfoel_translation_history';
$sql = "CREATE TABLE IF NOT EXISTS $table_name (
id bigint(20) NOT NULL AUTO_INCREMENT,
original_post_id bigint(20) NOT NULL,
translated_post_id bigint(20) NOT NULL,
source_lang varchar(10) NOT NULL DEFAULT '',
target_lang varchar(10) NOT NULL,
content_type varchar(20) NOT NULL DEFAULT 'elementor',
word_count int NOT NULL DEFAULT 0,
status varchar(20) NOT NULL DEFAULT 'success',
duration decimal(8,2) DEFAULT 0,
user_id bigint(20) NOT NULL DEFAULT 0,
created_at datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY original_post (original_post_id),
KEY target_lang (target_lang),
KEY created_at (created_at),
KEY user_id (user_id)
) $charset_collate;";
dbDelta($sql);
// Glossary table
$table_name = $wpdb->prefix . 'aitrfoel_glossary';
$sql = "CREATE TABLE IF NOT EXISTS $table_name (
id bigint(20) NOT NULL AUTO_INCREMENT,
source_text varchar(255) NOT NULL,
target_text varchar(255) NOT NULL,
lang_code varchar(10) NOT NULL,
action varchar(20) NOT NULL DEFAULT 'replace',
created_at datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY lang_code (lang_code)
) $charset_collate;";
dbDelta($sql);
}
/**
* Set default plugin options
*/
private function set_default_options() {
// Provider selection - use the correct option name that settings page uses
add_option('aitrfoel_api_provider', 'openai'); // Default to OpenAI
// Legacy option for backwards compatibility
add_option('aitrfoel_translation_provider', 'openai'); // Default to OpenAI
// Model settings (used on MeepTech server)
add_option('aitrfoel_model', 'gpt-4o-mini');
add_option('aitrfoel_temperature', 0.3);
add_option('aitrfoel_max_tokens', 4000);
// Translation settings
add_option('aitrfoel_cache_enabled', true);
add_option('aitrfoel_cache_duration', 30); // days
add_option('aitrfoel_batch_size', 5);
add_option('aitrfoel_rate_limit', 10); // requests per minute
// UI settings
add_option('aitrfoel_preview_enabled', true);
add_option('aitrfoel_auto_save', false);
add_option('aitrfoel_debug_mode', false);
// License
add_option('aitrfoel_license_key', '');
add_option('aitrfoel_license_status', 'inactive');
// Gap-Filler defaults
add_option('aitrfoel_gap_enabled', true);
add_option('aitrfoel_gap_max_retry', 1);
add_option('aitrfoel_gap_lev_threshold', 3);
add_option('aitrfoel_gap_residue_patterns', '/\\b(Read more|Číst dál|Читать далее)\\b/ui');
add_option('aitrfoel_gap_enable_postprocessor', true);
}
/**
* Create cache directory in uploads folder
*/
private function create_cache_directory() {
// Use uploads directory instead of wp-content/cache
$upload_dir = wp_upload_dir();
$cache_dir = $upload_dir['basedir'] . '/ai-translator-cache';
if (!file_exists($cache_dir)) {
wp_mkdir_p($cache_dir);
// Add .htaccess for security
$htaccess = $cache_dir . '/.htaccess';
if (!file_exists($htaccess)) {
file_put_contents($htaccess, 'Deny from all');
}
// Add index.php for additional security
$index = $cache_dir . '/index.php';
if (!file_exists($index)) {
file_put_contents($index, 'admin_notice_multilingual_missing();
}
/**
* Admin notice - No multilingual plugin (Polylang or WPML)
*/
public function admin_notice_multilingual_missing() {
// Don't show this notice on our own plugin page (the onboarding screen handles it)
$screen = get_current_screen();
if ($screen && strpos($screen->id, 'ai-translator-for-elementor') !== false) {
return;
}
?>
prefix . 'aitrfoel_bulk_jobs';
$pending_job = $wpdb->get_row("
SELECT * FROM {$table_name}
WHERE status = 'pending'
ORDER BY created_at ASC
LIMIT 1
");
if (!$pending_job) {
return false;
}
// Decode job data
$job_data = json_decode($pending_job->job_data, true);
if (!$job_data) {
error_log("EAT Error: Could not decode job data for {$pending_job->job_id}");
return false;
}
// Extract parameters
$page_ids = $job_data['page_ids'] ?? [];
$target_languages = $job_data['target_languages'] ?? [];
$source_language = $job_data['source_language'] ?? 'auto';
// Note: Background processing was removed
// Jobs are now processed directly via AJAX handlers
// This method is kept for compatibility but does nothing
return true;
}
/**
* Render welcome popup (shown once after activation)
*/
public function render_welcome_popup() {
// Only show on our plugin pages
$screen = get_current_screen();
if (!$screen || strpos($screen->id, 'ai-translator-for-elementor-and-polylang') === false) {
return;
}
// Allow forcing welcome popup with URL parameter for testing: ?show_welcome=1
$force_show = isset($_GET['show_welcome']) && $_GET['show_welcome'] === '1';
// Legacy popup is superseded by the onboarding wizard. Keep it only as an
// explicit test surface for ?show_welcome=1.
if (!$force_show) {
return;
}
// Check if we should show the popup
$show_popup = get_option('aitrfoel_show_welcome_popup', 'no');
if ($show_popup !== 'yes' && !$force_show) {
return;
}
// URLs
$polylang_url = admin_url('admin.php?page=mlang');
$wpml_url = admin_url('admin.php?page=sitepress-multilingual-cms/menu/languages.php');
$polylang_install_url = admin_url('plugin-install.php?s=polylang&tab=search&type=term');
$docs_url = 'https://meeptech.com/products/ai-translator-for-elementor-polylang/documentation';
// Detect multilingual plugin status
$multilingual = class_exists('AITRFOEL_Multilingual_Manager') ? AITRFOEL_Multilingual_Manager::get_instance() : null;
$has_multilingual = $multilingual && $multilingual->has_multilingual_plugin();
$has_languages = $multilingual && method_exists($multilingual, 'has_languages_configured') && $multilingual->has_languages_configured();
$language_count = $multilingual && method_exists($multilingual, 'get_language_count') ? $multilingual->get_language_count() : 0;
$detected_plugin = $multilingual ? $multilingual->get_detected_plugin() : null;
$plugin_display_name = $multilingual && $has_multilingual ? $multilingual->get_plugin_display_name() : null;
?>
.aitrfoel-language-switcher {
font-family: inherit;
--aitrfoel-text-color: currentColor;
--aitrfoel-text-hover-color: currentColor;
--aitrfoel-active-bg: #0073aa;
--aitrfoel-active-color: #fff;
--aitrfoel-hover-bg: rgba(0,0,0,0.06);
--aitrfoel-border-color: rgba(0,0,0,0.15);
--aitrfoel-bg: transparent;
--aitrfoel-dropdown-bg: #fff;
--aitrfoel-dropdown-shadow: 0 4px 16px rgba(0,0,0,0.12);
}
.aitrfoel-switcher-list { list-style: none; padding: 0; margin: 0; display: flex; flex-wrap: wrap; gap: 4px; }
.aitrfoel-switcher-list .aitrfoel-switcher-item a { display: flex; align-items: center; padding: 6px 12px; text-decoration: none; color: var(--aitrfoel-text-color); border-radius: 6px; transition: background-color 0.2s; font-size: 14px; }
.aitrfoel-switcher-list .aitrfoel-switcher-item a:hover { background-color: var(--aitrfoel-hover-bg); }
.aitrfoel-switcher-list .aitrfoel-switcher-item.active a { background-color: var(--aitrfoel-active-bg); color: var(--aitrfoel-active-color); font-weight: 600; }
.aitrfoel-switcher-flags { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.aitrfoel-flag-link { display: inline-flex; align-items: center; padding: 4px; border-radius: 4px; transition: transform 0.15s; text-decoration: none; }
.aitrfoel-flag-link:hover { transform: scale(1.15); }
.aitrfoel-flag-link.active { box-shadow: 0 0 0 2px var(--aitrfoel-active-bg); }
.aitrfoel-flag-link img { display: block; border-radius: 2px; }
.aitrfoel-switcher-flags-text { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.aitrfoel-flag-text-link { display: inline-flex; align-items: center; padding: 4px 10px; text-decoration: none; color: var(--aitrfoel-text-color); border-radius: 6px; font-size: 13px; font-weight: 500; transition: background-color 0.2s; }
.aitrfoel-flag-text-link:hover { background-color: var(--aitrfoel-hover-bg); }
.aitrfoel-flag-text-link.active { background-color: var(--aitrfoel-active-bg); color: var(--aitrfoel-active-color); }
.aitrfoel-switcher-dropdown-wrapper { display: inline-block; position: relative; }
.aitrfoel-dropdown-trigger { display: inline-flex; align-items: center; gap: 4px; padding: 6px 12px; border: 1px solid var(--aitrfoel-border-color); border-radius: 6px; font-size: 14px; font-family: inherit; background: var(--aitrfoel-bg); color: var(--aitrfoel-text-color); cursor: pointer; min-width: 120px; }
.aitrfoel-dropdown-trigger:hover { border-color: var(--aitrfoel-active-bg); }
.aitrfoel-dropdown-arrow { margin-left: auto; transition: transform 0.2s; flex-shrink: 0; }
.aitrfoel-dropdown-trigger[aria-expanded="true"] .aitrfoel-dropdown-arrow { transform: rotate(180deg); }
.aitrfoel-dropdown-list { position: absolute; top: 100%; left: 0; z-index: 9999; min-width: 100%; margin: 4px 0 0; padding: 4px 0; list-style: none; background: var(--aitrfoel-dropdown-bg); border: 1px solid var(--aitrfoel-border-color); border-radius: 6px; box-shadow: var(--aitrfoel-dropdown-shadow); }
.aitrfoel-dropdown-item a { display: flex; align-items: center; padding: 6px 12px; text-decoration: none; color: var(--aitrfoel-text-color); font-size: 14px; white-space: nowrap; transition: background-color 0.15s; }
.aitrfoel-dropdown-item a:hover { background-color: var(--aitrfoel-hover-bg); }
.aitrfoel-dropdown-item.active a { font-weight: 600; color: var(--aitrfoel-active-bg); }
' . (!empty(trim($custom_css)) ? "\n/* Custom CSS */\n" . $custom_css : '') . '
';
}
ob_start();
echo $css;
if ($style === 'list') {
echo '';
foreach ($switcher_data as $lang) {
$active_class = !empty($lang['is_current']) ? ' active' : '';
$flag_html = $show_flags && class_exists('AITRFOEL_Language_Mapper') ? AITRFOEL_Language_Mapper::get_flag_html($lang['code'], 20, 'margin-right: 6px;') : '';
$name_html = $show_names ? esc_html($lang['native_name'] ?? $lang['name'] ?? strtoupper($lang['code'])) : esc_html(strtoupper($lang['code']));
printf('- %s%s
', esc_attr($active_class), esc_url($lang['url'] ?? '#'), $flag_html, $name_html);
}
echo '
';
} elseif ($style === 'flags') {
echo '';
foreach ($switcher_data as $lang) {
$active_class = !empty($lang['is_current']) ? ' active' : '';
$flag_html = class_exists('AITRFOEL_Language_Mapper') ? AITRFOEL_Language_Mapper::get_flag_html($lang['code'], 24, '') : esc_html(strtoupper($lang['code']));
printf('
%s', esc_url($lang['url'] ?? '#'), esc_attr($active_class), esc_attr($lang['name'] ?? ''), $flag_html);
}
echo '
';
} elseif ($style === 'flags-text') {
echo '';
foreach ($switcher_data as $lang) {
$active_class = !empty($lang['is_current']) ? ' active' : '';
$flag_html = class_exists('AITRFOEL_Language_Mapper') ? AITRFOEL_Language_Mapper::get_flag_html($lang['code'], 20, 'margin-right: 4px;') : '';
printf('
%s%s', esc_url($lang['url'] ?? '#'), esc_attr($active_class), esc_attr($lang['name'] ?? ''), $flag_html, esc_html(strtoupper($lang['code'])));
}
echo '
';
} else {
// Custom dropdown with flag images
$current_lang = null;
foreach ($switcher_data as $lang) {
if (!empty($lang['is_current'])) { $current_lang = $lang; break; }
}
if (!$current_lang) $current_lang = $switcher_data[0];
$current_flag = $show_flags && class_exists('AITRFOEL_Language_Mapper') ? AITRFOEL_Language_Mapper::get_flag_html($current_lang['code'], 20, 'margin-right: 6px;') : '';
$current_label = $show_names ? esc_html($current_lang['native_name'] ?? $current_lang['name'] ?? strtoupper($current_lang['code'])) : esc_html(strtoupper($current_lang['code']));
echo '';
echo '
';
echo '
';
foreach ($switcher_data as $lang) {
$active_class = !empty($lang['is_current']) ? ' active' : '';
$flag_html = $show_flags && class_exists('AITRFOEL_Language_Mapper') ? AITRFOEL_Language_Mapper::get_flag_html($lang['code'], 20, 'margin-right: 6px;') : '';
$label = $show_names ? esc_html($lang['native_name'] ?? $lang['name'] ?? strtoupper($lang['code'])) : esc_html(strtoupper($lang['code']));
printf('- %s%s
', esc_attr($active_class), esc_url($lang['url'] ?? '#'), $flag_html, $label);
}
echo '
';
echo '
';
if (class_exists('AITRFOEL_Builtin_Integration')) {
AITRFOEL_Builtin_Integration::get_instance()->register_switcher_javascript();
}
}
return ob_get_clean();
}