|
| 1 | +/** |
| 2 | + * @copyright Copyright (c) 2019 Georg Ehrke |
| 3 | + * |
| 4 | + * @author Georg Ehrke <[email protected]> |
| 5 | + * |
| 6 | + * @license AGPL-3.0-or-later |
| 7 | + * |
| 8 | + * This program is free software: you can redistribute it and/or modify |
| 9 | + * it under the terms of the GNU Affero General Public License as |
| 10 | + * published by the Free Software Foundation, either version 3 of the |
| 11 | + * License, or (at your option) any later version. |
| 12 | + * |
| 13 | + * This program is distributed in the hope that it will be useful, |
| 14 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 16 | + * GNU Affero General Public License for more details. |
| 17 | + * |
| 18 | + * You should have received a copy of the GNU Affero General Public License |
| 19 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 20 | + * |
| 21 | + */ |
| 22 | +import { translate as t, translatePlural as n, getDayNames, getMonthNames } from '@nextcloud/l10n' |
| 23 | +import moment from '@nextcloud/moment' |
| 24 | + |
| 25 | +/** |
| 26 | + * Formats a recurrence-rule |
| 27 | + * |
| 28 | + * @param {object} recurrenceRule The recurrence-rule to format |
| 29 | + * @return {string} |
| 30 | + */ |
| 31 | +export default (recurrenceRule) => { |
| 32 | + if (recurrenceRule.frequency === 'NONE') { |
| 33 | + return t('calendar', 'Does not repeat') |
| 34 | + } |
| 35 | + |
| 36 | + let freqPart = '' |
| 37 | + if (recurrenceRule.interval === 1) { |
| 38 | + switch (recurrenceRule.frequency) { |
| 39 | + case 'DAILY': |
| 40 | + freqPart = t('calendar', 'Daily') |
| 41 | + break |
| 42 | + |
| 43 | + case 'WEEKLY': |
| 44 | + freqPart = t('calendar', 'Weekly') |
| 45 | + break |
| 46 | + |
| 47 | + case 'MONTHLY': |
| 48 | + freqPart = t('calendar', 'Monthly') |
| 49 | + break |
| 50 | + |
| 51 | + case 'YEARLY': |
| 52 | + freqPart = t('calendar', 'Yearly') |
| 53 | + break |
| 54 | + } |
| 55 | + } else { |
| 56 | + switch (recurrenceRule.frequency) { |
| 57 | + case 'DAILY': |
| 58 | + freqPart = n('calendar', 'Every %n day', 'Every %n days', recurrenceRule.interval) |
| 59 | + break |
| 60 | + |
| 61 | + case 'WEEKLY': |
| 62 | + freqPart = n('calendar', 'Every %n week', 'Every %n weeks', recurrenceRule.interval) |
| 63 | + break |
| 64 | + |
| 65 | + case 'MONTHLY': |
| 66 | + freqPart = n('calendar', 'Every %n month', 'Every %n months', recurrenceRule.interval) |
| 67 | + break |
| 68 | + |
| 69 | + case 'YEARLY': |
| 70 | + freqPart = n('calendar', 'Every %n year', 'Every %n years', recurrenceRule.interval) |
| 71 | + break |
| 72 | + } |
| 73 | + } |
| 74 | + |
| 75 | + let limitPart = '' |
| 76 | + if (recurrenceRule.frequency === 'WEEKLY' && recurrenceRule.byDay.length !== 0) { |
| 77 | + const formattedDays = getTranslatedByDaySet(recurrenceRule.byDay) |
| 78 | + |
| 79 | + limitPart = n('calendar', 'on {weekday}', 'on {weekdays}', recurrenceRule.byDay.length, { |
| 80 | + weekday: formattedDays, |
| 81 | + weekdays: formattedDays, |
| 82 | + }) |
| 83 | + } else if (recurrenceRule.frequency === 'MONTHLY') { |
| 84 | + if (recurrenceRule.byMonthDay.length !== 0) { |
| 85 | + const dayOfMonthList = recurrenceRule.byMonthDay.join(', ') |
| 86 | + |
| 87 | + limitPart = n('calendar', 'on day {dayOfMonthList}', 'on days {dayOfMonthList}', recurrenceRule.byMonthDay.length, { |
| 88 | + dayOfMonthList, |
| 89 | + }) |
| 90 | + } else { |
| 91 | + const ordinalNumber = getTranslatedOrdinalNumber(recurrenceRule.bySetPosition) |
| 92 | + const byDaySet = getTranslatedByDaySet(recurrenceRule.byDay) |
| 93 | + |
| 94 | + limitPart = t('calendar', 'on the {ordinalNumber} {byDaySet}', { |
| 95 | + ordinalNumber, |
| 96 | + byDaySet, |
| 97 | + }) |
| 98 | + } |
| 99 | + } else if (recurrenceRule.frequency === 'YEARLY') { |
| 100 | + const monthNames = getTranslatedMonths(recurrenceRule.byMonth) |
| 101 | + |
| 102 | + if (recurrenceRule.byDay.length === 0) { |
| 103 | + limitPart = t('calendar', 'in {monthNames}', { |
| 104 | + monthNames, |
| 105 | + }) |
| 106 | + } else { |
| 107 | + const ordinalNumber = getTranslatedOrdinalNumber(recurrenceRule.bySetPosition) |
| 108 | + const byDaySet = getTranslatedByDaySet(recurrenceRule.byDay) |
| 109 | + |
| 110 | + limitPart = t('calendar', 'in {monthNames} on the {ordinalNumber} {byDaySet}', { |
| 111 | + monthNames, |
| 112 | + ordinalNumber, |
| 113 | + byDaySet, |
| 114 | + }) |
| 115 | + } |
| 116 | + } |
| 117 | + |
| 118 | + let endPart = '' |
| 119 | + if (recurrenceRule.until !== null) { |
| 120 | + const untilDate = moment(recurrenceRule.until).format('L') |
| 121 | + |
| 122 | + endPart = t('calendar', 'until {untilDate}', { |
| 123 | + untilDate, |
| 124 | + }) |
| 125 | + } else if (recurrenceRule.count !== null) { |
| 126 | + endPart = n('calendar', '%n time', '%n times', recurrenceRule.count) |
| 127 | + } |
| 128 | + |
| 129 | + return [ |
| 130 | + freqPart, |
| 131 | + limitPart, |
| 132 | + endPart, |
| 133 | + ].join(' ').replace(/\s{2,}/g, ' ').trim() |
| 134 | +} |
| 135 | + |
| 136 | +/** |
| 137 | + * Gets the byDay list as formatted list of translated weekdays |
| 138 | + * |
| 139 | + * @param {string[]} byDayList The by-day-list to get formatted |
| 140 | + * @return {string} |
| 141 | + */ |
| 142 | +function getTranslatedByDaySet(byDayList) { |
| 143 | + const byDayNames = [] |
| 144 | + const allByDayNames = getDayNames() |
| 145 | + |
| 146 | + // TODO: This should be sorted by first day of week |
| 147 | + // TODO: This should summarise: |
| 148 | + // - SA, SU to weekend |
| 149 | + // - MO, TU, WE, TH, FR to weekday |
| 150 | + // - MO, TU, WE, TH, FR, SA, SU to day |
| 151 | + |
| 152 | + if (byDayList.includes('MO')) { |
| 153 | + byDayNames.push(allByDayNames[1]) |
| 154 | + } |
| 155 | + if (byDayList.includes('TU')) { |
| 156 | + byDayNames.push(allByDayNames[2]) |
| 157 | + } |
| 158 | + if (byDayList.includes('WE')) { |
| 159 | + byDayNames.push(allByDayNames[3]) |
| 160 | + } |
| 161 | + if (byDayList.includes('TH')) { |
| 162 | + byDayNames.push(allByDayNames[4]) |
| 163 | + } |
| 164 | + if (byDayList.includes('FR')) { |
| 165 | + byDayNames.push(allByDayNames[5]) |
| 166 | + } |
| 167 | + if (byDayList.includes('SA')) { |
| 168 | + byDayNames.push(allByDayNames[6]) |
| 169 | + } |
| 170 | + if (byDayList.includes('SU')) { |
| 171 | + byDayNames.push(allByDayNames[0]) |
| 172 | + } |
| 173 | + |
| 174 | + return byDayNames.join(', ') |
| 175 | +} |
| 176 | + |
| 177 | +/** |
| 178 | + * Gets the byMonth list as formatted list of translated month-names |
| 179 | + * |
| 180 | + * |
| 181 | + * @param {string[]} byMonthList The by-month list to get formatted |
| 182 | + * @return {string} |
| 183 | + */ |
| 184 | +function getTranslatedMonths(byMonthList) { |
| 185 | + const sortedByMonth = byMonthList.slice().map((n) => parseInt(n, 10)) |
| 186 | + sortedByMonth.sort((a, b) => a - b) |
| 187 | + |
| 188 | + const monthNames = [] |
| 189 | + const allMonthNames = getMonthNames() |
| 190 | + |
| 191 | + for (const month of sortedByMonth) { |
| 192 | + monthNames.push(allMonthNames[month - 1]) |
| 193 | + } |
| 194 | + |
| 195 | + return monthNames.join(', ') |
| 196 | +} |
| 197 | + |
| 198 | +/** |
| 199 | + * Gets the translated ordinal number for by-set-position |
| 200 | + * |
| 201 | + * @param {number} bySetPositionNum The by-set-position number to get the translation of |
| 202 | + * @return {string} |
| 203 | + */ |
| 204 | +function getTranslatedOrdinalNumber(bySetPositionNum) { |
| 205 | + switch (bySetPositionNum) { |
| 206 | + case 1: |
| 207 | + return t('calendar', 'first') |
| 208 | + |
| 209 | + case 2: |
| 210 | + return t('calendar', 'second') |
| 211 | + |
| 212 | + case 3: |
| 213 | + return t('calendar', 'third') |
| 214 | + |
| 215 | + case 4: |
| 216 | + return t('calendar', 'fourth') |
| 217 | + |
| 218 | + case 5: |
| 219 | + return t('calendar', 'fifth') |
| 220 | + |
| 221 | + case -2: |
| 222 | + return t('calendar', 'second to last') |
| 223 | + |
| 224 | + case -1: |
| 225 | + return t('calendar', 'last') |
| 226 | + |
| 227 | + default: |
| 228 | + return '' |
| 229 | + } |
| 230 | +} |
0 commit comments