# icalendrier/2.0/lib/Icalendrier.php

iCalendrier, version 2.0. 1,107 lines.

- Page: https://pluginprobe.com/plugins/icalendrier/2.0/code/lib/Icalendrier.php
- Raw: https://pluginprobe.com/plugins/icalendrier/2.0/raw/lib/Icalendrier.php
- Modified: 2026-09-03T13:46:06+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/icalendrier/2.0/code/lib/Icalendrier.php#L10-L20`.

```php
<?php
/*  Copyright 2014  Baptiste Placé

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License, version 2, as
    published by the Free Software Foundation.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/
/**
 * Class: iCalendrier
 * Description: Un simple calendrier qui affiche des infos du jour, comme le numéro de semaine, la date, la fête du jour et la phase de lune.
 * Version: 1.0
 * Author: Baptiste Placé
 * Author URI: https://icalendrier.fr/
 * License: GNU General Public License, version 2
 */

require_once dirname(__FILE__) . "/FeteDuJour.php";


class Icalendrier
{

    protected $nameDay;

    /** Reference time for everything rendered (injectable for tests). */
    protected DateTimeImmutable $now;

    protected $lang;

    protected $langs = array(
        "fr",
        "en",
        "en-GB",
        "es",
        "pt",
        "it",
        "de",
        "pt-BR",
        "ro",
        "sv",
        "da",
    );

    /** Languages that write day and month names in lowercase; the modern layout follows that convention like the site does. */
    protected $lowercaseDates = array('fr', 'es', 'pt', 'pt-BR', 'it', 'ro', 'sv', 'da');

    /** Site-style locale codes (config/websites.php on the iCalendrier sites) mapped to the plugin's short codes. */
    protected $aliases = array(
        'fr-FR' => 'fr',
        'en-US' => 'en',
        'es-ES' => 'es',
        'pt-PT' => 'pt',
        'it-IT' => 'it',
        'de-DE' => 'de',
        'ro-RO' => 'ro',
        'sv-SE' => 'sv',
        'da-DK' => 'da',
    );

    protected $siteUrls = array(
        'fr'    => "https://icalendrier.fr/",
        'en'    => "https://icalendars.net/",
        'es'    => "https://icalendario.net/",
        'pt'    => "https://icalendario.pt/",
        'pt-BR' => "https://icalendario.br.com/",
        'it'    => "https://icalendario.it/",
        'de'    => "https://ikalender.org/",
        'ro'    => "https://www.noutati-ortodoxe.ro/calendar-ortodox/",
        'en-GB' => "https://icalendar.co.uk/",
        'sv'    => "https://ikalendrar.se/",
        'da'    => "https://ikalender.dk/",
    );

    /** Site titles (config/websites.php), used as the credit line of the modern layout. */
    protected $siteLabels = array(
        'fr'    => "iCalendrier",
        'en'    => "iCalendars",
        'es'    => "iCalendario",
        'pt'    => "iCalendário",
        'pt-BR' => "iCalendario",
        'it'    => "iCalendario",
        'de'    => "iKalender",
        'ro'    => "noutati-ortodoxe.ro",
        'en-GB' => "iCalendar",
        'sv'    => "iKalendrar",
        'da'    => "iKalender",
    );

    protected $todayLabels = array(
        'fr'    => "Aujourd'hui",
        'en'    => "Today",
        'es'    => "Hoy en día",
        'pt'    => "Hoje",
        'pt-BR' => "Hoje",
        'it'    => "Oggi",
        'de'    => "Heute",
        'ro'    => "Astăzi",
        'en-GB' => "Today",
        'sv'    => "Idag",
        'da'    => "I dag",
    );

    protected $localDays = array(
        'fr'    => array(
            'Lundi',
            'Mardi',
            'Mercredi',
            'Jeudi',
            'Vendredi',
            'Samedi',
            'Dimanche',
        ),
        'en'    => array(
            'Monday',
            'Tuesday',
            'Wednesday',
            'Thursday',
            'Friday',
            'Saturday',
            'Sunday',
        ),
        'es'    => array(
            'Lunes',
            'Martes',
            'Miércoles',
            'Jueves',
            'Viernes',
            'Sábado',
            'Domingo',
        ),
        'pt'    => array(
            'Segunda-feira',
            'Terca-feira',
            'Quarta-feira',
            'Quinta-feira',
            'Sexta-feira',
            'Sábado',
            'Domingo',
        ),
        'pt-BR' => array(
            'Segunda-feira',
            'Terca-feira',
            'Quarta-feira',
            'Quinta-feira',
            'Sexta-feira',
            'Sábado',
            'Domingo',
        ),
        'it'    => array(
            'Lunedí',
            'Martedí',
            'Mercoledí',
            'Giovedì',
            'Venerdí',
            'Sabato',
            'Domenica',
        ),
        'de'    => array(
            'Montag',
            'Dienstag',
            'Mittwoch',
            'Donnerstag',
            'Freitag',
            'Samstag',
            'Sonntag',
        ),
        'ro'    => array(
            'Luni',
            'Marți',
            'Miercuri',
            'Joi',
            'Vineri',
            'Sâmbătă',
            'Duminică',
        ),
        'en-GB' => array(
            'Monday',
            'Tuesday',
            'Wednesday',
            'Thursday',
            'Friday',
            'Saturday',
            'Sunday',
        ),
        'sv'    => array(
            'Måndag',
            'Tisdag',
            'Onsdag',
            'Torsdag',
            'Fredag',
            'Lördag',
            'Söndag',
        ),
        'da'    => array(
            'Mandag',
            'Tirsdag',
            'Onsdag',
            'Torsdag',
            'Fredag',
            'Lørdag',
            'Søndag',
        ),
    );

    protected $shortLocalDays = array(
        'fr'    => array(
            'Lundi',
            'Mardi',
            'Mercredi',
            'Jeudi',
            'Vendredi',
            'Samedi',
            'Dimanche',
        ),
        'en'    => array(
            'Monday',
            'Tuesday',
            'Wednesday',
            'Thursday',
            'Friday',
            'Saturday',
            'Sunday',
        ),
        'es'    => array(
            'Lunes',
            'Martes',
            'Miércoles',
            'Jueves',
            'Viernes',
            'Sábado',
            'Domingo',
        ),
        'pt'    => array(
            'Seg.-feira',
            'Terca-feira',
            'Quarta-feira',
            'Quinta-feira',
            'Sexta-feira',
            'Sábado',
            'Domingo',
        ),
        'pt-BR' => array(
            'Seg.-feira',
            'Terca-feira',
            'Quarta-feira',
            'Quinta-feira',
            'Sexta-feira',
            'Sábado',
            'Domingo',
        ),
        'it'    => array(
            'Lunedí',
            'Martedí',
            'Mercoledí',
            'Giovedì',
            'Venerdí',
            'Sabato',
            'Domenica',
        ),
        'de'    => array(
            'Montag',
            'Dienstag',
            'Mittwoch',
            'Donnerstag',
            'Freitag',
            'Samstag',
            'Sonntag',
        ),
        'ro'    => array(
            'Luni',
            'Marți',
            'Miercuri',
            'Joi',
            'Vineri',
            'Sâmbătă',
            'Duminică',
        ),
        'en-GB' => array(
            'Monday',
            'Tuesday',
            'Wednesday',
            'Thursday',
            'Friday',
            'Saturday',
            'Sunday',
        ),
        'sv'    => array(
            'Måndag',
            'Tisdag',
            'Onsdag',
            'Torsdag',
            'Fredag',
            'Lördag',
            'Söndag',
        ),
        'da'    => array(
            'Mandag',
            'Tirsdag',
            'Onsdag',
            'Torsdag',
            'Fredag',
            'Lørdag',
            'Søndag',
        ),
    );

    protected $weekLabels = array(
        'fr'    => "Semaine",
        'en'    => "Week",
        'es'    => "Semana",
        'pt'    => "Semana",
        'pt-BR' => "Semana",
        'it'    => "Settimana",
        'de'    => "Woche",
        'ro'    => "Săptămâna",
        'en-GB' => "Week",
        'sv'    => "Vecka",
        'da'    => "Uge",
    );

    protected $shortWeekLabels = array(
        'fr'    => "Sem.",
        'en'    => "Week",
        'es'    => "Sem.",
        'pt'    => "Sem.",
        'pt-BR' => "Sem.",
        'it'    => "Sett.",
        'de'    => "Wo.",
        'ro'    => "Săpt.",
        'en-GB' => "Week",
        'sv'    => "V.",
        'da'    => "Uge",
    );

    protected $shortMonthLabels = array(
        'fr'    => array(
            'Jan.',
            'Fév.',
            'Mars',
            'Avr.',
            'Mai',
            'Juin',
            'Juil.',
            'Août',
            'Sep.',
            'Oct.',
            'Nov.',
            'Déc.'
        ),
        'en'    => array(
            'Jan.',
            'Feb.',
            'Mar.',
            'Apr.',
            'May',
            'June',
            'July',
            'Aug.',
            'Sep.',
            'Oct.',
            'Nov.',
            'Dec.'
        ),
        'es'    => array(
            'Ene.',
            'Feb.',
            'Mar.',
            'Abril',
            'Mayo',
            'Junio',
            'Julio',
            'Ago.',
            'Sep.',
            'Oct.',
            'Nov.',
            'Dic.'
        ),
        'pt'    => array(
            'Jan.',
            'Fev.',
            'Mar.',
            'Abr.',
            'Maio',
            'Jun.',
            'Jul.',
            'Ago.',
            'Set.',
            'Out.',
            'Nov.',
            'Dez.'
        ),
        'pt-BR' => array(
            'Jan.',
            'Fev.',
            'Mar.',
            'Abr.',
            'Maio',
            'Jun.',
            'Jul.',
            'Ago.',
            'Set.',
            'Out.',
            'Nov.',
            'Dez.'
        ),
        'it'    => array(
            'Gen.',
            'Feb.',
            'Mar.',
            'Apr.',
            'Mag.',
            'Giu.',
            'Lug.',
            'Ago.',
            'Set.',
            'Ott.',
            'Nov.',
            'Dic.'
        ),
        'de'    => array(
            'Jan.',
            'Feb.',
            'März',
            'Apr.',
            'Mai',
            'Juni',
            'Juli',
            'Aug.',
            'Sep.',
            'Okt.',
            'Nov.',
            'Dez.'
        ),
        'ro'    => array(
            'Ian.',
            'Feb.',
            'Mar.',
            'Apr.',
            'Mai',
            'Iun.',
            'Iul.',
            'Aug.',
            'Sep.',
            'Oct.',
            'Nov.',
            'Dec.'
        ),
        'en-GB' => array(
            'Jan.',
            'Feb.',
            'Mar.',
            'Apr.',
            'May',
            'June',
            'July',
            'Aug.',
            'Sep.',
            'Oct.',
            'Nov.',
            'Dec.'
        ),
        'sv'    => array(
            'Jan.',
            'Feb.',
            'Mars',
            'Apr.',
            'Maj',
            'Juni',
            'Juli',
            'Aug.',
            'Sep.',
            'Okt.',
            'Nov.',
            'Dec.',
        ),
        'da'    => array(
            'Jan.',
            'Feb.',
            'Mar.',
            'Apr.',
            'Maj',
            'Jun.',
            'Jul.',
            'Aug.',
            'Sep.',
            'Okt.',
            'Nov.',
            'Dec.',
        ),
    );

    protected $monthLabels = array(
        'fr'    => array(
            'Janvier',
            'Février',
            'Mars',
            'Avril',
            'Mai',
            'Juin',
            'Juillet',
            'Août',
            'Septembre',
            'Octobre',
            'Novembre',
            'Décembre'
        ),
        'en'    => array(
            'January',
            'February',
            'March',
            'April',
            'May',
            'June',
            'July',
            'August',
            'September',
            'October',
            'November',
            'December'
        ),
        'es'    => array(
            'Enero',
            'Febrero',
            'Marzo',
            'Abril',
            'Mayo',
            'Junio',
            'Julio',
            'Agosto',
            'Septiembre',
            'Octubre',
            'Noviembre',
            'Diciembre'
        ),
        'pt'    => array(
            'Janeiro',
            'Fevereiro',
            'Março',
            'Abril',
            'Maio',
            'Junho',
            'Julho',
            'Agosto',
            'Setembro',
            'Outubro',
            'Novembro',
            'Dezembro'
        ),
        'pt-BR' => array(
            'Janeiro',
            'Fevereiro',
            'Março',
            'Abril',
            'Maio',
            'Junho',
            'Julho',
            'Agosto',
            'Setembro',
            'Outubro',
            'Novembro',
            'Dezembro'
        ),
        'it'    => array(
            'Gennaio',
            'Febbraio',
            'Marzo',
            'Aprile',
            'Maggio',
            'Giugno',
            'Luglio',
            'Agosto',
            'Settembre',
            'Ottobre',
            'Novembre',
            'Dicembre'
        ),
        'de'    => array(
            'Januar',
            'Februar',
            'März',
            'April',
            'Mai',
            'Juni',
            'Juli',
            'August',
            'September',
            'Oktober',
            'November',
            'Dezember'
        ),
        'ro'    => array(
            'Ianuarie',
            'Februarie',
            'Martie',
            'Aprilie',
            'Mai',
            'Iunie',
            'Iulie',
            'August',
            'Septembrie',
            'Octombrie',
            'Noiembrie',
            'Decembrie'
        ),
        'en-GB' => array(
            'January',
            'February',
            'March',
            'April',
            'May',
            'June',
            'July',
            'August',
            'September',
            'October',
            'November',
            'December'
        ),
        'sv'    => array(
            'Januari',
            'Februari',
            'Mars',
            'April',
            'Maj',
            'Juni',
            'Juli',
            'Augusti',
            'September',
            'Oktober',
            'November',
            'December',
        ),
        'da'    => array(
            'Januar',
            'Februar',
            'Marts',
            'April',
            'Maj',
            'Juni',
            'Juli',
            'August',
            'September',
            'Oktober',
            'November',
            'December',
        ),
    );

    protected $localMoonPhases = array(
        'fr'    => array(
            'New Moon'        => 'Nouvelle lune',
            'Waxing Crescent' => 'Premier croissant',
            'First Quarter'   => 'Premier quartier',
            'Waxing Gibbous'  => 'Gibbeuse croissante',
            'Full Moon'       => 'Pleine lune',
            'Waning Gibbous'  => 'Gibbeuse décroissante',
            'Third Quarter'   => 'Dernier quartier',
            'Waning Crescent' => 'Dernier croissant',
        ),
        'en'    => array(
            'New Moon'        => 'New Moon',
            'Waxing Crescent' => 'Waxing Crescent',
            'First Quarter'   => 'First Quarter',
            'Waxing Gibbous'  => 'Waxing Gibbous',
            'Full Moon'       => 'Full Moon',
            'Waning Gibbous'  => 'Waning Gibbous',
            'Third Quarter'   => 'Third Quarter',
            'Waning Crescent' => 'Waning Crescent',
        ),
        'es'    => array(
            'New Moon'        => 'Luna Nueva',
            'Waxing Crescent' => 'Luna Nueva Visible',
            'First Quarter'   => 'Cuarto Creciente',
            'Waxing Gibbous'  => 'Luna Gibosa Creciente',
            'Full Moon'       => 'Luna Llena',
            'Waning Gibbous'  => 'Luna Gibosa Menguante',
            'Third Quarter'   => 'Cuarto Menguante',
            'Waning Crescent' => 'Luna Menguante',
        ),
        'pt'    => array(
            'New Moon'        => 'Lua nova',
            'Waxing Crescent' => 'Lua crescente',
            'First Quarter'   => 'Quarto Crescente',
            'Waxing Gibbous'  => 'Lua crescente convexa',
            'Full Moon'       => 'Lua Cheia',
            'Waning Gibbous'  => 'Lua minguante convexa',
            'Third Quarter'   => 'Quarto Minguante',
            'Waning Crescent' => 'Lua minguante',
        ),
        'pt-BR' => array(
            'New Moon'        => 'Lua nova',
            'Waxing Crescent' => 'Lua crescente',
            'First Quarter'   => 'Quarto Crescente',
            'Waxing Gibbous'  => 'Lua crescente convexa',
            'Full Moon'       => 'Lua Cheia',
            'Waning Gibbous'  => 'Lua minguante convexa',
            'Third Quarter'   => 'Quarto Minguante',
            'Waning Crescent' => 'Lua minguante',
        ),
        'it'    => array(
            'New Moon'        => 'Luna nuova',
            'Waxing Crescent' => 'Luna crescente',
            'First Quarter'   => 'Primo quarto',
            'Waxing Gibbous'  => 'Gibbosa crescente',
            'Full Moon'       => 'Luna piena',
            'Waning Gibbous'  => 'Gibbosa calante',
            'Third Quarter'   => 'Ultimo quarto',
            'Waning Crescent' => 'Luna calante',
        ),
        'de'    => array(
            'New Moon'        => 'Neumond',
            'Waxing Crescent' => 'Erstes Viertel',
            'First Quarter'   => 'Zunehmender Halbmond',
            'Waxing Gibbous'  => 'Zweites Viertel',
            'Full Moon'       => 'Vollmond',
            'Waning Gibbous'  => 'Drittes Viertel',
            'Third Quarter'   => 'Abnehmender Halbmond',
            'Waning Crescent' => 'Letztes Viertel',
        ),
        'ro'    => array(
            'New Moon'        => 'Lună nouă',
            'Waxing Crescent' => 'Prima creștere',
            'First Quarter'   => 'Primul pătrar',
            'Waxing Gibbous'  => 'Lună în creștere',
            'Full Moon'       => 'Lună plină',
            'Waning Gibbous'  => 'Lună în descreștere',
            'Third Quarter'   => 'Al treilea pătrar',
            'Waning Crescent' => 'Ultima creștere',
        ),
        'en-GB' => array(
            'New Moon'        => 'New Moon',
            'Waxing Crescent' => 'Waxing Crescent',
            'First Quarter'   => 'First Quarter',
            'Waxing Gibbous'  => 'Waxing Gibbous',
            'Full Moon'       => 'Full Moon',
            'Waning Gibbous'  => 'Waning Gibbous',
            'Third Quarter'   => 'Last Quarter',
            'Waning Crescent' => 'Waning Crescent',
        ),
        'sv'    => array(
            'New Moon'        => 'Nymåne',
            'Waxing Crescent' => 'Tilltagande skära',
            'First Quarter'   => 'Första kvarteret',
            'Waxing Gibbous'  => 'Tilltagande gibbeus',
            'Full Moon'       => 'Fullmåne',
            'Waning Gibbous'  => 'Avtagande gibbeus',
            'Third Quarter'   => 'Sista kvarteret',
            'Waning Crescent' => 'Avtagande skära',
        ),
        'da'    => array(
            'New Moon'        => 'Nymåne',
            'Waxing Crescent' => 'Tiltagende månesegl',
            'First Quarter'   => 'Første kvarter',
            'Waxing Gibbous'  => 'Tiltagende måne',
            'Full Moon'       => 'Fuldmåne',
            'Waning Gibbous'  => 'Aftagende måne',
            'Third Quarter'   => 'Sidste kvarter',
            'Waning Crescent' => 'Aftagende månesegl',
        ),
    );


    /**
     * @param string                     $lang
     * @param string|int                 $timezone PHP timezone identifier; 0, '0' or '' keep the server default
     * @param DateTimeInterface|int|null $now      Reference time (tests); defaults to now
     */
    public function __construct($lang = 'en', $timezone = 0, DateTimeInterface|int|null $now = null)
    {
        $lang = $this->aliases[$lang] ?? $lang;
        if ( ! in_array($lang, $this->langs, true)) {
            $lang = "en"; // Default En
        }
        $this->lang = $lang;

        // The zone only affects this instance's reference time; global PHP state is left alone.
        // null means "not requested": an injected $now keeps its own zone, anything else uses the server default.
        $zone = self::timezone($timezone);

        if ($now instanceof DateTimeInterface) {
            $this->now = DateTimeImmutable::createFromInterface($now);
            if (null !== $zone) {
                $this->now = $this->now->setTimezone($zone);
            }
        } elseif (is_int($now)) {
            $this->now = (new DateTimeImmutable('@' . $now))->setTimezone($zone ?? new DateTimeZone(date_default_timezone_get()));
        } else {
            $this->now = new DateTimeImmutable('now', $zone);
        }

        $this->nameDay = new NameDay();
    }

    /**
     * @param string|int $timezone
     */
    private static function timezone($timezone): ?DateTimeZone
    {
        if ( ! is_string($timezone) || '' === $timezone || '0' === $timezone) {
            return null;
        }

        try {
            return new DateTimeZone($timezone);
        } catch (Exception $e) {
            return null; // Unknown identifier: server default.
        }
    }

    /**
     * @param array $parameters showLink, bgColor, style, showNameDay (default on)
     *
     * @return string
     */
    public function iCalendrierComp($parameters = [])
    {
        $html = '';

        $showLink       = isset($parameters['showLink']) ? (bool)$parameters['showLink'] : false;
        $showNameDay    = $this->showNameDay($parameters);
        $bgColor        = isset($parameters['bgColor']) ? $this->secureParam($parameters['bgColor']) : false;
        $styleClassname = isset($parameters['style']) && 'default' !== $parameters['style'] ? ' ' . $this->secureParam($parameters['style']) : '';

        $html .= '<div class="icalComp' . $styleClassname . '">';
        $html .= '<div class="ccomp">';

        if ($bgColor) {
            $html .= '<div class="cheight" style="background:' . $bgColor . '!important;">';
        } else {
            $html .= '<div class="cheight">';
        }

        $html .= '<div class="ctitle">';
        if ($showLink) {
            $html .= '<a href="' . $this->siteUrls[$this->lang] . '">' . $this->todayLabels[$this->lang] . '</a>';
        } else {
            $html .= '<a href="javascript:void(0)">' . $this->todayLabels[$this->lang] . '</a>';
        }
        $html .= '</div>';

        $html .= '<div class="cephem">';
        $html .= '<div class="today">';

        $html .= '<span class="daysem">';
        $html .= $this->shortLocalDays[$this->lang][$this->now->format('N') - 1];
        $html .= " - ";
        $html .= $this->shortWeekLabels[$this->lang] . " " . ltrim($this->now->format('W'), '0');
        $html .= '</span>';

        $day     = $this->now->format('d');
        $daydig1 = substr($day, 0, 1);
        $daydig2 = substr($day, 1, 1);

        $html .= '<span class="day">';

        $html .= '<span class="daydig1">';
        $html .= $daydig1;
        $html .= '</span>';

        $html .= '<span class="daydig1">';
        $html .= $daydig2;
        $html .= '</span>';

        $html .= '</span>';


        $html .= '<span class="month">';
        $html .= $this->shortMonthLabels[$this->lang][$this->now->format('n') - 1];
        $html .= '</span>';

        if ($showNameDay) {
            $html .= '<span class="fete">';
            $html .= $this->nameDay->today($this->lang, (int)$this->now->format('n'), (int)$this->now->format('j'));
            $html .= '</span>';
        }

        $html .= '<span class="moon">';
        $html .= $this->getMoonSide();
        $html .= '</span>';

        $html .= '</div>';
        $html .= '</div>';
        $html .= '</div>';
        $html .= '</div>';
        $html .= '</div>';

        return $html;
    }

    /**
     * Wide calendar
     *
     * @param $parameters
     *
     * @return string
     */
    public function iCalendrierWide($parameters)
    {
        $html = "";

        $showLink       = isset($parameters['showLink']) ? (bool)$parameters['showLink'] : false;
        $showNameDay    = $this->showNameDay($parameters);
        $bgColor        = isset($parameters['bgColor']) ? $this->secureParam($parameters['bgColor']) : false;
        $styleClassname = isset($parameters['style']) && 'default' !== $parameters['style'] ? ' ' . $this->secureParam($parameters['style']) : '';

        if ($bgColor) {
            $html .= '<div class="icalWide' . $styleClassname . '" style="background:' . $bgColor . ' !important;">';
        } else {
            $html .= '<div class="icalWide' . $styleClassname . '">';
        }

        $html .= '<div class="today">';

        $html .= '<span class="ctitle">';
        if ($showLink) {
            $html .= '<a href="' . $this->siteUrls[$this->lang] . '">' . $this->todayLabels[$this->lang] . '</a>';
        } else {
            $html .= '<a href="javascript:void(0)">' . $this->todayLabels[$this->lang] . '</a>';
        }
        $html .= '</span>';


        $html .= '<span class="day">';

        $html .= $this->shortLocalDays[$this->lang][$this->now->format('N') - 1];

        $html .= '</span>';

        $html .= '<span class="num">' . $this->now->format('d') . '</span>';

        $html .= '<span class="month">';

        $html .= $this->monthLabels[$this->lang][$this->now->format('n') - 1];
        $html .= '</span>';

        $html .= '<span class="more">';
        $html .= $this->weekLabels[$this->lang] . " " . ltrim($this->now->format('W'), '0');

        if ($showNameDay) {
            $html .= ' | ';
            $html .= $this->nameDay->today($this->lang, (int)$this->now->format('n'), (int)$this->now->format('j'));
        }
        $html .= '</span>';

        $html .= '<span class="moon">';

        $html .= $this->getMoonSide();

        $html .= '</span>';

        $html .= '</div>';
        $html .= '</div>';

        return $html;
    }

    /**
     * Modern layout (v2), same markup and classes as the widget on the iCalendrier sites.
     *
     * @param array $parameters showLink, bgColor, style, showMoon (default on), showNameDay (default on)
     *
     * @return string
     */
    public function iCalendrierModern($parameters = [])
    {
        $showLink       = isset($parameters['showLink']) ? (bool)$parameters['showLink'] : false;
        $showMoon       = ! isset($parameters['showMoon']) || (bool)$parameters['showMoon'];
        $showNameDay    = $this->showNameDay($parameters);
        $bgColor        = isset($parameters['bgColor']) ? $this->secureParam($parameters['bgColor']) : false;
        $styleClassname = isset($parameters['style']) && 'default' !== $parameters['style'] ? ' ' . $this->secureParam($parameters['style']) : '';
        $lang           = $this->lang;

        $html = '<div class="c-ef__widget' . $styleClassname . '">';

        $html .= $bgColor
            ? '<div class="c-ef__fulldate" style="background:' . $bgColor . '!important;">'
            : '<div class="c-ef__fulldate">';
        if ($showLink) {
            $html .= '<a href="' . $this->siteUrls[$lang] . '">' . $this->siteLabels[$lang] . '</a>';
        }
        $html .= '<div class="c-ef__dow">' . $this->dateLabel($this->localDays[$lang][$this->now->format('N') - 1]) . '</div>';
        $html .= '<div class="c-ef__day">' . $this->now->format('d') . '</div>';
        $html .= '<div class="c-ef__monthyear">' . $this->dateLabel($this->monthLabels[$lang][$this->now->format('n') - 1]) . ' ' . $this->now->format('Y') . '</div>';
        $html .= '</div>';

        $html .= '<div class="c-ef__metadata-icons">';
        $html .= '<span class="c-ef__icon c-ef__icon--cal"></span>';
        if ($showMoon) {
            $html .= '<span class="c-ef__icon c-ef__icon--moon"></span>';
        }
        if ($showNameDay) {
            $html .= '<span class="c-ef__icon c-ef__icon--cheer"></span>';
        }
        $html .= '</div>';

        $html .= '<div class="c-ef__metadata">';
        $html .= '<div class="c-ef__weeknumber">' . $this->weekLabels[$lang] . ' ' . ltrim($this->now->format('W'), '0') . '</div>';
        if ($showMoon) {
            $moon  = $this->moon();
            $html .= '<div class="c-ef__moonphase"><div class="c-ef__moonphase-text">';
            $html .= $this->localMoonPhases[$lang][$moon->phase_name()];
            $html .= ' <span class="c-ef__moonphasechar c-ef__moonphasechar--' . $this->moonFrame($moon) . '"></span>';
            $html .= '</div></div>';
        }
        if ($showNameDay) {
            $html .= '<div class="c-ef__saint">' . $this->nameDay->today($lang, (int)$this->now->format('n'), (int)$this->now->format('j')) . '</div>';
        }
        $html .= '</div>';

        $html .= '</div>';

        return $html;
    }

    /** Name days are a Latin-country habit; Northern sites can switch them off. Default on. */
    private function showNameDay(array $parameters): bool
    {
        return ! isset($parameters['showNameDay']) || (bool)$parameters['showNameDay'];
    }

    /** Day or month name in the casing the current language uses in running text. */
    private function dateLabel(string $label): string
    {
        return in_array($this->lang, $this->lowercaseDates, true) ? mb_strtolower($label) : $label;
    }

    /**
     * Moon for the night that matters: before 7am the night in progress (last midnight),
     * from 7am the coming one (next midnight).
     */
    protected function moon(): Solaris_MoonPhase
    {
        require_once(dirname(__FILE__) . '/Solaris/MoonPhase.php');

        $midnight = $this->now->setTime(0, 0);
        if ((int)$this->now->format('G') >= 7) {
            $midnight = $midnight->add(new DateInterval('P1D'));
        }

        return new Solaris_MoonPhase((int)$midnight->format('U'));
    }

    /**
     * Frame 0-7 in the 8-image moon sprite of the modern layout (same formula as the site widget).
     */
    protected function moonFrame(Solaris_MoonPhase $moon): int
    {
        return (int)floor(($moon->phase() + 0.0625) * 8) % 8;
    }

    /**
     * Select the moon character
     * @return string
     */
    protected function getMoonSide()
    {
        $moon  = $this->moon();
        $phase = $moon->phase();

        // Utilisation de la font

        // 29 "phases" pour correspondre à la font
        $moonCharacters = array(0, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', '@', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 0);

        $totalCharacters = count($moonCharacters);
        $step            = 1 / ($totalCharacters - 1);

        if ($phase < $step / 2) {
            $index = 0;
        } elseif ($phase > (1 - $step / 2)) {
            $index = 28;
        } else {
            for ($i = 1; $i <= 27; $i++) {
                // Passage dans l'interval qui entoure l'illustration
                if ($phase >= ($step / 2 + ($i - 1) * $step) && $phase < ($step / 2 + $i * $step)) {
                    $index = $i;
                }
            }
        }

        $html = '<span class="phase">' . $moonCharacters[$index] . '</span>';
        $html .= $this->localMoonPhases[$this->lang][$moon->phase_name()];

        return $html;
    }

    /**
     * @param string $param
     *
     * @return string
     */
    private function secureParam(string $param)
    {
        return htmlentities(str_replace(["'", '"'], ['', ''], $param));
    }

}

```
