Plugin Directory

Changeset 3796366


Ignore:
Timestamp:
11/11/2026 01:08:04 PM (4 hours ago)
Author:
ilithio
Message:

new version

Location:
host-domination-booking-engine
Files:
32 added
7 edited

Legend:

Unmodified
Added
Removed
  • host-domination-booking-engine/trunk/assets/flatpickr/flatpickr.min.js

    r3738709 r3796366  
    2626    return false;
    2727  }
    28   function monthName(d){
    29     try { return d.toLocaleDateString('en-US', {month:'long', year:'numeric'}); }
    30     catch(e){ return d.getFullYear()+'-'+pad(d.getMonth()+1); }
     28  // WordPress passes its public locale through Flatpickr's standard locale option.
     29  // This bundled compact renderer also needs to honor it: the old implementation
     30  // always formatted months with en-US, no matter which locale was selected.
     31  function localeTag(opts){
     32    var raw = opts && opts.locale;
     33    if (typeof raw === 'string' && raw && raw !== 'default') {
     34      return raw.replace(/_/g, '-');
     35    }
     36    if (raw && typeof raw === 'object' && typeof raw.code === 'string') {
     37      return raw.code.replace(/_/g, '-');
     38    }
     39    return 'en-US';
     40  }
     41  function monthName(d, opts){
     42    var loc = opts && opts.locale;
     43    if (loc && typeof loc === 'object' && loc.months &&
     44        loc.months.longhand && loc.months.longhand[d.getMonth()]) {
     45      return loc.months.longhand[d.getMonth()] + ' ' + d.getFullYear();
     46    }
     47    try {
     48      return new Intl.DateTimeFormat(localeTag(opts), {month:'long', year:'numeric'}).format(d);
     49    } catch(e) {
     50      try { return d.toLocaleDateString('en-US', {month:'long', year:'numeric'}); }
     51      catch(ignored){ return d.getFullYear()+'-'+pad(d.getMonth()+1); }
     52    }
     53  }
     54  function weekdayInitials(opts){
     55    var loc = opts && opts.locale;
     56    if (loc && typeof loc === 'object' && loc.weekdays &&
     57        Array.isArray(loc.weekdays.shorthand) && loc.weekdays.shorthand.length === 7) {
     58      // Flatpickr locale arrays run Sunday to Saturday; this grid starts Monday.
     59      return loc.weekdays.shorthand.slice(1).concat(loc.weekdays.shorthand[0]);
     60    }
     61    try {
     62      var tag = localeTag(opts);
     63      var fmt = new Intl.DateTimeFormat(tag, {weekday:'narrow'});
     64      var initials = [];
     65      for (var day=0; day<7; day++) {
     66        // 2024-01-01 is a Monday; choose local noon to avoid DST boundaries.
     67        var label = fmt.format(new Date(2024, 0, 1 + day, 12));
     68        // Some locales (e.g. Catalan) use meaningful two-letter narrow
     69        // abbreviations; keep them instead of truncating every day to 'D'.
     70        if (Array.from(label).length === 1) label = label.toLocaleUpperCase(tag);
     71        initials.push(label);
     72      }
     73      if (initials.every(function(label){ return label.length > 0; })) return initials;
     74    } catch(e) {}
     75    return (opts && Array.isArray(opts.weekdaysShort) && opts.weekdaysShort.length === 7)
     76      ? opts.weekdaysShort : ['M','T','W','T','F','S','S'];
    3177  }
    3278  function build(input, opts){
    … …  
    86132        var title = document.createElement('div');
    87133        title.className = 'flatpickr-current-month';
    88         title.textContent = monthName(month);
     134        title.textContent = monthName(month, opts);
    89135
    90136        let next = document.createElement('span');
    … …  
    134180        var weekdays = document.createElement('div');
    135181        weekdays.className = 'flatpickr-weekdays';
    136         var weekdayLabels = (opts.weekdaysShort && opts.weekdaysShort.length === 7)
    137           ? opts.weekdaysShort
    138           : ['M','T','W','T','F','S','S'];
     182        var weekdayLabels = weekdayInitials(opts);
    139183        weekdayLabels.forEach(function(w){
    140184          var el=document.createElement('span'); el.className='flatpickr-weekday'; el.textContent=w; weekdays.appendChild(el);
  • host-domination-booking-engine/trunk/assets/js/frontend-triangoli-1.1.22.js

    r3791074 r3796366  
    674674        maxDate: frontendMaxDate,
    675675        dateFormat: dateFormat,
    676         locale: (localeCode === 'it' && window.flatpickr.l10ns && window.flatpickr.l10ns.it) ? window.flatpickr.l10ns.it : 'default',
     676        locale: localeCode, // Full WordPress/frontend locale (e.g. fr_FR), resolved per calendar.
    677677        weekdaysShort: weekdaysShort,
    678678        disable: [function(date){ return isFullyBlockedDay(date); }],
  • host-domination-booking-engine/trunk/assets/js/frontend.js

    r3791074 r3796366  
    600600        maxDate: frontendMaxDate,
    601601        dateFormat: dateFormat,
    602         locale: (localeCode === 'it' && window.flatpickr.l10ns && window.flatpickr.l10ns.it) ? window.flatpickr.l10ns.it : 'default',
     602        locale: localeCode, // Full WordPress/frontend locale (e.g. fr_FR), resolved per calendar.
    603603        weekdaysShort: weekdaysShort,
    604604        disable: [function(date){ return isFullyBlockedDay(date); }],
  • host-domination-booking-engine/trunk/assets/room-property-cards/js/calendar-preview.js

    r3791074 r3796366  
    3939        } catch (e) { formatted = amount.toFixed(2) + ' ' + currency; }
    4040        var template = (window.HDRPCCalendarPreview && HDRPCCalendarPreview.priceLabel) || 'Nights from %s';
    41         $price.text(template.replace('%s', formatted));
     41        $price.text(template.replace(/%1\$s|%s/g, formatted));
    4242    }
    4343    $(function(){
  • host-domination-booking-engine/trunk/host-domination-booking-engine.php

    r3791074 r3796366  
    44 * Plugin URI: https://hostdomination.com/booking-engine/
    55 * Description: Lightweight Booking Engine with Calendar Sync and Reservation Requests for Vacation Rentals, Apartments, B&Bs and Hosts.
    6  * Version: 1.5.33
     6 * Version: 1.5.44
    77 * Requires at least: 6.0
    88 * Requires PHP: 7.4
    … …  
    1717
    1818final class HDBE_Plugin {
    19     const VERSION = '1.5.33';
     19    const VERSION = '1.5.44';
    2020    const DB_VERSION = '0.16.7';
    2121    const OPT_SETTINGS = 'hdbe_settings';
    … …  
    10861086    }
    10871087
     1088    /**
     1089     * Locale announced by the actually loaded booking-engine gettext catalog.
     1090     * HD BE Translate installs its own language packs for the guest-facing
     1091     * interface, independently of the WordPress site or administrator locale.
     1092     * Reading the *active* catalog avoids treating WordPress's WPLANG as the
     1093     * definitive language for calendar labels.
     1094     */
     1095    private static function loaded_booking_catalog_locale() {
     1096        global $l10n;
     1097        $domain = 'host-domination-booking-engine';
     1098        if (!isset($l10n[$domain]) && function_exists('__')) {
     1099            // Allow WordPress/HD BE Translate to initialize the domain first.
     1100            __('Available', $domain);
     1101        }
     1102        if (!isset($l10n[$domain]) || !is_object($l10n[$domain])) return '';
     1103        $translations = $l10n[$domain];
     1104        $locale = '';
     1105        if (method_exists($translations, 'get_header')) {
     1106            $locale = (string) $translations->get_header('Language');
     1107        }
     1108        if ('' === trim($locale) && (property_exists($translations, 'headers') || method_exists($translations, '__get'))) {
     1109            // WordPress 6.5+ WP_Translations exposes headers through __get(),
     1110            // not __isset(), so do not guard it with isset().
     1111            $headers = $translations->headers;
     1112            if (is_array($headers)) {
     1113                $locale = (string) ($headers['Language'] ?? $headers['language'] ?? '');
     1114            }
     1115        }
     1116        return self::normalize_public_booking_locale($locale);
     1117    }
     1118
     1119    private static function normalize_public_booking_locale($locale) {
     1120        $locale = trim(str_replace('-', '_', (string) $locale));
     1121        if (!preg_match('/^[A-Za-z]{2,3}(?:_[A-Za-z]{2,4})?$/D', $locale)) return '';
     1122        $parts = explode('_', $locale, 2);
     1123        $lang = strtolower($parts[0]);
     1124        $regional = ['en'=>'US', 'it'=>'IT', 'fr'=>'FR', 'es'=>'ES', 'de'=>'DE', 'pt'=>'PT', 'ca'=>'ES'];
     1125        if (!in_array($lang, ['en','it','fr','es','de','pt','ca','eu'], true)) return '';
     1126        return $lang . '_' . (isset($parts[1]) ? strtoupper($parts[1]) : ($regional[$lang] ?? 'ES'));
     1127    }
     1128
     1129    /**
     1130     * Select the language of public booking content. HD BE Translate's loaded
     1131     * frontend catalog (when available) takes priority over the WordPress
     1132     * admin/site language. The documented hdbe_public_booking_locale filter
     1133     * remains available for an explicit frontend-language selection; plugins
     1134     * including HD BE Translate may use it to pass their selected language.
     1135     * Otherwise ordinary multilingual plugins and WordPress still work.
     1136     */
     1137    private static function public_booking_locale() {
     1138        $locale = 'en_US'; // English source strings are the built-in default.
     1139        if (function_exists('get_locale')) {
     1140            $site_locale = (string) get_locale();
     1141            if (function_exists('is_admin') && is_admin() && function_exists('get_option')) {
     1142                $site_locale = (string) get_option('WPLANG', '');
     1143            }
     1144            $locale = self::normalize_public_booking_locale($site_locale) ?: 'en_US';
     1145        }
     1146
     1147        if (function_exists('pll_current_language')) {
     1148            $selected = pll_current_language('locale');
     1149            if (is_string($selected)) {
     1150                $locale = self::normalize_public_booking_locale($selected) ?: $locale;
     1151            }
     1152        }
     1153        if (function_exists('has_filter') && has_filter('wpml_current_language')) {
     1154            $selected = apply_filters('wpml_current_language', null);
     1155            if (is_string($selected)) {
     1156                $locale = self::normalize_public_booking_locale($selected) ?: $locale;
     1157            }
     1158        }
     1159
     1160        // The catalog's Language header is useful for diagnostics, NOT for
     1161        // choosing the visitor's language: it could be a stale Italian MO.
     1162        // The selected frontend language always wins over an old catalog.
     1163
     1164        // Highest-priority integration point for an explicit guest language.
     1165        $selected = apply_filters('hdbe_public_booking_locale', $locale);
     1166        return self::normalize_public_booking_locale($selected) ?: $locale;
     1167    }
     1168
     1169    /**
     1170     * Calendar-only language bridge for HD BE Translate.
     1171     *
     1172     * Language settings are independent of the WordPress site language. Respect
     1173     * the explicit frontend selection first; use an explicit backend selection
     1174     * only when no frontend language is selected. An actual frontend selection
     1175     * must never be overwritten by the administration language.
     1176     *
     1177     * The translate client can override this without relying on option keys:
     1178     * add_filter('hdbe_calendar_locale', fn($locale) => 'fr_FR');
     1179     */
     1180    private static function translate_client_calendar_locale() {
     1181        if (!function_exists('get_option')) return '';
     1182
     1183        // HD BE Translate 0.2.15 stores its Language tab at 'hdmlp_settings',
     1184        // NOT at the speculative option names supported below for legacy add-ons.
     1185        // Its existing public frontend-config callback resolves the *effective*
     1186        // guest language, including 'auto' and external multilingual selectors.
     1187        // Use that result rather than the WordPress administrator/site locale.
     1188        if (class_exists('HDMLP_Client') && is_callable(['HDMLP_Client', 'filter_frontend_config'])) {
     1189            $translated_config = HDMLP_Client::filter_frontend_config([]);
     1190            if (is_array($translated_config)) {
     1191                $resolved = self::normalize_public_booking_locale($translated_config['numberLocale'] ?? '');
     1192                if ($resolved) return $resolved;
     1193            }
     1194        }
     1195
     1196        // Compatibility when the client is installed but does not expose the
     1197        // frontend config callback. Honor an explicitly selected frontend
     1198        // language; 'auto' must still follow WordPress, not the backend locale.
     1199        $hdmlp_settings = get_option('hdmlp_settings', []);
     1200        if (is_array($hdmlp_settings) && isset($hdmlp_settings['frontend_locale'])) {
     1201            $requested = (string) $hdmlp_settings['frontend_locale'];
     1202            $resolved = self::normalize_public_booking_locale($requested);
     1203            if ($resolved) return $resolved;
     1204            if ('auto' === strtolower(trim($requested))) return '';
     1205        }
     1206
     1207        $settings_keys = [
     1208            'hdbe_translate_settings',
     1209            'hdbe_translate_options',
     1210            'hdbe_translate_language_settings',
     1211            'hdbe_translate_client_settings',
     1212            'hdbe_translate_preferences',
     1213            'hdbe_language_settings',
     1214            'hd_be_translate_settings',
     1215        ];
     1216        $front_keys = [
     1217            'frontend_locale', 'frontend_language', 'front_end_locale',
     1218            'front_end_language', 'front_locale', 'front_language',
     1219            'language_frontend', 'locale_frontend', 'guest_locale',
     1220            'guest_language', 'frontend_lang', 'front_lang',
     1221            'frontend', 'front_end', 'front', 'public_language',
     1222        ];
     1223        $back_keys = [
     1224            'backend_locale', 'backend_language', 'back_end_locale',
     1225            'back_end_language', 'back_locale', 'back_language',
     1226            'language_backend', 'locale_backend', 'admin_locale',
     1227            'admin_language', 'backend_lang', 'back_lang',
     1228            'backend', 'back_end', 'back', 'admin',
     1229        ];
     1230
     1231        $front = '';
     1232        $back = '';
     1233        foreach ($settings_keys as $key) {
     1234            $settings = get_option($key, []);
     1235            if (!is_array($settings)) continue;
     1236            // Ignore unrelated values: only recognized, explicitly configured
     1237            // locale fields are eligible for calendar localization.
     1238            foreach ($front_keys as $field) {
     1239                if (isset($settings[$field]) && is_string($settings[$field])) {
     1240                    $candidate = self::normalize_public_booking_locale($settings[$field]);
     1241                    if ($candidate) { $front = $candidate; break; }
     1242                }
     1243            }
     1244            foreach ($back_keys as $field) {
     1245                if (isset($settings[$field]) && is_string($settings[$field])) {
     1246                    $candidate = self::normalize_public_booking_locale($settings[$field]);
     1247                    if ($candidate) { $back = $candidate; break; }
     1248                }
     1249            }
     1250            if ($front) break;
     1251        }
     1252
     1253        $individual_options = [
     1254            'hdbe_translate_frontend_locale', 'hdbe_translate_frontend_language',
     1255            'hdbe_translate_front_locale', 'hdbe_translate_front_language',
     1256            'hd_be_translate_frontend_locale',
     1257        ];
     1258        if (!$front) {
     1259            foreach ($individual_options as $key) {
     1260                $value = get_option($key, '');
     1261                if (!is_string($value)) continue;
     1262                $front = self::normalize_public_booking_locale($value);
     1263                if ($front) break;
     1264            }
     1265        }
     1266        if (!$back) {
     1267            foreach ([
     1268                'hdbe_translate_backend_locale', 'hdbe_translate_backend_language',
     1269                'hdbe_translate_admin_locale', 'hdbe_translate_admin_language',
     1270                'hd_be_translate_backend_locale',
     1271            ] as $key) {
     1272                $value = get_option($key, '');
     1273                if (!is_string($value)) continue;
     1274                $back = self::normalize_public_booking_locale($value);
     1275                if ($back) break;
     1276            }
     1277        }
     1278        return $front ?: $back;
     1279    }
     1280
     1281    /**
     1282     * Localization of calendar month headings and weekday initials only.
     1283     * Flatpickr's Intl implementation formats dates using this locale; the
     1284     * form's other translations and the actual booking/calendar data remain
     1285     * entirely untouched.
     1286     */
     1287    private static function calendar_display_locale() {
     1288        $fallback = self::public_booking_locale();
     1289
     1290        // If the client supplies an explicit, validated selection via settings,
     1291        // it takes precedence over the WordPress default locale.
     1292        $selected = self::translate_client_calendar_locale();
     1293        if (!$selected && function_exists('apply_filters')) {
     1294            // Existing WordPress plugin-locale integration used by some
     1295            // translation plugins. This may differ from get_locale().
     1296            $domain_locale = apply_filters('plugin_locale', $fallback, 'host-domination-booking-engine');
     1297            if (is_string($domain_locale)) {
     1298                $candidate = self::normalize_public_booking_locale($domain_locale);
     1299                if ($candidate && $candidate !== $fallback) $selected = $candidate;
     1300            }
     1301        }
     1302        if (!$selected) {
     1303            // A frontend language pack can be installed independently of
     1304            // WordPress's site language. Only trust its active catalog when
     1305            // the HD BE Translate client is actually enabled.
     1306            $enabled = false;
     1307            $active = (array) get_option('active_plugins', []);
     1308            foreach ($active as $plugin_file) {
     1309                if ('hd-be-translate/hd-be-translate.php' === $plugin_file) {
     1310                    $enabled = true;
     1311                    break;
     1312                }
     1313            }
     1314            if ($enabled) $selected = self::loaded_booking_catalog_locale();
     1315        }
     1316        $selected = $selected ?: $fallback;
     1317
     1318        // Highest priority: a direct integration from the client addon.
     1319        // Keep the hook calendar-specific to avoid rewriting other UI strings.
     1320        $selected = apply_filters('hdbe_calendar_locale', $selected, $fallback);
     1321        return self::normalize_public_booking_locale($selected) ?: $fallback;
     1322    }
    10881323
    10891324    /**
    … …  
    11771412        ];
    11781413        return $labels[$key] ?? $key;
     1414    }
     1415
     1416    /**
     1417     * Guest-facing copy is always translated from the installed gettext
     1418     * catalog published by HD BE Translate Manager (English source msgids).
     1419     * No hard-coded translations are maintained in the Booking Engine.
     1420     * If the loaded catalog is for a DIFFERENT locale than the selected site/
     1421     * visitor language, refuse to display that foreign-language translation:
     1422     * fall back to the original English string until the correct MO is loaded.
     1423     */
     1424    private static function public_booking_text($key, $translated = null) {
     1425        $sources = [
     1426            'min_nights_one' => 'Minimum %d night required',
     1427            'min_nights_many' => 'Minimum %d nights required',
     1428            'available' => 'Available',
     1429            'busy' => 'Not available',
     1430            'phone_placeholder' => 'Your phone number',
     1431            'contact_phone_label' => 'Contact phone',
     1432            'privacy_link' => 'Privacy Policy',
     1433            'terms_link' => 'Terms and Conditions',
     1434            'privacy_consent' => 'I have read and accept the %s.',
     1435            'privacy_terms_consent' => 'I have read and accept the %1$s and %2$s.',
     1436        ];
     1437        if (!isset($sources[$key])) return '';
     1438
     1439        if (null === $translated) {
     1440            switch ($key) {
     1441                case 'min_nights_one': $translated = __('Minimum %d night required', 'host-domination-booking-engine'); break;
     1442                case 'min_nights_many': $translated = __('Minimum %d nights required', 'host-domination-booking-engine'); break;
     1443                case 'available': $translated = __('Available', 'host-domination-booking-engine'); break;
     1444                case 'busy': $translated = __('Not available', 'host-domination-booking-engine'); break;
     1445                case 'phone_placeholder': $translated = __('Your phone number', 'host-domination-booking-engine'); break;
     1446                case 'contact_phone_label': $translated = __('Contact phone', 'host-domination-booking-engine'); break;
     1447                case 'privacy_link': $translated = __('Privacy Policy', 'host-domination-booking-engine'); break;
     1448                case 'terms_link': $translated = __('Terms and Conditions', 'host-domination-booking-engine'); break;
     1449                case 'privacy_consent': $translated = __('I have read and accept the %s.', 'host-domination-booking-engine'); break;
     1450                case 'privacy_terms_consent': $translated = __('I have read and accept the %1$s and %2$s.', 'host-domination-booking-engine'); break;
     1451            }
     1452        }
     1453
     1454        $catalog_locale = self::loaded_booking_catalog_locale();
     1455        $visitor_locale = self::public_booking_locale();
     1456        if ($catalog_locale !== '' && $visitor_locale !== '' && strcasecmp($catalog_locale, $visitor_locale) !== 0) {
     1457            return $sources[$key];
     1458        }
     1459
     1460        return (string) $translated;
     1461    }
     1462
     1463    private static function public_minimum_nights_text($nights) {
     1464        $nights = max(1, (int) $nights);
     1465        /* translators: %d is the minimum number of nights. */
     1466        $translated = _n('Minimum %d night required', 'Minimum %d nights required', $nights, 'host-domination-booking-engine');
     1467        $key = 1 === $nights ? 'min_nights_one' : 'min_nights_many';
     1468        $source = 1 === $nights ? 'Minimum %d night required' : 'Minimum %d nights required';
     1469        if ($translated === $source) {
     1470            // Some language packs index simple gettext strings but not plurals.
     1471            $translated = self::public_booking_text($key);
     1472        } else {
     1473            $translated = self::public_booking_text($key, $translated);
     1474        }
     1475        return sprintf($translated, $nights);
    11791476    }
    11801477
    … …  
    37974094            'dateFormat' => 'd-m-Y',
    37984095            'desktopCalendarMonths' => (int)($settings['desktop_calendar_months'] ?? 1) === 2 ? 2 : 1,
    3799             'locale' => self::lang(),
     4096            // Prefer the Language tab's explicit HD BE Translate choice over WordPress.
     4097            // Month/day formatting only; booking data and prices are unchanged.
     4098            'locale' => self::calendar_display_locale(),
    38004099            'numberLocale' => self::frontend_locale(),
    38014100            'weekdaysShort' => self::frontend_weekday_initials(),
    … …  
    38354134        // The base nightly price remains only a fallback when no advanced price exists for a day.
    38364135        $frontend_config = apply_filters('hdbe_frontend_config', $frontend_config, $cal, $settings);
     4136        // Calendar month/day labels honor HD BE Translate's explicit locale.
     4137        // Re-apply after PRO filters so date/price extensions cannot override it.
     4138        $frontend_config['locale'] = self::calendar_display_locale();
    38374139        // Reservation boundaries are owned by the base engine and must survive PRO config filters.
    38384140        $frontend_config['bookings'] = $bookings_for_edges;
    … …  
    38974199                        </div>
    38984200                        <?php /* translators: %d is the minimum required number of nights. */ ?>
    3899                         <span class="hdbe-min-nights-note"><?php echo esc_html(sprintf(_n('Minimum %d night required', 'Minimum %d nights required', $hdbe_min_nights_front, 'host-domination-booking-engine'), $hdbe_min_nights_front)); ?></span>
    3900                         <div class="hdbe-legend">
    3901                             <span><i class="hdbe-dot hdbe-dot-free"></i><?php echo esc_html(self::t('available')); ?></span>
    3902                             <span><i class="hdbe-dot hdbe-dot-busy"></i><?php echo esc_html(self::t('busy')); ?></span>
     4201                        <span class="hdbe-min-nights-note"><?php echo esc_html(self::public_minimum_nights_text($hdbe_min_nights_front)); ?></span>
     4202                        <div class="hdbe-legend" data-hdbe-calendar-locale="<?php echo esc_attr(self::calendar_display_locale()); ?>" data-hdbe-locale="<?php echo esc_attr(self::public_booking_locale()); ?>" data-hdbe-version="<?php echo esc_attr(self::VERSION); ?>" data-hdbe-catalog-locale="<?php echo esc_attr(self::loaded_booking_catalog_locale()); ?>">
     4203                            <span><i class="hdbe-dot hdbe-dot-free"></i><?php echo esc_html(self::public_booking_text('available')); ?></span>
     4204                            <span><i class="hdbe-dot hdbe-dot-busy"></i><?php echo esc_html(self::public_booking_text('busy')); ?></span>
    39034205                        </div>
    39044206                        <div class="hdbe-error"></div>
    … …  
    39244226                        <div class="hdbe-field"><label><?php echo esc_html__('Full name', 'host-domination-booking-engine'); ?></label><input name="guest_name" required placeholder="<?php echo esc_attr__('Full name', 'host-domination-booking-engine'); ?>"></div>
    39254227                        <div class="hdbe-field"><label><?php echo esc_html(self::t('email')); ?></label><input type="email" name="guest_email" required placeholder="email@example.com"></div>
    3926                         <div class="hdbe-field"><label><?php echo esc_html__('Contact phone', 'host-domination-booking-engine'); ?></label><input name="phone" required placeholder="<?php echo esc_attr__('Your phone number', 'host-domination-booking-engine'); ?>"></div>
     4228                        <div class="hdbe-field"><label><?php echo esc_html(self::public_booking_text('contact_phone_label')); ?></label><input name="phone" required placeholder="<?php echo esc_attr(self::public_booking_text('phone_placeholder')); ?>"></div>
    39274229                        <?php
    39284230                        $max_guests_front = max(1, (int)($cal->max_guests ?? 2));
    … …  
    39764278                        <?php if ($privacy_policy_configured): ?>
    39774279                            <?php
    3978                             $privacy_link = '<a href="' . esc_url($privacy_policy_url) . '" target="_blank" rel="noopener noreferrer">' . esc_html__('Privacy Policy', 'host-domination-booking-engine') . '</a>';
     4280                            $privacy_link = '<a href="' . esc_url($privacy_policy_url) . '" target="_blank" rel="noopener noreferrer">' . esc_html(self::public_booking_text('privacy_link')) . '</a>';
    39794281                            if ($terms_and_conditions_url) {
    3980                                 $terms_link = '<a href="' . esc_url($terms_and_conditions_url) . '" target="_blank" rel="noopener noreferrer">' . esc_html__('Terms and Conditions', 'host-domination-booking-engine') . '</a>';
     4282                                $terms_link = '<a href="' . esc_url($terms_and_conditions_url) . '" target="_blank" rel="noopener noreferrer">' . esc_html(self::public_booking_text('terms_link')) . '</a>';
    39814283                                $privacy_consent_text = sprintf(
    39824284                                    /* translators: 1: Privacy Policy link, 2: Terms and Conditions link. */
    3983                                     __('I have read and accept the %1$s and %2$s.', 'host-domination-booking-engine'),
     4285                                    self::public_booking_text('privacy_terms_consent'),
    39844286                                    $privacy_link,
    39854287                                    $terms_link
    … …  
    39884290                                $privacy_consent_text = sprintf(
    39894291                                    /* translators: %s: Privacy Policy link. */
    3990                                     __('I have read and accept the %s.', 'host-domination-booking-engine'),
     4292                                    self::public_booking_text('privacy_consent'),
    39914293                                    $privacy_link
    39924294                                );
  • host-domination-booking-engine/trunk/includes/class-hdbe-room-property-cards.php

    r3791074 r3796366  
    99
    1010final class HDBE_Free_Room_Property_Cards {
    11     const VERSION = '1.5.33';
     11    const VERSION = '1.5.35';
    1212    private static $admin_assets_enqueued = false;
    1313    const OPTION = 'hdbe_room_property_cards';
    … …  
    282282                'guestPlural' => __('guests', 'host-domination-booking-engine'),
    283283                /* translators: %s is the formatted nightly price. */
    284                 'priceLabel' => __('Nights from %s', 'host-domination-booking-engine'),
     284                'priceLabel' => self::starting_price_label_template(),
    285285                'unsavedWarning' => __('You have unsaved property changes. Continue to card settings without saving?', 'host-domination-booking-engine'),
    286286                'locale' => str_replace('_', '-', determine_locale()),
    … …  
    628628            $formatted = self::format_nightly_price((float) $card['starting_price'], (string) ($card['currency'] ?? 'EUR'));
    629629            /* translators: %s is the formatted currency amount (e.g. €85). */
    630             echo '<div class="hdrpc-card-nightly-price"' . (!empty($card['starting_price_is_advanced']) ? ' data-advanced-price="1"' : '') . '>' . '<span class="hdrpc-nightly-price-label">' . esc_html(sprintf(__('Nights from %s', 'host-domination-booking-engine'), $formatted)) . '</span></div>';
     630            echo '<div class="hdrpc-card-nightly-price"' . (!empty($card['starting_price_is_advanced']) ? ' data-advanced-price="1"' : '') . '>' . '<span class="hdrpc-nightly-price-label">' . esc_html(sprintf(self::starting_price_label_template(), $formatted)) . '</span></div>';
    631631        } elseif ($preview && !empty($card['show_starting_price'])) {
    632632            echo '<div class="hdrpc-card-nightly-price" hidden><span class="hdrpc-nightly-price-label"></span></div>';
    … …  
    743743    }
    744744
     745    /**
     746     * Localized template for the nightly starting price displayed on public cards
     747     * and the Property / Room editor's live preview.
     748     *
     749     * Keep the original gettext msgid for compatibility with existing HD BE
     750     * Translate language packs. The fallback is only applied when that msgid
     751     * has no translation for the selected public-booking language.
     752     * Translators: keep %s (formatted nightly price) in the translation.
     753     */
     754    private static function starting_price_label_template() {
     755        $source = 'Nights from %s';
     756        /* translators: %s is the formatted starting price, for example €110. */
     757        $translated = __('Nights from %s', 'host-domination-booking-engine');
     758
     759        $locale = function_exists('determine_locale') ? determine_locale() : get_locale();
     760        $locale = (string) apply_filters('hdbe_public_booking_locale', (string) $locale);
     761        $locale = strtolower(str_replace('_', '-', $locale));
     762        $lang = explode('-', $locale)[0];
     763        $fallbacks = [
     764            'it' => 'Da %s a notte',
     765            'es' => 'Desde %s por noche',
     766            'fr' => 'À partir de %s par nuit',
     767            'de' => 'Ab %s pro Nacht',
     768            'pt' => 'A partir de %s por noite',
     769            'eu' => 'Gau bakoitzeko %s-tik aurrera',
     770        ];
     771        if ($translated === $source && isset($fallbacks[$lang])) {
     772            $translated = $fallbacks[$lang];
     773        }
     774
     775        // Invalid / incomplete translations must not break sprintf or the JS
     776        // preview when the source is customized by language packs or filters.
     777        if (false === strpos($translated, '%s') && false === strpos($translated, '%1$s')) {
     778            $translated = $fallbacks[$lang] ?? $source;
     779        }
     780        $translated = apply_filters('hdbe_room_property_card_nightly_price_label', $translated, $locale, $source);
     781        return (is_string($translated) && (false !== strpos($translated, '%s') || false !== strpos($translated, '%1$s')))
     782            ? $translated : ($fallbacks[$lang] ?? $source);
     783    }
     784
    745785    private static function format_nightly_price($price, $currency) {
    746786        $amount = number_format_i18n($price, (abs($price - round($price)) < 0.0001 ? 0 : 2));
  • host-domination-booking-engine/trunk/readme.txt

    r3791081 r3796366  
    55Requires at least: 6.0
    66Tested up to: 7.1
    7 Stable tag: 1.5.33
     7Stable tag: 1.5.44
    88Requires PHP: 7.4
    99License: GPLv2 or later
    … …  
    577577== Changelog ==
    578578
     579= 1.5.44 =
     580* Fixed: Calendar months and weekdays now display in the correct language.
     581* Improved: Full integration with HD BE Translate language settings.
     582* Localization: Improved translation support and English fallback.
     583
    579584= 1.5.33 =
    580585* NEW! Bank Transfer Payments
Note: See TracChangeset for help on using the changeset viewer.