Plugin Directory

Changeset 3408023


Ignore:
Timestamp:
12/02/2025 01:09:03 PM (3 months ago)
Author:
reventor
Message:

Update to version 1.1.1

Location:
reventor-calendar-appointment-booking/trunk
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • reventor-calendar-appointment-booking/trunk/assets/js/frontend.js

    r3407895 r3408023  
    920920        const startingDayOfWeek = firstDay.getDay();
    921921       
    922         // Create header
    923         const monthNames = [
    924             'January', 'February', 'March', 'April', 'May', 'June',
    925             'July', 'August', 'September', 'October', 'November', 'December'
    926         ];
    927        
    928         const weekdayNames = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
     922        // Create header - use localized names from PHP
     923        const monthNames = reventorcab_frontend.locale.month_names;
     924        const weekdayNames = reventorcab_frontend.locale.weekday_names;
    929925       
    930926        let html = `
  • reventor-calendar-appointment-booking/trunk/includes/class-frontend.php

    r3407895 r3408023  
    2525            wp_enqueue_style('reventorcab-frontend-style', REVENTORCAB_PLUGIN_URL . 'assets/css/frontend.css', ['dashicons'], REVENTORCAB_VERSION);
    2626            wp_enqueue_script('reventorcab-frontend-script', REVENTORCAB_PLUGIN_URL . 'assets/js/frontend.js', ['jquery'], REVENTORCAB_VERSION, true);
    27            
     27
    2828            $theme_color = get_option('reventorcab_theme_color', '#007cba');
    29            
     29
    3030            // Add inline CSS to apply theme color to success message
    3131            $custom_css = "
     
    4141            ";
    4242            wp_add_inline_style('reventorcab-frontend-style', wp_strip_all_tags($custom_css));
    43            
     43
    4444            // Get available dates for calendar picker
    4545            $available_dates = reventorcab_get_available_dates();
    46            
     46
     47            // Detect browser language for JavaScript strings and calendar localization
     48            $use_german = false;
     49            if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
     50                $accept_lang = sanitize_text_field(wp_unslash($_SERVER['HTTP_ACCEPT_LANGUAGE']));
     51                $languages = explode(',', $accept_lang);
     52
     53                foreach ($languages as $lang) {
     54                    $lang = trim(explode(';', $lang)[0]);
     55                    if (strpos($lang, 'de') === 0) {
     56                        $use_german = true;
     57                        break;
     58                    }
     59                }
     60            }
     61
     62            // Get localized strings based on detected language
     63            $strings = $this->get_localized_strings($use_german);
     64
    4765            wp_localize_script('reventorcab-frontend-script', 'reventorcab_frontend', [
    4866                'ajax_url' => admin_url('admin-ajax.php'),
     
    5775                    'time_format' => get_option('reventorcab_time_format', '24h')
    5876                ],
    59                 'strings' => [
    60                     'loading' => __('Loading...', 'reventor-calendar-appointment-booking'),
    61             'no_slots' => __('No available time slots for this date.', 'reventor-calendar-appointment-booking'),
    62             'booking_success' => __('Appointment booked successfully!', 'reventor-calendar-appointment-booking'),
    63             'booking_error' => __('Error booking appointment. Please try again.', 'reventor-calendar-appointment-booking'),
    64             'required_fields' => __('Please fill in all required fields.', 'reventor-calendar-appointment-booking'),
    65             'invalid_email' => __('Please enter a valid email address.', 'reventor-calendar-appointment-booking'),
    66             'syncing_calendar' => __('Syncing with calendar...', 'reventor-calendar-appointment-booking'),
    67             'outside_working_hours' => __('This date is outside working hours.', 'reventor-calendar-appointment-booking'),
    68             'minimum_advance_required' => __('Please select a date that meets the minimum advance time requirement.', 'reventor-calendar-appointment-booking')
     77                'strings' => $strings,
     78                'locale' => [
     79                    'language' => $use_german ? 'de' : 'en',
     80                    'month_names' => $use_german ? $this->get_german_month_names() : $this->get_english_month_names(),
     81                    'weekday_names' => $use_german ? $this->get_german_weekday_names() : $this->get_english_weekday_names()
    6982                ]
    7083            ]);
    71            
     84
    7285            // Add dynamic CSS for theme color
    7386            wp_add_inline_style('reventorcab-frontend-style', $this->get_dynamic_css($theme_color));
    7487        }
     88    }
     89
     90    /**
     91     * Get localized strings based on detected language
     92     */
     93    private function get_localized_strings($use_german) {
     94        if ($use_german) {
     95            // Load German translations
     96            $po_file = REVENTORCAB_PLUGIN_PATH . 'languages/easy-calendar-appointment-booking-de_DE.po';
     97            if (file_exists($po_file)) {
     98                $german_translations = $this->parse_po_file($po_file);
     99
     100                return [
     101                    'loading' => $german_translations['Loading...'] ?? 'Lade...',
     102                    'no_slots' => $german_translations['No available time slots for this date.'] ?? 'Keine verfügbaren Zeitslots für dieses Datum.',
     103                    'booking_success' => $german_translations['Appointment booked successfully!'] ?? 'Termin erfolgreich gebucht!',
     104                    'booking_error' => $german_translations['Error booking appointment. Please try again.'] ?? 'Fehler beim Buchen des Termins. Bitte versuchen Sie es erneut.',
     105                    'required_fields' => $german_translations['Please fill in all required fields.'] ?? 'Bitte füllen Sie alle Pflichtfelder aus.',
     106                    'invalid_email' => $german_translations['Please enter a valid email address.'] ?? 'Bitte geben Sie eine gültige E-Mail-Adresse ein.',
     107                    'syncing_calendar' => $german_translations['Syncing with calendar...'] ?? 'Synchronisiere mit Kalender...',
     108                    'outside_working_hours' => $german_translations['This date is outside working hours.'] ?? 'Dieses Datum liegt außerhalb der Arbeitszeiten.',
     109                    'minimum_advance_required' => $german_translations['Please select a date that meets the minimum advance time requirement.'] ?? 'Bitte wählen Sie ein Datum, das die Mindestvorlaufzeit erfüllt.'
     110                ];
     111            }
     112        }
     113
     114        // Default English strings
     115        return [
     116            'loading' => 'Loading...',
     117            'no_slots' => 'No available time slots for this date.',
     118            'booking_success' => 'Appointment booked successfully!',
     119            'booking_error' => 'Error booking appointment. Please try again.',
     120            'required_fields' => 'Please fill in all required fields.',
     121            'invalid_email' => 'Please enter a valid email address.',
     122            'syncing_calendar' => 'Syncing with calendar...',
     123            'outside_working_hours' => 'This date is outside working hours.',
     124            'minimum_advance_required' => 'Please select a date that meets the minimum advance time requirement.'
     125        ];
     126    }
     127
     128    /**
     129     * Get English month names
     130     */
     131    private function get_english_month_names() {
     132        return [
     133            'January', 'February', 'March', 'April', 'May', 'June',
     134            'July', 'August', 'September', 'October', 'November', 'December'
     135        ];
     136    }
     137
     138    /**
     139     * Get German month names
     140     */
     141    private function get_german_month_names() {
     142        return [
     143            'Januar', 'Februar', 'März', 'April', 'Mai', 'Juni',
     144            'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'
     145        ];
     146    }
     147
     148    /**
     149     * Get English weekday names
     150     */
     151    private function get_english_weekday_names() {
     152        return ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
     153    }
     154
     155    /**
     156     * Get German weekday names
     157     */
     158    private function get_german_weekday_names() {
     159        return ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'];
     160    }
     161
     162    /**
     163     * Parse a .po file and return translations array
     164     */
     165    private function parse_po_file($po_file) {
     166        $translations = [];
     167        $content = file_get_contents($po_file);
     168
     169        if ($content === false) {
     170            return $translations;
     171        }
     172
     173        // Simple PO file parser
     174        $lines = explode("\n", $content);
     175        $current_msgid = '';
     176        $current_msgstr = '';
     177        $in_msgid = false;
     178        $in_msgstr = false;
     179
     180        foreach ($lines as $line) {
     181            $line = trim($line);
     182
     183            if (strpos($line, 'msgid "') === 0) {
     184                // Save previous translation if exists
     185                if (!empty($current_msgid) && !empty($current_msgstr)) {
     186                    $translations[$current_msgid] = $current_msgstr;
     187                }
     188
     189                $current_msgid = substr($line, 7, -1); // Remove msgid " and "
     190                $current_msgstr = '';
     191                $in_msgid = true;
     192                $in_msgstr = false;
     193            } elseif (strpos($line, 'msgstr "') === 0) {
     194                $current_msgstr = substr($line, 8, -1); // Remove msgstr " and "
     195                $in_msgid = false;
     196                $in_msgstr = true;
     197            } elseif (strpos($line, '"') === 0 && $in_msgstr) {
     198                // Continuation of msgstr
     199                $current_msgstr .= substr($line, 1, -1);
     200            } elseif (strpos($line, '"') === 0 && $in_msgid) {
     201                // Continuation of msgid
     202                $current_msgid .= substr($line, 1, -1);
     203            }
     204        }
     205
     206        // Save last translation
     207        if (!empty($current_msgid) && !empty($current_msgstr)) {
     208            $translations[$current_msgid] = $current_msgstr;
     209        }
     210
     211        return $translations;
    75212    }
    76213   
     
    846983        $jitsi_url = 'https://meet.jit.si/' . $jitsi_room_id;
    847984       
     985        // Detect browser language
     986        $browser_language = 'en';
     987        if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
     988            $accept_lang = sanitize_text_field(wp_unslash($_SERVER['HTTP_ACCEPT_LANGUAGE']));
     989            $languages = explode(',', $accept_lang);
     990            $browser_language = trim(explode(';', $languages[0])[0]);
     991        }
     992
    848993        // Prepare appointment data for CalDAV and action hook
    849994        $appointment_data = [
     
    8581003            'jitsi_url' => $jitsi_url,
    8591004            'user_timezone' => $user_timezone,
    860             'user_timezone_offset' => $user_timezone_offset
     1005            'user_timezone_offset' => $user_timezone_offset,
     1006            'browser_language' => $browser_language
    8611007        ];
    8621008       
  • reventor-calendar-appointment-booking/trunk/includes/email-functions.php

    r3352327 r3408023  
    3232 */
    3333function reventorcab_send_appointment_confirmation_email($appointment_id, $appointment_data, $is_update = false) {
    34    
     34
     35    // Detect browser language from appointment data or default to site language
     36    $use_german = false;
     37    if (isset($appointment_data['browser_language']) && strpos($appointment_data['browser_language'], 'de') === 0) {
     38        $use_german = true;
     39    }
     40
     41    // Set up translations if German is detected
     42    if ($use_german) {
     43        $po_file = REVENTORCAB_PLUGIN_PATH . 'languages/easy-calendar-appointment-booking-de_DE.po';
     44        if (file_exists($po_file)) {
     45            $german_translations = reventorcab_parse_po_file($po_file);
     46            add_filter('gettext', function($translated_text, $text, $domain) use ($german_translations) {
     47                if ($domain === 'reventor-calendar-appointment-booking' && isset($german_translations[$text])) {
     48                    return $german_translations[$text];
     49                }
     50                return $translated_text;
     51            }, 10, 3);
     52        }
     53    }
     54
    3555    // Get recipient email
    3656    $to = $appointment_data['email'];
    37    
     57
    3858    // Get site info
    3959    $site_name = get_bloginfo('name');
    4060    $admin_email = get_option('admin_email');
    41    
     61
    4262    // Get user timezone for proper formatting
    4363    $user_timezone = isset($appointment_data['user_timezone']) ? $appointment_data['user_timezone'] : null;
    44    
     64
    4565    // Format date and time for display in user's timezone
    4666    $formatted_date = reventorcab_format_date($appointment_data['appointment_date'], null, $user_timezone);
    4767    $formatted_time = reventorcab_format_time($appointment_data['appointment_time'], null, $user_timezone, $appointment_data['appointment_date']);
    48    
     68
    4969    // Use the Jitsi Meet URL that was already generated and passed in appointment data
    5070    // No need to generate a new one here - it should be consistent across all components
    51    
     71
    5272    // Set email subject and heading based on whether this is a new booking or an update
    5373    if ($is_update) {
     
    285305}
    286306
     307/**
     308 * Parse a .po file and return translations array
     309 */
     310function reventorcab_parse_po_file($po_file) {
     311    $translations = [];
     312    $content = file_get_contents($po_file);
     313
     314    if ($content === false) {
     315        return $translations;
     316    }
     317
     318    // Simple PO file parser
     319    $lines = explode("\n", $content);
     320    $current_msgid = '';
     321    $current_msgstr = '';
     322    $in_msgid = false;
     323    $in_msgstr = false;
     324
     325    foreach ($lines as $line) {
     326        $line = trim($line);
     327
     328        if (strpos($line, 'msgid "') === 0) {
     329            // Save previous translation if exists
     330            if (!empty($current_msgid) && !empty($current_msgstr)) {
     331                $translations[$current_msgid] = $current_msgstr;
     332            }
     333
     334            $current_msgid = substr($line, 7, -1); // Remove msgid " and "
     335            $current_msgstr = '';
     336            $in_msgid = true;
     337            $in_msgstr = false;
     338        } elseif (strpos($line, 'msgstr "') === 0) {
     339            $current_msgstr = substr($line, 8, -1); // Remove msgstr " and "
     340            $in_msgid = false;
     341            $in_msgstr = true;
     342        } elseif (strpos($line, '"') === 0 && $in_msgstr) {
     343            // Continuation of msgstr
     344            $current_msgstr .= substr($line, 1, -1);
     345        } elseif (strpos($line, '"') === 0 && $in_msgid) {
     346            // Continuation of msgid
     347            $current_msgid .= substr($line, 1, -1);
     348        }
     349    }
     350
     351    // Save last translation
     352    if (!empty($current_msgid) && !empty($current_msgstr)) {
     353        $translations[$current_msgid] = $current_msgstr;
     354    }
     355
     356    return $translations;
     357}
     358
    287359// VTIMEZONE generation functions removed - now using UTC timestamps for CalDAV compatibility
  • reventor-calendar-appointment-booking/trunk/includes/functions.php

    r3407895 r3408023  
    9898    try {
    9999        $tz = new DateTimeZone($timezone);
    100        
     100
    101101        // If we have a date, combine it with time for proper timezone conversion
    102102        if ($date) {
    103             // Parse in UTC first to avoid server timezone dependency
    104             $datetime = new DateTime($date . ' ' . $time, new DateTimeZone('UTC'));
    105             $datetime->setTimezone($tz);
     103            // Parse directly in the target timezone to avoid double conversion
     104            $datetime = new DateTime($date . ' ' . $time, $tz);
    106105        } else {
    107             // For time-only, assume today's date in UTC first
    108             $datetime = new DateTime('today ' . $time, new DateTimeZone('UTC'));
    109             $datetime->setTimezone($tz);
     106            // For time-only, assume today's date in target timezone
     107            $datetime = new DateTime('today ' . $time, $tz);
    110108        }
    111109       
  • reventor-calendar-appointment-booking/trunk/readme.txt

    r3407895 r3408023  
    33Tags: appointments, booking, calendar, scheduling
    44Requires at least: 6.3
    5 Tested up to: 6.8
    6 Stable tag: 1.1.0
     5Tested up to: 6.9
     6Stable tag: 1.1.1
    77Requires PHP: 8.1
    88License: GPL v2 or later
     
    4242== Changelog ==
    4343
     44= 1.1.1 =
     45* Fixed a language translation
     46* Tested the plugin for WordPress 6.9
     47
    4448= 1.1.0 =
    4549* Added automatic saving in admin settings
  • reventor-calendar-appointment-booking/trunk/reventor-calendar-appointment-booking.php

    r3407895 r3408023  
    44 * Description: A REVENTOR calendar appointment booking system with CalDAV integration for seamless scheduling and calendar synchronization.
    55 * Plugin URI: https://wordpress.org/plugins/reventor-calendar-appointment-booking/
    6  * Version: 1.1.0
     6 * Version: 1.1.1
    77 * Author: REVENTOR
    88 * Author URI: https://reventor.eu
     
    4040define('REVENTORCAB_PLUGIN_URL', plugin_dir_url(__FILE__));
    4141define('REVENTORCAB_PLUGIN_PATH', plugin_dir_path(__FILE__));
    42 define('REVENTORCAB_VERSION', '1.1.0');
     42define('REVENTORCAB_VERSION', '1.1.1');
    4343
    4444// Main plugin class
     
    106106            'type' => 'default'
    107107        ), $atts);
    108        
     108
     109        // Detect browser language and set up translation filter
     110        $this->setup_booking_form_translations();
     111
    109112        ob_start();
    110113        include REVENTORCAB_PLUGIN_PATH . 'templates/booking-form.php';
    111114        return ob_get_clean();
     115    }
     116
     117    /**
     118     * Set up translations for booking form based on browser language
     119     */
     120    private function setup_booking_form_translations() {
     121        static $translations_loaded = false;
     122
     123        if ($translations_loaded) {
     124            return; // Already set up
     125        }
     126
     127        if (defined('WP_DEBUG') && WP_DEBUG) {
     128            error_log('REVENTORCAB: setup_booking_form_translations called');
     129        }
     130
     131        // Detect browser language
     132        $use_german = false;
     133        if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
     134            $accept_lang = sanitize_text_field(wp_unslash($_SERVER['HTTP_ACCEPT_LANGUAGE']));
     135            if (defined('WP_DEBUG') && WP_DEBUG) {
     136                error_log('REVENTORCAB: Accept-Language: ' . $accept_lang);
     137            }
     138
     139            $languages = explode(',', $accept_lang);
     140
     141            foreach ($languages as $lang) {
     142                $lang = trim(explode(';', $lang)[0]); // Remove quality values
     143
     144                if (defined('WP_DEBUG') && WP_DEBUG) {
     145                    error_log('REVENTORCAB: Checking language: ' . $lang);
     146                }
     147
     148                // Check for German variants
     149                if (strpos($lang, 'de') === 0) {
     150                    $use_german = true;
     151                    if (defined('WP_DEBUG') && WP_DEBUG) {
     152                        error_log('REVENTORCAB: German detected, will use German translations');
     153                    }
     154                    break;
     155                }
     156            }
     157        }
     158
     159        if ($use_german) {
     160            // Load German translations from the .po file
     161            $po_file = REVENTORCAB_PLUGIN_PATH . 'languages/easy-calendar-appointment-booking-de_DE.po';
     162            if (file_exists($po_file)) {
     163                if (defined('WP_DEBUG') && WP_DEBUG) {
     164                    error_log('REVENTORCAB: Loading German translations from: ' . $po_file);
     165                }
     166
     167                // Parse the .po file and create a translation array
     168                $german_translations = $this->parse_po_file($po_file);
     169
     170                if (!empty($german_translations)) {
     171                    // Add gettext filter to intercept translations
     172                    add_filter('gettext', function($translated_text, $text, $domain) use ($german_translations) {
     173                        if ($domain === 'reventor-calendar-appointment-booking' && isset($german_translations[$text])) {
     174                            if (defined('WP_DEBUG') && WP_DEBUG) {
     175                                error_log('REVENTORCAB: Translating "' . $text . '" to "' . $german_translations[$text] . '"');
     176                            }
     177                            return $german_translations[$text];
     178                        }
     179                        return $translated_text;
     180                    }, 10, 3);
     181                }
     182            } else {
     183                if (defined('WP_DEBUG') && WP_DEBUG) {
     184                    error_log('REVENTORCAB: German PO file not found: ' . $po_file);
     185                }
     186            }
     187        }
     188
     189        $translations_loaded = true;
     190    }
     191
     192    /**
     193     * Parse a .po file and return translations array
     194     */
     195    private function parse_po_file($po_file) {
     196        $translations = [];
     197        $content = file_get_contents($po_file);
     198
     199        if ($content === false) {
     200            return $translations;
     201        }
     202
     203        // Simple PO file parser
     204        $lines = explode("\n", $content);
     205        $current_msgid = '';
     206        $current_msgstr = '';
     207        $in_msgid = false;
     208        $in_msgstr = false;
     209
     210        foreach ($lines as $line) {
     211            $line = trim($line);
     212
     213            if (strpos($line, 'msgid "') === 0) {
     214                // Save previous translation if exists
     215                if (!empty($current_msgid) && !empty($current_msgstr)) {
     216                    $translations[$current_msgid] = $current_msgstr;
     217                }
     218
     219                $current_msgid = substr($line, 7, -1); // Remove msgid " and "
     220                $current_msgstr = '';
     221                $in_msgid = true;
     222                $in_msgstr = false;
     223            } elseif (strpos($line, 'msgstr "') === 0) {
     224                $current_msgstr = substr($line, 8, -1); // Remove msgstr " and "
     225                $in_msgid = false;
     226                $in_msgstr = true;
     227            } elseif (strpos($line, '"') === 0 && $in_msgstr) {
     228                // Continuation of msgstr
     229                $current_msgstr .= substr($line, 1, -1);
     230            } elseif (strpos($line, '"') === 0 && $in_msgid) {
     231                // Continuation of msgid
     232                $current_msgid .= substr($line, 1, -1);
     233            }
     234        }
     235
     236        // Save last translation
     237        if (!empty($current_msgid) && !empty($current_msgstr)) {
     238            $translations[$current_msgid] = $current_msgstr;
     239        }
     240
     241        return $translations;
    112242    }
    113243   
     
    151281   
    152282    public function load_textdomain() {
    153         // WordPress automatically loads translations for plugins hosted on WordPress.org
    154         // since WordPress 4.6 using just-in-time loading. No manual load_plugin_textdomain() call needed.
    155         // The Text Domain header in the plugin file is sufficient for WordPress to locate translations.
    156     }
     283        // Load default text domain for admin and other areas
     284        load_plugin_textdomain(
     285            'reventor-calendar-appointment-booking',
     286            false,
     287            dirname(plugin_basename(__FILE__)) . '/languages/'
     288        );
     289    }
     290
    157291   
    158292    public function activation_notice() {
Note: See TracChangeset for help on using the changeset viewer.