mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-24 09:46:36 +00:00
Add calendar event recurrence rule translations (#14544)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
parent
f0f699a37e
commit
ecdd07ff4d
@ -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",
|
||||
})
|
||||
);
|
||||
|
10
src/common/translations/day_names.ts
Normal file
10
src/common/translations/day_names.ts
Normal 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)
|
||||
)
|
||||
);
|
@ -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}`
|
||||
|
10
src/common/translations/month_names.ts
Normal file
10
src/common/translations/month_names.ts
Normal 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)
|
||||
)
|
||||
);
|
@ -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":
|
||||
|
@ -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
|
||||
)}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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"
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user