Use same lock values as mobile clients
[ProtonMail-WebClient.git] / packages / shared / lib / calendar / recurrence / getFrequencyString.ts
blob6e16e7fb2fd47cbb6931c4b302526b9d25de84c9
1 import { c, msgid } from 'ttag';
3 import mod from '@proton/utils/mod';
4 import unique from '@proton/utils/unique';
6 import { format } from '../../date-fns-utc';
7 import type { WeekStartsOn } from '../../date-fns-utc/interface';
8 import { toUTCDate } from '../../date/timezone';
9 import type {
10     VcalDateOrDateTimeProperty,
11     VcalRruleProperty,
12     VcalRrulePropertyValue,
13 } from '../../interfaces/calendar/VcalModel';
14 import { END_TYPE, FREQUENCY, MONTHLY_TYPE } from '../constants';
15 import { getPropertyTzid } from '../vcalHelper';
16 import { getIsRruleCustom, getIsRruleSimple, getPositiveSetpos } from './rrule';
17 import { getEndType, getMonthType, getUntilDate, getWeeklyDays } from './rruleProperties';
19 interface RruleEnd {
20     type: END_TYPE;
21     count?: number;
22     until?: Date;
24 interface GetTimezonedFrequencyStringOptions {
25     currentTzid: string;
26     locale: Locale;
27     weekStartsOn: WeekStartsOn;
29 // NOTE: due to the different grammar of different languages, to allow for a proper translation,
30 // we need to expand all possible cases so there will be quite a bit of duplicated code
32 export const getOnDayString = (date: Date, monthlyType: MONTHLY_TYPE) => {
33     const dayOfMonth = date.getUTCDate();
34     const day = date.getUTCDay();
36     if (monthlyType === MONTHLY_TYPE.ON_NTH_DAY) {
37         const setPos = getPositiveSetpos(date);
38         if (setPos === 1) {
39             if (day === 0) {
40                 return c('Monthly recurring event, repeats on').t`on the first Sunday`;
41             }
42             if (day === 1) {
43                 return c('Monthly recurring event, repeats on').t`on the first Monday`;
44             }
45             if (day === 2) {
46                 return c('Monthly recurring event, repeats on').t`on the first Tuesday`;
47             }
48             if (day === 3) {
49                 return c('Monthly recurring event, repeats on').t`on the first Wednesday`;
50             }
51             if (day === 4) {
52                 return c('Monthly recurring event, repeats on').t`on the first Thursday`;
53             }
54             if (day === 5) {
55                 return c('Monthly recurring event, repeats on').t`on the first Friday`;
56             }
57             if (day === 6) {
58                 return c('Monthly recurring event, repeats on').t`on the first Saturday`;
59             }
60         }
61         if (setPos === 2) {
62             if (day === 0) {
63                 return c('Monthly recurring event, repeats on').t`on the second Sunday`;
64             }
65             if (day === 1) {
66                 return c('Monthly recurring event, repeats on').t`on the second Monday`;
67             }
68             if (day === 2) {
69                 return c('Monthly recurring event, repeats on').t`on the second Tuesday`;
70             }
71             if (day === 3) {
72                 return c('Monthly recurring event, repeats on').t`on the second Wednesday`;
73             }
74             if (day === 4) {
75                 return c('Monthly recurring event, repeats on').t`on the second Thursday`;
76             }
77             if (day === 5) {
78                 return c('Monthly recurring event, repeats on').t`on the second Friday`;
79             }
80             if (day === 6) {
81                 return c('Monthly recurring event, repeats on').t`on the second Saturday`;
82             }
83         }
84         if (setPos === 3) {
85             if (day === 0) {
86                 return c('Monthly recurring event, repeats on').t`on the third Sunday`;
87             }
88             if (day === 1) {
89                 return c('Monthly recurring event, repeats on').t`on the third Monday`;
90             }
91             if (day === 2) {
92                 return c('Monthly recurring event, repeats on').t`on the third Tuesday`;
93             }
94             if (day === 3) {
95                 return c('Monthly recurring event, repeats on').t`on the third Wednesday`;
96             }
97             if (day === 4) {
98                 return c('Monthly recurring event, repeats on').t`on the third Thursday`;
99             }
100             if (day === 5) {
101                 return c('Monthly recurring event, repeats on').t`on the third Friday`;
102             }
103             if (day === 6) {
104                 return c('Monthly recurring event, repeats on').t`on the third Saturday`;
105             }
106         }
107         if (setPos === 4) {
108             if (day === 0) {
109                 return c('Monthly recurring event, repeats on').t`on the fourth Sunday`;
110             }
111             if (day === 1) {
112                 return c('Monthly recurring event, repeats on').t`on the fourth Monday`;
113             }
114             if (day === 2) {
115                 return c('Monthly recurring event, repeats on').t`on the fourth Tuesday`;
116             }
117             if (day === 3) {
118                 return c('Monthly recurring event, repeats on').t`on the fourth Wednesday`;
119             }
120             if (day === 4) {
121                 return c('Monthly recurring event, repeats on').t`on the fourth Thursday`;
122             }
123             if (day === 5) {
124                 return c('Monthly recurring event, repeats on').t`on the fourth Friday`;
125             }
126             if (day === 6) {
127                 return c('Monthly recurring event, repeats on').t`on the fourth Saturday`;
128             }
129         }
130     }
131     if (monthlyType === MONTHLY_TYPE.ON_MINUS_NTH_DAY) {
132         if (day === 0) {
133             return c('Monthly recurring event, repeats on').t`on the last Sunday`;
134         }
135         if (day === 1) {
136             return c('Monthly recurring event, repeats on').t`on the last Monday`;
137         }
138         if (day === 2) {
139             return c('Monthly recurring event, repeats on').t`on the last Tuesday`;
140         }
141         if (day === 3) {
142             return c('Monthly recurring event, repeats on').t`on the last Wednesday`;
143         }
144         if (day === 4) {
145             return c('Monthly recurring event, repeats on').t`on the last Thursday`;
146         }
147         if (day === 5) {
148             return c('Monthly recurring event, repeats on').t`on the last Friday`;
149         }
150         if (day === 6) {
151             return c('Monthly recurring event, repeats on').t`on the last Saturday`;
152         }
153     }
154     return c('Monthly recurring event, repeats on').t`on day ${dayOfMonth}`;
157 const getTimesString = (count: number) => {
158     return c('Daily recurring event, frequency').ngettext(msgid`${count} time`, `${count} times`, count);
161 const getUntilString = (dateString: string) => {
162     return c('Daily recurring event, frequency').t`until ${dateString}`;
165 const getCustomDailyString = (
166     rruleValue: VcalRrulePropertyValue,
167     { type: endType, count = 1, until }: RruleEnd,
168     locale: Locale
169 ) => {
170     const { interval = 1 } = rruleValue;
172     if (endType === END_TYPE.NEVER) {
173         if (interval === 1) {
174             return c('Daily recurring event, frequency').t`Daily`;
175         }
176         // translator: When interval = 1 we do not use this string; we use 'daily' instead. Treat the case of interval = 1 as dummy
177         return c('Daily recurring event, frequency').ngettext(
178             msgid`Every ${interval} day`,
179             `Every ${interval} days`,
180             interval
181         );
182     }
184     if (endType === END_TYPE.AFTER_N_TIMES) {
185         const timesString = getTimesString(count);
186         if (interval === 1) {
187             return c('Daily recurring event, frequency (times)').t`Daily, ${timesString}`;
188         }
189         // translator: When interval = 1 we do not use this string; we use 'daily' instead. Treat the case of interval = 1 as dummy
190         return c('Daily recurring event, frequency (times)').ngettext(
191             msgid`Every ${interval} day, ${timesString}`,
192             `Every ${interval} days, ${timesString}`,
193             interval
194         );
195     }
197     if (endType === END_TYPE.UNTIL && until) {
198         const dateString = format(until, 'PP', { locale });
199         const untilString = getUntilString(dateString);
200         if (interval === 1) {
201             return c('Daily recurring event, frequency (until)').t`Daily, ${untilString}`;
202         }
203         // translator: When interval = 1 we do not use this string; we use 'daily' instead. Treat the case of interval = 1 as dummy
204         return c('Daily recurring event, frequency (until)').ngettext(
205             msgid`Every ${interval} day, ${untilString}`,
206             `Every ${interval} days, ${untilString}`,
207             interval
208         );
209     }
211 const getWeekdayString = (weekday: number) => {
212     if (weekday === 0) {
213         return c('Weekly recurring event, repeats on (multiple days), frequency').t`Sunday`;
214     }
215     if (weekday === 1) {
216         return c('Weekly recurring event, repeats on (multiple days), frequency').t`Monday`;
217     }
218     if (weekday === 2) {
219         return c('Weekly recurring event, repeats on (multiple days), frequency').t`Tuesday`;
220     }
221     if (weekday === 3) {
222         return c('Weekly recurring event, repeats on (multiple days), frequency').t`Wednesday`;
223     }
224     if (weekday === 4) {
225         return c('Weekly recurring event, repeats on (multiple days), frequency').t`Thursday`;
226     }
227     if (weekday === 5) {
228         return c('Weekly recurring event, repeats on (multiple days), frequency').t`Friday`;
229     }
230     if (weekday === 6) {
231         return c('Weekly recurring event, repeats on (multiple days), frequency').t`Saturday`;
232     }
233     throw new Error('Unknown weekday');
236 const getCustomWeeklyString = (
237     { interval = 1, byday }: VcalRrulePropertyValue,
238     { type: endType, count = 1, until }: RruleEnd,
239     weekStartsOn: WeekStartsOn,
240     startDate: Date,
241     locale: Locale
242 ) => {
243     const days = getWeeklyDays(byday);
244     const safeDays = unique([...days, startDate.getUTCDay()]);
245     // sort weekly days depending on the day the week starts
246     const sortedWeekDays = safeDays.slice().sort((a: number, b: number) => {
247         // shift days. Get a positive modulus
248         const A = mod(a - weekStartsOn, +7);
249         const B = mod(b - weekStartsOn, 7);
250         return A - B;
251     });
252     const multipleDaysString = sortedWeekDays.map(getWeekdayString).join(', ');
254     if (endType === END_TYPE.NEVER) {
255         if (days.length === 7) {
256             if (interval === 1) {
257                 return c('Weekly recurring event, frequency').t`Weekly on all days`;
258             }
259             // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
260             return c('Weekly recurring event, frequency').ngettext(
261                 msgid`Every ${interval} week on all days`,
262                 `Every ${interval} weeks on all days`,
263                 interval
264             );
265         }
266         if (days.length === 1) {
267             const startDate = days[0];
268             if (startDate === 0) {
269                 if (interval === 1) {
270                     return c('Weekly recurring event, frequency').t`Weekly on Sunday`;
271                 }
272                 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
273                 return c('Weekly recurring event, frequency').ngettext(
274                     msgid`Every ${interval} week on Sunday`,
275                     `Every ${interval} weeks on Sunday`,
276                     interval
277                 );
278             }
279             if (startDate === 1) {
280                 if (interval === 1) {
281                     return c('Weekly recurring event, frequency').t`Weekly on Monday`;
282                 }
283                 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
284                 return c('Weekly recurring event, frequency').ngettext(
285                     msgid`Every ${interval} week on Monday`,
286                     `Every ${interval} weeks on Monday`,
287                     interval
288                 );
289             }
290             if (startDate === 2) {
291                 if (interval === 1) {
292                     return c('Weekly recurring event, frequency').t`Weekly on Tuesday`;
293                 }
294                 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
295                 return c('Weekly recurring event, frequency').ngettext(
296                     msgid`Every ${interval} week on Tuesday`,
297                     `Every ${interval} weeks on Tuesday`,
298                     interval
299                 );
300             }
301             if (startDate === 3) {
302                 if (interval === 1) {
303                     return c('Weekly recurring event, frequency').t`Weekly on Wednesday`;
304                 }
305                 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
306                 return c('Weekly recurring event, frequency').ngettext(
307                     msgid`Every ${interval} week on Wednesday`,
308                     `Every ${interval} weeks on Wednesday`,
309                     interval
310                 );
311             }
312             if (startDate === 4) {
313                 if (interval === 1) {
314                     return c('Weekly recurring event, frequency').t`Weekly on Thursday`;
315                 }
316                 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
317                 return c('Weekly recurring event, frequency').ngettext(
318                     msgid`Every ${interval} week on Thursday`,
319                     `Every ${interval} weeks on Thursday`,
320                     interval
321                 );
322             }
323             if (startDate === 5) {
324                 if (interval === 1) {
325                     return c('Weekly recurring event, frequency').t`Weekly on Friday`;
326                 }
327                 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
328                 return c('Weekly recurring event, frequency').ngettext(
329                     msgid`Every ${interval} week on Friday`,
330                     `Every ${interval} weeks on Friday`,
331                     interval
332                 );
333             }
334             if (startDate === 6) {
335                 if (interval === 1) {
336                     return c('Weekly recurring event, frequency').t`Weekly on Saturday`;
337                 }
338                 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
339                 return c('Weekly recurring event, frequency').ngettext(
340                     msgid`Every ${interval} week on Saturday`,
341                     `Every ${interval} weeks on Saturday`,
342                     interval
343                 );
344             }
345         }
346         if (interval === 1) {
347             return c('Weekly recurring event, frequency').t`Weekly on ${multipleDaysString}`;
348         }
349         // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
350         return c('Weekly recurring event, frequency').ngettext(
351             msgid`Every ${interval} week on ${multipleDaysString}`,
352             `Every ${interval} weeks on ${multipleDaysString}`,
353             interval
354         );
355     }
356     if (endType === END_TYPE.AFTER_N_TIMES) {
357         const timesString = getTimesString(count);
358         if (days.length === 7) {
359             if (interval === 1) {
360                 return c('Weekly recurring event, frequency (times)').t`Weekly on all days, ${timesString}`;
361             }
362             // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
363             return c('Weekly recurring event, frequency (times)').ngettext(
364                 msgid`Every ${interval} week on all days, ${timesString}`,
365                 `Every ${interval} weeks on all days, ${timesString}`,
366                 interval
367             );
368         }
369         if (days.length === 1) {
370             const startDate = days[0];
371             if (startDate === 0) {
372                 if (interval === 1) {
373                     return c('Weekly recurring event, frequency (times)').t`Weekly on Sunday, ${timesString}`;
374                 }
375                 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
376                 return c('Weekly recurring event, frequency').ngettext(
377                     msgid`Every ${interval} week on Sunday, ${timesString}`,
378                     `Every ${interval} weeks on Sunday, ${timesString}`,
379                     interval
380                 );
381             }
382             if (startDate === 1) {
383                 if (interval === 1) {
384                     return c('Weekly recurring event, frequency (times)').t`Weekly on Monday, ${timesString}`;
385                 }
386                 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
387                 return c('Weekly recurring event, frequency').ngettext(
388                     msgid`Every ${interval} week on Monday, ${timesString}`,
389                     `Every ${interval} weeks on Monday, ${timesString}`,
390                     interval
391                 );
392             }
393             if (startDate === 2) {
394                 if (interval === 1) {
395                     return c('Weekly recurring event, frequency (times)').t`Weekly on Tuesday, ${timesString}`;
396                 }
397                 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
398                 return c('Weekly recurring event, frequency').ngettext(
399                     msgid`Every ${interval} week on Tuesday, ${timesString}`,
400                     `Every ${interval} weeks on Tuesday, ${timesString}`,
401                     interval
402                 );
403             }
404             if (startDate === 3) {
405                 if (interval === 1) {
406                     return c('Weekly recurring event, frequency (times)').t`Weekly on Wednesday, ${timesString}`;
407                 }
408                 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
409                 return c('Weekly recurring event, frequency (times)').ngettext(
410                     msgid`Every ${interval} week on Wednesday, ${timesString}`,
411                     `Every ${interval} weeks on Wednesday, ${timesString}`,
412                     interval
413                 );
414             }
415             if (startDate === 4) {
416                 if (interval === 1) {
417                     return c('Weekly recurring event, frequency (times)').t`Weekly on Thursday, ${timesString}`;
418                 }
419                 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
420                 return c('Weekly recurring event, frequency (times)').ngettext(
421                     msgid`Every ${interval} week on Thursday, ${timesString}`,
422                     `Every ${interval} weeks on Thursday, ${timesString}`,
423                     interval
424                 );
425             }
426             if (startDate === 5) {
427                 if (interval === 1) {
428                     return c('Weekly recurring event, frequency (times)').t`Weekly on Friday, ${timesString}`;
429                 }
430                 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
431                 return c('Weekly recurring event, frequency (times)').ngettext(
432                     msgid`Every ${interval} week on Friday, ${timesString}`,
433                     `Every ${interval} weeks on Friday, ${timesString}`,
434                     interval
435                 );
436             }
437             if (startDate === 6) {
438                 if (interval === 1) {
439                     return c('Weekly recurring event, frequency (times)').t`Weekly on Saturday, ${timesString}`;
440                 }
441                 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
442                 return c('Weekly recurring event, frequency (times)').ngettext(
443                     msgid`Every ${interval} week on Saturday, ${timesString}`,
444                     `Every ${interval} weeks on Saturday, ${timesString}`,
445                     interval
446                 );
447             }
448         }
449         if (interval === 1) {
450             return c('Weekly recurring event, frequency (times)').t`Weekly on ${multipleDaysString}, ${timesString}`;
451         }
452         // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
453         return c('Weekly recurring event, frequency (times)').ngettext(
454             msgid`Every ${interval} week on ${multipleDaysString}, ${timesString}`,
455             `Every ${interval} weeks on ${multipleDaysString}, ${timesString}`,
456             interval
457         );
458     }
459     if (endType === END_TYPE.UNTIL && until) {
460         const dateString = format(until, 'PP', { locale });
461         const untilString = getUntilString(dateString);
462         if (days.length === 7) {
463             if (interval === 1) {
464                 return c('Weekly recurring event, frequency (until)').t`Weekly on all days, ${untilString}`;
465             }
466             // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
467             return c('Weekly recurring event, frequency (until)').ngettext(
468                 msgid`Every ${interval} week on all days, ${untilString}`,
469                 `Every ${interval} weeks on all days, ${untilString}`,
470                 interval
471             );
472         }
473         if (days.length === 1) {
474             const startDate = days[0];
475             if (startDate === 0) {
476                 if (interval === 1) {
477                     return c('Weekly recurring event, frequency (until)').t`Weekly on Sunday, ${untilString}`;
478                 }
479                 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
480                 return c('Weekly recurring event, frequency (until)').ngettext(
481                     msgid`Every ${interval} week on Sunday, ${untilString}`,
482                     `Every ${interval} weeks on Sunday, ${untilString}`,
483                     interval
484                 );
485             }
486             if (startDate === 1) {
487                 if (interval === 1) {
488                     return c('Weekly recurring event, frequency (until)').t`Weekly on Monday, ${untilString}`;
489                 }
490                 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
491                 return c('Weekly recurring event, frequency (until)').ngettext(
492                     msgid`Every ${interval} week on Monday, ${untilString}`,
493                     `Every ${interval} weeks on Monday, ${untilString}`,
494                     interval
495                 );
496             }
497             if (startDate === 2) {
498                 if (interval === 1) {
499                     return c('Weekly recurring event, frequency (until)').t`Weekly on Tuesday, ${untilString}`;
500                 }
501                 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
502                 return c('Weekly recurring event, frequency (until)').ngettext(
503                     msgid`Every ${interval} week on Tuesday, ${untilString}`,
504                     `Every ${interval} weeks on Tuesday, ${untilString}`,
505                     interval
506                 );
507             }
508             if (startDate === 3) {
509                 if (interval === 1) {
510                     return c('Weekly recurring event, frequency (until)').t`Weekly on Wednesday, ${untilString}`;
511                 }
512                 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
513                 return c('Weekly recurring event, frequency (until)').ngettext(
514                     msgid`Every ${interval} week on Wednesday, ${untilString}`,
515                     `Every ${interval} weeks on Wednesday, ${untilString}`,
516                     interval
517                 );
518             }
519             if (startDate === 4) {
520                 if (interval === 1) {
521                     return c('Weekly recurring event, frequency (until)').t`Weekly on Thursday, ${untilString}`;
522                 }
523                 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
524                 return c('Weekly recurring event, frequency (until)').ngettext(
525                     msgid`Every ${interval} week on Thursday, ${untilString}`,
526                     `Every ${interval} weeks on Thursday, ${untilString}`,
527                     interval
528                 );
529             }
530             if (startDate === 5) {
531                 if (interval === 1) {
532                     return c('Weekly recurring event, frequency (until)').t`Weekly on Friday, ${untilString}`;
533                 }
534                 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
535                 return c('Weekly recurring event, frequency (until)').ngettext(
536                     msgid`Every ${interval} week on Friday, ${untilString}`,
537                     `Every ${interval} weeks on Friday, ${untilString}`,
538                     interval
539                 );
540             }
541             if (startDate === 6) {
542                 if (interval === 1) {
543                     return c('Weekly recurring event, frequency (until)').t`Weekly on Saturday, ${untilString}`;
544                 }
545                 // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
546                 return c('Weekly recurring event, frequency (until)').ngettext(
547                     msgid`Every ${interval} week on Saturday, ${untilString}`,
548                     `Every ${interval} weeks on Saturday, ${untilString}`,
549                     interval
550                 );
551             }
552         }
553         if (interval === 1) {
554             return c('Weekly recurring event, frequency (until)').t`Weekly on ${multipleDaysString}, ${untilString}`;
555         }
556         // translator: When interval = 1 we do not use this string; we use 'weekly' instead. Treat the case of interval = 1 as dummy
557         return c('Weekly recurring event, frequency (until)').ngettext(
558             msgid`Every ${interval} week on ${multipleDaysString}, ${untilString}`,
559             `Every ${interval} weeks on ${multipleDaysString}, ${untilString}`,
560             interval
561         );
562     }
565 const getCustomMonthlyString = (
566     rruleValue: VcalRrulePropertyValue,
567     { type: endType, count = 1, until }: RruleEnd,
568     monthlyType: MONTHLY_TYPE,
569     date: Date,
570     locale: Locale
571 ) => {
572     const { interval = 1 } = rruleValue;
573     const onDayString = date ? getOnDayString(date, monthlyType) : '';
574     if (endType === END_TYPE.NEVER) {
575         if (interval === 1) {
576             // translator: example: Monthly <on the last Thursday/on day 8>
577             return c('Monthly recurring event, frequency').t`Monthly ${onDayString}`;
578         }
579         // translator: When interval = 1 we do not use this string; we use 'monthly' instead. Treat the case of interval = 1 as dummy
580         return c('Monthly recurring event, frequency').ngettext(
581             msgid`Every ${interval} month ${onDayString}`,
582             `Every ${interval} months ${onDayString}`,
583             interval
584         );
585     }
586     if (endType === END_TYPE.AFTER_N_TIMES) {
587         const timesString = getTimesString(count);
588         if (interval === 1) {
589             // translator: example: Monthly <on the last Thursday/on day 8>, <3 times>
590             return c('Monthly recurring event, frequency (times)').t`Monthly ${onDayString}, ${timesString}`;
591         }
592         // translator: When interval = 1 we do not use this string; we use 'monthly' instead. Treat the case of interval = 1 as dummy
593         return c('Monthly recurring event, frequency (times)').ngettext(
594             msgid`Every ${interval} month ${onDayString}, ${timesString}`,
595             `Every ${interval} months ${onDayString}, ${timesString}`,
596             interval
597         );
598     }
599     if (endType === END_TYPE.UNTIL && until) {
600         const dateString = format(until, 'PP', { locale });
601         const untilString = getUntilString(dateString);
602         if (interval === 1) {
603             // translator: example: Monthly <on the last Thursday/on day 8>, <until Feb 29, 2024>
604             return c('Monthly recurring event, frequency (until)').t`Monthly ${onDayString}, ${untilString}`;
605         }
606         // translator: When interval = 1 we do not use this string; we use 'monthly' instead. Treat the case of interval = 1 as dummy. Example: Every <2> months <on the last Thursday/on day 8>, <until Feb 29, 2024>
607         return c('Monthly recurring event, frequency (until)').ngettext(
608             msgid`Every ${interval} month ${onDayString}, ${untilString}`,
609             `Every ${interval} months ${onDayString}, ${untilString}`,
610             interval
611         );
612     }
615 const getCustomYearlyString = (
616     { interval = 1 }: VcalRrulePropertyValue,
617     { type: endType, count = 1, until }: RruleEnd,
618     locale: Locale
619 ) => {
620     if (endType === END_TYPE.NEVER) {
621         if (interval === 1) {
622             return c('Yearly recurring event, frequency').t`Yearly`;
623         }
624         // translator: When interval = 1 we do not use this string; we use 'yearly' instead. Treat the case of interval = 1 as dummy
625         return c('Yearly recurring event, frequency').ngettext(
626             msgid`Every ${interval} year`,
627             `Every ${interval} years`,
628             interval
629         );
630     }
632     if (endType === END_TYPE.AFTER_N_TIMES) {
633         const timesString = getTimesString(count);
634         if (interval === 1) {
635             // translator: Example: Yearly, <3 times>
636             return c('Yearly recurring event, frequency (times)').t`Yearly, ${timesString}`;
637         }
638         // translator: When interval = 1 we do not use this string; we use 'yearly' instead. Treat the case of interval = 1 as dummy. Example: Every <2> years, <3 times>
639         return c('Yearly recurring event, frequency (times)').ngettext(
640             msgid`Every ${interval} year, ${timesString}`,
641             `Every ${interval} years, ${timesString}`,
642             interval
643         );
644     }
646     if (endType === END_TYPE.UNTIL && until) {
647         const dateString = format(until, 'PP', { locale });
648         const untilString = getUntilString(dateString);
649         if (interval === 1) {
650             // translator: Example: Yearly, <until Feb 29, 2024>
651             return c('Yearly recurring event, frequency (until)').t`Yearly, ${untilString}`;
652         }
653         // translator: When interval = 1 we do not use this string; we use 'yearly' instead. Treat the case of interval = 1 as dummy. Example: Every <2> years, <until Feb 29, 2024>
654         return c('Yearly recurring event, frequency (until)').ngettext(
655             msgid`Every ${interval} year, ${untilString}`,
656             `Every ${interval} years, ${untilString}`,
657             interval
658         );
659     }
662 export const getFrequencyString = (
663     rruleValue: VcalRrulePropertyValue,
664     dtstart: VcalDateOrDateTimeProperty,
665     { weekStartsOn, locale }: Pick<GetTimezonedFrequencyStringOptions, 'weekStartsOn' | 'locale'>
666 ) => {
667     const { freq, count, until } = rruleValue;
669     const isSimple = getIsRruleSimple(rruleValue);
670     const isCustom = getIsRruleCustom(rruleValue);
671     const startFakeUtcDate = toUTCDate(dtstart.value);
672     const startDay = startFakeUtcDate.getUTCDay();
673     const end = {
674         type: getEndType(count, until),
675         count,
676         until: getUntilDate(until, getPropertyTzid(dtstart)),
677     };
679     if (!isSimple) {
680         if (!isCustom) {
681             if (freq === FREQUENCY.DAILY) {
682                 return c('Info').t`Custom daily`;
683             }
684             if (freq === FREQUENCY.WEEKLY) {
685                 return c('Info').t`Custom weekly`;
686             }
687             if (freq === FREQUENCY.MONTHLY) {
688                 return c('Info').t`Custom monthly`;
689             }
690             if (freq === FREQUENCY.YEARLY) {
691                 return c('Info').t`Custom yearly`;
692             }
693             return c('Info').t`Custom`;
694         }
695         if (freq === FREQUENCY.DAILY) {
696             return getCustomDailyString(rruleValue, end, locale);
697         }
698         if (freq === FREQUENCY.WEEKLY) {
699             return getCustomWeeklyString(rruleValue, end, weekStartsOn, startFakeUtcDate, locale);
700         }
701         if (freq === FREQUENCY.MONTHLY) {
702             const { byday, bysetpos } = rruleValue;
703             const monthType = getMonthType(byday, bysetpos);
704             return getCustomMonthlyString(rruleValue, end, monthType, startFakeUtcDate, locale);
705         }
706         if (freq === FREQUENCY.YEARLY) {
707             return getCustomYearlyString(rruleValue, end, locale);
708         }
709     }
710     if (freq === FREQUENCY.DAILY) {
711         return c('Info').t`Daily`;
712     }
713     if (freq === FREQUENCY.WEEKLY) {
714         if (startDay === 0) {
715             return c('Weekly recurring event, frequency').t`Weekly on Sunday`;
716         }
717         if (startDay === 1) {
718             return c('Weekly recurring event, frequency').t`Weekly on Monday`;
719         }
720         if (startDay === 2) {
721             return c('Weekly recurring event, frequency').t`Weekly on Tuesday`;
722         }
723         if (startDay === 3) {
724             return c('Weekly recurring event, frequency').t`Weekly on Wednesday`;
725         }
726         if (startDay === 4) {
727             return c('Weekly recurring event, frequency').t`Weekly on Thursday`;
728         }
729         if (startDay === 5) {
730             return c('Weekly recurring event, frequency').t`Weekly on Friday`;
731         }
732         if (startDay === 6) {
733             return c('Weekly recurring event, frequency').t`Weekly on Saturday`;
734         }
735     }
736     if (freq === FREQUENCY.MONTHLY) {
737         const { byday, bysetpos } = rruleValue;
738         const monthType = getMonthType(byday, bysetpos);
739         const onDayString = getOnDayString(startFakeUtcDate, monthType);
740         return c('Info').t`Monthly ${onDayString}`;
741     }
742     if (freq === FREQUENCY.YEARLY) {
743         return c('Info').t`Yearly`;
744     }
745     return '';
748 export const getTimezonedFrequencyString = (
749     rrule: VcalRruleProperty | undefined,
750     dtstart: VcalDateOrDateTimeProperty,
751     options: GetTimezonedFrequencyStringOptions
752 ) => {
753     if (!rrule) {
754         return '';
755     }
756     const { value: rruleValue } = rrule;
757     const startTzid = getPropertyTzid(dtstart);
758     const { currentTzid } = options;
760     if (!startTzid || startTzid === currentTzid) {
761         return getFrequencyString(rruleValue, dtstart, options);
762     }
764     const isTimezoneStringNeeded = (() => {
765         const { freq, count, until, byday } = rruleValue;
767         const isCustom = getIsRruleCustom(rruleValue);
768         const endType = getEndType(count, until);
770         if (!freq) {
771             return false;
772         }
773         if ([FREQUENCY.DAILY, FREQUENCY.YEARLY].includes(freq as FREQUENCY)) {
774             return isCustom && endType === END_TYPE.UNTIL;
775         }
776         if (freq === FREQUENCY.WEEKLY) {
777             const days = getWeeklyDays(byday);
778             const hasCustomUntil = isCustom && endType === END_TYPE.UNTIL;
779             const hasDays = days.length !== 7;
780             return hasCustomUntil || hasDays;
781         }
782         if (freq === FREQUENCY.MONTHLY) {
783             return true;
784         }
785         return false;
786     })();
788     const timezoneString = isTimezoneStringNeeded ? ` (${startTzid})` : '';
789     return getFrequencyString(rruleValue, dtstart, options) + timezoneString;