1<?php 2 3/** 4 * webtrees: online genealogy 5 * Copyright (C) 2023 webtrees development team 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation, either version 3 of the License, or 9 * (at your option) any later version. 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <https://www.gnu.org/licenses/>. 16 */ 17 18declare(strict_types=1); 19 20namespace Fisharebest\Webtrees\Date; 21 22use Fisharebest\ExtCalendar\FrenchCalendar; 23use Fisharebest\Webtrees\I18N; 24use Fisharebest\Webtrees\Services\RomanNumeralsService; 25 26/** 27 * Definitions for French Republican dates. 28 */ 29class FrenchDate extends AbstractCalendarDate 30{ 31 // GEDCOM calendar escape 32 public const string ESCAPE = '@#DFRENCH R@'; 33 34 // Convert GEDCOM month names to month numbers 35 protected const array MONTH_TO_NUMBER = [ 36 'VEND' => 1, 37 'BRUM' => 2, 38 'FRIM' => 3, 39 'NIVO' => 4, 40 'PLUV' => 5, 41 'VENT' => 6, 42 'GERM' => 7, 43 'FLOR' => 8, 44 'PRAI' => 9, 45 'MESS' => 10, 46 'THER' => 11, 47 'FRUC' => 12, 48 'COMP' => 13, 49 ]; 50 51 protected const array NUMBER_TO_MONTH = [ 52 1 => 'VEND', 53 2 => 'BRUM', 54 3 => 'FRIM', 55 4 => 'NIVO', 56 5 => 'PLUV', 57 6 => 'VENT', 58 7 => 'GERM', 59 8 => 'FLOR', 60 9 => 'PRAI', 61 10 => 'MESS', 62 11 => 'THER', 63 12 => 'FRUC', 64 13 => 'COMP', 65 ]; 66 67 private RomanNumeralsService $roman_numerals_service; 68 69 /** 70 * Create a date from either: 71 * a Julian day number 72 * day/month/year strings from a GEDCOM date 73 * another CalendarDate object 74 * 75 * @param array<string>|int|AbstractCalendarDate $date 76 */ 77 public function __construct($date) 78 { 79 $this->roman_numerals_service = new RomanNumeralsService(); 80 $this->calendar = new FrenchCalendar(); 81 82 parent::__construct($date); 83 } 84 85 /** 86 * Full month name in nominative case. 87 * 88 * @param int $month 89 * @param bool $leap_year Some calendars use leap months 90 * 91 * @return string 92 */ 93 protected function monthNameNominativeCase(int $month, bool $leap_year): string 94 { 95 static $translated_month_names; 96 97 if ($translated_month_names === null) { 98 $translated_month_names = [ 99 0 => '', 100 /* I18N: a month in the French republican calendar */ 101 1 => I18N::translateContext('NOMINATIVE', 'Vendemiaire'), 102 /* I18N: a month in the French republican calendar */ 103 2 => I18N::translateContext('NOMINATIVE', 'Brumaire'), 104 /* I18N: a month in the French republican calendar */ 105 3 => I18N::translateContext('NOMINATIVE', 'Frimaire'), 106 /* I18N: a month in the French republican calendar */ 107 4 => I18N::translateContext('NOMINATIVE', 'Nivose'), 108 /* I18N: a month in the French republican calendar */ 109 5 => I18N::translateContext('NOMINATIVE', 'Pluviose'), 110 /* I18N: a month in the French republican calendar */ 111 6 => I18N::translateContext('NOMINATIVE', 'Ventose'), 112 /* I18N: a month in the French republican calendar */ 113 /* I18N: a month in the French republican calendar */ 114 7 => I18N::translateContext('NOMINATIVE', 'Germinal'), 115 /* I18N: a month in the French republican calendar */ 116 8 => I18N::translateContext('NOMINATIVE', 'Floreal'), 117 /* I18N: a month in the French republican calendar */ 118 9 => I18N::translateContext('NOMINATIVE', 'Prairial'), 119 /* I18N: a month in the French republican calendar */ 120 10 => I18N::translateContext('NOMINATIVE', 'Messidor'), 121 /* I18N: a month in the French republican calendar */ 122 11 => I18N::translateContext('NOMINATIVE', 'Thermidor'), 123 /* I18N: a month in the French republican calendar */ 124 12 => I18N::translateContext('NOMINATIVE', 'Fructidor'), 125 /* I18N: a month in the French republican calendar */ 126 13 => I18N::translateContext('NOMINATIVE', 'jours complementaires'), 127 ]; 128 } 129 130 return $translated_month_names[$month]; 131 } 132 133 /** 134 * Full month name in genitive case. 135 * 136 * @param int $month 137 * @param bool $leap_year Some calendars use leap months 138 * 139 * @return string 140 */ 141 protected function monthNameGenitiveCase(int $month, bool $leap_year): string 142 { 143 static $translated_month_names; 144 145 if ($translated_month_names === null) { 146 $translated_month_names = [ 147 0 => '', 148 /* I18N: a month in the French republican calendar */ 149 1 => I18N::translateContext('GENITIVE', 'Vendemiaire'), 150 /* I18N: a month in the French republican calendar */ 151 2 => I18N::translateContext('GENITIVE', 'Brumaire'), 152 /* I18N: a month in the French republican calendar */ 153 3 => I18N::translateContext('GENITIVE', 'Frimaire'), 154 /* I18N: a month in the French republican calendar */ 155 4 => I18N::translateContext('GENITIVE', 'Nivose'), 156 /* I18N: a month in the French republican calendar */ 157 5 => I18N::translateContext('GENITIVE', 'Pluviose'), 158 /* I18N: a month in the French republican calendar */ 159 6 => I18N::translateContext('GENITIVE', 'Ventose'), 160 /* I18N: a month in the French republican calendar */ 161 7 => I18N::translateContext('GENITIVE', 'Germinal'), 162 /* I18N: a month in the French republican calendar */ 163 8 => I18N::translateContext('GENITIVE', 'Floreal'), 164 /* I18N: a month in the French republican calendar */ 165 9 => I18N::translateContext('GENITIVE', 'Prairial'), 166 /* I18N: a month in the French republican calendar */ 167 10 => I18N::translateContext('GENITIVE', 'Messidor'), 168 /* I18N: a month in the French republican calendar */ 169 11 => I18N::translateContext('GENITIVE', 'Thermidor'), 170 /* I18N: a month in the French republican calendar */ 171 12 => I18N::translateContext('GENITIVE', 'Fructidor'), 172 /* I18N: a month in the French republican calendar */ 173 13 => I18N::translateContext('GENITIVE', 'jours complementaires'), 174 ]; 175 } 176 177 return $translated_month_names[$month]; 178 } 179 180 /** 181 * Full month name in locative case. 182 * 183 * @param int $month 184 * @param bool $leap_year Some calendars use leap months 185 * 186 * @return string 187 */ 188 protected function monthNameLocativeCase(int $month, bool $leap_year): string 189 { 190 static $translated_month_names; 191 192 if ($translated_month_names === null) { 193 $translated_month_names = [ 194 0 => '', 195 /* I18N: a month in the French republican calendar */ 196 1 => I18N::translateContext('LOCATIVE', 'Vendemiaire'), 197 /* I18N: a month in the French republican calendar */ 198 2 => I18N::translateContext('LOCATIVE', 'Brumaire'), 199 /* I18N: a month in the French republican calendar */ 200 3 => I18N::translateContext('LOCATIVE', 'Frimaire'), 201 /* I18N: a month in the French republican calendar */ 202 4 => I18N::translateContext('LOCATIVE', 'Nivose'), 203 /* I18N: a month in the French republican calendar */ 204 5 => I18N::translateContext('LOCATIVE', 'Pluviose'), 205 /* I18N: a month in the French republican calendar */ 206 6 => I18N::translateContext('LOCATIVE', 'Ventose'), 207 /* I18N: a month in the French republican calendar */ 208 7 => I18N::translateContext('LOCATIVE', 'Germinal'), 209 /* I18N: a month in the French republican calendar */ 210 8 => I18N::translateContext('LOCATIVE', 'Floreal'), 211 /* I18N: a month in the French republican calendar */ 212 9 => I18N::translateContext('LOCATIVE', 'Prairial'), 213 /* I18N: a month in the French republican calendar */ 214 10 => I18N::translateContext('LOCATIVE', 'Messidor'), 215 /* I18N: a month in the French republican calendar */ 216 11 => I18N::translateContext('LOCATIVE', 'Thermidor'), 217 /* I18N: a month in the French republican calendar */ 218 12 => I18N::translateContext('LOCATIVE', 'Fructidor'), 219 /* I18N: a month in the French republican calendar */ 220 13 => I18N::translateContext('LOCATIVE', 'jours complementaires'), 221 ]; 222 } 223 224 return $translated_month_names[$month]; 225 } 226 227 /** 228 * Full month name in instrumental case. 229 * 230 * @param int $month 231 * @param bool $leap_year Some calendars use leap months 232 * 233 * @return string 234 */ 235 protected function monthNameInstrumentalCase(int $month, bool $leap_year): string 236 { 237 static $translated_month_names; 238 239 if ($translated_month_names === null) { 240 $translated_month_names = [ 241 0 => '', 242 /* I18N: a month in the French republican calendar */ 243 1 => I18N::translateContext('INSTRUMENTAL', 'Vendemiaire'), 244 /* I18N: a month in the French republican calendar */ 245 2 => I18N::translateContext('INSTRUMENTAL', 'Brumaire'), 246 /* I18N: a month in the French republican calendar */ 247 3 => I18N::translateContext('INSTRUMENTAL', 'Frimaire'), 248 /* I18N: a month in the French republican calendar */ 249 4 => I18N::translateContext('INSTRUMENTAL', 'Nivose'), 250 /* I18N: a month in the French republican calendar */ 251 5 => I18N::translateContext('INSTRUMENTAL', 'Pluviose'), 252 /* I18N: a month in the French republican calendar */ 253 6 => I18N::translateContext('INSTRUMENTAL', 'Ventose'), 254 /* I18N: a month in the French republican calendar */ 255 7 => I18N::translateContext('INSTRUMENTAL', 'Germinal'), 256 /* I18N: a month in the French republican calendar */ 257 8 => I18N::translateContext('INSTRUMENTAL', 'Floreal'), 258 /* I18N: a month in the French republican calendar */ 259 9 => I18N::translateContext('INSTRUMENTAL', 'Prairial'), 260 /* I18N: a month in the French republican calendar */ 261 10 => I18N::translateContext('INSTRUMENTAL', 'Messidor'), 262 /* I18N: a month in the French republican calendar */ 263 11 => I18N::translateContext('INSTRUMENTAL', 'Thermidor'), 264 /* I18N: a month in the French republican calendar */ 265 12 => I18N::translateContext('INSTRUMENTAL', 'Fructidor'), 266 /* I18N: a month in the French republican calendar */ 267 13 => I18N::translateContext('INSTRUMENTAL', 'jours complementaires'), 268 ]; 269 } 270 271 return $translated_month_names[$month]; 272 } 273 274 /** 275 * Abbreviated month name 276 * 277 * @param int $month 278 * @param bool $leap_year Some calendars use leap months 279 * 280 * @return string 281 */ 282 protected function monthNameAbbreviated(int $month, bool $leap_year): string 283 { 284 return $this->monthNameNominativeCase($month, $leap_year); 285 } 286 287 /** 288 * Full day of the week 289 * 290 * @param int $day_number 291 * 292 * @return string 293 */ 294 public function dayNames(int $day_number): string 295 { 296 static $translated_day_names; 297 298 if ($translated_day_names === null) { 299 $translated_day_names = [ 300 /* I18N: The first day in the French republican calendar */ 301 0 => I18N::translate('Primidi'), 302 /* I18N: The second day in the French republican calendar */ 303 1 => I18N::translate('Duodi'), 304 /* I18N: The third day in the French republican calendar */ 305 2 => I18N::translate('Tridi'), 306 /* I18N: The fourth day in the French republican calendar */ 307 3 => I18N::translate('Quartidi'), 308 /* I18N: The fifth day in the French republican calendar */ 309 4 => I18N::translate('Quintidi'), 310 /* I18N: The sixth day in the French republican calendar */ 311 5 => I18N::translate('Sextidi'), 312 /* I18N: The seventh day in the French republican calendar */ 313 6 => I18N::translate('Septidi'), 314 /* I18N: The eighth day in the French republican calendar */ 315 7 => I18N::translate('Octidi'), 316 /* I18N: The ninth day in the French republican calendar */ 317 8 => I18N::translate('Nonidi'), 318 /* I18N: The tenth day in the French republican calendar */ 319 9 => I18N::translate('Decidi'), 320 ]; 321 } 322 323 return $translated_day_names[$day_number]; 324 } 325 326 /** 327 * Abbreviated day of the week 328 * 329 * @param int $day_number 330 * 331 * @return string 332 */ 333 protected function dayNamesAbbreviated(int $day_number): string 334 { 335 return $this->dayNames($day_number); 336 } 337 338 /** 339 * Generate the %Y format for a date. 340 * 341 * @return string 342 */ 343 protected function formatLongYear(): string 344 { 345 return 'An ' . $this->roman_numerals_service->numberToRomanNumerals($this->year); 346 } 347} 348