Add calendar event recurrence rule translations (#14544)

Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
Philip Allgaier 2022-12-05 18:28:48 +01:00 committed by GitHub
parent f0f699a37e
commit ecdd07ff4d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 126 additions and 15 deletions

View File

@ -7,10 +7,12 @@ if (__BUILD__ === "latest" && polyfillsLoaded) {
}
// Tuesday, August 10
export const formatDateWeekday = (dateObj: Date, locale: FrontendLocaleData) =>
formatDateWeekdayMem(locale).format(dateObj);
export const formatDateWeekdayDay = (
dateObj: Date,
locale: FrontendLocaleData
) => formatDateWeekdayDayMem(locale).format(dateObj);
const formatDateWeekdayMem = memoizeOne(
const formatDateWeekdayDayMem = memoizeOne(
(locale: FrontendLocaleData) =>
new Intl.DateTimeFormat(locale.language, {
weekday: "long",
@ -92,3 +94,14 @@ const formatDateYearMem = memoizeOne(
year: "numeric",
})
);
// Monday
export const formatDateWeekday = (dateObj: Date, locale: FrontendLocaleData) =>
formatDateWeekdayMem(locale).format(dateObj);
const formatDateWeekdayMem = memoizeOne(
(locale: FrontendLocaleData) =>
new Intl.DateTimeFormat(locale.language, {
weekday: "long",
})
);

View File

@ -0,0 +1,10 @@
import { addDays, startOfWeek } from "date-fns";
import memoizeOne from "memoize-one";
import { FrontendLocaleData } from "../../data/translation";
import { formatDateWeekday } from "../datetime/format_date";
export const dayNames = memoizeOne((locale: FrontendLocaleData): string[] =>
Array.from({ length: 7 }, (_, d) =>
formatDateWeekday(addDays(startOfWeek(new Date()), d), locale)
)
);

View File

@ -18,6 +18,7 @@ export type LocalizeKeys =
| `ui.card.alarm_control_panel.${string}`
| `ui.card.weather.attributes.${string}`
| `ui.card.weather.cardinal_direction.${string}`
| `ui.components.calendar.event.rrule.${string}`
| `ui.components.logbook.${string}`
| `ui.components.selectors.file.${string}`
| `ui.dialogs.entity_registry.editor.${string}`

View File

@ -0,0 +1,10 @@
import { addMonths, startOfYear } from "date-fns";
import memoizeOne from "memoize-one";
import { FrontendLocaleData } from "../../data/translation";
import { formatDateMonth } from "../datetime/format_date";
export const monthNames = memoizeOne((locale: FrontendLocaleData): string[] =>
Array.from({ length: 12 }, (_, m) =>
formatDateMonth(addMonths(startOfYear(new Date()), m), locale)
)
);

View File

@ -40,7 +40,7 @@ import {
formatDateMonth,
formatDateMonthYear,
formatDateShort,
formatDateWeekday,
formatDateWeekdayDay,
formatDateYear,
} from "../../common/datetime/format_date";
import {
@ -92,7 +92,7 @@ _adapters._date.override({
case "hour":
return formatTime(new Date(time), this.options.locale);
case "weekday":
return formatDateWeekday(new Date(time), this.options.locale);
return formatDateWeekdayDay(new Date(time), this.options.locale);
case "date":
return formatDate(new Date(time), this.options.locale);
case "day":

View File

@ -14,7 +14,7 @@ import {
TemplateResult,
} from "lit";
import { customElement, property } from "lit/decorators";
import { formatDateWeekday } from "../../../common/datetime/format_date";
import { formatDateWeekdayDay } from "../../../common/datetime/format_date";
import { formatTimeWeekday } from "../../../common/datetime/format_time";
import { formatNumber } from "../../../common/number/format_number";
import "../../../components/ha-svg-icon";
@ -170,7 +170,7 @@ class MoreInfoWeather extends LitElement {
`
: html`
<div class="main">
${formatDateWeekday(
${formatDateWeekdayDay(
new Date(item.datetime),
this.hass.locale
)}

View File

@ -3,13 +3,15 @@ import { mdiCalendarClock, mdiClose } from "@mdi/js";
import { addDays, isSameDay } from "date-fns/esm";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { property, state } from "lit/decorators";
import { RRule } from "rrule";
import { RRule, Weekday } from "rrule";
import { formatDate } from "../../common/datetime/format_date";
import { formatDateTime } from "../../common/datetime/format_date_time";
import { formatTime } from "../../common/datetime/format_time";
import { fireEvent } from "../../common/dom/fire_event";
import { capitalizeFirstLetter } from "../../common/string/capitalize-first-letter";
import { isDate } from "../../common/string/is_date";
import { dayNames } from "../../common/translations/day_names";
import { monthNames } from "../../common/translations/month_names";
import "../../components/entity/state-info";
import "../../components/ha-date-input";
import "../../components/ha-time-input";
@ -85,7 +87,7 @@ class DialogCalendarEventDetail extends LitElement {
<div class="value">
${this._formatDateRange()}<br />
${this._data!.rrule
? this._renderRruleAsText(this._data.rrule)
? this._renderRRuleAsText(this._data.rrule)
: ""}
${this._data.description
? html`<br />
@ -128,20 +130,59 @@ class DialogCalendarEventDetail extends LitElement {
`;
}
private _renderRruleAsText(value: string) {
// TODO: Make sure this handles translations
private _renderRRuleAsText(value: string) {
if (!value) {
return "";
}
try {
return html`<div id="text">
${capitalizeFirstLetter(RRule.fromString(`RRULE:${value}`).toText())}
</div>`;
const rule = RRule.fromString(`RRULE:${value}`);
if (rule.isFullyConvertibleToText()) {
return html`<div id="text">
${capitalizeFirstLetter(
rule.toText(
this._translateRRuleElement,
{
dayNames: dayNames(this.hass.locale),
monthNames: monthNames(this.hass.locale),
tokens: {},
},
this._formatDate
)
)}
</div>`;
}
return html`<div id="text">Cannot convert recurrence rule</div>`;
} catch (e) {
return "";
return "Error while processing the rule";
}
}
private _translateRRuleElement = (id: string | number | Weekday): string => {
if (typeof id === "string") {
return this.hass.localize(`ui.components.calendar.event.rrule.${id}`);
}
return "";
};
private _formatDate = (year: number, month: string, day: number): string => {
if (!year || !month || !day) {
return "";
}
// Build date so we can then format it
const date = new Date();
date.setFullYear(year);
// As input we already get the localized month name, so we now unfortunately
// need to convert it back to something Date can work with. The already localized
// months names are a must in the RRule.Language structure (an empty string[] would
// mean we get undefined months input in this method here).
date.setMonth(monthNames(this.hass.locale).indexOf(month));
date.setDate(day);
return formatDate(date, this.hass.locale);
};
private _formatDateRange() {
const start = new Date(this._data!.dtstart);
// All day events should be displayed as a day earlier

View File

@ -330,6 +330,13 @@ class DialogCalendarEventEditor extends LitElement {
}
private async _createEvent() {
if (!this._summary || !this._calendarId) {
this._error = this.hass.localize(
"ui.components.calendar.event.not_all_required_fields"
);
return;
}
this._submitting = true;
try {
await createCalendarEvent(
@ -418,6 +425,10 @@ class DialogCalendarEventEditor extends LitElement {
state-info {
line-height: 40px;
}
ha-alert {
display: block;
margin-bottom: 16px;
}
ha-textfield,
ha-textarea {
display: block;

View File

@ -642,6 +642,7 @@
"all_day": "All Day",
"start": "Start",
"end": "End",
"not_all_required_fields": "Not all required fields are filled in.",
"confirm_delete": {
"delete": "Delete Event",
"delete_this": "Delete Only This Event",
@ -666,6 +667,30 @@
"daily": "days"
}
},
"rrule": {
"every": "every",
"years": "years",
"year": "year",
"months": "months",
"month": "month",
"weeks": "weeks",
"week": "week",
"weekdays": "weekdays",
"weekday": "weekday",
"days": "days",
"day": "day",
"until": "until",
"for": "for",
"in": "in",
"on": "on",
"on the": "on the",
"and": "and",
"or": "or",
"at": "at",
"last": "last",
"time": "time",
"times": "times"
},
"summary": "Summary",
"description": "Description"
}