//-> #region wf_dates: LOCALE-NATIVE DATE RENDERING [Foundation Layer] //-> strftime() is deprecated in PHP 8.1 and REMOVED in PHP 9, so every call had to move. It was also being fed ONE //-> English-shaped pattern ("%A, %d %B %Y") for all four languages, which is not how any of them writes a date: //-> Portuguese wants "domingo, 2 de agosto de 2026" (both "de"s, unpadded day), German wants the ordinal point //-> "Sonntag, 2. August 2026", French takes no comma. These helpers ask ICU for each LOCALE'S OWN pattern instead of //-> imposing one, so every language is written the way that language writes it. Locale follows setlocale() in //-> _inc/_language.php, so nothing else has to change. NOTE ON CASE: months and weekdays are lowercase in modern //-> European Portuguese - the Acordo Ortografico de 1990 (in force in Portugal 2009, transition closed 2015) ended //-> the old "Agosto" capitalisation. ICU follows AO90, so lowercase here is correct and not a bug. if(!function_exists('wf_locale')){ function wf_locale(){ $loc = setlocale(LC_TIME, '0'); if(!$loc || $loc === 'C' || $loc === 'POSIX'){ $loc = 'en_GB'; } return str_replace(['.UTF8','.UTF-8','.utf8','.utf-8'], '', $loc); } //-> SENTENCE-INITIAL CAPITAL. AO90 lowercased the month AS A WORD IN RUNNING TEXT ("nasceu em agosto de 1975"); it //-> did NOT touch the universal rule that a sentence, heading or standalone line opens with a capital. Every string //-> these helpers produce is a standalone display line - a heading over a post, the date under a testimonial, a //-> badge - so the first letter is capitalised and the month stays lowercase inside: "Domingo, 2 de agosto de 2026". //-> That is orthographically correct AND gives the line the visual anchor a lowercase opening throws away. function wf_ucfirst_line($s){ if($s === '' || $s === null){ return $s; } return mb_strtoupper(mb_substr($s, 0, 1, 'UTF-8'), 'UTF-8') . mb_substr($s, 1, null, 'UTF-8'); } //-> Render a timestamp through an ICU skeleton, letting the locale choose its own field order and separators. function wf_date_skel($skeleton, $timestamp){ $loc = wf_locale(); $pattern = (new IntlDatePatternGenerator($loc))->getBestPattern($skeleton); $fmt = new IntlDateFormatter($loc, IntlDateFormatter::FULL, IntlDateFormatter::NONE, date_default_timezone_get(), IntlDateFormatter::GREGORIAN, $pattern); return $fmt->format($timestamp); } //-> Weekday + full date, the locale's own long form. Replaces strftime("%A, %d %B %Y"). function wf_date_full($timestamp){ $loc = wf_locale(); $fmt = new IntlDateFormatter($loc, IntlDateFormatter::FULL, IntlDateFormatter::NONE, date_default_timezone_get()); return wf_ucfirst_line($fmt->format($timestamp)); } //-> Month + year. Replaces strftime("%B %Y") - PT correctly gains its "de": "agosto de 2026". function wf_date_monthyear($timestamp){ return wf_ucfirst_line(wf_date_skel('yMMMM', $timestamp)); } //-> Weekday + day + abbreviated month + year. Replaces strftime("%A, %e %b %G"). //-> Pluralo's booking widget reads better with the full native date than ICU's numeric-fallback skeleton. function wf_date_weekday_abbr($timestamp){ return wf_date_full($timestamp); } //-> Abbreviated month on its own, for the date badges. Replaces strftime("%b"). //-> Standalone badge: drop ICU's trailing abbreviation point, which reads as a typo under a big day number. function wf_month_abbr($timestamp){ return wf_ucfirst_line(rtrim(wf_date_skel('MMM', $timestamp), '.')); } } //-> #endregion