From 5fc4e7a95dd4d0935828fe85a2f72514be8fbb08 Mon Sep 17 00:00:00 2001 From: Simon Lamon <32477463+silamon@users.noreply.github.com> Date: Tue, 13 Jun 2023 15:22:23 +0200 Subject: [PATCH] Introduce Intl.ListFormat (#16857) Co-authored-by: Steve Repsher Co-authored-by: Bram Kragten --- build-scripts/gulp/locale-data.js | 1 + package.json | 1 + src/data/automation_i18n.ts | 113 ++++++++++++-------------- src/resources/intl-polyfill-legacy.ts | 2 + src/resources/intl-polyfill.ts | 4 + src/resources/locale-data-polyfill.ts | 11 +++ tsconfig.json | 11 +-- yarn.lock | 12 +++ 8 files changed, 86 insertions(+), 69 deletions(-) diff --git a/build-scripts/gulp/locale-data.js b/build-scripts/gulp/locale-data.js index 5def7818fe..f902256877 100755 --- a/build-scripts/gulp/locale-data.js +++ b/build-scripts/gulp/locale-data.js @@ -17,6 +17,7 @@ const modules = { "intl-datetimeformat": "DateTimeFormat", "intl-numberformat": "NumberFormat", "intl-displaynames": "DisplayNames", + "intl-listformat": "ListFormat", }; gulp.task("create-locale-data", (done) => { diff --git a/package.json b/package.json index 2bad0faf13..d00dbbc956 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "@formatjs/intl-datetimeformat": "6.9.0", "@formatjs/intl-displaynames": "6.4.0", "@formatjs/intl-getcanonicallocales": "2.2.1", + "@formatjs/intl-listformat": "7.3.0", "@formatjs/intl-locale": "3.3.1", "@formatjs/intl-numberformat": "8.6.0", "@formatjs/intl-pluralrules": "5.2.3", diff --git a/src/data/automation_i18n.ts b/src/data/automation_i18n.ts index ab4c0056ea..89ea2257c4 100644 --- a/src/data/automation_i18n.ts +++ b/src/data/automation_i18n.ts @@ -21,6 +21,7 @@ import { localizeDeviceAutomationTrigger, } from "./device_automation"; import { EntityRegistryEntry } from "./entity_registry"; +import "../resources/intl-polyfill"; import { FrontendLocaleData } from "./translation"; const describeDuration = (forTime: number | string | ForDict) => { @@ -82,6 +83,11 @@ export const describeTrigger = ( return trigger.alias; } + const disjunctionFormatter = new Intl.ListFormat("en", { + style: "long", + type: "disjunction", + }); + // Event Trigger if (trigger.platform === "event" && trigger.event_type) { let eventTypes = ""; @@ -489,51 +495,43 @@ export const describeTrigger = ( // Zone Trigger if (trigger.platform === "zone" && trigger.entity_id && trigger.zone) { - let entities = ""; - let zones = ""; - let zonesPlural = false; + const entities: string[] = []; + const zones: string[] = []; const states = hass.states; if (Array.isArray(trigger.entity_id)) { - for (const [index, entity] of trigger.entity_id.entries()) { + for (const [entity] of trigger.entity_id.entries()) { if (states[entity]) { - entities += `${index > 0 ? "," : ""} ${ - trigger.entity_id.length > 1 && - index === trigger.entity_id.length - 1 - ? "or" - : "" - } ${computeStateName(states[entity]) || entity}`; + entities.push(`${computeStateName(states[entity]) || entity}`); } } } else { - entities = states[trigger.entity_id] - ? computeStateName(states[trigger.entity_id]) - : trigger.entity_id; + entities.push( + states[trigger.entity_id] + ? computeStateName(states[trigger.entity_id]) + : trigger.entity_id + ); } if (Array.isArray(trigger.zone)) { - if (trigger.zone.length > 1) { - zonesPlural = true; - } - - for (const [index, zone] of trigger.zone.entries()) { + for (const [zone] of trigger.zone.entries()) { if (states[zone]) { - zones += `${index > 0 ? "," : ""} ${ - trigger.zone.length > 1 && index === trigger.zone.length - 1 - ? "or" - : "" - } ${computeStateName(states[zone]) || zone}`; + zones.push(`${computeStateName(states[zone]) || zone}`); } } } else { - zones = states[trigger.zone] - ? computeStateName(states[trigger.zone]) - : trigger.zone; + zones.push( + states[trigger.zone] + ? computeStateName(states[trigger.zone]) + : trigger.zone + ); } - return `When ${entities} ${trigger.event}s ${zones} ${ - zonesPlural ? "zones" : "zone" + const entitiesString = disjunctionFormatter.format(entities); + const zonesString = disjunctionFormatter.format(zones); + return `When ${entitiesString} ${trigger.event}s ${zonesString} ${ + zones.length > 1 ? "zones" : "zone" }`; } @@ -636,6 +634,11 @@ export const describeCondition = ( return condition.alias; } + const disjunctionFormatter = new Intl.ListFormat("en", { + style: "long", + type: "disjunction", + }); + if (!condition.condition) { const shorthands: Array<"and" | "or" | "not"> = ["and", "or", "not"]; for (const key of shorthands) { @@ -938,56 +941,44 @@ export const describeCondition = ( // Zone condition if (condition.condition === "zone" && condition.entity_id && condition.zone) { - let entities = ""; - let entitiesPlural = false; - let zones = ""; - let zonesPlural = false; + const entities: string[] = []; + const zones: string[] = []; const states = hass.states; if (Array.isArray(condition.entity_id)) { - if (condition.entity_id.length > 1) { - entitiesPlural = true; - } - for (const [index, entity] of condition.entity_id.entries()) { + for (const [entity] of condition.entity_id.entries()) { if (states[entity]) { - entities += `${index > 0 ? "," : ""} ${ - condition.entity_id.length > 1 && - index === condition.entity_id.length - 1 - ? "or" - : "" - } ${computeStateName(states[entity]) || entity}`; + entities.push(`${computeStateName(states[entity]) || entity}`); } } } else { - entities = states[condition.entity_id] - ? computeStateName(states[condition.entity_id]) - : condition.entity_id; + entities.push( + states[condition.entity_id] + ? computeStateName(states[condition.entity_id]) + : condition.entity_id + ); } if (Array.isArray(condition.zone)) { - if (condition.zone.length > 1) { - zonesPlural = true; - } - - for (const [index, zone] of condition.zone.entries()) { + for (const [zone] of condition.zone.entries()) { if (states[zone]) { - zones += `${index > 0 ? "," : ""} ${ - condition.zone.length > 1 && index === condition.zone.length - 1 - ? "or" - : "" - } ${computeStateName(states[zone]) || zone}`; + zones.push(`${computeStateName(states[zone]) || zone}`); } } } else { - zones = states[condition.zone] - ? computeStateName(states[condition.zone]) - : condition.zone; + zones.push( + states[condition.zone] + ? computeStateName(states[condition.zone]) + : condition.zone + ); } - return `Confirm ${entities} ${entitiesPlural ? "are" : "is"} in ${zones} ${ - zonesPlural ? "zones" : "zone" - }`; + const entitiesString = disjunctionFormatter.format(entities); + const zonesString = disjunctionFormatter.format(zones); + return `Confirm ${entitiesString} ${ + entities.length > 1 ? "are" : "is" + } in ${zonesString} ${zones.length > 1 ? "zones" : "zone"}`; } if (condition.condition === "device") { diff --git a/src/resources/intl-polyfill-legacy.ts b/src/resources/intl-polyfill-legacy.ts index c3072b0e90..01a057d75a 100644 --- a/src/resources/intl-polyfill-legacy.ts +++ b/src/resources/intl-polyfill-legacy.ts @@ -15,3 +15,5 @@ import "@formatjs/intl-datetimeformat/locale-data/en"; import "@formatjs/intl-datetimeformat/add-all-tz"; import "@formatjs/intl-displaynames/polyfill"; import "@formatjs/intl-displaynames/locale-data/en"; +import "@formatjs/intl-listformat/polyfill"; +import "@formatjs/intl-listformat/locale-data/en"; diff --git a/src/resources/intl-polyfill.ts b/src/resources/intl-polyfill.ts index 8b09aea77a..b98fc722b8 100644 --- a/src/resources/intl-polyfill.ts +++ b/src/resources/intl-polyfill.ts @@ -3,6 +3,7 @@ import { shouldPolyfill as shouldPolyfillDisplayName } from "@formatjs/intl-disp import { shouldPolyfill as shouldPolyfillLocale } from "@formatjs/intl-locale/should-polyfill"; import { shouldPolyfill as shouldPolyfillPluralRules } from "@formatjs/intl-pluralrules/should-polyfill"; import { shouldPolyfill as shouldPolyfillRelativeTime } from "@formatjs/intl-relativetimeformat/should-polyfill"; +import { shouldPolyfill as shouldPolyfillListFormat } from "@formatjs/intl-listformat/should-polyfill"; import { getLocalLanguage } from "../util/common-translation"; import { polyfillLocaleData } from "./locale-data-polyfill"; @@ -29,6 +30,9 @@ const polyfillIntl = async () => { if (shouldPolyfillDisplayName(locale)) { polyfills.push(import("@formatjs/intl-displaynames/polyfill-force")); } + if (shouldPolyfillListFormat(locale)) { + polyfills.push(import("@formatjs/intl-listformat/polyfill-force")); + } if (polyfills.length === 0) { return; } diff --git a/src/resources/locale-data-polyfill.ts b/src/resources/locale-data-polyfill.ts index 2b8b424af0..e408947a65 100644 --- a/src/resources/locale-data-polyfill.ts +++ b/src/resources/locale-data-polyfill.ts @@ -53,6 +53,17 @@ export const polyfillLocaleData = async (language: string) => { // @ts-ignore Intl.DisplayNames.__addLocaleData(await result.json()); } + if ( + Intl.ListFormat && + // @ts-ignore + typeof Intl.ListFormat.__addLocaleData === "function" + ) { + const result = await fetch( + `${__STATIC_PATH__}locale-data/intl-listformat/${language}.json` + ); + // @ts-ignore + Intl.ListFormat.__addLocaleData(await result.json()); + } } catch (e) { // Ignore } diff --git a/tsconfig.json b/tsconfig.json index 13af372a4c..a5bac525d4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,13 +1,8 @@ { "compilerOptions": { // Language Options - "target": "ES2017", - "lib": [ - "ES2017", - "DOM", - "DOM.Iterable", - "WebWorker" - ], + "target": "ES2021", + "lib": ["ES2021", "DOM", "DOM.Iterable", "WebWorker"], "experimentalDecorators": true, // Modules "module": "ESNext", @@ -43,4 +38,4 @@ } ] } -} \ No newline at end of file +} diff --git a/yarn.lock b/yarn.lock index 64b7250dcc..6520047cb8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1669,6 +1669,17 @@ __metadata: languageName: node linkType: hard +"@formatjs/intl-listformat@npm:7.3.0": + version: 7.3.0 + resolution: "@formatjs/intl-listformat@npm:7.3.0" + dependencies: + "@formatjs/ecma402-abstract": 1.16.0 + "@formatjs/intl-localematcher": 0.3.0 + tslib: ^2.4.0 + checksum: 6638e6a3cad750ac5199cf293c549c4048f0de240762dd98c61cece645091f19262ef64c1fb2f7a44b5bb3e5c168a8f53e9aa604b4867290981c30baf8635c0d + languageName: node + linkType: hard + "@formatjs/intl-locale@npm:3.3.1": version: 3.3.1 resolution: "@formatjs/intl-locale@npm:3.3.1" @@ -9620,6 +9631,7 @@ __metadata: "@formatjs/intl-datetimeformat": 6.9.0 "@formatjs/intl-displaynames": 6.4.0 "@formatjs/intl-getcanonicallocales": 2.2.1 + "@formatjs/intl-listformat": 7.3.0 "@formatjs/intl-locale": 3.3.1 "@formatjs/intl-numberformat": 8.6.0 "@formatjs/intl-pluralrules": 5.2.3