Plugin Directory

Changeset 3408429


Ignore:
Timestamp:
12/02/2025 08:00:30 PM (6 weeks ago)
Author:
optiuser
Message:
  • Enhancement: Added French language translations for improved internationalization
  • Fix: Resolved sendPageView function issues for accurate page tracking
  • Fix: Corrected Returning Visitors calculation and display
  • Fix: Fixed Logged In Visitors detection and counting
  • Feature: Display username for logged-in visitors in Top Engaged Users widget
  • Enhancement: Extended device type support for all device categories (desktop, mobile, tablet, PC)
  • Code Quality: WordPress coding standards compliance improvements
  • Code Quality: Added debug logging controls via settings page
  • Security: Fixed nonce verification warnings
  • Security: Enhanced prepared SQL statements with proper phpcs annotations
Location:
opti-behavior
Files:
96 added
2 deleted
22 edited

Legend:

Unmodified
Added
Removed
  • opti-behavior/trunk/Opti-Behavior.php

    r3406294 r3408429  
    44 * Plugin URI:  https://optiuser.com/
    55 * Description: Transform your WordPress site with powerful analytics! Track user behavior with beautiful heatmaps and real-time insights. Boost conversions and optimize user experience with data-driven decisions.
    6  * Version:     1.0.6
     6 * Version:     1.0.7
    77 * Author:      OptiUser
    88 * Author URI:  https://optiuser.com/
     
    8080
    8181// Define plugin constants.
    82 define( 'OPTI_BEHAVIOR_HEATMAP_VERSION', '1.0.4.10' );
     82define( 'OPTI_BEHAVIOR_HEATMAP_VERSION', '1.0.7' );
    8383define( 'OPTI_BEHAVIOR_HEATMAP_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
    8484define( 'OPTI_BEHAVIOR_HEATMAP_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
     
    160160    'override_load_textdomain',
    161161    function( $override, $domain, $mofile ) {
    162         // Only apply to opti-behavior text domain
    163         if ( $domain !== 'opti-behavior' ) {
     162        // Only apply to opti-behavior and opti-behavior-pro text domains
     163        if ( $domain !== 'opti-behavior' && $domain !== 'opti-behavior-pro' ) {
    164164            return $override;
    165165        }
     
    178178            'opti-behavior-analytics',
    179179            'opti-behavior-heatmaps',
     180            'opti-behavior-heatmap-detail',
    180181            'opti-behavior-settings',
    181182            'opti-behavior-ai-insights',
     
    187188        }
    188189
    189         if ( ! in_array( $current_page, $opti_behavior_pages, true ) ) {
     190        // Check if we're doing an AJAX request for Opti-Behavior
     191        $is_opti_behavior_ajax = false;
     192        if ( wp_doing_ajax() ) {
     193            // phpcs:disable WordPress.Security.NonceVerification.Missing -- POST parameter used for action identification only (read-only operation, nonce verified in actual AJAX handlers)
     194            $ajax_action = isset( $_POST['action'] ) ? sanitize_text_field( wp_unslash( $_POST['action'] ) ) : '';
     195            // phpcs:enable WordPress.Security.NonceVerification.Missing
     196            if ( strpos( $ajax_action, 'opti_behavior' ) === 0 || strpos( $ajax_action, 'optibehavior' ) === 0 ) {
     197                $is_opti_behavior_ajax = true;
     198            }
     199        }
     200
     201        if ( ! in_array( $current_page, $opti_behavior_pages, true ) && ! $is_opti_behavior_ajax ) {
    190202            return $override; // Return original for non-plugin pages
    191203        }
     
    214226
    215227        // If a non-English language is selected, load the custom .mo file
    216         $custom_mofile = dirname( __FILE__ ) . '/languages/opti-behavior-' . $selected_language . '.mo';
     228        // Determine the plugin directory based on the domain
     229        if ( $domain === 'opti-behavior-pro' ) {
     230            $plugin_dir = dirname( __FILE__ ) . '/../opti-behavior-pro';
     231        } else {
     232            $plugin_dir = dirname( __FILE__ );
     233        }
     234
     235        $custom_mofile = $plugin_dir . '/languages/' . $domain . '-' . $selected_language . '.mo';
    217236
    218237        // Get debug manager if available
     
    220239            $core = Opti_Behavior_Heatmap_Core::get_instance();
    221240            $debug_manager = $core->get_debug_manager();
    222             $debug_manager->log( 'Override load textdomain. Original: ' . $mofile . ', Custom: ' . $custom_mofile . ' (exists: ' . ( file_exists( $custom_mofile ) ? 'yes' : 'no' ) . ')', 'debug', 'i18n' );
     241            $debug_manager->log( 'Override load textdomain. Domain: ' . $domain . ', Original: ' . $mofile . ', Custom: ' . $custom_mofile . ' (exists: ' . ( file_exists( $custom_mofile ) ? 'yes' : 'no' ) . ')', 'debug', 'i18n' );
    223242        }
    224243
     
    236255                    $core = Opti_Behavior_Heatmap_Core::get_instance();
    237256                    $debug_manager = $core->get_debug_manager();
    238                     $debug_manager->log( 'Successfully loaded custom .mo file with ' . count( $mo->entries ) . ' translations', 'info', 'i18n' );
     257                    $debug_manager->log( 'Successfully loaded custom .mo file for domain "' . $domain . '" with ' . count( $mo->entries ) . ' translations', 'info', 'i18n' );
    239258                }
    240259
    241260                return true; // Tell WordPress we handled the loading
     261            } else {
     262                // Log failure
     263                if ( class_exists( 'Opti_Behavior_Heatmap_Core' ) ) {
     264                    $core = Opti_Behavior_Heatmap_Core::get_instance();
     265                    $debug_manager = $core->get_debug_manager();
     266                    $debug_manager->log( 'Failed to import .mo file for domain "' . $domain . '": ' . $custom_mofile, 'error', 'i18n' );
     267                }
    242268            }
    243269        }
  • opti-behavior/trunk/admin/class-opti-behavior-heatmap-ajax-handler.php

    r3401441 r3408429  
    8181        add_action( 'wp_ajax_optibehavior_top_users', array( $this, 'ajax_top_users' ) );
    8282        add_action( 'wp_ajax_optibehavior_backfill_pageview_times', array( $this, 'ajax_backfill_pageview_times' ) );
     83
     84        // Heartbeat handler for session duration and scroll depth updates
     85        add_action( 'wp_ajax_opti_behavior_heatmap_heartbeat', array( $this, 'ajax_heartbeat' ) );
     86        add_action( 'wp_ajax_nopriv_opti_behavior_heatmap_heartbeat', array( $this, 'ajax_heartbeat' ) );
    8387
    8488        // Support form email handler
     
    475479
    476480        // Create new session with referrer and UTM data
     481        // Capture WordPress user ID if user is logged in
     482        $user_id = is_user_logged_in() ? get_current_user_id() : null;
     483
    477484        $session_data = array(
    478485            'id'           => $session_id,
    479486            'visitor_id'   => $visitor_id,
     487            'user_id'      => $user_id,
    480488            'start_time'   => current_time( 'mysql' ),
    481489            'end_time'     => current_time( 'mysql' ),
     
    488496            'utm_term'     => ! empty( $utm_term ) ? $utm_term : null,
    489497            'utm_content'  => ! empty( $utm_content ) ? $utm_content : null,
     498            'is_bounce'    => 1,
    490499        );
    491500
     
    493502            "{$wpdb->prefix}optibehavior_sessions",
    494503            $session_data,
    495             array( '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s' )
     504            array( '%s', '%s', '%d', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%d' )
    496505        );
    497506
     
    645654        }
    646655
    647         // Create new pageview with default scroll depth of 50%
    648         // (This simulates user scrolling halfway down the page)
     656        // Create new pageview with scroll depth of 0 (will be updated by JavaScript tracking)
    649657        $wpdb->insert(
    650658            "{$wpdb->prefix}optibehavior_pageviews",
     
    656664                'title'       => $title,
    657665                'view_time'   => current_time( 'mysql' ),
    658                 'scroll_depth' => 50,
     666                'scroll_depth' => 0,
    659667            ),
    660668            array( '%s', '%s', '%d', '%s', '%s', '%s', '%d' )
     
    927935
    928936        // Build date condition directly in SQL based on whitelisted period
    929         $allowed_periods = array( 'today', 'yesterday', 'last7days', 'last30days', 'thismonth' );
     937        $allowed_periods = array( 'today', 'yesterday', 'last7days', 'last30days', 'thismonth', 'custom' );
    930938        if ( ! in_array( $period, $allowed_periods, true ) ) {
     939            $period = 'last7days';
     940        }
     941
     942        // Validate custom dates if period is custom
     943        if ( $period === 'custom' && ( empty( $start_date ) || empty( $end_date ) ) ) {
    931944            $period = 'last7days';
    932945        }
     
    939952            v.device_type,
    940953            v.last_visit,
     954            MAX(s.user_id) as user_id,
    941955            COUNT(DISTINCT s.id) as session_count,
    942956            COALESCE(SUM(s.duration), 0) as total_duration,
     
    961975                $sql .= "s.start_time >= DATE_FORMAT(NOW(), '%Y-%m-01')";
    962976                break;
     977            case 'custom':
     978                // Use prepared statement for custom dates
     979                $sql .= $wpdb->prepare( 's.start_time >= %s AND s.start_time <= DATE_ADD(%s, INTERVAL 1 DAY)', $start_date, $end_date );
     980                break;
    963981            case 'last7days':
    964982            default:
     
    967985        }
    968986
     987        // Add visitor date filter based on same period
     988        $visitor_date_filter = '';
     989        switch ( $period ) {
     990            case 'today':
     991                $visitor_date_filter = ' AND v.last_visit >= CURDATE()';
     992                break;
     993            case 'yesterday':
     994                $visitor_date_filter = ' AND v.last_visit >= DATE_SUB(CURDATE(), INTERVAL 1 DAY) AND v.last_visit < CURDATE()';
     995                break;
     996            case 'last30days':
     997                $visitor_date_filter = ' AND v.last_visit >= DATE_SUB(NOW(), INTERVAL 30 DAY)';
     998                break;
     999            case 'thismonth':
     1000                $visitor_date_filter = " AND v.last_visit >= DATE_FORMAT(NOW(), '%Y-%m-01')";
     1001                break;
     1002            case 'custom':
     1003                $visitor_date_filter = $wpdb->prepare( ' AND v.last_visit >= %s AND v.last_visit <= DATE_ADD(%s, INTERVAL 1 DAY)', $start_date, $end_date );
     1004                break;
     1005            case 'last7days':
     1006            default:
     1007                $visitor_date_filter = ' AND v.last_visit >= DATE_SUB(NOW(), INTERVAL 7 DAY)';
     1008                break;
     1009        }
     1010
     1011
    9691012        $sql .= "
    9701013        LEFT JOIN {$wpdb->prefix}optibehavior_pageviews pv ON s.id = pv.session_id
    971         WHERE v.last_visit >= DATE_SUB(NOW(), INTERVAL 30 DAY)
     1014    WHERE 1=1";
     1015        $sql .= $visitor_date_filter;
     1016        $sql .= "
    9721017        GROUP BY v.id, v.country_name, v.country, v.device_type, v.last_visit
    9731018        HAVING session_count > 0
    9741019        ORDER BY session_count DESC, total_duration DESC
    975         LIMIT 10";
    976 
    977         // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- SQL built from whitelisted period values only, no user input interpolated
     1020        LIMIT 100";
     1021
     1022        // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter -- SQL built from whitelisted period values only, custom dates use $wpdb->prepare(), no user input interpolated
    9781023        $results = $wpdb->get_results( $sql );
     1024
     1025        // Debug: Check sessions table for user_id values
     1026        $debug_manager = $this->core->get_debug_manager();
     1027        if ( $debug_manager->is_php_debug_enabled() ) {
     1028            $debug_sessions = $wpdb->get_results( "SELECT visitor_id, user_id, start_time FROM {$wpdb->prefix}optibehavior_sessions WHERE visitor_id LIKE 'visitor_1764698224486%' ORDER BY start_time DESC LIMIT 3" );
     1029            // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log, WordPress.PHP.DevelopmentFunctions.error_log_print_r -- Debug logging controlled by settings
     1030            error_log( 'Sessions for hamdo visitor: ' . print_r( $debug_sessions, true ) );
     1031
     1032            // Debug: Log the SQL query and first result
     1033            // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Debug logging controlled by settings
     1034            error_log( 'Top Users SQL: ' . $sql );
     1035            if ( ! empty( $results ) ) {
     1036                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Debug logging controlled by settings
     1037                error_log( 'First result username: ' . ( isset( $results[0]->username ) ? $results[0]->username : 'NULL' ) );
     1038                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Debug logging controlled by settings
     1039                error_log( 'First result visitor_id: ' . $results[0]->visitor_id );
     1040            }
     1041        }
    9791042
    9801043        // Format the results for frontend consumption with correct field names
     
    9941057            }
    9951058
    996             // Create visitor short ID for display
    997             $visitor_short = substr( $row->visitor_id, 0, 8 ) . '...';
     1059            // Create visitor display - show username for logged-in users, visitor ID otherwise
     1060            // Get username for logged-in users
     1061        $username = null;
     1062        if ( ! empty( $row->user_id ) && $row->user_id > 0 ) {
     1063            $user = get_user_by( 'id', $row->user_id );
     1064            if ( $user ) {
     1065                $username = $user->display_name;
     1066            }
     1067        }
     1068        $visitor_short = ! empty( $username ) ? $username : substr( $row->visitor_id, 0, 8 ) . '...';
    9981069
    9991070            $formatted_results[] = array(
     
    10011072                'visitor_id'        => $row->visitor_id,
    10021073                'visitor_short'     => $visitor_short,
     1074                'display_name'      => $username,
     1075                'user_id'           => ! empty( $row->user_id ) ? intval( $row->user_id ) : null,
     1076                'profile_url'       => ! empty( $username ) ? admin_url( 'user-edit.php?user_id=' . $row->user_id ) : '',
    10031077                'country'           => ( $row->country_name ?: ( ( $row->country && strtoupper($row->country) !== 'UN' ) ? $this->get_country_name( strtoupper($row->country) ) : 'Unknown' ) ),
    10041078                'country_name'      => ( $row->country_name ?: ( ( $row->country && strtoupper($row->country) !== 'UN' ) ? $this->get_country_name( strtoupper($row->country) ) : 'Unknown' ) ),
     
    10211095        }
    10221096
     1097        if ( $debug_manager->is_php_debug_enabled() ) {
     1098            // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Debug logging controlled by settings
     1099            error_log( 'Formatted results count: ' . count( $formatted_results ) );
     1100            if ( ! empty( $formatted_results ) ) {
     1101                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Debug logging controlled by settings
     1102                error_log( 'First formatted result visitor_short: ' . $formatted_results[0]['visitor_short'] );
     1103                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Debug logging controlled by settings
     1104                error_log( 'First formatted result display_name: ' . ( $formatted_results[0]['display_name'] ?? 'NULL' ) );
     1105                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Debug logging controlled by settings
     1106                error_log( 'First formatted result user_id: ' . ( $formatted_results[0]['user_id'] ?? 'NULL' ) );
     1107            }
     1108        }
    10231109        wp_send_json_success( array( 'items' => $formatted_results ) );
    10241110    }
     
    15701656            'browser'      => isset( $data['device']['browser'] ) ? $data['device']['browser'] : 'unknown',
    15711657            'os'           => isset( $data['device']['os'] ) ? $data['device']['os'] : 'unknown',
     1658            'screen_width' => isset( $data['screen_width'] ) ? intval( $data['screen_width'] ) : null,
     1659            'screen_height' => isset( $data['screen_height'] ) ? intval( $data['screen_height'] ) : null,
    15721660            'country'      => $country,
    15731661            'country_name' => $country_name,
     
    15841672
    15851673        if ( $existing_visitor ) {
    1586             // Update existing visitor with location data if available
     1674            // Update existing visitor with device and location data
    15871675            $update_data = array(
    1588                 'last_visit'   => current_time( 'mysql' ),
    1589                 'visit_count'  => $existing_visitor->visit_count + 1,
     1676                'last_visit'    => current_time( 'mysql' ),
     1677                'visit_count'   => $existing_visitor->visit_count + 1,
     1678                'device_type'   => isset( $data['device']['deviceType'] ) ? $data['device']['deviceType'] : 'unknown',
     1679                'browser'       => isset( $data['device']['browser'] ) ? $data['device']['browser'] : 'unknown',
     1680                'os'            => isset( $data['device']['os'] ) ? $data['device']['os'] : 'unknown',
     1681                'screen_width'  => isset( $data['screen_width'] ) ? intval( $data['screen_width'] ) : null,
     1682                'screen_height' => isset( $data['screen_height'] ) ? intval( $data['screen_height'] ) : null,
    15901683            );
    1591             $update_format = array( '%s', '%d' );
     1684            $update_format = array( '%s', '%d', '%s', '%s', '%s', '%d', '%d' );
    15921685
    15931686            // Add location data to update if available
     
    16131706                "{$wpdb->prefix}optibehavior_visitors",
    16141707                $visitor_data,
    1615                 array( '%s', '%s', '%s', '%d', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s' )
     1708                array( '%s', '%s', '%s', '%d', '%s', '%s', '%s', '%d', '%d', '%s', '%s', '%s', '%s', '%s' )
    16161709            );
    16171710        }
     
    16411734        } else {
    16421735            // Insert new session
     1736            // Capture WordPress user ID if user is logged in
     1737            $user_id = is_user_logged_in() ? get_current_user_id() : null;
     1738
    16431739            $session_data = array(
    16441740                'id'         => $session_id,
    16451741                'visitor_id' => $visitor_id,
     1742                'user_id'    => $user_id,
    16461743                'start_time' => current_time( 'mysql' ),
    16471744                'referrer'   => isset( $data['referrer'] ) ? $data['referrer'] : '',
    16481745                'entry_page' => $session_url,
    16491746                'ip'         => $this->get_client_ip(),
     1747                'is_bounce'  => 1,
    16501748            );
    16511749
     
    16531751                "{$wpdb->prefix}optibehavior_sessions",
    16541752                $session_data,
    1655                 array( '%s', '%s', '%s', '%s', '%s', '%s' )
     1753                array( '%s', '%s', '%d', '%s', '%s', '%s', '%s', '%d' )
    16561754            );
    16571755            $debug_manager->log( 'New session created: ' . $session_id, 'debug', 'ajax' );
     
    17311829            // Handle click events for heatmaps
    17321830            if ( $event['type'] === 'click' && isset( $event['data']['x'], $event['data']['y'] ) ) {
     1831                // A click indicates engagement - session is not a bounce
     1832                $wpdb->update(
     1833                    "{$wpdb->prefix}optibehavior_sessions",
     1834                    array( 'is_bounce' => 0 ),
     1835                    array( 'id' => $session_id ),
     1836                    array( '%d' ),
     1837                    array( '%s' )
     1838                );
     1839                $debug_manager->log( 'Bounce status updated to 0 due to click engagement for session: ' . $session_id, 'debug', 'ajax' );
     1840
    17331841                $current_url = isset( $event['data']['url'] ) ? $event['data']['url'] : ( isset( $data['url'] ) ? $data['url'] : '' );
    17341842                if ( empty( $current_url ) ) {
     
    18431951                    }
    18441952
     1953                    // If user scrolled more than 25%, consider it engagement - not a bounce
     1954                    if ( $scroll_depth > 25 ) {
     1955                        $wpdb->update(
     1956                            "{$wpdb->prefix}optibehavior_sessions",
     1957                            array( 'is_bounce' => 0 ),
     1958                            array( 'id' => $session_id ),
     1959                            array( '%d' ),
     1960                            array( '%s' )
     1961                        );
     1962                        $debug_manager->log( 'Bounce status updated to 0 due to scroll engagement (' . intval( $scroll_depth ) . '%) for session: ' . $session_id, 'debug', 'ajax' );
     1963                    }
     1964
    18451965                    // Update the pageview record with the latest scroll depth
    18461966                    $wpdb->update(
     
    18962016                ) );
    18972017
    1898                 // It's a bounce if only one page was viewed
    1899                 if ( $pageview_count <= 1 ) {
     2018                // It's a bounce if only one page was viewed AND no prior engagement was detected
     2019                // Don't override is_bounce = 0 that was set by scroll/click handlers
     2020                // First, check current bounce status to preserve engagement flags
     2021                $current_bounce = $wpdb->get_var( $wpdb->prepare(
     2022                    "SELECT is_bounce FROM {$wpdb->prefix}optibehavior_sessions WHERE id = %s",
     2023                    $session_id
     2024                ) );
     2025
     2026                if ( $pageview_count <= 1 && intval( $current_bounce ) !== 0 ) {
    19002027                    $is_bounce = 1;
     2028                } else if ( intval( $current_bounce ) === 0 ) {
     2029                    // Preserve engagement flag - user scrolled > 25% or clicked
     2030                    $is_bounce = 0;
    19012031                }
    19022032
     
    22672397    }
    22682398
     2399    /**
     2400     * AJAX handler for heartbeat updates (session duration and scroll depth)
     2401     *
     2402     * @since 1.0.6.7
     2403     */
     2404    public function ajax_heartbeat() {
     2405        $debug_manager = $this->core->get_debug_manager();
     2406        $debug_manager->log( 'AJAX request received: opti_behavior_heatmap_heartbeat', 'debug', 'ajax' );
     2407
     2408        // Verify nonce for security
     2409        if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'opti_behavior_heatmap_nonce' ) ) {
     2410            $debug_manager->log( 'Heartbeat request failed: invalid nonce', 'warning', 'ajax' );
     2411            wp_send_json_error( __( 'Invalid nonce', 'opti-behavior' ) );
     2412        }
     2413
     2414        global $wpdb;
     2415
     2416        $session_id = isset( $_POST['session_id'] ) ? sanitize_text_field( wp_unslash( $_POST['session_id'] ) ) : '';
     2417        $visitor_id = isset( $_POST['visitor_id'] ) ? sanitize_text_field( wp_unslash( $_POST['visitor_id'] ) ) : '';
     2418        $url        = isset( $_POST['url'] ) ? esc_url_raw( wp_unslash( $_POST['url'] ) ) : '';
     2419        $title      = isset( $_POST['title'] ) ? sanitize_text_field( wp_unslash( $_POST['title'] ) ) : '';
     2420        $duration   = isset( $_POST['duration'] ) ? intval( $_POST['duration'] ) : 0;
     2421        $scroll_depth = isset( $_POST['scroll_depth'] ) ? intval( $_POST['scroll_depth'] ) : 0;
     2422
     2423        if ( empty( $session_id ) || empty( $visitor_id ) ) {
     2424            $debug_manager->log( 'Heartbeat request failed: missing session_id or visitor_id', 'warning', 'ajax' );
     2425            wp_send_json_error( __( 'Invalid session', 'opti-behavior' ) );
     2426        }
     2427
     2428        $debug_manager->log( "Heartbeat update - Session: $session_id, Duration: {$duration}s, Scroll: {$scroll_depth}%", 'info', 'ajax' );
     2429
     2430        // Ensure session exists before updating
     2431        $session_exists = $wpdb->get_var( $wpdb->prepare(
     2432            "SELECT id FROM {$wpdb->prefix}optibehavior_sessions WHERE id = %s",
     2433            $session_id
     2434        ) );
     2435
     2436        if ( ! $session_exists ) {
     2437            // Session doesn't exist yet, create it using ensure_session_exists
     2438            $this->ensure_session_exists( $session_id, $visitor_id, $url, '', null, null );
     2439        }
     2440
     2441        // Update session duration and end_time
     2442        $update_data = array(
     2443            'end_time' => current_time( 'mysql' ),
     2444            'duration' => $duration,
     2445        );
     2446        $format_array = array( '%s', '%d' );
     2447
     2448        // If user scrolled more than 25%, consider it engagement - not a bounce
     2449        if ( $scroll_depth > 25 ) {
     2450            $update_data['is_bounce'] = 0;
     2451            $format_array[] = '%d';
     2452            $debug_manager->log( 'Bounce status updated to 0 due to scroll engagement (' . intval( $scroll_depth ) . '%) in heartbeat for session: ' . $session_id, 'debug', 'ajax' );
     2453        }
     2454
     2455        $result = $wpdb->update(
     2456            "{$wpdb->prefix}optibehavior_sessions",
     2457            $update_data,
     2458            array( 'id' => $session_id ),
     2459            $format_array,
     2460            array( '%s' )
     2461        );
     2462
     2463        $debug_manager->log( "Session update result: " . ($result !== false ? 'success' : 'failed') . ", rows affected: " . ($result ? $result : '0'), 'info', 'ajax' );
     2464
     2465        // Update scroll depth in pageviews table
     2466        $analytics = $this->core->get_analytics();
     2467        $page_id   = $analytics->get_or_create_page_id( $url, $title );
     2468
     2469        // Ensure pageview exists before updating scroll depth
     2470        $this->ensure_pageview_exists( $session_id, $visitor_id, $page_id, $url, $title );
     2471
     2472        $scroll_result = $wpdb->query( $wpdb->prepare(
     2473            "UPDATE {$wpdb->prefix}optibehavior_pageviews
     2474             SET scroll_depth = GREATEST(scroll_depth, %d)
     2475             WHERE session_id = %s AND page_id = %d",
     2476            $scroll_depth,
     2477            $session_id,
     2478            $page_id
     2479        ) );
     2480
     2481        $debug_manager->log( "Scroll depth update result: rows affected: " . $wpdb->rows_affected, 'info', 'ajax' );
     2482        $debug_manager->log( "Heartbeat completed - Session duration: {$duration}s, Scroll depth: {$scroll_depth}%", 'info', 'ajax' );
     2483
     2484        wp_send_json_success( array(
     2485            'status' => 'updated',
     2486            'duration' => $duration,
     2487            'scroll_depth' => $scroll_depth,
     2488        ) );
     2489    }
     2490
    22692491}
  • opti-behavior/trunk/admin/class-opti-behavior-heatmap-dashboard.php

    r3406294 r3408429  
    266266            'noData'           => __( 'No data available', 'opti-behavior' ),
    267267            'error'            => __( 'Error loading data', 'opti-behavior' ),
     268            // Dashboard widget strings
     269            'newVisitors'      => __( 'New Visitors', 'opti-behavior' ),
     270            'returningVisitors' => __( 'Returning Visitors', 'opti-behavior' ),
     271            'totalVisitors'    => __( 'Total Visitors', 'opti-behavior' ),
     272            'loggedInVisitors' => __( 'Logged In Visitors', 'opti-behavior' ),
     273            'newRegistrations' => __( 'New Registrations', 'opti-behavior' ),
     274            'homePage'         => __( 'Home Page', 'opti-behavior' ),
     275            'posts'            => __( 'Posts', 'opti-behavior' ),
     276            // User intent
     277            'lowIntent'        => __( 'Low intent', 'opti-behavior' ),
     278            'mediumIntent'     => __( 'Medium intent', 'opti-behavior' ),
     279            'highIntent'       => __( 'High intent', 'opti-behavior' ),
     280            // Device types
     281            'desktop'          => __( 'Desktop', 'opti-behavior' ),
     282            'mobile'           => __( 'Mobile', 'opti-behavior' ),
     283            'tablet'           => __( 'Tablet', 'opti-behavior' ),
     284            'pc'               => __( 'PC', 'opti-behavior' ),
     285            // Operating systems
     286            'windows'          => __( 'Windows', 'opti-behavior' ),
     287            'chrome'           => __( 'Chrome', 'opti-behavior' ),
     288            // Empty state messages
     289            'noReferrerData'   => __( 'No referrer data available', 'opti-behavior' ),
     290            'noCountryData'    => __( 'No country data available', 'opti-behavior' ),
     291            'noScreenResolutionData' => __( 'No screen resolution data available', 'opti-behavior' ),
     292            'tryBroadeningDateRange' => __( 'Try broadening the date range or check back later.', 'opti-behavior' ),
     293        'noPageData'              => __( 'No page data available', 'opti-behavior' ),
     294        'noVisitorData'           => __( 'No visitor data available', 'opti-behavior' ),
     295        'noBrowserData'           => __( 'No browser data available', 'opti-behavior' ),
     296        'noDeviceData'            => __( 'No device data available', 'opti-behavior' ),
     297        'dataWillAppear'          => __( 'Data will appear as visitors interact with your site.', 'opti-behavior' ),
     298        'noDataYet'               => __( 'No data yet', 'opti-behavior' ),
    268299        );
    269300    }
     
    12861317
    12871318            // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Static query with no user input
    1288             $results = $wpdb->get_results($query);
     1319            $results = $wpdb->get_results( $query );
    12891320        }
    12901321
     
    26962727                        WHERE id IN ($placeholders)";
    26972728                    // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Dynamic placeholders are built safely with array_fill and implode, table prefix is from wpdb
    2698                     $pages_info = $wpdb->get_results( $wpdb->prepare(
    2699                         $query,
    2700                         ...$page_ids
    2701                     ), OBJECT_K );
     2729                    $pages_info = $wpdb->get_results(
     2730                        // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- $query variable with dynamic placeholders
     2731                        $wpdb->prepare(
     2732                            $query,
     2733                            ...$page_ids
     2734                        ),
     2735                        OBJECT_K
     2736                    );
    27022737                } else {
    27032738                    $pages_info = array();
  • opti-behavior/trunk/admin/class-opti-behavior-heatmap-post-metabox.php

    r3406294 r3408429  
    370370    public function ajax_post_analytics() {
    371371        if ( ! isset( $_POST['post_id'] ) ) {
    372             wp_send_json_error( array( 'message' => 'No post_id provided' ) );
     372            wp_send_json_error( array( 'message' => esc_html__( 'No post_id provided', 'opti-behavior' ) ) );
    373373        }
    374374
     
    377377
    378378        if ( ! current_user_can( 'edit_post', $post_id ) ) {
    379             wp_send_json_error( array( 'message' => 'Permission denied' ) );
     379            wp_send_json_error( array( 'message' => esc_html__( 'Permission denied', 'opti-behavior' ) ) );
     380        }
     381
     382        // Check if post is published - only show analytics for published posts
     383        $post_status = get_post_status( $post_id );
     384        if ( 'publish' !== $post_status ) {
     385            wp_send_json_error( array(
     386                'message' => esc_html__( 'Post not published yet', 'opti-behavior' ),
     387                'not_published' => true,
     388                'post_status' => $post_status
     389            ) );
    380390        }
    381391
     
    383393        $url = get_permalink( $post_id );
    384394        if ( ! $url ) {
    385             wp_send_json_error( array( 'message' => 'No permalink found' ) );
     395            wp_send_json_error( array( 'message' => esc_html__( 'No permalink found', 'opti-behavior' ) ) );
    386396        }
    387397
     
    762772    public function ajax_post_analytics_chart() {
    763773        if ( ! isset( $_POST['post_id'] ) ) {
    764             wp_send_json_error( array( 'message' => 'No post_id provided' ) );
     774            wp_send_json_error( array( 'message' => esc_html__( 'No post_id provided', 'opti-behavior' ) ) );
    765775        }
    766776
     
    769779
    770780        if ( ! current_user_can( 'edit_post', $post_id ) ) {
    771             wp_send_json_error( array( 'message' => 'Permission denied' ) );
     781            wp_send_json_error( array( 'message' => esc_html__( 'Permission denied', 'opti-behavior' ) ) );
     782        }
     783
     784        // Check if post is published
     785        $post_status = get_post_status( $post_id );
     786        if ( 'publish' !== $post_status ) {
     787            wp_send_json_success( array(
     788                'labels' => array(),
     789                'total_visitors' => array(),
     790                'desktop_visitors' => array(),
     791                'mobile_visitors' => array(),
     792                'desktop_events' => array(),
     793                'mobile_events' => array(),
     794                'group' => '%Y-%m-%d',
     795            ) );
    772796        }
    773797
     
    775799        $url = get_permalink( $post_id );
    776800        if ( ! $url ) {
    777             wp_send_json_error( array( 'message' => 'No permalink found' ) );
     801            wp_send_json_error( array( 'message' => esc_html__( 'No permalink found', 'opti-behavior' ) ) );
    778802        }
    779803
     
    11251149        }
    11261150
     1151        // Check if post is published
     1152        $post_status = get_post_status( $post_id );
     1153        if ( 'publish' !== $post_status ) {
     1154            wp_send_json_success( array() );
     1155        }
     1156
    11271157        global $wpdb;
    11281158        $url = get_permalink( $post_id );
     
    11991229        if ( ! current_user_can( 'edit_post', $post_id ) ) {
    12001230            wp_send_json_error();
     1231        }
     1232
     1233        // Check if post is published
     1234        $post_status = get_post_status( $post_id );
     1235        if ( 'publish' !== $post_status ) {
     1236            wp_send_json_success( array() );
    12011237        }
    12021238
     
    13511387        }
    13521388
     1389        // Check if post is published
     1390        $post_status = get_post_status( $post_id );
     1391        if ( 'publish' !== $post_status ) {
     1392            wp_send_json_success( array() );
     1393        }
     1394
    13531395        global $wpdb;
    13541396        $url = get_permalink( $post_id );
     
    14001442        if ( ! current_user_can( 'edit_post', $post_id ) ) {
    14011443            wp_send_json_error();
     1444        }
     1445
     1446        // Check if post is published
     1447        $post_status = get_post_status( $post_id );
     1448        if ( 'publish' !== $post_status ) {
     1449            wp_send_json_success( array() );
    14021450        }
    14031451
     
    14531501        }
    14541502
     1503        // Check if post is published
     1504        $post_status = get_post_status( $post_id );
     1505        if ( 'publish' !== $post_status ) {
     1506            wp_send_json_success( array() );
     1507        }
     1508
    14551509        global $wpdb;
    14561510        $url = get_permalink( $post_id );
  • opti-behavior/trunk/admin/recordings-upgrade-page.php

    r3401441 r3408429  
    6464
    6565                <a href="https://optiuser.com/" target="_blank" rel="noopener noreferrer" class="button button-primary button-hero">
    66                     <i data-lucide="download"></i>
    67                     <?php esc_html_e( 'Download PRO Version', 'opti-behavior' ); ?>
     66                    <i data-lucide="clock"></i>
     67                    <?php esc_html_e( 'Coming Soon', 'opti-behavior' ); ?>
    6868                </a>
    6969
  • opti-behavior/trunk/assets/css/dashboard_styles.css

    r3406294 r3408429  
    24382438    margin-bottom: 8px;
    24392439}
     2440
     2441/* Logged-in User Row Highlighting */
     2442.tu-logged-in-user {
     2443    background-color: rgba(16, 185, 129, 0.08) !important;
     2444    border-left: 3px solid #10b981 !important;
     2445}
     2446
     2447.tu-logged-in-user:hover {
     2448    background-color: rgba(16, 185, 129, 0.15) !important;
     2449}
     2450
     2451/* Make the row stand out more on hover */
     2452.tu-logged-in-user td {
     2453    position: relative;
     2454}
  • opti-behavior/trunk/assets/css/recordings-upgrade.css

    r3401441 r3408429  
    229229    display: inline-flex;
    230230    align-items: center;
    231     gap: 12px;
    232     font-size: 16px;
    233     padding: 14px 32px;
     231    justify-content: center;
     232    gap: 10px;
     233    font-size: 17px;
     234    padding: 16px 40px;
    234235    height: auto;
    235236    line-height: 1.5;
     
    239240    background: #2271b1;
    240241    border-color: #2271b1;
     242    text-decoration: none;
     243    box-shadow: 0 4px 12px rgba(34, 113, 177, 0.2);
    241244}
    242245
     
    251254    width: 22px;
    252255    height: 22px;
     256    flex-shrink: 0;
     257    margin-right: 4px;
    253258}
    254259
  • opti-behavior/trunk/assets/js/dashboard.js

    r3406294 r3408429  
    183183
    184184            // Prepare device types array with all three types
     185            const i18n = window.opti_behaviorData?.strings || {};
     186            const deviceLabels = {
     187              'Desktop': i18n.desktop || 'Desktop',
     188              'Mobile': i18n.mobile || 'Mobile',
     189              'Tablet': i18n.tablet || 'Tablet'
     190            };
    185191            const deviceTypes = ['Desktop', 'Mobile', 'Tablet'].map(name => {
    186192              const item = data.find(d => d.name === name);
    187193              return {
    188                 name: name,
     194                name: deviceLabels[name],
     195                rawName: name,
    189196                count: item ? (item.count || 0) : 0,
    190197                prevCount: item ? (item.prevCount || 0) : 0,
     
    239246
    240247            // Prepare chart data
    241             const deviceTypes = ['Desktop', 'Mobile', 'Tablet'];
    242             const chartData = deviceTypes.map(name => {
     248            const i18n = window.opti_behaviorData?.strings || {};
     249            const deviceTypeKeys = ['Desktop', 'Mobile', 'Tablet'];
     250            const deviceLabels = {
     251              'Desktop': i18n.desktop || 'Desktop',
     252              'Mobile': i18n.mobile || 'Mobile',
     253              'Tablet': i18n.tablet || 'Tablet'
     254            };
     255            const chartData = deviceTypeKeys.map(name => {
    243256              const item = deviceData.find(d => d.name === name);
    244257              return item ? (item.count || 0) : 0;
    245258            });
     259            const chartLabels = deviceTypeKeys.map(name => deviceLabels[name]);
    246260
    247261            // Check if we have any data
     
    255269              // Show empty state
    256270              if (window.optibehavior_showEmptyStateForCanvas) {
    257                 window.optibehavior_showEmptyStateForCanvas('device-types-pie', 'device', 'No device data available');
     271                const emptyMsg = i18n.noDeviceData || 'No device data available';
     272                window.optibehavior_showEmptyStateForCanvas('device-types-pie', 'device', emptyMsg);
    258273              }
    259274              return;
     
    275290            }
    276291
    277             const chartColors = deviceTypes.map(name => DEVICE_COLORS[name]);
     292            const chartColors = deviceTypeKeys.map(name => DEVICE_COLORS[name]);
    278293
    279294            // Destroy existing chart if it exists
     
    285300              type: 'doughnut',
    286301              data: {
    287                 labels: deviceTypes,
     302                labels: chartLabels,
    288303                datasets: [{
    289304                  data: chartData,
     
    14581473
    14591474                // Create rows for each intent type
     1475                const i18n = window.opti_behaviorData?.strings || {};
    14601476                const intentTypes = [
    1461                   { name: 'Low intent', count: lowSessions, percentage: lowPct, prevCount: prevLowSessions, color: '#e91e63' },
    1462                   { name: 'Medium intent', count: mediumSessions, percentage: mediumPct, prevCount: prevMediumSessions, color: '#7c3aed' },
    1463                   { name: 'High intent', count: highSessions, percentage: highPct, prevCount: prevHighSessions, color: '#f59e0b' }
     1477                  { name: i18n.lowIntent || 'Low intent', count: lowSessions, percentage: lowPct, prevCount: prevLowSessions, color: '#e91e63' },
     1478                  { name: i18n.mediumIntent || 'Medium intent', count: mediumSessions, percentage: mediumPct, prevCount: prevMediumSessions, color: '#7c3aed' },
     1479                  { name: i18n.highIntent || 'High intent', count: highSessions, percentage: highPct, prevCount: prevHighSessions, color: '#f59e0b' }
    14641480                ];
    14651481
     
    15011517                const data = [];
    15021518                const backgroundColors = ['#e91e63', '#7c3aed', '#f59e0b']; // low, medium, high
     1519                const i18n = window.opti_behaviorData?.strings || {};
    15031520
    15041521                if (lowSessions > 0) {
    1505                   labels.push('Low intent');
     1522                  labels.push(i18n.lowIntent || 'Low intent');
    15061523                  data.push(lowSessions);
    15071524                }
    15081525                if (mediumSessions > 0) {
    1509                   labels.push('Medium intent');
     1526                  labels.push(i18n.mediumIntent || 'Medium intent');
    15101527                  data.push(mediumSessions);
    15111528                }
    15121529                if (highSessions > 0) {
    1513                   labels.push('High intent');
     1530                  labels.push(i18n.highIntent || 'High intent');
    15141531                  data.push(highSessions);
    15151532                }
     
    16271644
    16281645            // Create rows for each visitor type
     1646            const i18n = window.opti_behaviorData?.strings || {};
    16291647            const visitorTypes = [
    1630               { name: 'New Visitors', count: newCount, percentage: newPct, prevCount: prevNewCount, color: '#10B981' },
    1631               { name: 'Returning Visitors', count: returningCount, percentage: returningPct, prevCount: prevReturningCount, color: '#6366F1' }
     1648              { name: i18n.newVisitors || 'New Visitors', count: newCount, percentage: newPct, prevCount: prevNewCount, color: '#10B981' },
     1649              { name: i18n.returningVisitors || 'Returning Visitors', count: returningCount, percentage: returningPct, prevCount: prevReturningCount, color: '#6366F1' }
    16321650            ];
    16331651
     
    17031721            if (existingChart) existingChart.destroy();
    17041722
     1723            // Get i18n strings
     1724            const i18n = window.opti_behaviorData?.strings || {};
     1725
    17051726            // Create Chart.js donut chart
    17061727            new Chart(ctx, {
    17071728              type: 'doughnut',
    17081729              data: {
    1709                 labels: ['New Visitors', 'Returning Visitors'],
     1730                labels: [i18n.newVisitors || 'New Visitors', i18n.returningVisitors || 'Returning Visitors'],
    17101731                datasets: [{
    17111732                  label: 'Visitors',
     
    17671788                const newRegPct = total > 0 ? Math.round((newRegistrations / total) * 100) : 0;
    17681789
     1790                const i18n = window.opti_behaviorData?.strings || {};
    17691791                const visitorTypes = [
    17701792                  {
    1771                     name: 'Total Visitors',
     1793                    name: i18n.totalVisitors || 'Total Visitors',
    17721794                    count: totalVisitors,
    17731795                    percentage: totalPct,
     
    17761798                  },
    17771799                  {
    1778                     name: 'Logged In Visitors',
     1800                    name: i18n.loggedInVisitors || 'Logged In Visitors',
    17791801                    count: loggedInVisitors,
    17801802                    percentage: loggedInPct,
     
    17831805                  },
    17841806                  {
    1785                     name: 'New Registrations',
     1807                    name: i18n.newRegistrations || 'New Registrations',
    17861808                    count: newRegistrations,
    17871809                    percentage: newRegPct,
     
    22252247                if (!topPages || !topPages.length) {
    22262248                    // Show empty state
     2249                    const i18n = window.opti_behaviorData?.strings || {};
    22272250                    container.innerHTML = `
    22282251                        <div class="optibehavior-empty-state is-visible">
    22292252                            <i data-lucide="file-text" style="width: 48px; height: 48px; color: #9ca3af; stroke-width: 1.5;"></i>
    2230                             <div class="optibehavior-empty-title">No page data available</div>
    2231                             <div class="optibehavior-empty-sub">Try broadening the date range or check back later.</div>
     2253                            <div class="optibehavior-empty-title">${i18n.noPageData || 'No page data available'}</div>
     2254                            <div class="optibehavior-empty-sub">${i18n.tryBroadeningDateRange || 'Try broadening the date range or check back later.'}</div>
    22322255                        </div>
    22332256                    `;
     
    23522375                // Show empty state
    23532376                if (window.optibehavior_showEmptyStateForCanvas) {
    2354                     window.optibehavior_showEmptyStateForCanvas('sessions-chart', 'sessions', 'No visitor data available');
     2377                    const i18n = window.opti_behaviorData?.strings || {}; window.optibehavior_showEmptyStateForCanvas('sessions-chart', 'sessions', i18n.noVisitorData || 'No visitor data available');
    23552378                }
    23562379                return;
     
    24302453                            emptyState.className = 'optibehavior-empty-state is-visible';
    24312454                            emptyState.innerHTML = `
     2455t                   const i18n = window.opti_behaviorData?.strings || {};
    24322456                                <i data-lucide="compass" style="width: 48px; height: 48px; color: #9ca3af; stroke-width: 1.5;"></i>
    2433                                 <div class="optibehavior-empty-title">No browser data available</div>
    2434                                 <div class="optibehavior-empty-sub">Try broadening the date range or check back later.</div>
     2457                                <div class="optibehavior-empty-title">${i18n.noBrowserData || "No browser data available"}</div>
     2458                                <div class="optibehavior-empty-sub">${i18n.tryBroadeningDateRange || "Try broadening the date range or check back later."}</div>
    24352459                            `;
    24362460                            widgetContent.appendChild(emptyState);
     
    35193543                var rows = resp.data.items.map(function(it,idx){
    35203544                    // Display username with profile link if available, otherwise show visitor ID
     3545                    var isLoggedIn = it.user_id && it.user_id > 0 && it.display_name;
    35213546                    var visitorDisplay = '';
    3522                     if (it.user_id && it.user_id > 0 && it.display_name) {
     3547                    if (isLoggedIn) {
    35233548                        // User is a logged-in WordPress user - show display name with profile link
    35243549                        visitorDisplay = '<a href="' + esc(it.profile_url||'') + '" target="_blank" rel="noopener" title="View user profile">' +
     
    35293554                    }
    35303555
    3531                     return '<tr>'+
     3556                    return '<tr'+ (isLoggedIn ? ' class="tu-logged-in-user"' : '') +'>'+
    35323557                        '<td class="tu-rank">'+ (idx+1) +'</td>'+
    35333558                        '<td class="tu-visitor">'+ visitorDisplay +'</td>'+
  • opti-behavior/trunk/assets/js/metabox-analytics.js

    r3406294 r3408429  
    7474            .then(function(resp) {
    7575            if (!resp || !resp.success || !resp.data) {
     76                // Check if it's because the post is not published
     77                if (resp && !resp.success && resp.data && resp.data.not_published) {
     78                    // Show a message indicating the post needs to be published first
     79                    if (qs('#optibehavior-chart-loading')) qs('#optibehavior-chart-loading').style.display = 'none';
     80                    var emptyEl = qs('#optibehavior-empty');
     81                    emptyEl.textContent = 'Analytics will be available after this post is published.';
     82                    emptyEl.style.display = 'block';
     83                    qs('#optibehavior-chart').style.display = 'none';
     84                    return;
     85                }
    7686                // Hide loading, show empty message
    7787                if (qs('#optibehavior-chart-loading')) qs('#optibehavior-chart-loading').style.display = 'none';
  • opti-behavior/trunk/assets/js/opti-behavior-heatmap-simple.js

    r3401441 r3408429  
    7070        this.lastSendTime = Date.now();
    7171        this.sendTimer = null;
     72        this.sessionStartTime = Date.now();
     73        this.maxScrollDepth = 0;
     74        this.heartbeatTimer = null;
     75        this.lastScrollUpdate = 0;
     76        // Store session and visitor IDs in memory for reliable access
     77        this.sessionId = null;
     78        this.visitorId = null;
    7279    }
    7380   
     
    8794        console.log('[Heatmap] start() called');
    8895
     96        // Send initial page view event immediately to create session
     97        this.sendPageView();
     98
    8999        // Attach click event listener
    90100        document.addEventListener('click', this.handleClick.bind(this), true);
     
    93103        document.addEventListener('click', this.handleOutboundClick.bind(this), true);
    94104
     105        // Attach scroll event listener for scroll depth tracking
     106        window.addEventListener('scroll', this.handleScroll.bind(this), { passive: true });
     107
     108        // Calculate initial scroll depth
     109        this.updateScrollDepth();
     110
    95111        // Start periodic send timer
    96112        this.startSendTimer();
    97113
     114        // Start heartbeat timer to update session duration and scroll depth
     115        this.startHeartbeat();
     116
    98117        // Send data before page unload
    99         window.addEventListener('beforeunload', this.sendData.bind(this, true));
    100 
    101         console.log('[Heatmap] Click tracking started');
     118        window.addEventListener('beforeunload', this.sendSessionEnd.bind(this));
     119
     120        console.log('[Heatmap] Tracking started (clicks, scroll, duration)');
    102121    };
    103122   
     
    268287    };
    269288
     289    HeatmapTracker.prototype.handleScroll = function() {
     290        this.updateScrollDepth();
     291    };
     292
     293    HeatmapTracker.prototype.updateScrollDepth = function() {
     294        const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
     295        const windowHeight = window.innerHeight;
     296        const documentHeight = Math.max(
     297            document.documentElement.scrollHeight,
     298            document.body.scrollHeight
     299        );
     300
     301        const currentScrollDepth = Math.min(100, Math.round(
     302            ((scrollTop + windowHeight) / documentHeight) * 100
     303        ));
     304
     305        if (currentScrollDepth > this.maxScrollDepth) {
     306            this.maxScrollDepth = currentScrollDepth;
     307            console.log('[Heatmap] Max scroll depth updated:', this.maxScrollDepth + '%');
     308        }
     309    };
     310
    270311    HeatmapTracker.prototype.startSendTimer = function() {
    271312        // Clear existing timer
     
    273314            clearInterval(this.sendTimer);
    274315        }
    275        
     316
    276317        // Start new timer
    277318        this.sendTimer = setInterval(function() {
     
    280321            }
    281322        }.bind(this), this.config.ajax_interval);
     323    };
     324
     325    HeatmapTracker.prototype.startHeartbeat = function() {
     326        // Clear existing timer
     327        if (this.heartbeatTimer) {
     328            clearInterval(this.heartbeatTimer);
     329        }
     330
     331        // Send heartbeat every 5 seconds to update session duration and scroll depth
     332        this.heartbeatTimer = setInterval(function() {
     333            this.sendHeartbeat();
     334        }.bind(this), 5000); // 5 seconds
     335    };
     336
     337    HeatmapTracker.prototype.sendHeartbeat = function() {
     338        const sessionDuration = Math.round((Date.now() - this.sessionStartTime) / 1000);
     339
     340        // Use stored IDs instead of reading cookies
     341        if (!this.sessionId || !this.visitorId) {
     342            console.log('[Heatmap] Heartbeat skipped - no session/visitor ID');
     343            return;
     344        }
     345
     346        const sessionId = this.sessionId;
     347        const visitorId = this.visitorId;
     348
     349        const formData = new FormData();
     350        formData.append('action', 'opti_behavior_heatmap_heartbeat');
     351        formData.append('nonce', this.config.nonce);
     352        formData.append('session_id', sessionId);
     353        formData.append('visitor_id', visitorId);
     354        formData.append('url', window.location.href);
     355        formData.append('title', document.title);
     356        formData.append('duration', sessionDuration);
     357        formData.append('scroll_depth', this.maxScrollDepth);
     358
     359        console.log('[Heatmap] Sending heartbeat - Duration:', sessionDuration + 's', 'Scroll:', this.maxScrollDepth + '%');
     360
     361        fetch(this.config.ajax_url, {
     362            method: 'POST',
     363            body: formData,
     364            mode: 'same-origin',
     365            cache: 'no-cache',
     366            keepalive: true
     367        }).then(function(response) {
     368            return response.json();
     369        }).then(function(data) {
     370            console.log('[Heatmap] Heartbeat response:', data);
     371        }).catch(function(error) {
     372            console.error('[Heatmap] Heartbeat error:', error);
     373        });
     374    };
     375
     376    HeatmapTracker.prototype.sendSessionEnd = function() {
     377        const sessionDuration = Math.round((Date.now() - this.sessionStartTime) / 1000);
     378
     379        // Use stored IDs instead of reading cookies
     380        if (!this.sessionId || !this.visitorId) {
     381            return;
     382        }
     383
     384        const sessionId = this.sessionId;
     385        const visitorId = this.visitorId;
     386
     387        const formData = new FormData();
     388        formData.append('action', 'opti_behavior_heatmap_heartbeat');
     389        formData.append('nonce', this.config.nonce);
     390        formData.append('session_id', sessionId);
     391        formData.append('visitor_id', visitorId);
     392        formData.append('url', window.location.href);
     393        formData.append('title', document.title);
     394        formData.append('duration', sessionDuration);
     395        formData.append('scroll_depth', this.maxScrollDepth);
     396
     397        console.log('[Heatmap] Session end - Duration:', sessionDuration + 's', 'Scroll:', this.maxScrollDepth + '%');
     398
     399        // Use sendBeacon for reliable delivery on page unload
     400        if (navigator.sendBeacon) {
     401            navigator.sendBeacon(this.config.ajax_url, formData);
     402        }
    282403    };
    283404   
     
    408529        }
    409530    };
    410    
     531
     532    /**
     533     * Send initial page view to create session immediately on page load
     534     * This ensures sessions are tracked even if user doesn't click anything
     535     */
     536    /**
     537     * Detect browser from user agent
     538     */
     539    HeatmapTracker.prototype.detectBrowser = function(userAgent) {
     540        if (!userAgent) return 'unknown';
     541
     542        if (userAgent.indexOf('Edg') > -1) return 'Edge';
     543        if (userAgent.indexOf('Chrome') > -1 && userAgent.indexOf('Edg') === -1) return 'Chrome';
     544        if (userAgent.indexOf('Safari') > -1 && userAgent.indexOf('Chrome') === -1) return 'Safari';
     545        if (userAgent.indexOf('Firefox') > -1) return 'Firefox';
     546        if (userAgent.indexOf('MSIE') > -1 || userAgent.indexOf('Trident') > -1) return 'Internet Explorer';
     547        if (userAgent.indexOf('Opera') > -1 || userAgent.indexOf('OPR') > -1) return 'Opera';
     548
     549        return 'unknown';
     550    };
     551
     552    /**
     553     * Detect operating system from user agent
     554     */
     555    HeatmapTracker.prototype.detectOS = function(userAgent) {
     556        if (!userAgent) return 'unknown';
     557
     558        if (userAgent.indexOf('Win') > -1) return 'Windows';
     559        if (userAgent.indexOf('Mac') > -1 && userAgent.indexOf('iPhone') === -1 && userAgent.indexOf('iPad') === -1) return 'macOS';
     560        if (userAgent.indexOf('Linux') > -1 && userAgent.indexOf('Android') === -1) return 'Linux';
     561        if (userAgent.indexOf('Android') > -1) return 'Android';
     562        if (userAgent.indexOf('iPhone') > -1 || userAgent.indexOf('iPad') > -1) return 'iOS';
     563
     564        return 'unknown';
     565    };
     566
     567    /**
     568     * Detect device type from user agent and screen width
     569     */
     570    HeatmapTracker.prototype.detectDeviceType = function(userAgent, screenWidth) {
     571        if (!userAgent) return 'unknown';
     572
     573        // Check for mobile indicators in user agent
     574        const mobileKeywords = ['Mobile', 'Android', 'iPhone', 'iPad', 'iPod', 'BlackBerry', 'IEMobile', 'Opera Mini'];
     575        const isMobileUA = mobileKeywords.some(function(keyword) {
     576            return userAgent.indexOf(keyword) > -1;
     577        });
     578
     579        // Check for tablet indicators
     580        const isTablet = userAgent.indexOf('iPad') > -1 ||
     581                        (userAgent.indexOf('Android') > -1 && userAgent.indexOf('Mobile') === -1);
     582
     583        if (isTablet) return 'tablet';
     584        if (isMobileUA) return 'mobile';
     585
     586        // Fallback to screen width detection
     587        if (screenWidth <= 768) return 'mobile';
     588        if (screenWidth <= 1024) return 'tablet';
     589
     590        return 'desktop';
     591    };
     592
     593    HeatmapTracker.prototype.sendPageView = function() {
     594        console.log('[Heatmap] sendPageView() called - creating session');
     595
     596        // Get session ID from cookie (short-lived, per session)
     597        let sessionId = this.getCookie('optibehavior_sid') || this.getCookie('opti_behavior_session_id');
     598
     599        // Get visitor ID from localStorage (persistent, survives bounce tracker protection)
     600        // Fallback to cookie for backwards compatibility
     601        let visitorId = null;
     602        try {
     603            visitorId = localStorage.getItem('optibehavior_vid') || this.getCookie('optibehavior_vid') || this.getCookie('opti_behavior_visitor_id');
     604        } catch (e) {
     605            // localStorage not available (privacy mode, old browsers), fallback to cookie
     606            visitorId = this.getCookie('optibehavior_vid') || this.getCookie('opti_behavior_visitor_id');
     607        }
     608
     609        const trackingConfig = window.optiBehaviorHeatmapConfig || this.config;
     610
     611        // Generate IDs if they don't exist
     612        if (!sessionId) {
     613            sessionId = 'session_' + Date.now() + '_' + Math.random().toString(36).substring(7);
     614            document.cookie = 'optibehavior_sid=' + sessionId + '; path=/; max-age=1800; SameSite=Lax'; // 30 min
     615        }
     616        if (!visitorId) {
     617            visitorId = 'visitor_' + Date.now() + '_' + Math.random().toString(36).substring(7);
     618            // Store visitor ID in localStorage (immune to bounce tracker protection)
     619            try {
     620                localStorage.setItem('optibehavior_vid', visitorId);
     621            } catch (e) {
     622                // localStorage not available, fallback to cookie
     623                document.cookie = 'optibehavior_vid=' + visitorId + '; path=/; max-age=31536000; SameSite=Lax'; // 1 year
     624            }
     625        }
     626
     627        // Store IDs in memory for reliable access throughout the session
     628        this.sessionId = sessionId;
     629        this.visitorId = visitorId;
     630
     631        console.log('[Heatmap] Page view - Session ID:', sessionId);
     632        console.log('[Heatmap] Page view - Visitor ID:', visitorId);
     633
     634        // Detect device information
     635        const userAgent = navigator.userAgent;
     636        const screenWidth = window.screen.width;
     637        const browser = this.detectBrowser(userAgent);
     638        const os = this.detectOS(userAgent);
     639        const deviceType = this.detectDeviceType(userAgent, screenWidth);
     640
     641        console.log('[Heatmap] Device info - Browser:', browser, 'OS:', os, 'Type:', deviceType);
     642
     643        // Build the data object matching server's expected format
     644        const data = {
     645            action: 'session_start',
     646            sessionId: sessionId,
     647            visitorId: visitorId,
     648            url: window.location.href,
     649            title: document.title,
     650            userAgent: userAgent,
     651            language: navigator.language || navigator.userLanguage || '',
     652            screen_width: screenWidth,
     653            screen_height: window.screen.height,
     654            referrer: document.referrer || trackingConfig.referrer || '',
     655            utm_source: trackingConfig.utm_source || '',
     656            utm_medium: trackingConfig.utm_medium || '',
     657            utm_campaign: trackingConfig.utm_campaign || '',
     658            utm_term: trackingConfig.utm_term || '',
     659            utm_content: trackingConfig.utm_content || '',
     660            entry_page: window.location.href,
     661            device: {
     662                browser: browser,
     663                os: os,
     664                deviceType: deviceType
     665            }
     666        };
     667
     668        // Prepare form data
     669        const formData = new FormData();
     670        formData.append('action', 'opti_behavior_heatmap_record');
     671        formData.append('nonce', this.config.nonce);
     672        formData.append('data', JSON.stringify(data));
     673
     674        console.log('[Heatmap] Sending initial session start:', data);
     675
     676        // Send page view data immediately
     677        fetch(this.config.ajax_url, {
     678            method: 'POST',
     679            body: formData,
     680            mode: 'same-origin',
     681            cache: 'no-cache'
     682        }).then(function(response) {
     683            console.log('[Heatmap] Session start response status:', response.status);
     684            return response.json();
     685        }).then(function(responseData) {
     686            console.log('[Heatmap] Session start response data:', responseData);
     687        }).catch(function(error) {
     688            console.error('[Heatmap] Session start error:', error);
     689        });
     690    };
     691
    411692})();
    412693
  • opti-behavior/trunk/includes/class-opti-behavior-heatmap-analytics.php

    r3406294 r3408429  
    865865
    866866        if ( empty( $path ) ) {
    867             return 'Home Page';
     867            return __( 'Home Page', 'opti-behavior' );
    868868        }
    869869
  • opti-behavior/trunk/includes/class-opti-behavior-heatmap-core.php

    r3399182 r3408429  
    370370        wp_register_script(
    371371            'opti-behavior',
    372             OPTI_BEHAVIOR_HEATMAP_ASSETS_URL . 'js/opti-behavior-heatmap.min.js',
     372            OPTI_BEHAVIOR_HEATMAP_ASSETS_URL . 'js/opti-behavior-heatmap-simple.js',
    373373            array( 'jquery', 'opti-behavior-debug' ),
    374374            OPTI_BEHAVIOR_HEATMAP_VERSION,
  • opti-behavior/trunk/includes/class-opti-behavior-heatmap-database.php

    r3406294 r3408429  
    414414            page_views   int(5)      UNSIGNED DEFAULT 0,
    415415            events_count int(10)     UNSIGNED DEFAULT 0,
    416             is_bounce    tinyint(1)           DEFAULT 0,
     416            is_bounce    tinyint(1)           DEFAULT 1,
    417417            referrer     text                 DEFAULT NULL,
    418418            utm_source   varchar(100)         DEFAULT NULL,
  • opti-behavior/trunk/includes/class-opti-behavior-heatmap-session.php

    r3406294 r3408429  
    156156                'entry_page'   => isset( $data['entry_page'] ) ? $data['entry_page'] : '',
    157157                'ip'           => $this->get_client_ip(),
     158                'is_bounce'    => 1,
    158159            );
    159160
     
    161162                "{$wpdb->prefix}optibehavior_sessions",
    162163                $session_data,
    163                 array( '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s' )
     164                array( '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%d' )
    164165            );
    165166        }
  • opti-behavior/trunk/includes/trait-opti-behavior-ai-insights-views.php

    r3401441 r3408429  
    201201                    <form id="opti-behavior-support-form" class="support-form">
    202202                        <div class="form-group">
    203                             <label for="support-name">Your Name</label>
     203                            <label for="support-name"><?php esc_html_e( 'Your Name', 'opti-behavior' ); ?></label>
    204204                            <input type="text" id="support-name" name="support_name" required class="form-control" />
    205205                        </div>
    206206
    207207                        <div class="form-group">
    208                             <label for="support-email">Your Email</label>
     208                            <label for="support-email"><?php esc_html_e( 'Your Email', 'opti-behavior' ); ?></label>
    209209                            <input type="email" id="support-email" name="support_email" value="<?php echo esc_attr( $user_email ); ?>" required class="form-control" />
    210                             <small class="form-hint">We will use this to respond to your message</small>
    211                         </div>
    212 
    213                         <div class="form-group">
    214                             <label for="support-subject">Subject</label>
     210                            <small class="form-hint"><?php esc_html_e( 'We will use this to respond to your message', 'opti-behavior' ); ?></small>
     211                        </div>
     212
     213                        <div class="form-group">
     214                            <label for="support-subject"><?php esc_html_e( 'Subject', 'opti-behavior' ); ?></label>
    215215                            <input type="text" id="support-subject" name="support_subject" required class="form-control" />
    216216                        </div>
    217217
    218218                        <div class="form-group">
    219                             <label for="support-message">Message</label>
     219                            <label for="support-message"><?php esc_html_e( 'Message', 'opti-behavior' ); ?></label>
    220220                            <textarea id="support-message" name="support_message" rows="5" required class="form-control"></textarea>
    221221                        </div>
     
    224224                            <button type="submit" class="feedback-btn primary">
    225225                                <span>&#128231;</span>
    226                                 Send Message
     226                                <?php esc_html_e( 'Send Message', 'opti-behavior' ); ?>
    227227                            </button>
    228228                        </div>
  • opti-behavior/trunk/includes/trait-opti-behavior-dashboard-views.php

    r3406294 r3408429  
    277277                                        <svg viewBox='0 0 24 24' fill='none' stroke='#9ca3af' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'><path d='M4 3h14a2 2 0 0 1 2 2v14l-4-4H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z'/></svg>
    278278                                        <div class="optibehavior-empty-title"><?php esc_html_e( 'No page views yet', 'opti-behavior' ); ?></div>
    279                                         <div class="optibehavior-empty-sub"><?php esc_html_e( 'Once visitors view pages, you\'ll see them here.', 'opti-behavior' ); ?></div>
     279                                        <div class="optibehavior-empty-sub"><?php esc_html_e( 'Once visitors view pages, you will see them here.', 'opti-behavior' ); ?></div>
    280280                                    </div>
    281281                                <?php else : ?>
  • opti-behavior/trunk/includes/trait-opti-behavior-settings-views.php

    r3399182 r3408429  
    696696                        <?php if ($settings_saved): ?>
    697697                            <div class="notice notice-success is-dismissible" style="margin: 20px 0;">
    698                                 <p><?php esc_html_e('Language settings saved successfully. The new language will be applied on the next page load.', 'opti-behavior'); ?></p>
    699                             </div>
     698                                <p><?php esc_html_e('Language settings saved successfully. Reloading page to apply new language...', 'opti-behavior'); ?></p>
     699                            </div>
     700                            <script>
     701                                // Auto-reload page after 1 second to apply new language
     702                                setTimeout(function() {
     703                                    window.location.reload();
     704                                }, 1000);
     705                            </script>
    700706                        <?php endif; ?>
    701707
  • opti-behavior/trunk/languages/opti-behavior-fr_FR.po

    r3399182 r3408429  
    11msgid ""
    22msgstr ""
    3 "Project-Id-Version: Opti-Behavior 1.0.0\n"
     3"Project-Id-Version: Opti-Behavior 1.0.6\n"
    44"Report-Msgid-Bugs-To: \n"
    5 "POT-Creation-Date: 2025-11-11 19:27+0100\n"
    6 "PO-Revision-Date: 2025-11-11 13:13:14+0000\n"
     5"POT-Creation-Date: 2025-11-30 19:27+0100\n"
     6"PO-Revision-Date: 2025-11-30 20:00:00+0000\n"
    77"Last-Translator: Opti-Behavior Team\n"
    88"Language-Team: French\n"
     
    178178#: includes/trait-opti-behavior-settings-views.php:1663
    179179msgid "Insufficient permissions"
    180 msgstr "Permissions insuffisantes"
     180msgstr "Permissions Insuffisantes"
    181181
    182182#: admin/class-opti-behavior-heatmap-ajax-handler.php:1339
     
    214214#: admin/class-opti-behavior-heatmap-dashboard.php:238
    215215msgid "Dashboard"
    216 msgstr "Tableau de bord"
     216msgstr "Tableau de Bord"
    217217
    218218#: admin/class-opti-behavior-heatmap-dashboard.php:159
     
    220220#: includes/trait-opti-behavior-heatmaps-views.php:35
    221221msgid "Heatmaps"
    222 msgstr "Cartes de chaleur"
     222msgstr "Cartes Chaleur"
    223223
    224224#: admin/class-opti-behavior-heatmap-dashboard.php:169
     
    228228#: includes/trait-opti-behavior-settings-views.php:158
    229229msgid "Session Recordings"
    230 msgstr "Enregistrements de session"
     230msgstr "Enregistrements Session"
    231231
    232232#: admin/class-opti-behavior-heatmap-dashboard.php:170
     
    257257#: includes/trait-opti-behavior-dashboard-views.php:99
    258258msgid "Page Views"
    259 msgstr "Pages vues"
     259msgstr "Pages Vues"
    260260
    261261#: admin/class-opti-behavior-heatmap-dashboard.php:242
    262262#: includes/trait-opti-behavior-dashboard-views.php:126
    263263msgid "Bounce Rate"
    264 msgstr "Taux de rebond"
     264msgstr "Taux de Rebond"
    265265
    266266#: admin/class-opti-behavior-heatmap-dashboard.php:243
    267267#: includes/trait-opti-behavior-dashboard-views.php:108
    268268msgid "Avg. Session Time"
    269 msgstr "Temps de session moyen"
     269msgstr "Durée Moy. Session"
    270270
    271271#: admin/class-opti-behavior-heatmap-dashboard.php:244
    272272#: includes/trait-opti-behavior-dashboard-views.php:264
    273273msgid "Top Pages"
    274 msgstr "Pages principales"
     274msgstr "Pages Principales"
    275275
    276276#: admin/class-opti-behavior-heatmap-dashboard.php:245
     
    287287#: includes/trait-opti-behavior-dashboard-views.php:170
    288288msgid "Real-time Visitors"
    289 msgstr "Visiteurs en temps réel"
     289msgstr "Visiteurs en Temps Réel"
    290290
    291291#: admin/class-opti-behavior-heatmap-dashboard.php:249
     
    338338#: includes/trait-opti-behavior-dashboard-views.php:48
    339339msgid "Last 7 Days"
    340 msgstr "Les 7 derniers jours"
     340msgstr "7 Derniers Jours"
    341341
    342342#: admin/class-opti-behavior-heatmap-dashboard.php:258
     
    345345#: includes/trait-opti-behavior-dashboard-views.php:49
    346346msgid "Last 30 Days"
    347 msgstr "Les 30 derniers jours"
     347msgstr "30 Derniers Jours"
    348348
    349349#: admin/class-opti-behavior-heatmap-dashboard.php:259
    350350#: includes/trait-opti-behavior-dashboard-views.php:50
    351351msgid "This Month"
    352 msgstr "Ce mois-ci"
     352msgstr "Ce Mois"
    353353
    354354#: admin/class-opti-behavior-heatmap-dashboard.php:260
    355355msgid "Last Month"
    356 msgstr "Le mois dernier"
     356msgstr "Mois Dernier"
    357357
    358358#: admin/class-opti-behavior-heatmap-dashboard.php:261
     
    366366#: includes/trait-opti-behavior-dashboard-views.php:1139
    367367msgid "No data available"
    368 msgstr "Aucune donnée disponible"
     368msgstr "Aucune Donnée"
    369369
    370370#: admin/class-opti-behavior-heatmap-dashboard.php:263
     
    376376#: views/dashboard-views.php:233
    377377msgid "Top Engaged Users"
    378 msgstr "Utilisateurs les plus engagés"
     378msgstr "Utilisateurs les Plus Engagés"
    379379
    380380#: admin/class-opti-behavior-heatmap-dashboard.php:390
     
    435435#: includes/trait-opti-behavior-dashboard-views.php:169
    436436msgid "Live"
    437 msgstr "En direct"
     437msgstr "En Direct"
    438438
    439439#: admin/class-opti-behavior-heatmap-dashboard.php:449
    440440#: includes/trait-opti-behavior-dashboard-views.php:188
    441441msgid "No active visitors right now"
    442 msgstr "Aucun visiteur actif pour le moment"
     442msgstr "Aucun Visiteur Actif"
    443443
    444444#: admin/class-opti-behavior-heatmap-dashboard.php:450
    445445#: includes/trait-opti-behavior-dashboard-views.php:189
    446446msgid "Traffic updates in real-time."
    447 msgstr "Le trafic se met à jour en temps réel."
     447msgstr "Le Trafic se Met à Jour en Temps Réel."
    448448
    449449#: admin/class-opti-behavior-heatmap-dashboard.php:463
     
    518518#: includes/trait-opti-behavior-sessions-views.php:190
    519519msgid "View Details"
    520 msgstr "Voir les détails"
     520msgstr "Voir Détails"
    521521
    522522#: admin/class-opti-behavior-heatmap-dashboard.php:635
     
    546546#: admin/class-opti-behavior-heatmap-dashboard.php:714
    547547msgid "Loading analytics data..."
    548 msgstr "Chargement des données d'analyse..."
     548msgstr "Chargement des Données..."
    549549
    550550#: admin/class-opti-behavior-heatmap-dashboard.php:775
     
    596596#: admin/class-opti-behavior-heatmap-dashboard.php:815
    597597msgid "First"
    598 msgstr "Première"
     598msgstr "Premier"
    599599
    600600#: admin/class-opti-behavior-heatmap-dashboard.php:816
    601601msgid "Prev"
    602 msgstr "Préc"
     602msgstr "Précédent"
    603603
    604604#: admin/class-opti-behavior-heatmap-dashboard.php:830
    605605msgid "Next"
    606 msgstr "Suiv"
     606msgstr "Suivant"
    607607
    608608#: admin/class-opti-behavior-heatmap-dashboard.php:831
    609609msgid "Last"
    610 msgstr "Dernière"
     610msgstr "Dernier"
    611611
    612612#: admin/class-opti-behavior-heatmap-dashboard.php:2025
     
    712712#: admin/class-opti-behavior-heatmap-post-metabox.php:208
    713713msgid "Exit Behavior"
    714 msgstr "Comportement de sortie"
     714msgstr "Comportement de Sortie"
    715715
    716716#: admin/class-opti-behavior-heatmap-post-metabox.php:209
     
    764764#: admin/recordings-page.php:32
    765765msgid "Date Range:"
    766 msgstr "Plage de dates :"
     766msgstr "Période :"
    767767
    768768#: admin/recordings-page.php:38
    769769#: includes/trait-opti-behavior-dashboard-views.php:51
    770770msgid "Custom Range"
    771 msgstr "Plage personnalisée"
     771msgstr "Période Personnalisée"
    772772
    773773#: admin/recordings-page.php:41
     
    777777#: admin/recordings-page.php:43
    778778msgid "All Durations"
    779 msgstr "Toutes les durées"
     779msgstr "Toutes Durées"
    780780
    781781#: admin/recordings-page.php:44
     
    818818#: admin/recordings-page.php:64
    819819msgid "Advanced Filters"
    820 msgstr "Filtres avancés"
     820msgstr "Filtres Avancés"
    821821
    822822#: admin/recordings-page.php:69
    823823msgid "Clear All"
    824 msgstr "Tout effacer"
     824msgstr "Effacer Tout"
    825825
    826826#: admin/recordings-page.php:76
     
    839839#: admin/recordings-page.php:93
    840840msgid "Visitor Attributes"
    841 msgstr "Attributs du visiteur"
     841msgstr "Attributs Visiteur"
    842842
    843843#: admin/recordings-page.php:96
    844844msgid "Browser Name"
    845 msgstr "Nom du navigateur"
     845msgstr "Navigateur"
    846846
    847847#: admin/recordings-page.php:98
    848848msgid "All Browsers"
    849 msgstr "Tous les navigateurs"
     849msgstr "Tous Navigateurs"
    850850
    851851#: admin/recordings-page.php:105
    852852msgid "All Countries"
    853 msgstr "Tous les pays"
     853msgstr "Tous Pays"
    854854
    855855#: admin/recordings-page.php:110
    856856msgid "Device Type"
    857 msgstr "Type d'appareil"
     857msgstr "Type d'Appareil"
    858858
    859859#: admin/recordings-page.php:112
    860860msgid "All Devices"
    861 msgstr "Tous les appareils"
     861msgstr "Tous Appareils"
    862862
    863863#: admin/recordings-page.php:113
     
    873873#: admin/recordings-page.php:120 admin/recordings-page.php:364
    874874msgid "Operating System"
    875 msgstr "Système d'exploitation"
     875msgstr "Système d'Exploitation"
    876876
    877877#: admin/recordings-page.php:122
    878878msgid "All OS"
    879 msgstr "Tous les OS"
     879msgstr "Tous SE"
    880880
    881881#: admin/recordings-page.php:127
    882882msgid "Visitor Type"
    883 msgstr "Type de visiteur"
     883msgstr "Type Visiteur"
    884884
    885885#: admin/recordings-page.php:129
    886886msgid "All Visitors"
    887 msgstr "Tous les visiteurs"
     887msgstr "Tous Visiteurs"
    888888
    889889#: admin/recordings-page.php:130
    890890msgid "New Visitor"
    891 msgstr "Nouveau visiteur"
     891msgstr "Nouveau Visiteur"
    892892
    893893#: admin/recordings-page.php:131
    894894msgid "Returning Visitor"
    895 msgstr "Visiteur récurrent"
     895msgstr "Visiteur Récurrent"
    896896
    897897#: admin/recordings-page.php:138
    898898msgid "Session Attributes"
    899 msgstr "Attributs de session"
     899msgstr "Attributs Session"
    900900
    901901#: admin/recordings-page.php:141
    902902msgid "Recording ID"
    903 msgstr "ID d'enregistrement"
     903msgstr "ID Enregistrement"
    904904
    905905#: admin/recordings-page.php:142
    906906msgid "Enter recording ID"
    907 msgstr "Entrer l'ID d'enregistrement"
     907msgstr "Entrez ID"
    908908
    909909#: admin/recordings-page.php:146
    910910msgid "Min Duration (seconds)"
    911 msgstr "Durée minimale (secondes)"
     911msgstr "Durée Minimale (secondes)"
    912912
    913913#: admin/recordings-page.php:151
    914914msgid "Max Duration (seconds)"
    915 msgstr "Durée maximale (secondes)"
     915msgstr "Durée Max (s)"
    916916
    917917#: admin/recordings-page.php:156
    918918msgid "Min Page Count"
    919 msgstr "Nombre minimum de pages"
     919msgstr "Pages Min"
    920920
    921921#: admin/recordings-page.php:161
    922922msgid "Max Page Count"
    923 msgstr "Nombre maximum de pages"
     923msgstr "Pages Max"
    924924
    925925#: admin/recordings-page.php:166
    926926msgid "Watched Status"
    927 msgstr "Statut de visionnage"
     927msgstr "Statut Vue"
    928928
    929929#: admin/recordings-page.php:168
    930930msgid "All Recordings"
    931 msgstr "Tous les enregistrements"
     931msgstr "Tous Enregistrements"
    932932
    933933#: admin/recordings-page.php:169
    934934msgid "Watched"
    935 msgstr "Regardé"
     935msgstr "Vu"
    936936
    937937#: admin/recordings-page.php:170
    938938msgid "Not Watched"
    939 msgstr "Non regardé"
     939msgstr "Non Vu"
    940940
    941941#: admin/recordings-page.php:177
    942942msgid "Pages & Traffic"
    943 msgstr "Pages et trafic"
     943msgstr "Pages & Trafic"
    944944
    945945#: admin/recordings-page.php:180
    946946msgid "Contains Page"
    947 msgstr "Contient la page"
     947msgstr "Contient Page"
    948948
    949949#: admin/recordings-page.php:181
    950950msgid "e.g., /product"
    951 msgstr "ex., /produit"
     951msgstr "ex. /produit"
    952952
    953953#: admin/recordings-page.php:185
    954954msgid "Does Not Contain Page"
    955 msgstr "Ne contient pas la page"
     955msgstr "Exclut Page"
    956956
    957957#: admin/recordings-page.php:186
    958958msgid "e.g., /checkout"
    959 msgstr "ex., /checkout"
     959msgstr "ex. /paiement"
    960960
    961961#: admin/recordings-page.php:190
    962962msgid "Entry Page"
    963 msgstr "Page d'entrée"
     963msgstr "Page Entrée"
    964964
    965965#: admin/recordings-page.php:191
    966966msgid "e.g., /home"
    967 msgstr "ex., /home"
     967msgstr "ex. /accueil"
    968968
    969969#: admin/recordings-page.php:195
    970970msgid "Exit Page"
    971 msgstr "Page de sortie"
     971msgstr "Page Sortie"
    972972
    973973#: admin/recordings-page.php:196
    974974msgid "e.g., /thank-you"
    975 msgstr "ex., /merci"
     975msgstr "ex. /merci"
    976976
    977977#: admin/recordings-page.php:200
    978978msgid "Referrer URL"
    979 msgstr "URL du référent"
     979msgstr "URL Référent"
    980980
    981981#: admin/recordings-page.php:201
    982982msgid "e.g., google.com"
    983 msgstr "ex., google.com"
     983msgstr "ex. google.fr"
    984984
    985985#: admin/recordings-page.php:205
    986986msgid "Traffic Channel"
    987 msgstr "Canal de trafic"
     987msgstr "Canal Trafic"
    988988
    989989#: admin/recordings-page.php:207
    990990msgid "All Channels"
    991 msgstr "Tous les canaux"
     991msgstr "Tous Canaux"
    992992
    993993#: admin/recordings-page.php:208
    994994msgid "Direct Visit"
    995 msgstr "Visite directe"
     995msgstr "Visite Directe"
    996996
    997997#: admin/recordings-page.php:209
    998998msgid "Organic Search"
    999 msgstr "Recherche organique"
     999msgstr "Recherche Organique"
    10001000
    10011001#: admin/recordings-page.php:210
    10021002msgid "Paid Ads"
    1003 msgstr "Publicités payantes"
     1003msgstr "Pubs Payantes"
    10041004
    10051005#: admin/recordings-page.php:211
    10061006msgid "Social Media"
    1007 msgstr "Médias sociaux"
     1007msgstr "Réseaux Sociaux"
    10081008
    10091009#: admin/recordings-page.php:212
     
    10251025#: admin/recordings-page.php:225
    10261026msgid "All Campaigns"
    1027 msgstr "Toutes les campagnes"
     1027msgstr "Toutes Campagnes"
    10281028
    10291029#: admin/recordings-page.php:230
     
    10331033#: admin/recordings-page.php:232
    10341034msgid "All Sources"
    1035 msgstr "Toutes les sources"
     1035msgstr "Toutes Sources"
    10361036
    10371037#: admin/recordings-page.php:237
    10381038msgid "UTM Medium"
    1039 msgstr "Médium UTM"
     1039msgstr "Medium UTM"
    10401040
    10411041#: admin/recordings-page.php:239
    10421042msgid "All Mediums"
    1043 msgstr "Tous les médiums"
     1043msgstr "Tous Mediums"
    10441044
    10451045#: admin/recordings-page.php:248
    10461046msgid "Apply Filters"
    1047 msgstr "Appliquer les filtres"
     1047msgstr "Appliquer Filtres"
    10481048
    10491049#: admin/recordings-page.php:252
     
    10531053#: admin/recordings-page.php:261
    10541054msgid "List View"
    1055 msgstr "Vue liste"
     1055msgstr "Vue Liste"
    10561056
    10571057#: admin/recordings-page.php:265
    10581058msgid "Table View (By Page)"
    1059 msgstr "Vue tableau (par page)"
     1059msgstr "Vue Tableau"
    10601060
    10611061#: admin/recordings-page.php:274 admin/recordings-page.php:299
    10621062msgid "Loading recordings..."
    1063 msgstr "Chargement des enregistrements..."
     1063msgstr "Chargement..."
    10641064
    10651065#: admin/recordings-page.php:289
    10661066msgid "Total Duration"
    1067 msgstr "Durée totale"
     1067msgstr "Durée Totale"
    10681068
    10691069#: admin/recordings-page.php:290
    10701070msgid "Avg Duration"
    1071 msgstr "Durée moyenne"
     1071msgstr "Durée Moy."
    10721072
    10731073#: admin/recordings-page.php:291
    10741074msgid "Last Recorded"
    1075 msgstr "Dernier enregistrement"
     1075msgstr "Dernier Enreg."
    10761076
    10771077#: admin/recordings-page.php:321
     
    10861086#: includes/trait-opti-behavior-dashboard-views.php:396
    10871087msgid "Screen Resolution"
    1088 msgstr "Résolution d'écran"
     1088msgstr "Résolution Écran"
    10891089
    10901090#: admin/recordings-page.php:387
    10911091msgid "Loading recording..."
    1092 msgstr "Chargement de l'enregistrement..."
     1092msgstr "Chargement..."
    10931093
    10941094#: admin/recordings-page.php:420
     
    10981098#: admin/recordings-page.php:431
    10991099msgid "Skip Inactive"
    1100 msgstr "Ignorer les inactifs"
     1100msgstr "Ignorer Inactivité"
    11011101
    11021102#: admin/recordings-page.php:439
    11031103msgid "Session Timeline"
    1104 msgstr "Chronologie de session"
     1104msgstr "Chronologie"
    11051105
    11061106#: admin/recordings-page.php:445
    11071107msgid "Loading timeline..."
    1108 msgstr "Chargement de la chronologie..."
     1108msgstr "Chargement..."
    11091109
    11101110#: clear-cache.php:14
     
    11341134#: includes/trait-opti-behavior-ai-insights-views.php:58
    11351135msgid "Coming Soon"
    1136 msgstr ""
     1136msgstr "Bientôt Disponible"
    11371137
    11381138#: includes/trait-opti-behavior-ai-insights-views.php:96
     
    11601160#: includes/trait-opti-behavior-ai-insights-views.php:112
    11611161msgid "Issue Detection"
    1162 msgstr "Détection de bot"
     1162msgstr "Détection de Bot"
    11631163
    11641164#: includes/trait-opti-behavior-ai-insights-views.php:113
     
    11801180#: includes/trait-opti-behavior-ai-insights-views.php:124
    11811181msgid "Performance Optimization"
    1182 msgstr ""
     1182msgstr "Optimisation des Performances"
    11831183
    11841184#: includes/trait-opti-behavior-ai-insights-views.php:125
     
    12101210#: includes/trait-opti-behavior-ai-insights-views.php:143
    12111211msgid "What Our AI Will Analyze"
    1212 msgstr ""
     1212msgstr "Ce que Notre IA Analysera"
    12131213
    12141214#: includes/trait-opti-behavior-ai-insights-views.php:147
    12151215msgid "Traffic Patterns"
    1216 msgstr "Enregistrer les paramètres de trafic"
     1216msgstr "Modèles de Trafic"
    12171217
    12181218#: includes/trait-opti-behavior-ai-insights-views.php:151
    12191219msgid "Sales Funnels"
    1220 msgstr ""
     1220msgstr "Tunnels de Vente"
    12211221
    12221222#: includes/trait-opti-behavior-ai-insights-views.php:155
    12231223msgid "Engagement Time"
    1224 msgstr ""
     1224msgstr "Temps d'Engagement"
    12251225
    12261226#: includes/trait-opti-behavior-ai-insights-views.php:159
    12271227msgid "Click Behavior"
    1228 msgstr "Comportement de sortie"
     1228msgstr "Comportement de Sortie"
    12291229
    12301230#: includes/trait-opti-behavior-ai-insights-views.php:163
    12311231msgid "Device Performance"
    1232 msgstr ""
     1232msgstr "Performance par Appareil"
    12331233
    12341234#: includes/trait-opti-behavior-ai-insights-views.php:167
    12351235msgid "Geographic Trends"
    1236 msgstr ""
     1236msgstr "Tendances Géographiques"
    12371237
    12381238#: includes/trait-opti-behavior-ai-insights-views.php:171
    12391239msgid "User Journeys"
    1240 msgstr ""
     1240msgstr "Parcours Utilisateurs"
    12411241
    12421242#: includes/trait-opti-behavior-ai-insights-views.php:175
    12431243msgid "Revenue Impact"
    1244 msgstr ""
     1244msgstr "Impact sur les Revenus"
    12451245
    12461246#: includes/trait-opti-behavior-ai-insights-views.php:184
     
    12681268#: includes/trait-opti-behavior-ai-insights-views.php:212
    12691269msgid "Development Roadmap"
    1270 msgstr ""
     1270msgstr "Plan de Développement"
    12711271
    12721272#: includes/trait-opti-behavior-ai-insights-views.php:217
    12731273msgid "Phase 1: Foundation"
    1274 msgstr ""
     1274msgstr "Phase 1 : Fondation"
    12751275
    12761276#: includes/trait-opti-behavior-ai-insights-views.php:218
    12771277msgid "Advanced analytics data collection and processing infrastructure"
    1278 msgstr ""
     1278msgstr "Infrastructure de Collecte et de Traitement de Données"
    12791279
    12801280#: includes/trait-opti-behavior-ai-insights-views.php:224
    12811281msgid "Phase 2: AI Integration"
    1282 msgstr ""
     1282msgstr "Phase 2 : Intégration de l'IA"
    12831283
    12841284#: includes/trait-opti-behavior-ai-insights-views.php:225
    12851285msgid "Machine learning models for pattern recognition and insights"
    1286 msgstr ""
     1286msgstr "Modèles d'Apprentissage pour Reconnaissance de Modèles"
    12871287
    12881288#: includes/trait-opti-behavior-ai-insights-views.php:231
     
    12961296#: includes/trait-opti-behavior-ai-insights-views.php:238
    12971297msgid "Phase 4: Launch"
    1298 msgstr ""
     1298msgstr "Phase 4 : Lancement"
    12991299
    13001300#: includes/trait-opti-behavior-ai-insights-views.php:239
    13011301msgid "Full AI-powered analytics suite with predictive capabilities"
    1302 msgstr ""
     1302msgstr "Suite Complète d'Analyses IA avec Capacités Prédictives"
    13031303
    13041304#: includes/trait-opti-behavior-ajax-handlers.php:88
     
    13741374#: includes/trait-opti-behavior-dashboard-views.php:39
    13751375msgid "Analytics Dashboard"
    1376 msgstr "Tableau de bord analytique"
     1376msgstr "Tableau de Bord Analytique"
    13771377
    13781378#: includes/trait-opti-behavior-dashboard-views.php:41
    13791379msgid "Real-time insights and user behavior analytics"
    1380 msgstr ""
     1380msgstr "Analyses en Temps Réel"
    13811381"Informations en temps réel et analyses du comportement des utilisateurs"
    13821382
     
    13871387#: includes/trait-opti-behavior-dashboard-views.php:117
    13881388msgid "Avg. Scroll Depth"
    1389 msgstr "Profondeur de défilement moy."
     1389msgstr "Défilement Moy."
    13901390
    13911391#: includes/trait-opti-behavior-dashboard-views.php:199
    13921392msgid "Real-time Visitor Map"
    1393 msgstr "Carte des visiteurs en temps réel"
     1393msgstr "Carte des Visiteurs en Temps Réel"
    13941394
    13951395#: includes/trait-opti-behavior-dashboard-views.php:214
    13961396msgid "Traffic Overview"
    1397 msgstr "Aperçu du trafic"
     1397msgstr "Aperçu du Trafic"
    13981398
    13991399#: includes/trait-opti-behavior-dashboard-views.php:273
    14001400msgid "Once visitors view pages, you'll see them here."
    1401 msgstr "Une fois que les visiteurs consultent des pages, vous les verrez ici."
     1401msgstr "Les Pages Vues Apparaîtront Ici."
    14021402
    14031403#: includes/trait-opti-behavior-dashboard-views.php:299
    14041404msgid "Visitor Activity Heatmap"
    1405 msgstr "Carte de chaleur de l'activité des visiteurs"
     1405msgstr "Carte Activité Visiteurs"
    14061406
    14071407#: includes/trait-opti-behavior-dashboard-views.php:306
     
    14111411#: includes/trait-opti-behavior-dashboard-views.php:332
    14121412msgid "User Intent"
    1413 msgstr "Intention de l'utilisateur"
     1413msgstr "Intention de l'Utilisateur"
    14141414
    14151415#: includes/trait-opti-behavior-dashboard-views.php:344
    14161416msgid "Top Referrers"
    1417 msgstr "Principaux référents"
     1417msgstr "Principaux Référents"
    14181418
    14191419#: includes/trait-opti-behavior-dashboard-views.php:354
    14201420msgid "Top Countries"
    1421 msgstr "Principaux pays"
     1421msgstr "Principaux Pays"
    14221422
    14231423#: includes/trait-opti-behavior-dashboard-views.php:376
    14241424msgid "Device Types"
    1425 msgstr "Types d'appareils"
     1425msgstr "Types d'Appareils"
    14261426
    14271427#: includes/trait-opti-behavior-dashboard-views.php:386
    14281428msgid "Operating Systems"
    1429 msgstr "Systèmes d'exploitation"
     1429msgstr "Systèmes d'Exploitation"
    14301430
    14311431#: includes/trait-opti-behavior-dashboard-views.php:448
     
    14341434#: includes/trait-opti-behavior-dashboard-views.php:1140
    14351435msgid "Try broadening the date range or check back later."
    1436 msgstr "Essayez d'élargir la plage de dates ou revenez plus tard."
     1436msgstr "Élargir la Période ou Revenir Plus Tard."
    14371437
    14381438#: includes/trait-opti-behavior-dashboard-views.php:459
    14391439msgid "Low intent"
    1440 msgstr "Faible intention"
     1440msgstr "Faible Intention"
    14411441
    14421442#: includes/trait-opti-behavior-dashboard-views.php:461
     
    14561456#: includes/trait-opti-behavior-dashboard-views.php:465
    14571457msgid "Medium intent"
    1458 msgstr "Intention moyenne"
     1458msgstr "Intention Moyenne"
    14591459
    14601460#: includes/trait-opti-behavior-dashboard-views.php:471
    14611461msgid "High intent"
    1462 msgstr "Forte intention"
     1462msgstr "Forte Intention"
    14631463
    14641464#: includes/trait-opti-behavior-dashboard-views.php:780
     
    14661466#: includes/trait-opti-behavior-settings-views.php:1747
    14671467msgid "Traffic Classification"
    1468 msgstr "Classification du trafic"
     1468msgstr "Classification du Trafic"
    14691469
    14701470#: includes/trait-opti-behavior-dashboard-views.php:824
     
    14921492#: includes/trait-opti-behavior-dashboard-views.php:944
    14931493msgid "Bot Traffic"
    1494 msgstr "Trafic de bots"
     1494msgstr "Trafic de Bots"
    14951495
    14961496#: includes/trait-opti-behavior-dashboard-views.php:927
    14971497msgid "No bot visits detected"
    1498 msgstr "Aucune visite de bot détectée"
     1498msgstr "Aucune Visite de Bot Détectée"
    14991499
    15001500#: includes/trait-opti-behavior-dashboard-views.php:928
    15011501msgid "Bot visits will appear here when detected."
    1502 msgstr "Les visites de bots apparaîtront ici lorsqu'elles seront détectées."
     1502msgstr "Les Visites de Bots Apparaîtront Ici."
    15031503
    15041504#: includes/trait-opti-behavior-heatmaps-views.php:37
     
    15081508#: includes/trait-opti-behavior-heatmaps-views.php:58
    15091509msgid "Total Heatmaps"
    1510 msgstr "Cartes de chaleur"
     1510msgstr "Total des Cartes de Chaleur"
    15111511
    15121512#: includes/trait-opti-behavior-heatmaps-views.php:59
    15131513msgid "this week"
    1514 msgstr "cette semaine"
     1514msgstr "cette Semaine"
    15151515
    15161516#: includes/trait-opti-behavior-heatmaps-views.php:67
    15171517msgid "Total Clicks"
    1518 msgstr "Total des clics"
     1518msgstr "Total des Clics"
    15191519
    15201520#: includes/trait-opti-behavior-heatmaps-views.php:68
     
    15241524#: includes/trait-opti-behavior-heatmaps-views.php:76
    15251525msgid "Mobile Traffic"
    1526 msgstr "Trafic mobile"
     1526msgstr "Trafic Mobile"
    15271527
    15281528#: includes/trait-opti-behavior-heatmaps-views.php:77
     
    15321532#: includes/trait-opti-behavior-heatmaps-views.php:85
    15331533msgid "Avg. Time on Page"
    1534 msgstr "Temps moyen sur la page"
     1534msgstr "Temps Moy. Page"
    15351535
    15361536#: includes/trait-opti-behavior-heatmaps-views.php:86
     
    15401540#: includes/trait-opti-behavior-heatmaps-views.php:94
    15411541msgid "Hottest Page"
    1542 msgstr "Dernière page"
     1542msgstr "Page la Plus Chaude"
    15431543
    15441544#: includes/trait-opti-behavior-heatmaps-views.php:103
    15451545msgid "Click-through Rate"
    1546 msgstr "Taux de clics"
     1546msgstr "Taux de Clics"
    15471547
    15481548#: includes/trait-opti-behavior-heatmaps-views.php:104
     
    15521552#: includes/trait-opti-behavior-heatmaps-views.php:124
    15531553msgid "Available Heatmaps"
    1554 msgstr "Visualisation des cartes de chaleur"
     1554msgstr "Cartes de Chaleur Disponibles"
    15551555
    15561556#: includes/trait-opti-behavior-maintenance.php:88
     
    15721572#: includes/trait-opti-behavior-sessions-views.php:49
    15731573msgid "Active"
    1574 msgstr "Actions"
     1574msgstr "Actif"
    15751575
    15761576#: includes/trait-opti-behavior-sessions-views.php:57
     
    15881588#: includes/trait-opti-behavior-sessions-views.php:83
    15891589msgid "Select All"
    1590 msgstr "Tout effacer"
     1590msgstr "Tout Sélectionner"
    15911591
    15921592#: includes/trait-opti-behavior-sessions-views.php:84
     
    16251625#: includes/trait-opti-behavior-settings-views.php:1736
    16261626msgid "Dashboard Settings"
    1627 msgstr "Paramètres du tableau de bord"
     1627msgstr "Paramètres du Tableau de Bord"
    16281628
    16291629#: includes/trait-opti-behavior-settings-views.php:153
    16301630#: includes/trait-opti-behavior-settings-views.php:246
    16311631msgid "Heatmap Settings"
    1632 msgstr "Paramètres de carte de chaleur"
     1632msgstr "Paramètres de Carte de Chaleur"
    16331633
    16341634#: includes/trait-opti-behavior-settings-views.php:163
    16351635msgid "License & Quota"
    1636 msgstr "Licence et quota"
     1636msgstr "Licence et Quota"
    16371637
    16381638#: includes/trait-opti-behavior-settings-views.php:168
    16391639msgid "Data Protection"
    1640 msgstr "Protection des données"
     1640msgstr "Protection des Données"
    16411641
    16421642#: includes/trait-opti-behavior-settings-views.php:173
     
    16461646#: includes/trait-opti-behavior-settings-views.php:178
    16471647msgid "Debug & Logging"
    1648 msgstr "Débogage et journalisation"
     1648msgstr "Débogage et Journalisation"
    16491649
    16501650#: includes/trait-opti-behavior-settings-views.php:183
    16511651msgid "Danger Zone"
    1652 msgstr "Zone dangereuse"
     1652msgstr "Zone Dangereuse"
    16531653
    16541654#: includes/trait-opti-behavior-settings-views.php:188
    16551655#: includes/trait-opti-behavior-settings-views.php:588
    16561656msgid "Uninstall Settings"
    1657 msgstr "Paramètres de désinstallation"
     1657msgstr "Paramètres de Désinstallation"
    16581658
    16591659#: includes/trait-opti-behavior-settings-views.php:248
     
    23802380#: includes/trait-opti-behavior-settings-views.php:1379
    23812381msgid "days"
    2382 msgstr ""
     2382msgstr "jours"
    23832383
    23842384#: includes/trait-opti-behavior-settings-views.php:1386
     
    24802480#: includes/trait-opti-behavior-settings-views.php:2126
    24812481msgid "Save Recording Settings"
    2482 msgstr "Enregistrer les paramètres d'enregistrement"
     2482msgstr "Enregistrer Paramètres"
    24832483
    24842484#: includes/trait-opti-behavior-settings-views.php:1544
     
    25372537#: includes/trait-opti-behavior-settings-views.php:2267
    25382538msgid "Upgrade to Pro"
    2539 msgstr "Mettre à niveau vers Pro"
     2539msgstr "Passer à Pro"
    25402540
    25412541#: includes/trait-opti-behavior-settings-views.php:1574
     
    25612561#: includes/trait-opti-behavior-settings-views.php:1756
    25622562msgid "User Intent Rules"
    2563 msgstr "Règles d'intention utilisateur"
     2563msgstr "Règles d'Intention Utilisateur"
    25642564
    25652565#: includes/trait-opti-behavior-settings-views.php:1789
     
    25702570#: includes/trait-opti-behavior-settings-views.php:1793
    25712571msgid "Bot Detection"
    2572 msgstr "Détection de bot"
     2572msgstr "Détection de Bot"
    25732573
    25742574#: includes/trait-opti-behavior-settings-views.php:1797
    25752575msgid "Enable bot traffic detection"
    2576 msgstr "Activer la détection de trafic de bot"
     2576msgstr "Activer Détection Trafic Bot"
    25772577
    25782578#: includes/trait-opti-behavior-settings-views.php:1799
     
    25862586#: includes/trait-opti-behavior-settings-views.php:1804
    25872587msgid "Automated Traffic Detection"
    2588 msgstr "Détection de trafic automatisé"
     2588msgstr "Détection de Trafic Automatisé"
    25892589
    25902590#: includes/trait-opti-behavior-settings-views.php:1808
    25912591msgid "Enable automated traffic detection"
    2592 msgstr "Activer la détection de trafic automatisé"
     2592msgstr "Activer Détection Trafic Automatisé"
    25932593
    25942594#: includes/trait-opti-behavior-settings-views.php:1810
     
    26022602#: includes/trait-opti-behavior-settings-views.php:1815
    26032603msgid "Spam Traffic Detection"
    2604 msgstr "Détection de trafic spam"
     2604msgstr "Détection de Trafic Spam"
    26052605
    26062606#: includes/trait-opti-behavior-settings-views.php:1819
    26072607msgid "Enable spam traffic detection"
    2608 msgstr "Activer la détection de trafic spam"
     2608msgstr "Activer Détection Trafic Spam"
    26092609
    26102610#: includes/trait-opti-behavior-settings-views.php:1821
     
    26162616#: includes/trait-opti-behavior-settings-views.php:1826
    26172617msgid "Spam Duration Threshold"
    2618 msgstr "Seuil de durée de spam"
     2618msgstr "Seuil de Durée de Spam"
    26192619
    26202620#: includes/trait-opti-behavior-settings-views.php:1830
     
    26252625#: includes/trait-opti-behavior-settings-views.php:1835
    26262626msgid "Spam Bounce Threshold"
    2627 msgstr "Seuil de rebond de spam"
     2627msgstr "Seuil de Rebond de Spam"
    26282628
    26292629#: includes/trait-opti-behavior-settings-views.php:1839
     
    26342634#: includes/trait-opti-behavior-settings-views.php:1844
    26352635msgid "Custom Bot Patterns"
    2636 msgstr "Modèles de bot personnalisés"
     2636msgstr "Modèles de Bot Personnalisés"
    26372637
    26382638#: includes/trait-opti-behavior-settings-views.php:1847
     
    26462646#: includes/trait-opti-behavior-settings-views.php:1852
    26472647msgid "Save Traffic Settings"
    2648 msgstr "Enregistrer les paramètres de trafic"
     2648msgstr "Enregistrer Paramètres Trafic"
    26492649
    26502650#: includes/trait-opti-behavior-settings-views.php:1886
     
    26552655#: includes/trait-opti-behavior-settings-views.php:1892
    26562656msgid "Low Intent"
    2657 msgstr ""
     2657msgstr "Faible Intention"
    26582658
    26592659#: includes/trait-opti-behavior-settings-views.php:1896
     
    26812681#: includes/trait-opti-behavior-settings-views.php:1988
    26822682msgid "Scroll Depth (%)"
    2683 msgstr ""
     2683msgstr "Profondeur de Défilement (%)"
    26842684
    26852685#: includes/trait-opti-behavior-settings-views.php:1919
     
    26892689#: includes/trait-opti-behavior-settings-views.php:1929
    26902690msgid "Medium Intent"
    2691 msgstr ""
     2691msgstr "Intention Moyenne"
    26922692
    26932693#: includes/trait-opti-behavior-settings-views.php:1938
     
    27052705#: includes/trait-opti-behavior-settings-views.php:1966
    27062706msgid "High Intent"
    2707 msgstr ""
     2707msgstr "Forte Intention"
    27082708
    27092709#: includes/trait-opti-behavior-settings-views.php:1975
     
    27212721#: includes/trait-opti-behavior-settings-views.php:1999
    27222722msgid "Save User Intent Rules"
    2723 msgstr "Règles d'intention utilisateur"
     2723msgstr "Enregistrer Règles Intention"
    27242724
    27252725#: includes/trait-opti-behavior-settings-views.php:2027
    27262726msgid "Recording settings saved successfully!"
    2727 msgstr "Paramètres d'enregistrement enregistrés avec succès !"
     2727msgstr "Paramètres enregistrés !"
    27282728
    27292729#: includes/trait-opti-behavior-settings-views.php:2038
    27302730msgid "Session Recording Settings"
    2731 msgstr "Paramètres d'enregistrement de session"
     2731msgstr "Paramètres Enregistrement"
    27322732
    27332733#: includes/trait-opti-behavior-settings-views.php:2040
    27342734msgid "Configure how session recordings are captured and stored"
    2735 msgstr ""
     2735msgstr "Configuration capture et stockage"
    27362736"Configurer comment les enregistrements de session sont capturés et stockés"
    27372737
    27382738#: includes/trait-opti-behavior-settings-views.php:2046
    27392739msgid "Enable Session Recording"
    2740 msgstr "Activer l'enregistrement de session"
     2740msgstr "Activer Enregistrement"
    27412741
    27422742#: includes/trait-opti-behavior-settings-views.php:2047
    27432743msgid "Record user sessions for playback and analysis"
    2744 msgstr "Enregistrer les sessions utilisateur pour la lecture et l'analyse"
     2744msgstr "Enregistrer sessions pour analyse"
    27452745
    27462746#: includes/trait-opti-behavior-settings-views.php:2059
    27472747msgid "Max Recording Duration"
    2748 msgstr "Durée maximale d'enregistrement"
     2748msgstr "Durée Max Enregistrement"
    27492749
    27502750#: includes/trait-opti-behavior-settings-views.php:2060
    27512751msgid "Maximum duration for each recording session (in seconds)"
    2752 msgstr "Durée maximale pour chaque session d'enregistrement (en secondes)"
     2752msgstr "Durée maximale par session (secondes)"
    27532753
    27542754#: includes/trait-opti-behavior-settings-views.php:2072
    27552755msgid "Mouse Movement Sample Rate"
    2756 msgstr "Taux d'échantillonnage du mouvement de la souris"
     2756msgstr "Taux Échantillonnage Souris"
    27572757
    27582758#: includes/trait-opti-behavior-settings-views.php:2073
    27592759msgid "How often to capture mouse movements (lower = more data)"
    2760 msgstr ""
     2760msgstr "Fréquence capture (+ bas = + données)"
    27612761"À quelle fréquence capturer les mouvements de souris (plus bas = plus de "
    27622762"données)"
     
    27642764#: includes/trait-opti-behavior-settings-views.php:2089
    27652765msgid "Privacy Settings"
    2766 msgstr "Paramètres de confidentialité"
     2766msgstr "Paramètres de Confidentialité"
    27672767
    27682768#: includes/trait-opti-behavior-settings-views.php:2091
    27692769msgid "Control what data is captured during recordings"
    2770 msgstr "Contrôler quelles données sont capturées pendant les enregistrements"
     2770msgstr "Contrôle des données capturées"
    27712771
    27722772#: includes/trait-opti-behavior-settings-views.php:2097
    27732773msgid "Mask All Input Fields"
    2774 msgstr "Masquer tous les champs de saisie"
     2774msgstr "Masquer Tous Champs"
    27752775
    27762776#: includes/trait-opti-behavior-settings-views.php:2098
    27772777msgid "Hide all user input for privacy (recommended)"
    2778 msgstr ""
     2778msgstr "Masquer saisies (recommandé)"
    27792779"Masquer toutes les saisies utilisateur pour la confidentialité (recommandé)"
    27802780
    27812781#: includes/trait-opti-behavior-settings-views.php:2110
    27822782msgid "Record Canvas Elements"
    2783 msgstr "Enregistrer les éléments Canvas"
     2783msgstr "Enregistrer Canvas"
    27842784
    27852785#: includes/trait-opti-behavior-settings-views.php:2111
    27862786msgid "Capture canvas and WebGL content"
    2787 msgstr "Capturer le contenu canvas et WebGL"
     2787msgstr "Capturer canvas/WebGL"
    27882788
    27892789#: includes/trait-opti-behavior-settings-views.php:2153
    27902790msgid "License Status"
    2791 msgstr "Licence et quota"
     2791msgstr "Statut Licence"
    27922792
    27932793#: includes/trait-opti-behavior-settings-views.php:2155
    27942794msgid "Your current license and registration status"
    2795 msgstr "Votre statut de licence et d'enregistrement actuel"
     2795msgstr "Statut licence et enregistrement"
    27962796
    27972797#: includes/trait-opti-behavior-settings-views.php:2162
     
    28012801#: includes/trait-opti-behavior-settings-views.php:2166
    28022802msgid "Installation ID:"
    2803 msgstr "ID d'installation :"
     2803msgstr "ID Installation :"
    28042804
    28052805#: includes/trait-opti-behavior-settings-views.php:2170
    28062806msgid "License Type:"
    2807 msgstr "Type de licence :"
     2807msgstr "Type Licence :"
    28082808
    28092809#: includes/trait-opti-behavior-settings-views.php:2171
    28102810msgid "Free Tier"
    2811 msgstr "Niveau gratuit"
     2811msgstr "Gratuit"
    28122812
    28132813#: includes/trait-opti-behavior-settings-views.php:2174
     
    28172817#: includes/trait-opti-behavior-settings-views.php:2181
    28182818msgid "Not Registered"
    2819 msgstr "Non enregistré"
     2819msgstr "Non Enregistré"
    28202820
    28212821#: includes/trait-opti-behavior-settings-views.php:2183
     
    28292829#: includes/trait-opti-behavior-settings-views.php:2192
    28302830msgid "Monthly Quota Usage"
    2831 msgstr "Utilisation du quota mensuel"
     2831msgstr "Quota Mensuel"
    28322832
    28332833#: includes/trait-opti-behavior-settings-views.php:2194
    28342834msgid "Track your session recording usage for the current month"
    2835 msgstr "Suivez votre utilisation d'enregistrement de session pour le mois en cours"
     2835msgstr "Utilisation du mois en cours"
    28362836
    28372837#: includes/trait-opti-behavior-settings-views.php:2208
     
    28452845#: includes/trait-opti-behavior-settings-views.php:2216
    28462846msgid "Monthly Limit"
    2847 msgstr "Limite mensuelle"
     2847msgstr "Limite Mensuelle"
    28482848
    28492849#: includes/trait-opti-behavior-settings-views.php:2224
     
    28662866#: includes/trait-opti-behavior-settings-views.php:2241
    28672867msgid "Unable to retrieve quota information. Please check your API connection."
    2868 msgstr "Impossible de récupérer les informations de quota. Veuillez vérifier votre connexion API."
     2868msgstr "Impossible récupérer quota. Vérifiez connexion API."
    28692869
    28702870#: includes/trait-opti-behavior-settings-views.php:2252
    28712871msgid "Unlock unlimited recordings and advanced features"
    2872 msgstr "Débloquez des enregistrements illimités et des fonctionnalités avancées"
     2872msgstr "Enregistrements illimités et fonctions avancées"
    28732873
    28742874#: includes/trait-opti-behavior-settings-views.php:2257
    28752875msgid "Pro Features:"
    2876 msgstr "Fonctionnalités Pro :"
     2876msgstr "Fonctions Pro :"
    28772877
    28782878#: includes/trait-opti-behavior-settings-views.php:2259
    28792879msgid "Unlimited session recordings"
    2880 msgstr "Enregistrements de session illimités"
     2880msgstr "Enregistrements illimités"
    28812881
    28822882#: includes/trait-opti-behavior-settings-views.php:2260
    28832883msgid "Advanced heatmap analytics"
    2884 msgstr "Analyses de carte de chaleur avancées"
     2884msgstr "Analyses cartes avancées"
    28852885
    28862886#: includes/trait-opti-behavior-settings-views.php:2261
    28872887msgid "Priority support"
    2888 msgstr "Support prioritaire"
     2888msgstr "Support Prioritaire"
    28892889
    28902890#: includes/trait-opti-behavior-settings-views.php:2262
    28912891msgid "Extended data retention"
    2892 msgstr "Rétention de données étendue"
     2892msgstr "Rétention données étendue"
    28932893
    28942894#: includes/trait-opti-behavior-settings-views.php:2263
    28952895msgid "Custom integrations"
    2896 msgstr "Intégrations personnalisées"
     2896msgstr "Intégrations Personnalisées"
    28972897
    28982898#: Opti-Behavior.php:40
     
    29992999#: includes/class-opti-behavior-session-recording.php
    30003000msgid "New visitor"
    3001 msgstr "Nouveau visiteur"
     3001msgstr "Nouveau Visiteur"
    30023002
    30033003#: includes/class-opti-behavior-session-recording.php
     
    30323032msgid "Delete Recording"
    30333033msgstr "Supprimer l'enregistrement"
     3034
     3035#: admin/class-opti-behavior-heatmap-admin-settings.php:295
     3036msgid "Delete all heatmap data? This cannot be undone."
     3037msgstr "Supprimer toutes les données de carte de chaleur ? Cette action est irréversible."
     3038
     3039#: admin/recordings-page.php:22
     3040msgid "Watch real user sessions to understand behavior patterns and identify UX issues."
     3041msgstr "Regardez les sessions pour comprendre le comportement et identifier les problèmes UX."
     3042
     3043#: includes/class-opti-behavior-session-encryption.php:407
     3044msgid "You have reached the free tier limit of 500 session recordings per month. Please upgrade to continue recording sessions."
     3045msgstr "Vous avez atteint la limite gratuite de 500 enregistrements de session par mois. Veuillez passer à la version Pro pour continuer à enregistrer des sessions."
     3046
     3047#: includes/trait-opti-behavior-ai-insights-views.php:53
     3048msgid "AI-Powered Analytics Insights"
     3049msgstr "Analyses basées sur l'IA"
     3050
     3051#: includes/trait-opti-behavior-ai-insights-views.php:54
     3052msgid "The Future of Website Optimization is Here"
     3053msgstr "L'avenir de l'optimisation web est ici"
     3054
     3055#: includes/trait-opti-behavior-ai-insights-views.php:58
     3056msgid "Coming Soon"
     3057msgstr "Bientôt Disponible"
     3058
     3059#: includes/trait-opti-behavior-ai-insights-views.php:96
     3060msgid "Next-Level Analytics with Artificial Intelligence"
     3061msgstr "Analyses de nouvelle génération avec intelligence artificielle"
     3062
     3063#: includes/trait-opti-behavior-ai-insights-views.php:98
     3064msgid "This is a revolutionary new project that will take your Analytics to the next level by using AI to analyze everything happening on your website. We help you understand what works well to optimize it and achieve more performance in your business."
     3065msgstr "Il s'agit d'un nouveau projet révolutionnaire qui amènera vos analyses au niveau supérieur en utilisant l'IA pour analyser tout ce qui se passe sur votre site web. Nous vous aidons à comprendre ce qui fonctionne bien pour l'optimiser et obtenir plus de performance dans votre entreprise."
     3066
     3067#: includes/trait-opti-behavior-ai-insights-views.php:106
     3068msgid "Smart Analysis"
     3069msgstr "Analyse Intelligente"
     3070
     3071#: includes/trait-opti-behavior-ai-insights-views.php:107
     3072msgid "AI identifies patterns and trends in user behavior that humans might miss, providing actionable insights automatically."
     3073msgstr "L'IA identifie des modèles et tendances dans le comportement des utilisateurs que les humains pourraient manquer, fournissant automatiquement des informations exploitables."
     3074
     3075#: includes/trait-opti-behavior-ai-insights-views.php:113
     3076msgid "Automatically detect problems affecting user experience, conversion rates, and engagement before they impact your business."
     3077msgstr "Détectez automatiquement les problèmes affectant l'expérience utilisateur, les taux de conversion et l'engagement avant qu'ils n'impactent votre entreprise."
     3078
     3079#: includes/trait-opti-behavior-ai-insights-views.php:118
     3080msgid "Auto-Fix & Suggestions"
     3081msgstr "Corrections Automatiques et Suggestions"
     3082
     3083#: includes/trait-opti-behavior-ai-insights-views.php:119
     3084msgid "Get intelligent recommendations and automatic fixes to improve traffic, sales, engagement time, and overall performance."
     3085msgstr "Obtenez des recommandations intelligentes et des corrections automatiques pour améliorer le trafic, les ventes, le temps d'engagement et la performance globale."
     3086
     3087#: includes/trait-opti-behavior-ai-insights-views.php:124
     3088msgid "Performance Optimization"
     3089msgstr "Optimisation des Performances"
     3090
     3091#: includes/trait-opti-behavior-ai-insights-views.php:125
     3092msgid "Optimize your website for maximum conversions, longer session times, and increased revenue with AI-driven strategies."
     3093msgstr "Optimisez votre site web pour des conversions maximales, des temps de session plus longs et des revenus accrus avec des stratégies basées sur l'IA."
     3094
     3095#: includes/trait-opti-behavior-ai-insights-views.php:130
     3096msgid "UX Enhancement"
     3097msgstr "Amélioration de l'UX"
     3098
     3099#: includes/trait-opti-behavior-ai-insights-views.php:131
     3100msgid "Improve user experience based on real behavioral data and AI predictions of what will work best for your audience."
     3101msgstr "Améliorez l'expérience utilisateur en vous basant sur des données comportementales réelles et les prédictions de l'IA sur ce qui fonctionnera le mieux pour votre audience."
     3102
     3103#: includes/trait-opti-behavior-ai-insights-views.php:137
     3104msgid "Forecast trends and user behavior to stay ahead of the curve and make data-driven decisions with confidence."
     3105msgstr "Prévoyez les tendances et le comportement des utilisateurs pour garder une longueur d'avance et prendre des décisions basées sur les données en toute confiance."
     3106
     3107#: includes/trait-opti-behavior-ai-insights-views.php:143
     3108msgid "What Our AI Will Analyze"
     3109msgstr "Ce que Notre IA Analysera"
     3110
     3111#: includes/trait-opti-behavior-ai-insights-views.php:151
     3112msgid "Sales Funnels"
     3113msgstr "Tunnels de Vente"
     3114
     3115#: includes/trait-opti-behavior-ai-insights-views.php:155
     3116msgid "Engagement Time"
     3117msgstr "Temps d'Engagement"
     3118
     3119#: includes/trait-opti-behavior-ai-insights-views.php:163
     3120msgid "Device Performance"
     3121msgstr "Performance par Appareil"
     3122
     3123#: includes/trait-opti-behavior-ai-insights-views.php:167
     3124msgid "Geographic Trends"
     3125msgstr "Tendances Géographiques"
     3126
     3127#: includes/trait-opti-behavior-ai-insights-views.php:171
     3128msgid "User Journeys"
     3129msgstr "Parcours Utilisateurs"
     3130
     3131#: includes/trait-opti-behavior-ai-insights-views.php:175
     3132msgid "Revenue Impact"
     3133msgstr "Impact sur les Revenus"
     3134
     3135#: includes/trait-opti-behavior-ai-insights-views.php:184
     3136msgid "Help Shape the Future!"
     3137msgstr "Aidez à façonner l'avenir !"
     3138
     3139#: includes/trait-opti-behavior-ai-insights-views.php:186
     3140msgid "We're building this amazing AI-powered feature and we'd love to hear from you! Your experience and feedback are invaluable in making this the best analytics tool possible."
     3141msgstr "Nous construisons cette fonctionnalité incroyable basée sur l'IA et nous aimerions avoir votre avis ! Votre expérience et vos retours sont inestimables pour faire de cet outil le meilleur possible."
     3142
     3143#: includes/trait-opti-behavior-ai-insights-views.php:191
     3144msgid "Share Your Ideas"
     3145msgstr "Partagez vos idées"
     3146
     3147#: includes/trait-opti-behavior-ai-insights-views.php:199
     3148msgid "%1$sEarly adopters%2$s who share feedback will get %3$sexclusive early access%4$s when we launch! 🎉"
     3149msgstr "%1$sLes premiers utilisateurs%2$s qui partagent leurs retours auront %3$sun accès anticipé exclusif%4$s lors du lancement ! 🎉"
     3150
     3151#: includes/trait-opti-behavior-ai-insights-views.php:212
     3152msgid "Development Roadmap"
     3153msgstr "Plan de Développement"
     3154
     3155#: includes/trait-opti-behavior-ai-insights-views.php:217
     3156msgid "Phase 1: Foundation"
     3157msgstr "Phase 1 : Fondation"
     3158
     3159#: includes/trait-opti-behavior-ai-insights-views.php:218
     3160msgid "Advanced analytics data collection and processing infrastructure"
     3161msgstr "Infrastructure de Collecte et de Traitement de Données"
     3162
     3163#: includes/trait-opti-behavior-ai-insights-views.php:224
     3164msgid "Phase 2: AI Integration"
     3165msgstr "Phase 2 : Intégration de l'IA"
     3166
     3167#: includes/trait-opti-behavior-ai-insights-views.php:225
     3168msgid "Machine learning models for pattern recognition and insights"
     3169msgstr "Modèles d'Apprentissage pour Reconnaissance de Modèles"
     3170
     3171#: includes/trait-opti-behavior-ai-insights-views.php:231
     3172msgid "Phase 3: Auto-Optimization"
     3173msgstr "Phase 3 : Auto-optimisation"
     3174
     3175#: includes/trait-opti-behavior-ai-insights-views.php:232
     3176msgid "Automated fixes and intelligent recommendations engine"
     3177msgstr "Moteur de corrections automatiques et de recommandations intelligentes"
     3178
     3179#: includes/trait-opti-behavior-ai-insights-views.php:238
     3180msgid "Phase 4: Launch"
     3181msgstr "Phase 4 : Lancement"
     3182
     3183#: includes/trait-opti-behavior-ai-insights-views.php:239
     3184msgid "Full AI-powered analytics suite with predictive capabilities"
     3185msgstr "Suite Complète d'Analyses IA avec Capacités Prédictives"
     3186
     3187#: includes/trait-opti-behavior-ajax-handlers.php:88
     3188msgid "Unauthorized"
     3189msgstr "Non autorisé"
     3190
     3191#: includes/trait-opti-behavior-ajax-handlers.php:534
     3192msgid "Recordings directory not found"
     3193msgstr "Répertoire d'enregistrements introuvable"
     3194
     3195#: includes/trait-opti-behavior-dashboard-views.php:41
     3196msgid "Real-time insights and user behavior analytics"
     3197msgstr "Analyses en Temps Réel"
     3198
     3199#: includes/trait-opti-behavior-dashboard-views.php:856
     3200msgid "Human"
     3201msgstr "Humain"
     3202
     3203#: includes/trait-opti-behavior-dashboard-views.php:856
     3204msgid "Spam"
     3205msgstr "Spam"
     3206
     3207#: includes/trait-opti-behavior-maintenance.php:88
     3208msgid "All opti-behavior Analytics tables were emptied successfully."
     3209msgstr "Toutes les tables d'analyses opti-behavior ont été vidées avec succès."
     3210
     3211#: includes/trait-opti-behavior-sessions-views.php:40
     3212msgid "Recordings (incl. Alarming Behavior) Remaining:"
     3213msgstr "Enregistrements restants (y compris comportements alarmants) :"
     3214
     3215#: includes/trait-opti-behavior-sessions-views.php:57
     3216msgid "Install Date → Today"
     3217msgstr "Date d'installation → Aujourd'hui"
     3218
     3219#: includes/trait-opti-behavior-sessions-views.php:82
     3220msgid "Select ∨"
     3221msgstr "Sélectionner ∨"
     3222
     3223#: includes/trait-opti-behavior-sessions-views.php:84
     3224msgid "Select None"
     3225msgstr "Ne rien sélectionner"
     3226
     3227#: includes/trait-opti-behavior-settings-views.php:123
     3228msgid "Configure opti-behavior: Heatmap, tracking, Data and display options"
     3229msgstr "Configurer opti-behavior : Options de carte de chaleur, suivi, données et affichage"
     3230
     3231#: includes/trait-opti-behavior-settings-views.php:248
     3232msgid "Configure how heatmap data is collected, tracked, and displayed"
     3233msgstr "Configurez comment les données de carte de chaleur sont collectées, suivies et affichées"
     3234
     3235#: includes/trait-opti-behavior-settings-views.php:255
     3236msgid "Controls which screen sizes are tracked for heatmap data"
     3237msgstr "Contrôle quelles tailles d'écran sont suivies pour les données de carte de chaleur"
     3238
     3239#: includes/trait-opti-behavior-settings-views.php:276
     3240msgid "Track category pages, archives, and other non-singular pages"
     3241msgstr "Suivre les pages de catégories, archives et autres pages non singulières"
     3242
     3243#: includes/trait-opti-behavior-settings-views.php:415
     3244msgid "Maximum number of data points to display on heatmaps"
     3245msgstr "Nombre maximum de points de données à afficher sur les cartes de chaleur"
     3246
     3247#: includes/trait-opti-behavior-settings-views.php:446
     3248msgid "Show interaction count information on heatmaps"
     3249msgstr "Afficher les informations de comptage des interactions sur les cartes de chaleur"
     3250
     3251#: includes/trait-opti-behavior-settings-views.php:525
     3252msgid "Permanently delete analytics data from your database. These actions cannot be undone."
     3253msgstr "Supprimer définitivement les données d'analyse de votre base de données. Ces actions ne peuvent pas être annulées."
     3254
     3255#: includes/trait-opti-behavior-settings-views.php:530
     3256msgid "This will permanently delete ALL analytics data from your database. This cannot be undone."
     3257msgstr "Ceci supprimera définitivement TOUTES les données d'analyse de votre base de données. Cette action est irréversible."
     3258
     3259#: includes/trait-opti-behavior-settings-views.php:540
     3260msgid "This will permanently delete ALL analytics data. Are you absolutely sure?"
     3261msgstr "Ceci supprimera définitivement TOUTES les données d'analyse. Êtes-vous absolument sûr ?"
     3262
     3263#: includes/trait-opti-behavior-settings-views.php:591
     3264msgid "Configure what happens when you uninstall this plugin. Choose whether to keep or remove all data and database tables."
     3265msgstr "Configurez ce qui se passe lorsque vous désinstallez ce plugin. Choisissez de conserver ou de supprimer toutes les données et tables de base de données."
     3266
     3267#: includes/trait-opti-behavior-settings-views.php:596
     3268msgid "These settings determine what happens when you uninstall the plugin."
     3269msgstr "Ces paramètres déterminent ce qui se passe lorsque vous désinstallez le plugin."
     3270
     3271#: includes/trait-opti-behavior-settings-views.php:597
     3272msgid "If you enable \"Delete on Uninstall\", the following will be permanently removed:"
     3273msgstr "Si vous activez \"Supprimer à la désinstallation\", les éléments suivants seront définitivement supprimés :"
     3274
     3275#: includes/trait-opti-behavior-settings-views.php:599
     3276msgid "All database tables (events, pages, sessions, analytics)"
     3277msgstr "Toutes les tables de base de données (événements, pages, sessions, analyses)"
     3278
     3279#: includes/trait-opti-behavior-settings-views.php:617
     3280msgid "Delete all data and tables on plugin uninstall"
     3281msgstr "Supprimer toutes les données et tables lors de la désinstallation du plugin"
     3282
     3283#: includes/trait-opti-behavior-settings-views.php:621
     3284msgid "When enabled, all plugin data and database tables will be permanently deleted when you uninstall the plugin. Leave unchecked if you plan to reinstall the plugin later and want to keep your data."
     3285msgstr "Lorsqu'activé, toutes les données du plugin et tables de base de données seront définitivement supprimées lors de la désinstallation. Laissez décoché si vous prévoyez de réinstaller le plugin plus tard et souhaitez conserver vos données."
     3286
     3287#: includes/trait-opti-behavior-settings-views.php:663
     3288msgid "Select your preferred language for the Opti-Behavior plugin admin interface."
     3289msgstr "Sélectionnez votre langue préférée pour l'interface d'administration du plugin Opti-Behavior."
     3290
     3291#: includes/trait-opti-behavior-settings-views.php:672
     3292#: includes/trait-opti-behavior-settings-views.php:741
     3293msgid "Language settings saved successfully. The new language will be applied on the next page load."
     3294msgstr "Paramètres de langue enregistrés avec succès. La nouvelle langue sera appliquée au prochain chargement de page."
     3295
     3296#: includes/trait-opti-behavior-settings-views.php:697
     3297msgid "This setting only affects the Opti-Behavior plugin admin interface. It does not change the language of your WordPress admin or other plugins."
     3298msgstr "Ce paramètre affecte uniquement l'interface d'administration du plugin Opti-Behavior. Il ne modifie pas la langue de votre administration WordPress ou d'autres plugins."
     3299
     3300#: includes/trait-opti-behavior-settings-views.php:875
     3301msgid "✅ Data Recovery Completed!"
     3302msgstr "✅ Récupération de données terminée !"
     3303
     3304#: includes/trait-opti-behavior-settings-views.php:876
     3305msgid "Found and recovered: %s events, %s accessible heatmaps."
     3306msgstr "Trouvé et récupéré : %s événements, %s cartes de chaleur accessibles."
     3307
     3308#: includes/trait-opti-behavior-settings-views.php:878
     3309msgid "Issues fixed: %s"
     3310msgstr "Problèmes résolus : %s"
     3311
     3312#: includes/trait-opti-behavior-settings-views.php:886
     3313msgid "✅ Data Check Completed!"
     3314msgstr "✅ Vérification des données terminée !"
     3315
     3316#: includes/trait-opti-behavior-settings-views.php:887
     3317msgid "No issues detected. Your data is healthy with %s events."
     3318msgstr "Aucun problème détecté. Vos données sont saines avec %s événements."
     3319
     3320#: includes/trait-opti-behavior-settings-views.php:920
     3321msgid "This plugin includes automatic data protection that prevents data loss when the plugin is disabled, renamed, or moved. The system runs integrity checks and automatically recovers your data if any issues are detected."
     3322msgstr "Ce plugin inclut une protection automatique des données qui empêche la perte de données lorsque le plugin est désactivé, renommé ou déplacé. Le système effectue des vérifications d'intégrité et récupère automatiquement vos données si des problèmes sont détectés."
     3323
     3324#: includes/trait-opti-behavior-settings-views.php:976
     3325msgid "Click this button if you suspect data integrity issues. The system will scan and automatically fix any problems."
     3326msgstr "Cliquez sur ce bouton si vous suspectez des problèmes d'intégrité des données. Le système analysera et corrigera automatiquement les problèmes."
     3327
     3328#: includes/trait-opti-behavior-settings-views.php:1001
     3329msgid "Configure debug logging for PHP and JavaScript. Enable debugging to troubleshoot issues and monitor plugin behavior. Logs are stored securely and can be viewed or downloaded below."
     3330msgstr "Configurez la journalisation de débogage pour PHP et JavaScript. Activez le débogage pour résoudre les problèmes et surveiller le comportement du plugin. Les journaux sont stockés en toute sécurité et peuvent être consultés ou téléchargés ci-dessous."
     3331
     3332#: includes/trait-opti-behavior-settings-views.php:1318
     3333msgid "Choose how to store recordings and events data"
     3334msgstr "Choisissez comment stocker les enregistrements et les données d'événements"
     3335
     3336#: includes/trait-opti-behavior-settings-views.php:1325
     3337msgid "File storage recommended for high-traffic sites (30K+ visits/day)"
     3338msgstr "Stockage de fichiers recommandé pour les sites à fort trafic (30K+ visites/jour)"
     3339
     3340#: includes/trait-opti-behavior-settings-views.php:1367
     3341msgid "Configure how long to keep data before automatic deletion"
     3342msgstr "Configurez la durée de conservation des données avant suppression automatique"
     3343
     3344#: includes/trait-opti-behavior-settings-views.php:1379
     3345msgid "days"
     3346msgstr "jours"
     3347
     3348#: includes/trait-opti-behavior-settings-views.php:1415
     3349msgid "Delete oldest files when this limit is exceeded"
     3350msgstr "Supprimer les fichiers les plus anciens lorsque cette limite est dépassée"
     3351
     3352#: includes/trait-opti-behavior-settings-views.php:1465
     3353msgid "Remove all recording files that don't have corresponding database entries"
     3354msgstr "Supprimer tous les fichiers d'enregistrement qui n'ont pas d'entrées de base de données correspondantes"
     3355
     3356#: includes/trait-opti-behavior-settings-views.php:1480
     3357msgid "Clean up recording files that have no database records"
     3358msgstr "Nettoyer les fichiers d'enregistrement qui n'ont pas d'enregistrements de base de données"
     3359
     3360#: includes/trait-opti-behavior-settings-views.php:1495
     3361msgid "This will permanently delete all .json.gz files in the recordings directory that don't have matching database records."
     3362msgstr "Ceci supprimera définitivement tous les fichiers .json.gz du répertoire d'enregistrements qui n'ont pas d'enregistrements de base de données correspondants."
     3363
     3364#: includes/trait-opti-behavior-settings-views.php:1558
     3365msgid "Upgrade to unlock advanced file storage capabilities including:"
     3366msgstr "Passez à la version Pro pour débloquer les capacités avancées de stockage de fichiers, notamment :"
     3367
     3368#: includes/trait-opti-behavior-settings-views.php:1561
     3369msgid "File-based storage to prevent database bloat"
     3370msgstr "Stockage basé sur des fichiers pour éviter l'encombrement de la base de données"
     3371
     3372#: includes/trait-opti-behavior-settings-views.php:1739
     3373msgid "Configure dashboard behavior, traffic classification, and user intent detection rules."
     3374msgstr "Configurez le comportement du tableau de bord, la classification du trafic et les règles de détection d'intention utilisateur."
     3375
     3376#: includes/trait-opti-behavior-settings-views.php:1789
     3377msgid "Configure how Opti-Behavior classifies different types of traffic."
     3378msgstr "Configurez comment Opti-Behavior classe les différents types de trafic."
     3379
     3380#: includes/trait-opti-behavior-settings-views.php:1799
     3381msgid "Detect and classify legitimate bots (search engines, social crawlers, SEO tools)."
     3382msgstr "Détecter et classifier les bots légitimes (moteurs de recherche, robots sociaux, outils SEO)."
     3383
     3384#: includes/trait-opti-behavior-settings-views.php:1810
     3385msgid "Detect headless browsers, automation tools, and scripts (Selenium, Puppeteer, curl, etc.)."
     3386msgstr "Détecter les navigateurs sans interface, outils d'automatisation et scripts (Selenium, Puppeteer, curl, etc.)."
     3387
     3388#: includes/trait-opti-behavior-settings-views.php:1821
     3389msgid "Detect low-quality traffic based on session behavior patterns."
     3390msgstr "Détecter le trafic de faible qualité basé sur les modèles de comportement de session."
     3391
     3392#: includes/trait-opti-behavior-settings-views.php:1830
     3393msgid "Sessions shorter than this duration may be classified as spam."
     3394msgstr "Les sessions plus courtes que cette durée peuvent être classées comme spam."
     3395
     3396#: includes/trait-opti-behavior-settings-views.php:1839
     3397msgid "Bounce rate threshold for spam detection (100% = immediate exit)."
     3398msgstr "Seuil de taux de rebond pour la détection de spam (100% = sortie immédiate)."
     3399
     3400#: includes/trait-opti-behavior-settings-views.php:1847
     3401msgid "Enter custom user agent patterns to detect as bots (one per line). Example: MyCustomBot"
     3402msgstr "Entrez des modèles d'agent utilisateur personnalisés à détecter comme bots (un par ligne). Exemple : MyCustomBot"
     3403
     3404#: includes/trait-opti-behavior-settings-views.php:1886
     3405msgid "Define rules for classifying user intent levels based on engagement metrics."
     3406msgstr "Définissez des règles pour classifier les niveaux d'intention utilisateur basés sur les métriques d'engagement."
     3407
     3408#: includes/trait-opti-behavior-settings-views.php:1892
     3409msgid "Low Intent"
     3410msgstr "Faible Intention"
     3411
     3412#: includes/trait-opti-behavior-settings-views.php:1901
     3413msgid "Maximum time spent on page for low intent classification."
     3414msgstr "Temps maximum passé sur la page pour la classification d'intention faible."
     3415
     3416#: includes/trait-opti-behavior-settings-views.php:1905
     3417#: includes/trait-opti-behavior-settings-views.php:1942
     3418#: includes/trait-opti-behavior-settings-views.php:1979
     3419msgid "Number of Clicks"
     3420msgstr "Nombre de clics"
     3421
     3422#: includes/trait-opti-behavior-settings-views.php:1910
     3423msgid "Maximum number of clicks for low intent classification."
     3424msgstr "Nombre maximum de clics pour la classification d'intention faible."
     3425
     3426#: includes/trait-opti-behavior-settings-views.php:1914
     3427#: includes/trait-opti-behavior-settings-views.php:1951
     3428#: includes/trait-opti-behavior-settings-views.php:1988
     3429msgid "Scroll Depth (%)"
     3430msgstr "Profondeur de Défilement (%)"
     3431
     3432#: includes/trait-opti-behavior-settings-views.php:1919
     3433msgid "Maximum scroll depth percentage for low intent classification."
     3434msgstr "Pourcentage maximum de profondeur de défilement pour la classification d'intention faible."
     3435
     3436#: includes/trait-opti-behavior-settings-views.php:1929
     3437msgid "Medium Intent"
     3438msgstr "Intention Moyenne"
     3439
     3440#: includes/trait-opti-behavior-settings-views.php:1938
     3441msgid "Minimum time spent on page for medium intent classification."
     3442msgstr "Temps minimum passé sur la page pour la classification d'intention moyenne."
     3443
     3444#: includes/trait-opti-behavior-settings-views.php:1947
     3445msgid "Minimum number of clicks for medium intent classification."
     3446msgstr "Nombre minimum de clics pour la classification d'intention moyenne."
     3447
     3448#: includes/trait-opti-behavior-settings-views.php:1956
     3449msgid "Minimum scroll depth percentage for medium intent classification."
     3450msgstr "Pourcentage minimum de profondeur de défilement pour la classification d'intention moyenne."
     3451
     3452#: includes/trait-opti-behavior-settings-views.php:1966
     3453msgid "High Intent"
     3454msgstr "Forte Intention"
     3455
     3456#: includes/trait-opti-behavior-settings-views.php:1975
     3457msgid "Minimum time spent on page for high intent classification."
     3458msgstr "Temps minimum passé sur la page pour la classification d'intention élevée."
     3459
     3460#: includes/trait-opti-behavior-settings-views.php:1984
     3461msgid "Minimum number of clicks for high intent classification."
     3462msgstr "Nombre minimum de clics pour la classification d'intention élevée."
     3463
     3464#: includes/trait-opti-behavior-settings-views.php:1993
     3465msgid "Minimum scroll depth percentage for high intent classification."
     3466msgstr "Pourcentage minimum de profondeur de défilement pour la classification d'intention élevée."
     3467
     3468#: includes/trait-opti-behavior-settings-views.php:2040
     3469msgid "Configure how session recordings are captured and stored"
     3470msgstr "Configuration capture et stockage"
     3471
     3472#: includes/trait-opti-behavior-settings-views.php:2073
     3473msgid "How often to capture mouse movements (lower = more data)"
     3474msgstr "Fréquence capture (+ bas = + données)"
     3475
     3476#: includes/trait-opti-behavior-settings-views.php:2098
     3477msgid "Hide all user input for privacy (recommended)"
     3478msgstr "Masquer saisies (recommandé)"
     3479
     3480#: includes/trait-opti-behavior-settings-views.php:2183
     3481msgid "Your installation is not registered. Session recordings will use offline encryption only."
     3482msgstr "Installation non enregistrée. Chiffrement hors ligne uniquement."
     3483
     3484#: includes/trait-opti-behavior-settings-views.php:2230
     3485msgid "You have exceeded your monthly quota! New recordings will not be saved until next month or you upgrade to Pro."
     3486msgstr "Quota dépassé ! Enregistrements bloqués jusqu'au mois prochain ou mise à niveau Pro."
     3487
     3488#: includes/trait-opti-behavior-settings-views.php:2235
     3489msgid "You're running low on quota. Consider upgrading to Pro for unlimited recordings."
     3490msgstr "Quota faible. Envisagez Pro pour enregistrements illimités."
     3491
     3492#: Opti-Behavior.php:40
     3493msgid "Opti-Behavior requires PHP version %1$s or higher. You are running PHP version %2$s. Please upgrade PHP to activate this plugin."
     3494msgstr "Opti-Behavior nécessite PHP version %1$s ou supérieure. Vous utilisez PHP version %2$s. Veuillez mettre à jour PHP pour activer ce plugin."
     3495
     3496#: Opti-Behavior.php:66
     3497msgid "Fail to activate. Another version of opti-behavior Heatmap is already active."
     3498msgstr "Échec de l'activation. Une autre version d'opti-behavior Heatmap est déjà active."
     3499
     3500#: views/heatmaps-views.php:122
     3501msgid "As visitors interact, this table will populate."
     3502msgstr "Au fur et à mesure que les visiteurs interagissent, ce tableau se remplira."
     3503
     3504#: admin/class-opti-behavior-heatmap-dashboard.php:282
     3505msgid "Mobile"
     3506msgstr "Mobile"
     3507
     3508#: admin/class-opti-behavior-heatmap-dashboard.php:283
     3509msgid "PC"
     3510msgstr "PC"
     3511
     3512#: admin/class-opti-behavior-heatmap-dashboard.php:293
     3513msgid "No page data available"
     3514msgstr "Aucune donnée de page disponible"
     3515
     3516#: admin/class-opti-behavior-heatmap-dashboard.php:294
     3517msgid "No visitor data available"
     3518msgstr "Aucune donnée de visiteur disponible"
     3519
     3520#: admin/class-opti-behavior-heatmap-dashboard.php:295
     3521msgid "No browser data available"
     3522msgstr "Aucune Donnée de Navigateur"
     3523
     3524#: admin/class-opti-behavior-heatmap-dashboard.php:296
     3525msgid "No device data available"
     3526msgstr "Aucune donnée d'appareil disponible"
     3527
     3528#: admin/class-opti-behavior-heatmap-dashboard.php:297
     3529msgid "Data will appear as visitors interact with your site."
     3530msgstr "Les données apparaîtront lorsque les visiteurs interagiront avec votre site."
     3531
     3532#: admin/class-opti-behavior-heatmap-dashboard.php:297
     3533msgid "No data yet"
     3534msgstr "Pas encore de données"
     3535
     3536#: includes/class-opti-behavior-heatmap-analytics.php:867
     3537msgid "Home Page"
     3538msgstr "Page d'accueil"
     3539
     3540#: views/dashboard-views.php:74
     3541msgid "📊 Analytics Dashboard"
     3542msgstr "📊 Tableau de bord analytique"
     3543
     3544#: views/dashboard-views.php:76
     3545msgid "Real-time insights and user behavior analytics"
     3546msgstr "Analyses en Temps Réel"
     3547
     3548#: views/dashboard-views.php:92
     3549msgid "Apply"
     3550msgstr "Appliquer"
     3551
     3552#: views/dashboard-views.php:96
     3553msgid "Refresh"
     3554msgstr "Actualiser"
     3555
     3556#: views/dashboard-views.php:117
     3557msgid "%s%% vs last period"
     3558msgstr "%s%% vs période précédente"
     3559
     3560#: views/dashboard-views.php:198
     3561msgid "Traffic Overview"
     3562msgstr "Aperçu du Trafic"
     3563
     3564#: views/dashboard-views.php:209
     3565msgid "Browsers"
     3566msgstr "Navigateurs"
     3567
     3568#: views/dashboard-views.php:219
     3569msgid "Device Types"
     3570msgstr "Types d'Appareils"
     3571
     3572#: views/dashboard-views.php:229
     3573msgid "Operating Systems"
     3574msgstr "Systèmes d'Exploitation"
     3575
     3576#: views/dashboard-views.php:241
     3577msgid "User Intent"
     3578msgstr "Intention de l'Utilisateur"
     3579
     3580#: views/dashboard-views.php:264
     3581msgid "Daily Freq"
     3582msgstr "Fréq. quotidienne"
     3583
     3584#: views/dashboard-views.php:265
     3585msgid "Avg Session"
     3586msgstr "Session moy."
     3587
     3588#: views/dashboard-views.php:268
     3589msgid "Pages/Sess"
     3590msgstr "Pages/Sess"
     3591
     3592#: views/dashboard-views.php:289
     3593msgid "Top Countries"
     3594msgstr "Principaux Pays"
     3595
     3596#: views/dashboard-views.php:300
     3597msgid "Live"
     3598msgstr "En Direct"
     3599
     3600#: views/dashboard-views.php:301
     3601msgid "Real-time Visitors"
     3602msgstr "Visiteurs en Temps Réel"
     3603
     3604#: views/dashboard-views.php:317
     3605msgid "—"
     3606msgstr "—"
     3607
     3608#: views/dashboard-views.php:333
     3609msgid "No active visitors right now"
     3610msgstr "Aucun Visiteur Actif"
     3611
     3612#: views/dashboard-views.php:334
     3613msgid "Traffic updates in real-time."
     3614msgstr "Le Trafic se Met à Jour en Temps Réel."
     3615
     3616#: views/dashboard-views.php:343
     3617msgid "Real-time Visitor Map"
     3618msgstr "Carte des Visiteurs en Temps Réel"
     3619
     3620#: views/dashboard-views.php:358
     3621msgid "Top Pages"
     3622msgstr "Pages Principales"
     3623
     3624#: views/dashboard-views.php:366
     3625msgid "No page views yet"
     3626msgstr "Aucune page vue pour le moment"
     3627
     3628#: views/dashboard-views.php:367
     3629msgid "Once visitors view pages, you'll see them here."
     3630msgstr "Les Pages Vues Apparaîtront Ici."
     3631
     3632#: views/dashboard-views.php:405
     3633msgid "PC"
     3634msgstr "PC"
     3635
     3636#: views/dashboard-views.php:409
     3637msgid "Mobile"
     3638msgstr "Mobile"
     3639
     3640#: views/dashboard-views.php:446
     3641msgid "Top Referrers"
     3642msgstr "Principaux Référents"
     3643
     3644#: includes/trait-opti-behavior-dashboard-views.php:257
     3645msgid "No engaged users for this period"
     3646msgstr "Aucun Utilisateur Engagé pour cette Période"
     3647
     3648#: includes/trait-opti-behavior-dashboard-views.php:258
     3649msgid "As visitors engage more, you'll see them ranked here."
     3650msgstr "Lorsque les visiteurs s'engagent davantage, vous les verrez classés ici."
     3651
     3652#: includes/trait-opti-behavior-dashboard-views.php:279
     3653msgid "Once visitors view pages, you will see them here."
     3654msgstr "Les Pages Vues Apparaîtront Ici."
     3655
     3656#: includes/trait-opti-behavior-dashboard-views.php:438
     3657msgid "No referrer data available"
     3658msgstr "Aucune Donnée de Référent"
     3659
     3660#: includes/trait-opti-behavior-dashboard-views.php:439
     3661#: includes/trait-opti-behavior-dashboard-views.php:484
     3662#: includes/trait-opti-behavior-dashboard-views.php:564
     3663#: includes/trait-opti-behavior-dashboard-views.php:901
     3664#: includes/trait-opti-behavior-dashboard-views.php:1165
     3665#: includes/trait-opti-behavior-dashboard-views.php:1200
     3666msgid "Try broadening the date range or check back later."
     3667msgstr "Élargir la Période ou Revenir Plus Tard."
     3668
     3669#: includes/trait-opti-behavior-dashboard-views.php:1158
     3670msgid "New vs Returning Visitors"
     3671msgstr "Nouveaux vs Visiteurs Récurrents"
     3672
     3673msgid "New Visitors"
     3674msgstr "Nouveaux Visiteurs"
     3675
     3676msgid "Returning Visitors"
     3677msgstr "Visiteurs Récurrents"
     3678
     3679#: includes/trait-opti-behavior-dashboard-views.php:1193
     3680msgid "Visited Directories"
     3681msgstr "Répertoires Visités"
     3682
     3683msgid "Posts"
     3684msgstr "Articles"
     3685
     3686#: includes/trait-opti-behavior-dashboard-views.php:1227
     3687msgid "Visitor Authentication"
     3688msgstr "Authentification des Visiteurs"
     3689
     3690msgid "Total Visitors"
     3691msgstr "Visiteurs Totaux"
     3692
     3693msgid "Logged In Visitors"
     3694msgstr "Visiteurs Connectés"
     3695
     3696msgid "New Registrations"
     3697msgstr "Nouvelles Inscriptions"
     3698
     3699msgid "No country data available"
     3700msgstr "Aucune Donnée de Pays"
     3701
     3702msgid "Try broadening the date range or check back later."
     3703msgstr "Élargir la Période ou Revenir Plus Tard."
     3704
     3705msgid "No screen resolution data available"
     3706msgstr "Aucune Donnée de Résolution"
     3707
     3708msgid "Page"
     3709msgstr "Page"
     3710
     3711msgid "Interactions"
     3712msgstr "Interactions"
     3713
     3714msgid "Sessions"
     3715msgstr "Sessions"
     3716
     3717msgid "Device Split"
     3718msgstr "Répartition des appareils"
     3719
     3720msgid "Last Updated"
     3721msgstr "Dernière mise à jour"
     3722
     3723msgid "Actions"
     3724msgstr "Actions"
     3725
     3726#. translators: %s: number of desktop views
     3727msgid "Desktop (%s)"
     3728msgstr "Bureau (%s)"
     3729
     3730#. translators: %s: number of mobile views
     3731msgid "Mobile (%s)"
     3732msgstr "Mobile (%s)"
     3733
     3734msgid "Open desktop heatmap"
     3735msgstr "Ouvrir la carte de chaleur bureau"
     3736
     3737msgid "Open mobile heatmap"
     3738msgstr "Ouvrir la carte de chaleur mobile"
     3739
     3740msgid "No heatmaps available."
     3741msgstr "Aucune carte de chaleur disponible."
     3742
     3743#. translators: %1$s: start range, %2$s: end range, %3$s: total items, %4$s: current page, %5$s: total pages
     3744msgid "Showing %1$s–%2$s of %3$s · Page %4$s / %5$s"
     3745msgstr "Affichage de %1$s–%2$s sur %3$s · Page %4$s / %5$s"
     3746
     3747msgid "First"
     3748msgstr "Premier"
     3749
     3750msgid "Prev"
     3751msgstr "Précédent"
     3752
     3753msgid "Next"
     3754msgstr "Suivant"
     3755
     3756msgid "Last"
     3757msgstr "Dernier"
     3758
     3759msgid "Total Heatmaps"
     3760msgstr "Total des Cartes de Chaleur"
     3761
     3762msgid "Avg. Time on Page"
     3763msgstr "Temps Moy. Page"
     3764
     3765msgid "Hottest Page"
     3766msgstr "Page la Plus Chaude"
     3767
     3768msgid "Click-through Rate"
     3769msgstr "Taux de Clic"
     3770
     3771msgid "Available Heatmaps"
     3772msgstr "Cartes de Chaleur Disponibles"
     3773
     3774msgid "View detailed heatmap analysis"
     3775msgstr "Voir l'analyse détaillée de la carte de chaleur"
     3776
     3777msgid "Back to Heatmaps"
     3778msgstr "Retour aux Cartes"
     3779
     3780msgid "recordings"
     3781msgstr "enregistrements"
     3782
     3783msgid "Refresh"
     3784msgstr "Actualiser"
     3785
     3786msgid "Desktop"
     3787msgstr "Bureau"
     3788
     3789msgid "Mobile"
     3790msgstr "Mobile"
     3791
     3792msgid "Tablet"
     3793msgstr "Tablette"
     3794
     3795msgid "Click Heatmap"
     3796msgstr "Carte Clics"
     3797
     3798msgid "Move Heatmap"
     3799msgstr "Carte Mouvements"
     3800
     3801msgid "Scroll Heatmap"
     3802msgstr "Carte Défilement"
     3803
     3804msgid "Views:"
     3805msgstr "Vues :"
     3806
     3807msgid "Clicks:"
     3808msgstr "Clics :"
     3809
     3810msgid "Avg Time:"
     3811msgstr "Temps moyen :"
     3812
     3813msgid "No heatmap data available for this page and device combination."
     3814msgstr "Aucune donnée pour cette page/appareil."
     3815
     3816msgid "Top Clicked Elements"
     3817msgstr "Éléments Plus Cliqués"
     3818
     3819msgid "Simple"
     3820msgstr "Simple"
     3821
     3822msgid "Detailed"
     3823msgstr "Détaillé"
     3824
     3825msgid "No heatmap data available"
     3826msgstr "Aucune donnée disponible"
     3827
     3828
     3829#: includes/trait-opti-behavior-settings-views.php:698
     3830msgid "Language settings saved successfully. Reloading page to apply new language..."
     3831msgstr "Paramètres de Langue Enregistrés avec Succès. Rechargement pour Appliquer la Nouvelle Langue..."
  • opti-behavior/trunk/readme.txt

    r3406294 r3408429  
    55
    66Requires at least: 5.8
    7 Tested up to: 6.9
     7Tested up to: 6.8
    88Requires PHP: 7.4
    9 Stable tag: 1.0.6
     9Stable tag: 1.0.7
    1010License: GPLv2 or later
    1111License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    246246
    247247== Changelog ==
     248
     249= 1.0.7 - 2025-12-02 =
     250* Enhancement: Added French language translations for improved internationalization
     251* Fix: Resolved sendPageView function issues for accurate page tracking
     252* Fix: Corrected Returning Visitors calculation and display
     253* Fix: Fixed Logged In Visitors detection and counting
     254* Feature: Display username for logged-in visitors in Top Engaged Users widget
     255* Enhancement: Extended device type support for all device categories (desktop, mobile, tablet, PC)
     256* Code Quality: WordPress coding standards compliance improvements
     257* Code Quality: Added debug logging controls via settings page
     258* Security: Fixed nonce verification warnings
     259* Security: Enhanced prepared SQL statements with proper phpcs annotations
    248260
    249261= 1.0.6 - 2025-11-30 =
     
    317329== Upgrade Notice ==
    318330
     331= 1.0.7 =
     332Important update with French translations, visitor tracking improvements, and username display in Top Engaged Users. Enhanced coding standards compliance and debug logging controls.
     333
    319334= 1.0.5 =
    320335WordPress.org submission update: Fixes debug code, timezone handling, and i18n compliance. All users should update for WordPress coding standards compliance.
  • opti-behavior/trunk/views/dashboard-views.php

    r3399182 r3408429  
    7272        <div class="dashboard-header">
    7373            <div class="dashboard-title-section">
    74                 <h1 class="dashboard-title">📊 Analytics Dashboard</h1>
     74                <h1 class="dashboard-title"><?php echo esc_html__( '📊 Analytics Dashboard', 'opti-behavior' ); ?></h1>
    7575                <div class="dashboard-subtitle">
    76                     <span class="subtitle-text">Real-time insights and user behavior analytics</span>
     76                    <span class="subtitle-text"><?php echo esc_html__( 'Real-time insights and user behavior analytics', 'opti-behavior' ); ?></span>
    7777                </div>
    7878            </div>
    7979            <div class="dashboard-controls">
    8080                <select class="period-selector" id="dashboard-period">
    81                     <option value="today" <?php selected( $period, 'today' ); ?>>Today</option>
    82                     <option value="yesterday" <?php selected( $period, 'yesterday' ); ?>>Yesterday</option>
    83                     <option value="last7days" <?php selected( $period, 'last7days' ); ?>>Last 7 Days</option>
    84                     <option value="last30days" <?php selected( $period, 'last30days' ); ?>>Last 30 Days</option>
    85                     <option value="thismonth" <?php selected( $period, 'thismonth' ); ?>>This Month</option>
    86                     <option value="custom" <?php selected( $period, 'custom' ); ?>>Custom Range</option>
     81                    <option value="today" <?php selected( $period, 'today' ); ?>><?php echo esc_html__( 'Today', 'opti-behavior' ); ?></option>
     82                    <option value="yesterday" <?php selected( $period, 'yesterday' ); ?>><?php echo esc_html__( 'Yesterday', 'opti-behavior' ); ?></option>
     83                    <option value="last7days" <?php selected( $period, 'last7days' ); ?>><?php echo esc_html__( 'Last 7 Days', 'opti-behavior' ); ?></option>
     84                    <option value="last30days" <?php selected( $period, 'last30days' ); ?>><?php echo esc_html__( 'Last 30 Days', 'opti-behavior' ); ?></option>
     85                    <option value="thismonth" <?php selected( $period, 'thismonth' ); ?>><?php echo esc_html__( 'This Month', 'opti-behavior' ); ?></option>
     86                    <option value="custom" <?php selected( $period, 'custom' ); ?>><?php echo esc_html__( 'Custom Range', 'opti-behavior' ); ?></option>
    8787                </select>
    8888                <input type="date" id="start-date" value="<?php echo esc_attr( $start_val ); ?>" />
     
    9090                <button class="refresh-btn" id="apply-range">
    9191                    <span class="refresh-icon">📅</span>
    92                     Apply
     92                    <?php echo esc_html__( 'Apply', 'opti-behavior' ); ?>
    9393                </button>
    9494                <button class="refresh-btn" id="refresh-dashboard">
    9595                    <span class="refresh-icon">🔄</span>
    96                     Refresh
     96                    <?php echo esc_html__( 'Refresh', 'opti-behavior' ); ?>
    9797                </button>
    9898            </div>
     
    114114        $fmt = function ( $delta ) {
    115115            $sign = ( $delta > 0 ? '+' : ( $delta < 0 ? '' : '' ) );
    116             return $sign . (int) round( $delta ) . '% vs last period';
     116            /* translators: %s: percentage change value with sign */
     117            return sprintf( esc_html__( '%s%% vs last period', 'opti-behavior' ), $sign . (int) round( $delta ) );
    117118        };
    118119        $cls = function ( $delta ) {
     
    125126                <div class="stat-content">
    126127                    <div class="stat-value"><?php echo esc_html( $stats['visitors'] ); ?></div>
    127                     <div class="stat-label">Visitors</div>
     128                    <div class="stat-label"><?php echo esc_html__( 'Visitors', 'opti-behavior' ); ?></div>
    128129                    <div class="stat-change <?php echo esc_attr( $cls( $changes['visitors'] ?? 0 ) ); ?>"><?php echo esc_html( $fmt( $changes['visitors'] ?? 0 ) ); ?></div>
    129130                </div>
     
    133134                <div class="stat-content">
    134135                    <div class="stat-value"><?php echo esc_html( $stats['sessions'] ); ?></div>
    135                     <div class="stat-label">Sessions</div>
     136                    <div class="stat-label"><?php echo esc_html__( 'Sessions', 'opti-behavior' ); ?></div>
    136137                    <div class="stat-change <?php echo esc_attr( $cls( $changes['sessions'] ?? 0 ) ); ?>"><?php echo esc_html( $fmt( $changes['sessions'] ?? 0 ) ); ?></div>
    137138                </div>
     
    141142                <div class="stat-content">
    142143                    <div class="stat-value"><?php echo esc_html( $stats['pageviews'] ); ?></div>
    143                     <div class="stat-label">Page Views</div>
     144                    <div class="stat-label"><?php echo esc_html__( 'Page Views', 'opti-behavior' ); ?></div>
    144145                    <div class="stat-change <?php echo esc_attr( $cls( $changes['pageviews'] ?? 0 ) ); ?>"><?php echo esc_html( $fmt( $changes['pageviews'] ?? 0 ) ); ?></div>
    145146                </div>
     
    154155                        ?>
    155156                    </div>
    156                     <div class="stat-label">Avg. Session Time</div>
     157                    <div class="stat-label"><?php echo esc_html__( 'Avg. Session Time', 'opti-behavior' ); ?></div>
    157158                    <div class="stat-change <?php echo esc_attr( $cls( $changes['avg_session_time'] ?? 0 ) ); ?>"><?php echo esc_html( $fmt( $changes['avg_session_time'] ?? 0 ) ); ?></div>
    158159                </div>
     
    162163                <div class="stat-content">
    163164                    <div class="stat-value"><?php echo esc_html( $stats['avg_scroll_depth'] ); ?>%</div>
    164                     <div class="stat-label">Avg. Scroll Depth</div>
     165                    <div class="stat-label"><?php echo esc_html__( 'Avg. Scroll Depth', 'opti-behavior' ); ?></div>
    165166                    <div class="stat-change <?php echo esc_attr( $cls( $changes['avg_scroll_depth'] ?? 0 ) ); ?>"><?php echo esc_html( $fmt( $changes['avg_scroll_depth'] ?? 0 ) ); ?></div>
    166167                </div>
     
    170171                <div class="stat-content">
    171172                    <div class="stat-value"><?php echo esc_html( $stats['bounce_rate'] ); ?>%</div>
    172                     <div class="stat-label">Bounce Rate</div>
     173                    <div class="stat-label"><?php echo esc_html__( 'Bounce Rate', 'opti-behavior' ); ?></div>
    173174                    <div class="stat-change <?php echo esc_attr( $cls( $changes['bounce_rate'] ?? 0 ) ); ?>"><?php echo esc_html( $fmt( $changes['bounce_rate'] ?? 0 ) ); ?></div>
    174175                </div>
     
    196197                        <h3 class="widget-title">
    197198                            <span class="widget-icon">📈</span>
    198                             Traffic Overview
     199                            <?php echo esc_html__( 'Traffic Overview', 'opti-behavior' ); ?>
    199200                        </h3>
    200201                    </div>
     
    207208                    <?php opti_behavior_render_widget_loading(); ?>
    208209                    <div class="widget-header">
    209                         <h3 class="widget-title"><span class="widget-icon">🧭</span> Browsers</h3>
     210                        <h3 class="widget-title"><span class="widget-icon">🧭</span> <?php echo esc_html__( 'Browsers', 'opti-behavior' ); ?></h3>
    210211                    </div>
    211212                    <div class="widget-content compact">
     
    217218                    <?php opti_behavior_render_widget_loading(); ?>
    218219                    <div class="widget-header">
    219                         <h3 class="widget-title"><span class="widget-icon">📱</span> Device Types</h3>
     220                        <h3 class="widget-title"><span class="widget-icon">📱</span> <?php echo esc_html__( 'Device Types', 'opti-behavior' ); ?></h3>
    220221                    </div>
    221222                    <div class="widget-content compact">
     
    227228                    <?php opti_behavior_render_widget_loading(); ?>
    228229                    <div class="widget-header">
    229                         <h3 class="widget-title"><span class="widget-icon">💻</span> Operating Systems</h3>
     230                        <h3 class="widget-title"><span class="widget-icon">💻</span> <?php echo esc_html__( 'Operating Systems', 'opti-behavior' ); ?></h3>
    230231                    </div>
    231232                    <div class="widget-content compact">
     
    239240                        <h3 class="widget-title">
    240241                            <span class="widget-icon">🎯</span>
    241                             User Intent
     242                            <?php echo esc_html__( 'User Intent', 'opti-behavior' ); ?>
    242243                        </h3>
    243244                    </div>
     
    262263                                    <th>#</th>
    263264                                    <th><?php echo esc_html__( 'Visitor', 'opti-behavior' ); ?></th>
    264                                     <th title="Average sessions per active day">Daily Freq</th>
    265                                     <th title="Average time per session">Avg Session</th>
     265                                    <th title="Average sessions per active day"><?php echo esc_html__( 'Daily Freq', 'opti-behavior' ); ?></th>
     266                                    <th title="Average time per session"><?php echo esc_html__( 'Avg Session', 'opti-behavior' ); ?></th>
    266267                                    <th><?php echo esc_html__( 'Total Time', 'opti-behavior' ); ?></th>
    267268                                    <th><?php echo esc_html__( 'Sessions', 'opti-behavior' ); ?></th>
    268                                     <th title="Pages per session">Pages/Sess</th>
     269                                    <th title="Pages per session"><?php echo esc_html__( 'Pages/Sess', 'opti-behavior' ); ?></th>
    269270                                    <th><?php echo esc_html__( 'Country', 'opti-behavior' ); ?></th>
    270271                                    <th><?php echo esc_html__( 'Last Seen', 'opti-behavior' ); ?></th>
     
    287288                    <?php opti_behavior_render_widget_loading(); ?>
    288289                    <div class="widget-header">
    289                         <h3 class="widget-title"><span class="widget-icon">🌍</span> Top Countries</h3>
     290                        <h3 class="widget-title"><span class="widget-icon">🌍</span> <?php echo esc_html__( 'Top Countries', 'opti-behavior' ); ?></h3>
    290291                    </div>
    291292                    <div class="widget-content compact">
     
    298299                    <div class="widget-header">
    299300                        <h3 class="widget-title">
    300                             <span class="live-badge" aria-label="Live"><span class="live-dot" aria-hidden="true"></span>Live</span>
    301                             Real-time Visitors
     301                            <span class="live-badge" aria-label="Live"><span class="live-dot" aria-hidden="true"></span><?php echo esc_html__( 'Live', 'opti-behavior' ); ?></span>
     302                            <?php echo esc_html__( 'Real-time Visitors', 'opti-behavior' ); ?>
    302303                            <span class="visitor-count"><?php echo count( $dashboard_data['realtime']['active_visitors'] ); ?></span>
    303304                        </h3>
     
    315316                                    $ip_raw = trim( $ip_raw );
    316317                                    if ( '' === $ip_raw ) {
    317                                         echo '-';
     318                                        echo esc_html__( '—', 'opti-behavior' );
    318319                                    } elseif ( false !== strpos( $ip_raw, ':' ) ) {
    319320                                        $start = substr( $ip_raw, 0, 7 );
     
    331332                            <div class="optibehavior-empty-state is-visible">
    332333                                <svg viewBox='0 0 24 24' fill='none' stroke='#9ca3af' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'><path d='M16 11a4 4 0 1 0-8 0'/><path d='M3 21a7 7 0 0 1 18 0'/></svg>
    333                                 <div class="optibehavior-empty-title">No active visitors right now</div>
    334                                 <div class="optibehavior-empty-sub">Traffic updates in real-time.</div>
     334                                <div class="optibehavior-empty-title"><?php echo esc_html__( 'No active visitors right now', 'opti-behavior' ); ?></div>
     335                                <div class="optibehavior-empty-sub"><?php echo esc_html__( 'Traffic updates in real-time.', 'opti-behavior' ); ?></div>
    335336                            </div>
    336337                            <?php endif; ?>
     
    341342                <div class="dashboard-widget realtime-map-widget">
    342343                    <div class="widget-header">
    343                         <h3 class="widget-title"><span class="widget-icon">🗺️</span> Real-time Visitor Map</h3>
     344                        <h3 class="widget-title"><span class="widget-icon">🗺️</span> <?php echo esc_html__( 'Real-time Visitor Map', 'opti-behavior' ); ?></h3>
    344345                    </div>
    345346                    <div class="widget-content">
     
    356357                                <svg class="widget-icon-svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#4f46e5" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><path d="M14 2v6h6"></path></svg>
    357358                            </span>
    358                             Top Pages
     359                            <?php echo esc_html__( 'Top Pages', 'opti-behavior' ); ?>
    359360                        </h3>
    360361                    </div>
     
    364365                                <div class="optibehavior-empty-state is-visible">
    365366                                    <svg viewBox='0 0 24 24' fill='none' stroke='#9ca3af' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'><path d='M4 3h14a2 2 0 0 1 2 2v14l-4-4H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z'/></svg>
    366                                     <div class="optibehavior-empty-title">No page views yet</div>
    367                                     <div class="optibehavior-empty-sub">Once visitors view pages, you’ll see them here.</div>
     367                                    <div class="optibehavior-empty-title"><?php echo esc_html__( 'No page views yet', 'opti-behavior' ); ?></div>
     368                                    <div class="optibehavior-empty-sub"><?php echo esc_html__( 'Once visitors view pages, you\'ll see them here.', 'opti-behavior' ); ?></div>
    368369                                </div>
    369370                            <?php else : ?>
     
    403404                                                    <a class="optibehavior-heatmap-btn" target="_blank" href="<?php echo esc_url( $page['pc_heatmap'] ); ?>" aria-label="Open PC heatmap for <?php echo esc_attr( $page_title ); ?>">
    404405                                                        <span class="optibehavior-heatmap-icon" aria-hidden="true">🖥️</span>
    405                                                         <span class="optibehavior-heatmap-label">PC</span>
     406                                                        <span class="optibehavior-heatmap-label"><?php echo esc_html__( 'PC', 'opti-behavior' ); ?></span>
    406407                                                    </a>
    407408                                                    <a class="optibehavior-heatmap-btn alt" target="_blank" href="<?php echo esc_url( $page['mobile_heatmap'] ); ?>" aria-label="Open Mobile heatmap for <?php echo esc_attr( $page_title ); ?>">
    408409                                                        <span class="optibehavior-heatmap-icon" aria-hidden="true">📱</span>
    409                                                         <span class="optibehavior-heatmap-label">Mobile</span>
     410                                                        <span class="optibehavior-heatmap-label"><?php echo esc_html__( 'Mobile', 'opti-behavior' ); ?></span>
    410411                                                    </a>
    411412                                                </div>
     
    444445                    <?php opti_behavior_render_widget_loading(); ?>
    445446                    <div class="widget-header">
    446                         <h3 class="widget-title"><span class="widget-icon">🔗</span> Top Referrers</h3>
     447                        <h3 class="widget-title"><span class="widget-icon">🔗</span> <?php echo esc_html__( 'Top Referrers', 'opti-behavior' ); ?></h3>
    447448                    </div>
    448449                    <div class="widget-content compact">
  • opti-behavior/trunk/views/heatmaps-views.php

    r3399182 r3408429  
    4141        ?>
    4242        <div class="heatmaps-header">
    43             <h1 class="heatmaps-title">🔥 Heatmaps</h1>
     43            <h1 class="heatmaps-title"><?php esc_html_e( '🔥 Heatmaps', 'opti-behavior' ); ?></h1>
    4444            <div class="heatmaps-controls">
    4545                <select id="heatmaps-preset">
     
    4949                    <option value="top-engagement" <?php selected( $preset, 'top-engagement' ); ?>>Top Engagement</option>
    5050                </select>
    51                 <button id="refresh-heatmaps" class="button">Refresh</button>
     51                <button id="refresh-heatmaps" class="button"><?php esc_html_e( 'Refresh', 'opti-behavior' ); ?></button>
    5252            </div>
    5353        </div>
Note: See TracChangeset for help on using the changeset viewer.