diff --git a/cast/src/receiver/layout/hc-lovelace.ts b/cast/src/receiver/layout/hc-lovelace.ts
index b48609894b..be23073207 100644
--- a/cast/src/receiver/layout/hc-lovelace.ts
+++ b/cast/src/receiver/layout/hc-lovelace.ts
@@ -39,7 +39,7 @@ class HcLovelace extends LitElement {
urlPath: this.urlPath!,
enableFullEditMode: () => undefined,
mode: "storage",
- language: "en",
+ locale: this.hass.locale,
saveConfig: async () => undefined,
deleteConfig: async () => undefined,
setEditMode: () => undefined,
diff --git a/src/common/datetime/format_date.ts b/src/common/datetime/format_date.ts
index 050006fe7d..c7c02d38fe 100644
--- a/src/common/datetime/format_date.ts
+++ b/src/common/datetime/format_date.ts
@@ -1,9 +1,10 @@
import { format } from "fecha";
+import { FrontendTranslationData } from "../../data/translation";
import { toLocaleDateStringSupportsOptions } from "./check_options_support";
export const formatDate = toLocaleDateStringSupportsOptions
- ? (dateObj: Date, locales: string) =>
- dateObj.toLocaleDateString(locales, {
+ ? (dateObj: Date, locales: FrontendTranslationData) =>
+ dateObj.toLocaleDateString(locales.language, {
year: "numeric",
month: "long",
day: "numeric",
@@ -11,8 +12,8 @@ export const formatDate = toLocaleDateStringSupportsOptions
: (dateObj: Date) => format(dateObj, "longDate");
export const formatDateWeekday = toLocaleDateStringSupportsOptions
- ? (dateObj: Date, locales: string) =>
- dateObj.toLocaleDateString(locales, {
+ ? (dateObj: Date, locales: FrontendTranslationData) =>
+ dateObj.toLocaleDateString(locales.language, {
weekday: "long",
month: "short",
day: "numeric",
diff --git a/src/common/datetime/format_date_time.ts b/src/common/datetime/format_date_time.ts
index 3a20491ed5..d4b76a1da6 100644
--- a/src/common/datetime/format_date_time.ts
+++ b/src/common/datetime/format_date_time.ts
@@ -1,9 +1,10 @@
import { format } from "fecha";
+import { FrontendTranslationData } from "../../data/translation";
import { toLocaleStringSupportsOptions } from "./check_options_support";
export const formatDateTime = toLocaleStringSupportsOptions
- ? (dateObj: Date, locales: string) =>
- dateObj.toLocaleString(locales, {
+ ? (dateObj: Date, locales: FrontendTranslationData) =>
+ dateObj.toLocaleString(locales.language, {
year: "numeric",
month: "long",
day: "numeric",
@@ -13,8 +14,8 @@ export const formatDateTime = toLocaleStringSupportsOptions
: (dateObj: Date) => format(dateObj, "MMMM D, YYYY, HH:mm");
export const formatDateTimeWithSeconds = toLocaleStringSupportsOptions
- ? (dateObj: Date, locales: string) =>
- dateObj.toLocaleString(locales, {
+ ? (dateObj: Date, locales: FrontendTranslationData) =>
+ dateObj.toLocaleString(locales.language, {
year: "numeric",
month: "long",
day: "numeric",
diff --git a/src/common/datetime/format_time.ts b/src/common/datetime/format_time.ts
index 6b79f0c173..d2068f7638 100644
--- a/src/common/datetime/format_time.ts
+++ b/src/common/datetime/format_time.ts
@@ -1,17 +1,18 @@
import { format } from "fecha";
+import { FrontendTranslationData } from "../../data/translation";
import { toLocaleTimeStringSupportsOptions } from "./check_options_support";
export const formatTime = toLocaleTimeStringSupportsOptions
- ? (dateObj: Date, locales: string) =>
- dateObj.toLocaleTimeString(locales, {
+ ? (dateObj: Date, locales: FrontendTranslationData) =>
+ dateObj.toLocaleTimeString(locales.language, {
hour: "numeric",
minute: "2-digit",
})
: (dateObj: Date) => format(dateObj, "shortTime");
export const formatTimeWithSeconds = toLocaleTimeStringSupportsOptions
- ? (dateObj: Date, locales: string) =>
- dateObj.toLocaleTimeString(locales, {
+ ? (dateObj: Date, locales: FrontendTranslationData) =>
+ dateObj.toLocaleTimeString(locales.language, {
hour: "numeric",
minute: "2-digit",
second: "2-digit",
@@ -19,8 +20,8 @@ export const formatTimeWithSeconds = toLocaleTimeStringSupportsOptions
: (dateObj: Date) => format(dateObj, "mediumTime");
export const formatTimeWeekday = toLocaleTimeStringSupportsOptions
- ? (dateObj: Date, locales: string) =>
- dateObj.toLocaleTimeString(locales, {
+ ? (dateObj: Date, locales: FrontendTranslationData) =>
+ dateObj.toLocaleTimeString(locales.language, {
weekday: "long",
hour: "numeric",
minute: "2-digit",
diff --git a/src/common/entity/compute_state_display.ts b/src/common/entity/compute_state_display.ts
index 91b7a606dc..0ba0c36cb2 100644
--- a/src/common/entity/compute_state_display.ts
+++ b/src/common/entity/compute_state_display.ts
@@ -1,5 +1,6 @@
import { HassEntity } from "home-assistant-js-websocket";
import { UNAVAILABLE, UNKNOWN } from "../../data/entity";
+import { FrontendTranslationData } from "../../data/translation";
import { formatDate } from "../datetime/format_date";
import { formatDateTime } from "../datetime/format_date_time";
import { formatTime } from "../datetime/format_time";
@@ -10,7 +11,7 @@ import { computeStateDomain } from "./compute_state_domain";
export const computeStateDisplay = (
localize: LocalizeFunc,
stateObj: HassEntity,
- language: string,
+ locale: FrontendTranslationData,
state?: string
): string => {
const compareState = state !== undefined ? state : stateObj.state;
@@ -20,7 +21,7 @@ export const computeStateDisplay = (
}
if (stateObj.attributes.unit_of_measurement) {
- return `${formatNumber(compareState, language)} ${
+ return `${formatNumber(compareState, locale)} ${
stateObj.attributes.unit_of_measurement
}`;
}
@@ -35,7 +36,7 @@ export const computeStateDisplay = (
stateObj.attributes.month - 1,
stateObj.attributes.day
);
- return formatDate(date, language);
+ return formatDate(date, locale);
}
if (!stateObj.attributes.has_date) {
const now = new Date();
@@ -48,7 +49,7 @@ export const computeStateDisplay = (
stateObj.attributes.hour,
stateObj.attributes.minute
);
- return formatTime(date, language);
+ return formatTime(date, locale);
}
date = new Date(
@@ -58,7 +59,7 @@ export const computeStateDisplay = (
stateObj.attributes.hour,
stateObj.attributes.minute
);
- return formatDateTime(date, language);
+ return formatDateTime(date, locale);
}
if (domain === "humidifier") {
@@ -67,8 +68,9 @@ export const computeStateDisplay = (
}
}
- if (domain === "counter") {
- return formatNumber(compareState, language);
+ // `counter` and `number` domains do not have a unit of measurement but should still use `formatNumber`
+ if (domain === "counter" || domain === "number") {
+ return formatNumber(compareState, locale);
}
return (
diff --git a/src/common/string/format_number.ts b/src/common/string/format_number.ts
index 3dc345fbbc..0f171bd0ea 100644
--- a/src/common/string/format_number.ts
+++ b/src/common/string/format_number.ts
@@ -1,14 +1,36 @@
+import { FrontendTranslationData, NumberFormat } from "../../data/translation";
+
/**
- * Formats a number based on the specified language with thousands separator(s) and decimal character for better legibility.
+ * Formats a number based on the user's preference with thousands separator(s) and decimal character for better legibility.
*
* @param num The number to format
- * @param language The language to use when formatting the number
+ * @param locale The user-selected language and number format, from `hass.locale`
+ * @param options Intl.NumberFormatOptions to use
*/
export const formatNumber = (
num: string | number,
- language: string,
+ locale?: FrontendTranslationData,
options?: Intl.NumberFormatOptions
): string => {
+ let format: string | string[] | undefined;
+
+ switch (locale?.number_format) {
+ case NumberFormat.comma_decimal:
+ format = ["en-US", "en"]; // Use United States with fallback to English formatting 1,234,567.89
+ break;
+ case NumberFormat.decimal_comma:
+ format = ["de", "es", "it"]; // Use German with fallback to Spanish then Italian formatting 1.234.567,89
+ break;
+ case NumberFormat.space_comma:
+ format = ["fr", "sv", "cs"]; // Use French with fallback to Swedish and Czech formatting 1 234 567,89
+ break;
+ case NumberFormat.system:
+ format = undefined;
+ break;
+ default:
+ format = locale?.language;
+ }
+
// Polyfill for Number.isNaN, which is more reliable than the global isNaN()
Number.isNaN =
Number.isNaN ||
@@ -16,13 +38,27 @@ export const formatNumber = (
return typeof input === "number" && isNaN(input);
};
- if (!Number.isNaN(Number(num)) && Intl) {
- return new Intl.NumberFormat(
- language,
- getDefaultFormatOptions(num, options)
- ).format(Number(num));
+ if (
+ !Number.isNaN(Number(num)) &&
+ Intl &&
+ locale?.number_format !== NumberFormat.none
+ ) {
+ try {
+ return new Intl.NumberFormat(
+ format,
+ getDefaultFormatOptions(num, options)
+ ).format(Number(num));
+ } catch (error) {
+ // Don't fail when using "TEST" language
+ // eslint-disable-next-line no-console
+ console.error(error);
+ return new Intl.NumberFormat(
+ undefined,
+ getDefaultFormatOptions(num, options)
+ ).format(Number(num));
+ }
}
- return num.toString();
+ return num ? num.toString() : "";
};
/**
diff --git a/src/components/entity/ha-chart-base.js b/src/components/entity/ha-chart-base.js
index a6ed78fe48..2ae4d7cf26 100644
--- a/src/components/entity/ha-chart-base.js
+++ b/src/components/entity/ha-chart-base.js
@@ -371,7 +371,7 @@ class HaChartBase extends mixinBehaviors(
return value;
}
const date = new Date(values[index].value);
- return formatTime(date, this.hass.language);
+ return formatTime(date, this.hass.locale);
}
drawChart() {
diff --git a/src/components/entity/ha-state-label-badge.ts b/src/components/entity/ha-state-label-badge.ts
index 86e4d899b1..340677ff41 100644
--- a/src/components/entity/ha-state-label-badge.ts
+++ b/src/components/entity/ha-state-label-badge.ts
@@ -116,12 +116,8 @@ export class HaStateLabelBadge extends LitElement {
: state.state === UNKNOWN
? "-"
: state.attributes.unit_of_measurement
- ? formatNumber(state.state, this.hass!.language)
- : computeStateDisplay(
- this.hass!.localize,
- state,
- this.hass!.language
- );
+ ? formatNumber(state.state, this.hass!.locale)
+ : computeStateDisplay(this.hass!.localize, state, this.hass!.locale);
}
}
diff --git a/src/components/entity/state-info.ts b/src/components/entity/state-info.ts
index d493fb625d..0bb035cc7f 100644
--- a/src/components/entity/state-info.ts
+++ b/src/components/entity/state-info.ts
@@ -84,7 +84,7 @@ class StateInfo extends LitElement {
}
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
- if (!oldHass || oldHass.language !== this.hass.language) {
+ if (!oldHass || oldHass.locale !== this.hass.locale) {
this.rtl = computeRTL(this.hass);
}
}
diff --git a/src/components/ha-climate-state.ts b/src/components/ha-climate-state.ts
index 19a8bc0b68..aac651d6af 100644
--- a/src/components/ha-climate-state.ts
+++ b/src/components/ha-climate-state.ts
@@ -53,14 +53,14 @@ class HaClimateState extends LitElement {
if (this.stateObj.attributes.current_temperature != null) {
return `${formatNumber(
this.stateObj.attributes.current_temperature,
- this.hass!.language
+ this.hass.locale
)} ${this.hass.config.unit_system.temperature}`;
}
if (this.stateObj.attributes.current_humidity != null) {
return `${formatNumber(
this.stateObj.attributes.current_humidity,
- this.hass!.language
+ this.hass.locale
)} %`;
}
@@ -78,17 +78,17 @@ class HaClimateState extends LitElement {
) {
return `${formatNumber(
this.stateObj.attributes.target_temp_low,
- this.hass!.language
+ this.hass.locale
)}-${formatNumber(
this.stateObj.attributes.target_temp_high,
- this.hass!.language
+ this.hass.locale
)} ${this.hass.config.unit_system.temperature}`;
}
if (this.stateObj.attributes.temperature != null) {
return `${formatNumber(
this.stateObj.attributes.temperature,
- this.hass!.language
+ this.hass.locale
)} ${this.hass.config.unit_system.temperature}`;
}
if (
@@ -97,17 +97,17 @@ class HaClimateState extends LitElement {
) {
return `${formatNumber(
this.stateObj.attributes.target_humidity_low,
- this.hass!.language
+ this.hass.locale
)}-${formatNumber(
this.stateObj.attributes.target_humidity_high,
- this.hass!.language
+ this.hass.locale
)} %`;
}
if (this.stateObj.attributes.humidity != null) {
return `${formatNumber(
this.stateObj.attributes.humidity,
- this.hass!.language
+ this.hass.locale
)} %`;
}
diff --git a/src/components/ha-date-range-picker.ts b/src/components/ha-date-range-picker.ts
index 91db9cab23..c3780244cd 100644
--- a/src/components/ha-date-range-picker.ts
+++ b/src/components/ha-date-range-picker.ts
@@ -43,7 +43,7 @@ export class HaDateRangePicker extends LitElement {
protected updated(changedProps: PropertyValues) {
if (changedProps.has("hass")) {
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
- if (!oldHass || oldHass.language !== this.hass.language) {
+ if (!oldHass || oldHass.locale !== this.hass.locale) {
this._hour24format = this._compute24hourFormat();
this._rtlDirection = computeRTLDirection(this.hass);
}
@@ -62,7 +62,7 @@ export class HaDateRangePicker extends LitElement {
@@ -61,10 +61,7 @@ class MoreInfoSun extends LitElement {
${this.hass.localize("ui.dialogs.more_info_control.sun.elevation")}
- ${formatNumber(
- this.stateObj.attributes.elevation,
- this.hass!.language
- )}
+ ${formatNumber(this.stateObj.attributes.elevation, this.hass.locale)}
`;
diff --git a/src/dialogs/more-info/controls/more-info-weather.ts b/src/dialogs/more-info/controls/more-info-weather.ts
index d750957fbc..323f327fd0 100644
--- a/src/dialogs/more-info/controls/more-info-weather.ts
+++ b/src/dialogs/more-info/controls/more-info-weather.ts
@@ -68,7 +68,7 @@ class MoreInfoWeather extends LitElement {
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
if (
!oldHass ||
- oldHass.language !== this.hass.language ||
+ oldHass.locale !== this.hass.locale ||
oldHass.config.unit_system !== this.hass.config.unit_system
) {
return true;
@@ -91,7 +91,7 @@ class MoreInfoWeather extends LitElement {
${formatNumber(
this.stateObj.attributes.temperature,
- this.hass!.language
+ this.hass.locale
)}
${getWeatherUnit(this.hass, "temperature")}
@@ -106,7 +106,7 @@ class MoreInfoWeather extends LitElement {
${formatNumber(
this.stateObj.attributes.pressure,
- this.hass!.language
+ this.hass.locale
)}
${getWeatherUnit(this.hass, "air_pressure")}
@@ -123,7 +123,7 @@ class MoreInfoWeather extends LitElement {
${formatNumber(
this.stateObj.attributes.humidity,
- this.hass!.language
+ this.hass.locale
)}
%
@@ -157,7 +157,7 @@ class MoreInfoWeather extends LitElement {
${formatNumber(
this.stateObj.attributes.visibility,
- this.hass!.language
+ this.hass.locale
)}
${getWeatherUnit(this.hass, "length")}
@@ -184,7 +184,7 @@ class MoreInfoWeather extends LitElement {
${formatTimeWeekday(
new Date(item.datetime),
- this.hass.language
+ this.hass.locale
)}
`
@@ -194,17 +194,17 @@ class MoreInfoWeather extends LitElement {
${formatDateWeekday(
new Date(item.datetime),
- this.hass.language
+ this.hass.locale
)}
- ${formatNumber(item.templow, this.hass!.language)}
+ ${formatNumber(item.templow, this.hass.locale)}
${getWeatherUnit(this.hass, "temperature")}
`
: ""}
- ${formatNumber(item.temperature, this.hass!.language)}
+ ${formatNumber(item.temperature, this.hass.locale)}
${getWeatherUnit(this.hass, "temperature")}
diff --git a/src/dialogs/notifications/configurator-notification-item.ts b/src/dialogs/notifications/configurator-notification-item.ts
index a29f1db335..eb998f76be 100644
--- a/src/dialogs/notifications/configurator-notification-item.ts
+++ b/src/dialogs/notifications/configurator-notification-item.ts
@@ -42,7 +42,7 @@ export class HuiConfiguratorNotificationItem extends LitElement {
>${computeStateDisplay(
this.hass.localize,
this.notification,
- this.hass.language
+ this.hass.locale
)}
diff --git a/src/dialogs/notifications/persistent-notification-item.ts b/src/dialogs/notifications/persistent-notification-item.ts
index cc5359f306..c70748ce30 100644
--- a/src/dialogs/notifications/persistent-notification-item.ts
+++ b/src/dialogs/notifications/persistent-notification-item.ts
@@ -9,6 +9,7 @@ import {
property,
TemplateResult,
} from "lit-element";
+import { formatDateTime } from "../../common/datetime/format_date_time";
import "../../components/ha-markdown";
import "../../components/ha-relative-time";
import { PersistentNotification } from "../../data/persistent_notification";
@@ -92,13 +93,7 @@ export class HuiPersistentNotificationItem extends LitElement {
}
const d = new Date(notification.created_at!);
- return d.toLocaleDateString(hass.language, {
- year: "numeric",
- month: "short",
- day: "numeric",
- minute: "numeric",
- hour: "numeric",
- });
+ return formatDateTime(d, hass.locale);
}
}
diff --git a/src/fake_data/provide_hass.ts b/src/fake_data/provide_hass.ts
index dc1d29593a..dbbe930314 100644
--- a/src/fake_data/provide_hass.ts
+++ b/src/fake_data/provide_hass.ts
@@ -5,6 +5,7 @@ import {
} from "../common/dom/apply_themes_on_element";
import { computeLocalize } from "../common/translations/localize";
import { DEFAULT_PANEL } from "../data/panel";
+import { NumberFormat } from "../data/translation";
import { translationMetadata } from "../resources/translations-metadata";
import { HomeAssistant } from "../types";
import { getTranslation, getLocalLanguage } from "../util/hass-translation";
@@ -198,9 +199,12 @@ export const provideHass = (
},
panelUrl: "lovelace",
defaultPanel: DEFAULT_PANEL,
-
language: localLanguage,
selectedLanguage: localLanguage,
+ locale: {
+ language: localLanguage,
+ number_format: NumberFormat.language,
+ },
resources: null as any,
localize: () => "",
diff --git a/src/panels/config/automation/ha-automation-picker.ts b/src/panels/config/automation/ha-automation-picker.ts
index 8bce620971..3beebba9cd 100644
--- a/src/panels/config/automation/ha-automation-picker.ts
+++ b/src/panels/config/automation/ha-automation-picker.ts
@@ -55,7 +55,7 @@ class HaAutomationPicker extends LitElement {
});
private _columns = memoizeOne(
- (narrow: boolean, _language): DataTableColumnContainer => {
+ (narrow: boolean, _locale): DataTableColumnContainer => {
const columns: DataTableColumnContainer = {
toggle: {
title: "",
@@ -83,7 +83,7 @@ class HaAutomationPicker extends LitElement {
${automation.attributes.last_triggered
? formatDateTime(
new Date(automation.attributes.last_triggered),
- this.hass.language
+ this.hass.locale
)
: this.hass.localize("ui.components.relative_time.never")}
@@ -192,7 +192,7 @@ class HaAutomationPicker extends LitElement {
back-path="/config"
.route=${this.route}
.tabs=${configSections.automation}
- .columns=${this._columns(this.narrow, this.hass.language)}
+ .columns=${this._columns(this.narrow, this.hass.locale)}
.data=${this._automations(this.automations)}
id="entity_id"
.noDataText=${this.hass.localize(
diff --git a/src/panels/config/automation/trace/ha-automation-trace.ts b/src/panels/config/automation/trace/ha-automation-trace.ts
index 5f936c3e8d..b54a773009 100644
--- a/src/panels/config/automation/trace/ha-automation-trace.ts
+++ b/src/panels/config/automation/trace/ha-automation-trace.ts
@@ -85,7 +85,7 @@ export class HaAutomationTrace extends LitElement {
html``
)}
diff --git a/src/panels/config/cloud/account/cloud-account.js b/src/panels/config/cloud/account/cloud-account.js
index 139921206d..cbe717df16 100644
--- a/src/panels/config/cloud/account/cloud-account.js
+++ b/src/panels/config/cloud/account/cloud-account.js
@@ -221,7 +221,7 @@ class CloudAccount extends EventsMixin(LocalizeMixin(PolymerElement)) {
"{periodEnd}",
formatDateTime(
new Date(subInfo.plan_renewal_date * 1000),
- this.hass.language
+ this.hass.locale
)
);
}
diff --git a/src/panels/config/cloud/dialog-cloud-certificate/dialog-cloud-certificate.ts b/src/panels/config/cloud/dialog-cloud-certificate/dialog-cloud-certificate.ts
index 92ca9af54c..3a5e6811b2 100644
--- a/src/panels/config/cloud/dialog-cloud-certificate/dialog-cloud-certificate.ts
+++ b/src/panels/config/cloud/dialog-cloud-certificate/dialog-cloud-certificate.ts
@@ -48,7 +48,7 @@ class DialogCloudCertificate extends LitElement {
)}
${formatDateTime(
new Date(certificateInfo.expire_date),
- this.hass!.language
+ this.hass!.locale
)}
(${this.hass!.localize(
"ui.panel.config.cloud.dialog_certificate.will_be_auto_renewed"
diff --git a/src/panels/config/devices/device-detail/ha-device-automation-card.ts b/src/panels/config/devices/device-detail/ha-device-automation-card.ts
index 7cb25de0c6..179d0a387d 100644
--- a/src/panels/config/devices/device-detail/ha-device-automation-card.ts
+++ b/src/panels/config/devices/device-detail/ha-device-automation-card.ts
@@ -49,8 +49,8 @@ export abstract class HaDeviceAutomationCard<
if (changedProps.has("deviceId") || changedProps.has("automations")) {
return true;
}
- const oldHass = changedProps.get("hass");
- if (!oldHass || this.hass.language !== oldHass.language) {
+ const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
+ if (!oldHass || oldHass.language !== this.hass.language) {
return true;
}
return false;
diff --git a/src/panels/config/devices/device-detail/integration-elements/mqtt/mqtt-messages.ts b/src/panels/config/devices/device-detail/integration-elements/mqtt/mqtt-messages.ts
index de55b224bd..549b4cfe00 100644
--- a/src/panels/config/devices/device-detail/integration-elements/mqtt/mqtt-messages.ts
+++ b/src/panels/config/devices/device-detail/integration-elements/mqtt/mqtt-messages.ts
@@ -61,7 +61,7 @@ class MQTTMessages extends LitElement {
Received
${formatTimeWithSeconds(
new Date(message.time),
- this.hass.language
+ this.hass.locale
)}
${this._renderSingleMessage(message)}
diff --git a/src/panels/config/info/system-health-card.ts b/src/panels/config/info/system-health-card.ts
index bda8d409fb..032467c31a 100644
--- a/src/panels/config/info/system-health-card.ts
+++ b/src/panels/config/info/system-health-card.ts
@@ -101,7 +101,7 @@ class SystemHealthCard extends LitElement {
`}
`;
} else if (info.type === "date") {
- value = formatDateTime(new Date(info.value), this.hass.language);
+ value = formatDateTime(new Date(info.value), this.hass.locale);
}
} else {
value = domainInfo.info[key];
@@ -228,7 +228,7 @@ class SystemHealthCard extends LitElement {
} else if (info.type === "failed") {
value = `failed to load: ${info.error}`;
} else if (info.type === "date") {
- value = formatDateTime(new Date(info.value), this.hass.language);
+ value = formatDateTime(new Date(info.value), this.hass.locale);
}
} else {
value = domainInfo.info[key];
diff --git a/src/panels/config/integrations/integration-panels/mqtt/mqtt-subscribe-card.ts b/src/panels/config/integrations/integration-panels/mqtt/mqtt-subscribe-card.ts
index 8818169cfb..532b8f7873 100644
--- a/src/panels/config/integrations/integration-panels/mqtt/mqtt-subscribe-card.ts
+++ b/src/panels/config/integrations/integration-panels/mqtt/mqtt-subscribe-card.ts
@@ -77,7 +77,7 @@ class MqttSubscribeCard extends LitElement {
"topic",
msg.message.topic,
"time",
- formatTime(msg.time, this.hass!.language)
+ formatTime(msg.time, this.hass!.locale)
)}
${msg.payload}
diff --git a/src/panels/config/logs/dialog-system-log-detail.ts b/src/panels/config/logs/dialog-system-log-detail.ts
index c76e0894b7..4744303163 100644
--- a/src/panels/config/logs/dialog-system-log-detail.ts
+++ b/src/panels/config/logs/dialog-system-log-detail.ts
@@ -116,15 +116,12 @@ class DialogSystemLogDetail extends LitElement {
${item.count > 0
? html`
First occurred:
- ${formatSystemLogTime(
- item.first_occurred,
- this.hass!.language
- )}
+ ${formatSystemLogTime(item.first_occurred, this.hass!.locale)}
(${item.count} occurrences)
`
: ""}
Last logged:
- ${formatSystemLogTime(item.timestamp, this.hass!.language)}
+ ${formatSystemLogTime(item.timestamp, this.hass!.locale)}
${item.message.length > 1
? html`
diff --git a/src/panels/config/logs/system-log-card.ts b/src/panels/config/logs/system-log-card.ts
index e7e55c0087..b4a020014e 100644
--- a/src/panels/config/logs/system-log-card.ts
+++ b/src/panels/config/logs/system-log-card.ts
@@ -68,7 +68,7 @@ export class SystemLogCard extends LitElement {
${formatSystemLogTime(
item.timestamp,
- this.hass!.language
+ this.hass!.locale
)}
–
${html`( {
+export const formatSystemLogTime = (date, locale: FrontendTranslationData) => {
const today = new Date().setHours(0, 0, 0, 0);
const dateTime = new Date(date * 1000);
const dateTimeDay = new Date(date * 1000).setHours(0, 0, 0, 0);
return dateTimeDay < today
- ? formatDateTimeWithSeconds(dateTime, language)
- : formatTimeWithSeconds(dateTime, language);
+ ? formatDateTimeWithSeconds(dateTime, locale)
+ : formatTimeWithSeconds(dateTime, locale);
};
diff --git a/src/panels/config/script/ha-script-picker.ts b/src/panels/config/script/ha-script-picker.ts
index 81a3d71f4d..88f3574fad 100644
--- a/src/panels/config/script/ha-script-picker.ts
+++ b/src/panels/config/script/ha-script-picker.ts
@@ -89,7 +89,7 @@ class HaScriptPicker extends LitElement {
${script.attributes.last_triggered
? formatDateTime(
new Date(script.attributes.last_triggered),
- this.hass.language
+ this.hass.locale
)
: this.hass.localize("ui.components.relative_time.never")}
diff --git a/src/panels/developer-tools/event/event-subscribe-card.ts b/src/panels/developer-tools/event/event-subscribe-card.ts
index 7d8350d0de..5324b31acf 100644
--- a/src/panels/developer-tools/event/event-subscribe-card.ts
+++ b/src/panels/developer-tools/event/event-subscribe-card.ts
@@ -82,10 +82,7 @@ class EventSubscribeCard extends LitElement {
"name",
ev.id
)}
- ${formatTime(
- new Date(ev.event.time_fired),
- this.hass!.language
- )}:
+ ${formatTime(new Date(ev.event.time_fired), this.hass!.locale)}:
${JSON.stringify(ev.event, null, 4)}
`
diff --git a/src/panels/developer-tools/state/developer-tools-state.js b/src/panels/developer-tools/state/developer-tools-state.js
index 6ead7a103b..6eb56c8656 100644
--- a/src/panels/developer-tools/state/developer-tools-state.js
+++ b/src/panels/developer-tools/state/developer-tools-state.js
@@ -462,14 +462,14 @@ class HaPanelDevState extends EventsMixin(LocalizeMixin(PolymerElement)) {
lastChangedString(entity) {
return formatDateTimeWithSeconds(
new Date(entity.last_changed),
- this.hass.language
+ this.hass.locale
);
}
lastUpdatedString(entity) {
return formatDateTimeWithSeconds(
new Date(entity.last_updated),
- this.hass.language
+ this.hass.locale
);
}
diff --git a/src/panels/history/ha-panel-history.ts b/src/panels/history/ha-panel-history.ts
index 80f9591547..ba0fc03517 100644
--- a/src/panels/history/ha-panel-history.ts
+++ b/src/panels/history/ha-panel-history.ts
@@ -181,8 +181,7 @@ class HaPanelHistory extends LitElement {
this._stateHistory = computeHistory(
this.hass,
dateHistory,
- this.hass.localize,
- this.hass.language
+ this.hass.localize
);
this._isLoading = false;
}
diff --git a/src/panels/logbook/ha-logbook.ts b/src/panels/logbook/ha-logbook.ts
index 514b838f0e..1fed1769b3 100644
--- a/src/panels/logbook/ha-logbook.ts
+++ b/src/panels/logbook/ha-logbook.ts
@@ -62,7 +62,7 @@ class HaLogbook extends LitElement {
protected shouldUpdate(changedProps: PropertyValues) {
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
const languageChanged =
- oldHass === undefined || oldHass.language !== this.hass.language;
+ oldHass === undefined || oldHass.locale !== this.hass.locale;
return (
changedProps.has("entries") ||
@@ -139,7 +139,7 @@ class HaLogbook extends LitElement {
new Date(previous.when).toDateString())
? html`
- ${formatDate(new Date(item.when), this.hass.language)}
+ ${formatDate(new Date(item.when), this.hass.locale)}
`
: html``}
@@ -204,7 +204,7 @@ class HaLogbook extends LitElement {
${formatTimeWithSeconds(
new Date(item.when),
- this.hass.language
+ this.hass.locale
)}
-
diff --git a/src/panels/lovelace/cards/hui-alarm-panel-card.ts b/src/panels/lovelace/cards/hui-alarm-panel-card.ts
index 66b53d16a5..f63edb98cc 100644
--- a/src/panels/lovelace/cards/hui-alarm-panel-card.ts
+++ b/src/panels/lovelace/cards/hui-alarm-panel-card.ts
@@ -132,7 +132,7 @@ class HuiAlarmPanelCard extends LitElement implements LovelaceCard {
if (
!oldHass ||
oldHass.themes !== this.hass!.themes ||
- oldHass.language !== this.hass!.language
+ oldHass.locale !== this.hass!.locale
) {
return true;
}
diff --git a/src/panels/lovelace/cards/hui-button-card.ts b/src/panels/lovelace/cards/hui-button-card.ts
index 8b36381c0e..2a0fc4dcc6 100644
--- a/src/panels/lovelace/cards/hui-button-card.ts
+++ b/src/panels/lovelace/cards/hui-button-card.ts
@@ -113,7 +113,7 @@ export class HuiButtonCard extends LitElement implements LovelaceCard {
if (
!oldHass ||
oldHass.themes !== this.hass!.themes ||
- oldHass.language !== this.hass!.language
+ oldHass.locale !== this.hass!.locale
) {
return true;
}
@@ -196,7 +196,7 @@ export class HuiButtonCard extends LitElement implements LovelaceCard {
${computeStateDisplay(
this.hass.localize,
stateObj,
- this.hass.language
+ this.hass.locale
)}
`
: ""}
diff --git a/src/panels/lovelace/cards/hui-entity-card.ts b/src/panels/lovelace/cards/hui-entity-card.ts
index 735aee9c90..7c8c1b9a36 100644
--- a/src/panels/lovelace/cards/hui-entity-card.ts
+++ b/src/panels/lovelace/cards/hui-entity-card.ts
@@ -129,11 +129,11 @@ export class HuiEntityCard extends LitElement implements LovelaceCard {
? stateObj.attributes[this._config.attribute!] ??
this.hass.localize("state.default.unknown")
: stateObj.attributes.unit_of_measurement
- ? formatNumber(stateObj.state, this.hass!.language)
+ ? formatNumber(stateObj.state, this.hass.locale)
: computeStateDisplay(
this.hass.localize,
stateObj,
- this.hass.language
+ this.hass.locale
)}${showUnit
? html`
diff --git a/src/panels/lovelace/cards/hui-gauge-card.ts b/src/panels/lovelace/cards/hui-gauge-card.ts
index 092b48f294..a87bb5a9ba 100644
--- a/src/panels/lovelace/cards/hui-gauge-card.ts
+++ b/src/panels/lovelace/cards/hui-gauge-card.ts
@@ -130,7 +130,7 @@ class HuiGaugeCard extends LitElement implements LovelaceCard {
.min=${this._config.min!}
.max=${this._config.max!}
.value=${stateObj.state}
- .language=${this.hass!.language}
+ .locale=${this.hass!.locale}
.label=${this._config!.unit ||
this.hass?.states[this._config!.entity].attributes
.unit_of_measurement ||
diff --git a/src/panels/lovelace/cards/hui-glance-card.ts b/src/panels/lovelace/cards/hui-glance-card.ts
index daf60ca629..180cb8514b 100644
--- a/src/panels/lovelace/cards/hui-glance-card.ts
+++ b/src/panels/lovelace/cards/hui-glance-card.ts
@@ -134,7 +134,7 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard {
!this._configEntities ||
!oldHass ||
oldHass.themes !== this.hass!.themes ||
- oldHass.language !== this.hass!.language
+ oldHass.locale !== this.hass!.locale
) {
return true;
}
@@ -301,7 +301,7 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard {
: computeStateDisplay(
this.hass!.localize,
stateObj,
- this.hass!.language
+ this.hass!.locale
)}
`
diff --git a/src/panels/lovelace/cards/hui-light-card.ts b/src/panels/lovelace/cards/hui-light-card.ts
index a1b49d9378..521cc5c7b8 100644
--- a/src/panels/lovelace/cards/hui-light-card.ts
+++ b/src/panels/lovelace/cards/hui-light-card.ts
@@ -158,7 +158,7 @@ export class HuiLightCard extends LitElement implements LovelaceCard {
${computeStateDisplay(
this.hass.localize,
stateObj,
- this.hass.language
+ this.hass.locale
)}
`
diff --git a/src/panels/lovelace/cards/hui-logbook-card.ts b/src/panels/lovelace/cards/hui-logbook-card.ts
index 78fa5026ff..29e7adbee8 100644
--- a/src/panels/lovelace/cards/hui-logbook-card.ts
+++ b/src/panels/lovelace/cards/hui-logbook-card.ts
@@ -101,7 +101,7 @@ export class HuiLogbookCard extends LitElement implements LovelaceCard {
!this._configEntities ||
!oldHass ||
oldHass.themes !== this.hass!.themes ||
- oldHass.language !== this.hass!.language
+ oldHass.locale !== this.hass!.locale
) {
return true;
}
diff --git a/src/panels/lovelace/cards/hui-picture-entity-card.ts b/src/panels/lovelace/cards/hui-picture-entity-card.ts
index 49818e0b8a..0329de6ba8 100644
--- a/src/panels/lovelace/cards/hui-picture-entity-card.ts
+++ b/src/panels/lovelace/cards/hui-picture-entity-card.ts
@@ -125,7 +125,7 @@ class HuiPictureEntityCard extends LitElement implements LovelaceCard {
const state = computeStateDisplay(
this.hass!.localize,
stateObj,
- this.hass.language
+ this.hass.locale
);
let footer: TemplateResult | string = "";
diff --git a/src/panels/lovelace/cards/hui-picture-glance-card.ts b/src/panels/lovelace/cards/hui-picture-glance-card.ts
index 3f9a9930bf..bc4b62419b 100644
--- a/src/panels/lovelace/cards/hui-picture-glance-card.ts
+++ b/src/panels/lovelace/cards/hui-picture-glance-card.ts
@@ -118,7 +118,7 @@ class HuiPictureGlanceCard extends LitElement implements LovelaceCard {
if (
!oldHass ||
oldHass.themes !== this.hass!.themes ||
- oldHass.language !== this.hass!.language
+ oldHass.locale !== this.hass!.locale
) {
return true;
}
@@ -259,7 +259,7 @@ class HuiPictureGlanceCard extends LitElement implements LovelaceCard {
${computeStateName(stateObj)} : ${computeStateDisplay(
this.hass!.localize,
stateObj,
- this.hass!.language
+ this.hass!.locale
)}
`}
>
@@ -276,7 +276,7 @@ class HuiPictureGlanceCard extends LitElement implements LovelaceCard {
: computeStateDisplay(
this.hass!.localize,
stateObj,
- this.hass!.language
+ this.hass!.locale
)}
`}
diff --git a/src/panels/lovelace/cards/hui-thermostat-card.ts b/src/panels/lovelace/cards/hui-thermostat-card.ts
index c12cfc9a61..95843602ef 100644
--- a/src/panels/lovelace/cards/hui-thermostat-card.ts
+++ b/src/panels/lovelace/cards/hui-thermostat-card.ts
@@ -146,7 +146,7 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
!isNaN(stateObj.attributes.current_temperature)
? svg`${formatNumber(
stateObj.attributes.current_temperature,
- this.hass!.language
+ this.hass.locale
)}
${this.hass.config.unit_system.temperature}
@@ -169,31 +169,31 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
: Array.isArray(this._setTemp)
? this._stepSize === 1
? svg`
- ${formatNumber(this._setTemp[0], this.hass!.language, {
+ ${formatNumber(this._setTemp[0], this.hass.locale, {
maximumFractionDigits: 0,
})} -
- ${formatNumber(this._setTemp[1], this.hass!.language, {
+ ${formatNumber(this._setTemp[1], this.hass.locale, {
maximumFractionDigits: 0,
})}
`
: svg`
- ${formatNumber(this._setTemp[0], this.hass!.language, {
+ ${formatNumber(this._setTemp[0], this.hass.locale, {
minimumFractionDigits: 1,
maximumFractionDigits: 1,
})} -
- ${formatNumber(this._setTemp[1], this.hass!.language, {
+ ${formatNumber(this._setTemp[1], this.hass.locale, {
minimumFractionDigits: 1,
maximumFractionDigits: 1,
})}
`
: this._stepSize === 1
? svg`
- ${formatNumber(this._setTemp, this.hass!.language, {
+ ${formatNumber(this._setTemp, this.hass.locale, {
maximumFractionDigits: 0,
})}
`
: svg`
- ${formatNumber(this._setTemp, this.hass!.language, {
+ ${formatNumber(this._setTemp, this.hass.locale, {
minimumFractionDigits: 1,
maximumFractionDigits: 1,
})}
diff --git a/src/panels/lovelace/cards/hui-weather-forecast-card.ts b/src/panels/lovelace/cards/hui-weather-forecast-card.ts
index aca161b379..9d1a325759 100644
--- a/src/panels/lovelace/cards/hui-weather-forecast-card.ts
+++ b/src/panels/lovelace/cards/hui-weather-forecast-card.ts
@@ -219,7 +219,7 @@ class HuiWeatherForecastCard extends LitElement implements LovelaceCard {
${computeStateDisplay(
this.hass.localize,
stateObj,
- this.hass.language
+ this.hass.locale
)}
@@ -230,7 +230,7 @@ class HuiWeatherForecastCard extends LitElement implements LovelaceCard {
${formatNumber(
stateObj.attributes.temperature,
- this.hass!.language
+ this.hass.locale
)} ${getWeatherUnit(this.hass, "temperature")}
@@ -260,7 +260,7 @@ class HuiWeatherForecastCard extends LitElement implements LovelaceCard {
stateObj.attributes[
this._config.secondary_info_attribute
],
- this.hass!.language
+ this.hass.locale
)}
${getWeatherUnit(
this.hass,
@@ -298,7 +298,7 @@ class HuiWeatherForecastCard extends LitElement implements LovelaceCard {
? html`
${formatTime(
new Date(item.datetime),
- this.hass!.language
+ this.hass!.locale
)}
`
: html`
@@ -325,7 +325,7 @@ class HuiWeatherForecastCard extends LitElement implements LovelaceCard {
${formatNumber(
item.temperature,
- this.hass!.language
+ this.hass!.locale
)}°
`
@@ -333,10 +333,7 @@ class HuiWeatherForecastCard extends LitElement implements LovelaceCard {
${item.templow !== undefined && item.templow !== null
? html`
- ${formatNumber(
- item.templow,
- this.hass!.language
- )}°
+ ${formatNumber(item.templow, this.hass!.locale)}°
`
: ""}
diff --git a/src/panels/lovelace/common/has-changed.ts b/src/panels/lovelace/common/has-changed.ts
index db4b9de518..f054ece6ba 100644
--- a/src/panels/lovelace/common/has-changed.ts
+++ b/src/panels/lovelace/common/has-changed.ts
@@ -15,7 +15,7 @@ function hasConfigChanged(element: any, changedProps: PropertyValues): boolean {
if (
oldHass.connected !== element.hass!.connected ||
oldHass.themes !== element.hass!.themes ||
- oldHass.language !== element.hass!.language ||
+ oldHass.locale !== element.hass!.locale ||
oldHass.localize !== element.hass.localize ||
oldHass.config.state !== element.hass.config.state
) {
diff --git a/src/panels/lovelace/components/hui-timestamp-display.ts b/src/panels/lovelace/components/hui-timestamp-display.ts
index 08bcfe8770..04663d12ae 100644
--- a/src/panels/lovelace/components/hui-timestamp-display.ts
+++ b/src/panels/lovelace/components/hui-timestamp-display.ts
@@ -11,10 +11,13 @@ import { formatDate } from "../../../common/datetime/format_date";
import { formatDateTime } from "../../../common/datetime/format_date_time";
import { formatTime } from "../../../common/datetime/format_time";
import relativeTime from "../../../common/datetime/relative_time";
+import { FrontendTranslationData } from "../../../data/translation";
import { HomeAssistant } from "../../../types";
import { TimestampRenderingFormats } from "./types";
-const FORMATS: { [key: string]: (ts: Date, lang: string) => string } = {
+const FORMATS: {
+ [key: string]: (ts: Date, lang: FrontendTranslationData) => string;
+} = {
date: formatDate,
datetime: formatDateTime,
time: formatTime,
@@ -64,7 +67,7 @@ class HuiTimestampDisplay extends LitElement {
return html` ${this._relative} `;
}
if (format in FORMATS) {
- return html` ${FORMATS[format](this.ts, this.hass.language)} `;
+ return html` ${FORMATS[format](this.ts, this.hass.locale)} `;
}
return html`${this.hass.localize(
"ui.panel.lovelace.components.timestamp-display.invalid_format"
diff --git a/src/panels/lovelace/editor/card-editor/hui-card-picker.ts b/src/panels/lovelace/editor/card-editor/hui-card-picker.ts
index 48fbdecdfa..75c9a95bc5 100644
--- a/src/panels/lovelace/editor/card-editor/hui-card-picker.ts
+++ b/src/panels/lovelace/editor/card-editor/hui-card-picker.ts
@@ -147,7 +147,7 @@ export class HuiCardPicker extends LitElement {
return true;
}
- if (oldHass.language !== this.hass!.language) {
+ if (oldHass.locale !== this.hass!.locale) {
return true;
}
diff --git a/src/panels/lovelace/elements/hui-state-label-element.ts b/src/panels/lovelace/elements/hui-state-label-element.ts
index 5c16451496..be5a114d7a 100644
--- a/src/panels/lovelace/elements/hui-state-label-element.ts
+++ b/src/panels/lovelace/elements/hui-state-label-element.ts
@@ -85,11 +85,7 @@ class HuiStateLabelElement extends LitElement implements LovelaceElement {
)}
>
${this._config.prefix}${!this._config.attribute
- ? computeStateDisplay(
- this.hass.localize,
- stateObj,
- this.hass.language
- )
+ ? computeStateDisplay(this.hass.localize, stateObj, this.hass.locale)
: stateObj.attributes[this._config.attribute]}${this._config.suffix}
`;
diff --git a/src/panels/lovelace/entity-rows/hui-group-entity-row.ts b/src/panels/lovelace/entity-rows/hui-group-entity-row.ts
index 3b256f629d..89e9813c3a 100644
--- a/src/panels/lovelace/entity-rows/hui-group-entity-row.ts
+++ b/src/panels/lovelace/entity-rows/hui-group-entity-row.ts
@@ -76,7 +76,7 @@ class HuiGroupEntityRow extends LitElement implements LovelaceRow {
${computeStateDisplay(
this.hass!.localize,
stateObj,
- this.hass.language
+ this.hass.locale
)}
`}
diff --git a/src/panels/lovelace/entity-rows/hui-input-number-entity-row.ts b/src/panels/lovelace/entity-rows/hui-input-number-entity-row.ts
index 8780106c10..344d5d4d06 100644
--- a/src/panels/lovelace/entity-rows/hui-input-number-entity-row.ts
+++ b/src/panels/lovelace/entity-rows/hui-input-number-entity-row.ts
@@ -10,6 +10,7 @@ import {
PropertyValues,
TemplateResult,
} from "lit-element";
+import { formatNumber } from "../../../common/string/format_number";
import { computeRTLDirection } from "../../../common/util/compute_rtl";
import "../../../components/ha-slider";
import { UNAVAILABLE_STATES } from "../../../data/entity";
@@ -88,7 +89,7 @@ class HuiInputNumberEntityRow extends LitElement implements LovelaceRow {
id="input"
>
- ${Number(stateObj.state)}
+ ${formatNumber(Number(stateObj.state), this.hass.locale)}
${stateObj.attributes.unit_of_measurement}
diff --git a/src/panels/lovelace/entity-rows/hui-media-player-entity-row.ts b/src/panels/lovelace/entity-rows/hui-media-player-entity-row.ts
index 03a79c8bd5..f252ea2b88 100644
--- a/src/panels/lovelace/entity-rows/hui-media-player-entity-row.ts
+++ b/src/panels/lovelace/entity-rows/hui-media-player-entity-row.ts
@@ -136,7 +136,7 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow {
.hass=${this.hass}
.config=${this._config}
.secondaryText=${mediaDescription ||
- computeStateDisplay(this.hass.localize, stateObj, this.hass.language)}
+ computeStateDisplay(this.hass.localize, stateObj, this.hass.locale)}
>
${supportsFeature(stateObj, SUPPORT_TURN_ON) &&
diff --git a/src/panels/lovelace/entity-rows/hui-sensor-entity-row.ts b/src/panels/lovelace/entity-rows/hui-sensor-entity-row.ts
index d91e841d09..758c8570e3 100644
--- a/src/panels/lovelace/entity-rows/hui-sensor-entity-row.ts
+++ b/src/panels/lovelace/entity-rows/hui-sensor-entity-row.ts
@@ -84,7 +84,7 @@ class HuiSensorEntityRow extends LitElement implements LovelaceRow {
: computeStateDisplay(
this.hass!.localize,
stateObj,
- this.hass.language
+ this.hass.locale
)}
diff --git a/src/panels/lovelace/entity-rows/hui-text-entity-row.ts b/src/panels/lovelace/entity-rows/hui-text-entity-row.ts
index 2296fa163b..1fb732ca5c 100644
--- a/src/panels/lovelace/entity-rows/hui-text-entity-row.ts
+++ b/src/panels/lovelace/entity-rows/hui-text-entity-row.ts
@@ -76,7 +76,7 @@ class HuiTextEntityRow extends LitElement implements LovelaceRow {
${computeStateDisplay(
this.hass!.localize,
stateObj,
- this.hass.language
+ this.hass.locale
)}
diff --git a/src/panels/lovelace/entity-rows/hui-toggle-entity-row.ts b/src/panels/lovelace/entity-rows/hui-toggle-entity-row.ts
index a2b52c9115..08fb08f86f 100644
--- a/src/panels/lovelace/entity-rows/hui-toggle-entity-row.ts
+++ b/src/panels/lovelace/entity-rows/hui-toggle-entity-row.ts
@@ -64,7 +64,7 @@ class HuiToggleEntityRow extends LitElement implements LovelaceRow {
${computeStateDisplay(
this.hass!.localize,
stateObj,
- this.hass!.language
+ this.hass!.locale
)}
`}
diff --git a/src/panels/lovelace/entity-rows/hui-weather-entity-row.ts b/src/panels/lovelace/entity-rows/hui-weather-entity-row.ts
index dae998fb9f..1d4a23f185 100644
--- a/src/panels/lovelace/entity-rows/hui-weather-entity-row.ts
+++ b/src/panels/lovelace/entity-rows/hui-weather-entity-row.ts
@@ -16,6 +16,7 @@ import { computeDomain } from "../../../common/entity/compute_domain";
import { computeStateDisplay } from "../../../common/entity/compute_state_display";
import { computeStateName } from "../../../common/entity/compute_state_name";
import { stateIcon } from "../../../common/entity/state_icon";
+import { formatNumber } from "../../../common/string/format_number";
import "../../../components/entity/state-badge";
import { UNAVAILABLE_STATES } from "../../../data/entity";
import { ActionHandlerEvent } from "../../../data/lovelace";
@@ -111,10 +112,13 @@ class HuiWeatherEntityRow extends LitElement implements LovelaceRow {
? computeStateDisplay(
this.hass.localize,
stateObj,
- this.hass.language
+ this.hass.locale
)
: html`
- ${stateObj.attributes.temperature}
+ ${formatNumber(
+ stateObj.attributes.temperature,
+ this.hass.locale
+ )}
${getWeatherUnit(this.hass, "temperature")}
`}
diff --git a/src/panels/lovelace/ha-panel-lovelace.ts b/src/panels/lovelace/ha-panel-lovelace.ts
index c1cefca507..0d6b38716a 100644
--- a/src/panels/lovelace/ha-panel-lovelace.ts
+++ b/src/panels/lovelace/ha-panel-lovelace.ts
@@ -68,7 +68,7 @@ class LovelacePanel extends LitElement {
if (
this.lovelace &&
this.hass &&
- this.lovelace.language !== this.hass.language
+ this.lovelace.locale !== this.hass.locale
) {
// language has been changed, rebuild UI
this._setLovelaceConfig(this.lovelace.config, this.lovelace.mode);
@@ -285,7 +285,7 @@ class LovelacePanel extends LitElement {
mode,
urlPath: this.urlPath,
editMode: this.lovelace ? this.lovelace.editMode : false,
- language: this.hass!.language,
+ locale: this.hass!.locale,
enableFullEditMode: () => {
if (!editorLoaded) {
editorLoaded = true;
diff --git a/src/panels/lovelace/types.ts b/src/panels/lovelace/types.ts
index 292d8e00e7..17bd8bc465 100644
--- a/src/panels/lovelace/types.ts
+++ b/src/panels/lovelace/types.ts
@@ -3,6 +3,7 @@ import {
LovelaceCardConfig,
LovelaceConfig,
} from "../../data/lovelace";
+import { FrontendTranslationData } from "../../data/translation";
import { Constructor, HomeAssistant } from "../../types";
import { LovelaceRow, LovelaceRowConfig } from "./entity-rows/types";
import { LovelaceHeaderFooterConfig } from "./header-footer/types";
@@ -20,7 +21,7 @@ export interface Lovelace {
editMode: boolean;
urlPath: string | null;
mode: "generated" | "yaml" | "storage";
- language: string;
+ locale: FrontendTranslationData;
enableFullEditMode: () => void;
setEditMode: (editMode: boolean) => void;
saveConfig: (newConfig: LovelaceConfig) => Promise;
diff --git a/src/panels/mailbox/ha-panel-mailbox.js b/src/panels/mailbox/ha-panel-mailbox.js
index 98e3e5aba6..1ebf9e4a88 100644
--- a/src/panels/mailbox/ha-panel-mailbox.js
+++ b/src/panels/mailbox/ha-panel-mailbox.js
@@ -214,7 +214,7 @@ class HaPanelMailbox extends EventsMixin(LocalizeMixin(PolymerElement)) {
for (let i = 0; i < arrayLength; i++) {
const datetime = formatDateTime(
new Date(values[i].info.origtime * 1000),
- this.hass.language
+ this.hass.locale
);
platformItems.push({
timestamp: datetime,
diff --git a/src/panels/profile/ha-advanced-mode-row.ts b/src/panels/profile/ha-advanced-mode-row.ts
index ed9a2a0222..4f5744e52a 100644
--- a/src/panels/profile/ha-advanced-mode-row.ts
+++ b/src/panels/profile/ha-advanced-mode-row.ts
@@ -50,6 +50,7 @@ class AdvancedModeRow extends LitElement {
private async _advancedToggled(ev) {
getOptimisticFrontendUserDataCollection(this.hass.connection, "core").save({
+ ...this.coreUserData,
showAdvanced: ev.currentTarget.checked,
});
}
diff --git a/src/panels/profile/ha-panel-profile.ts b/src/panels/profile/ha-panel-profile.ts
index 7c007c8a3b..cca363678c 100644
--- a/src/panels/profile/ha-panel-profile.ts
+++ b/src/panels/profile/ha-panel-profile.ts
@@ -34,6 +34,7 @@ import "./ha-long-lived-access-tokens-card";
import "./ha-mfa-modules-card";
import "./ha-pick-dashboard-row";
import "./ha-pick-language-row";
+import "./ha-pick-number-format-row";
import "./ha-pick-theme-row";
import "./ha-push-notifications-row";
import "./ha-refresh-tokens-card";
@@ -100,6 +101,10 @@ class HaPanelProfile extends LitElement {
.narrow=${this.narrow}
.hass=${this.hass}
>
+
+
+ ${this.hass.localize("ui.panel.profile.number_format.header")}
+
+
+ ${this.hass.localize("ui.panel.profile.number_format.description")}
+
+
+
+ ${Object.values(NumberFormat).map((format) => {
+ const formattedNumber = formatNumber(1234567.89, {
+ language: this.hass.locale.language,
+ number_format: format,
+ });
+ const value = this.hass.localize(
+ `ui.panel.profile.number_format.formats.${format}`
+ );
+ const twoLine = value.slice(value.length - 2) !== "89"; // Display explicit number formats on one line
+ return html`
+
+
+ ${value}
+ ${twoLine
+ ? html`${formattedNumber}
`
+ : ""}
+
+
+ `;
+ })}
+
+
+
+ `;
+ }
+
+ private async _handleFormatSelection(ev: CustomEvent) {
+ fireEvent(this, "hass-number-format-select", ev.detail.item.format);
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "ha-pick-number-format-row": NumberFormatRow;
+ }
+}
diff --git a/src/state-summary/state-card-configurator.js b/src/state-summary/state-card-configurator.js
index 38ced37982..6240406ba4 100644
--- a/src/state-summary/state-card-configurator.js
+++ b/src/state-summary/state-card-configurator.js
@@ -58,11 +58,7 @@ class StateCardConfigurator extends LocalizeMixin(PolymerElement) {
}
_localizeState(stateObj) {
- return computeStateDisplay(
- this.hass.localize,
- stateObj,
- this.hass.language
- );
+ return computeStateDisplay(this.hass.localize, stateObj, this.hass.locale);
}
}
customElements.define("state-card-configurator", StateCardConfigurator);
diff --git a/src/state-summary/state-card-display.ts b/src/state-summary/state-card-display.ts
index 23b31b8d6b..c89668cfe2 100755
--- a/src/state-summary/state-card-display.ts
+++ b/src/state-summary/state-card-display.ts
@@ -56,7 +56,7 @@ export class StateCardDisplay extends LitElement {
: computeStateDisplay(
this.hass!.localize,
this.stateObj,
- this.hass.language
+ this.hass.locale
)}
diff --git a/src/state-summary/state-card-media_player.js b/src/state-summary/state-card-media_player.js
index 164e1b52cb..aae6990dda 100644
--- a/src/state-summary/state-card-media_player.js
+++ b/src/state-summary/state-card-media_player.js
@@ -86,7 +86,7 @@ class StateCardMediaPlayer extends LocalizeMixin(PolymerElement) {
computePrimaryText(localize, playerObj) {
return (
playerObj.primaryTitle ||
- computeStateDisplay(localize, playerObj.stateObj, this.hass.language)
+ computeStateDisplay(localize, playerObj.stateObj, this.hass.locale)
);
}
}
diff --git a/src/state/connection-mixin.ts b/src/state/connection-mixin.ts
index b8a03f756d..52164b59a0 100644
--- a/src/state/connection-mixin.ts
+++ b/src/state/connection-mixin.ts
@@ -13,6 +13,7 @@ import { broadcastConnectionStatus } from "../data/connection-status";
import { subscribeFrontendUserData } from "../data/frontend";
import { forwardHaptic } from "../data/haptics";
import { DEFAULT_PANEL } from "../data/panel";
+import { NumberFormat } from "../data/translation";
import { subscribePanels } from "../data/ws-panels";
import { translationMetadata } from "../resources/translations-metadata";
import { Constructor, ServiceCallResponse } from "../types";
@@ -27,6 +28,8 @@ export const connectionMixin = >(
) =>
class extends superClass {
protected initializeHass(auth: Auth, conn: Connection) {
+ const language = getLocalLanguage();
+
this.hass = {
auth,
connection: conn,
@@ -39,8 +42,12 @@ export const connectionMixin = >(
user: null as any,
panelUrl: (this as any)._panelUrl,
defaultPanel: DEFAULT_PANEL,
- language: getLocalLanguage(),
+ language,
selectedLanguage: null,
+ locale: {
+ language,
+ number_format: NumberFormat.language,
+ },
resources: null as any,
localize: () => "",
diff --git a/src/state/translations-mixin.ts b/src/state/translations-mixin.ts
index 2258833b72..6c5bf64302 100644
--- a/src/state/translations-mixin.ts
+++ b/src/state/translations-mixin.ts
@@ -5,6 +5,7 @@ import { debounce } from "../common/util/debounce";
import {
getHassTranslations,
getHassTranslationsPre109,
+ NumberFormat,
saveTranslationPreferences,
TranslationCategory,
} from "../data/translation";
@@ -14,10 +15,22 @@ import { storeState } from "../util/ha-pref-storage";
import {
getTranslation,
getLocalLanguage,
- getUserLanguage,
+ getUserLocale,
} from "../util/hass-translation";
import { HassBaseEl } from "./hass-base-mixin";
+declare global {
+ // for fire event
+ interface HASSDomEvents {
+ "hass-language-select": {
+ language: string;
+ };
+ "hass-number-format-select": {
+ number_format: NumberFormat;
+ };
+ }
+}
+
interface LoadedTranslationCategory {
// individual integrations loaded for this category
integrations: string[];
@@ -45,9 +58,12 @@ export default >(superClass: T) =>
protected firstUpdated(changedProps) {
super.firstUpdated(changedProps);
- this.addEventListener("hass-language-select", (e) =>
- this._selectLanguage((e as CustomEvent).detail.language, true)
- );
+ this.addEventListener("hass-language-select", (e) => {
+ this._selectLanguage((e as CustomEvent).detail, true);
+ });
+ this.addEventListener("hass-number-format-select", (e) => {
+ this._selectNumberFormat((e as CustomEvent).detail, true);
+ });
this._loadCoreTranslations(getLocalLanguage());
}
@@ -56,20 +72,31 @@ export default >(superClass: T) =>
if (!changedProps.has("hass")) {
return;
}
- const oldHass = changedProps.get("hass");
- if (this.hass?.panels && oldHass.panels !== this.hass.panels) {
+ const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
+ if (
+ this.hass?.panels &&
+ (!oldHass || oldHass.panels !== this.hass.panels)
+ ) {
this._loadFragmentTranslations(this.hass.language, this.hass.panelUrl);
}
}
protected hassConnected() {
super.hassConnected();
- getUserLanguage(this.hass!).then((language) => {
- if (language && this.hass!.language !== language) {
- // We just get language from backend, no need to save back
- this._selectLanguage(language, false);
+ getUserLocale(this.hass!).then((locale) => {
+ if (locale?.language && this.hass!.language !== locale.language) {
+ // We just got language from backend, no need to save back
+ this._selectLanguage(locale.language, false);
+ }
+ if (
+ locale?.number_format &&
+ this.hass!.locale.number_format !== locale.number_format
+ ) {
+ // We just got number_format from backend, no need to save back
+ this._selectNumberFormat(locale.number_format, false);
}
});
+
this.hass!.connection.subscribeEvents(
debounce(() => {
this._refetchCachedHassTranslations(false, false);
@@ -94,6 +121,18 @@ export default >(superClass: T) =>
);
}
+ private _selectNumberFormat(
+ number_format: NumberFormat,
+ saveToBackend: boolean
+ ) {
+ this._updateHass({
+ locale: { ...this.hass!.locale, number_format: number_format },
+ });
+ if (saveToBackend) {
+ saveTranslationPreferences(this.hass!, this.hass!.locale);
+ }
+ }
+
private _selectLanguage(language: string, saveToBackend: boolean) {
if (!this.hass) {
// should not happen, do it to avoid use this.hass!
@@ -101,10 +140,14 @@ export default >(superClass: T) =>
}
// update selectedLanguage so that it can be saved to local storage
- this._updateHass({ language, selectedLanguage: language });
+ this._updateHass({
+ locale: { ...this.hass!.locale, language: language },
+ language: language,
+ selectedLanguage: language,
+ });
storeState(this.hass);
if (saveToBackend) {
- saveTranslationPreferences(this.hass, { language });
+ saveTranslationPreferences(this.hass, this.hass.locale);
}
this._applyTranslations(this.hass);
this._refetchCachedHassTranslations(true, true);
diff --git a/src/translations/en.json b/src/translations/en.json
index 8433cebfbf..fd2fb79fba 100755
--- a/src/translations/en.json
+++ b/src/translations/en.json
@@ -3110,6 +3110,19 @@
"link_promo": "Help translating",
"dropdown_label": "Language"
},
+ "number_format": {
+ "header": "Number Format",
+ "dropdown_label": "Number format",
+ "description": "Choose how numbers are formatted.",
+ "formats": {
+ "language": "Auto (use language setting)",
+ "system": "Use system locale",
+ "comma_decimal": "1,234,567.89",
+ "decimal_comma": "1.234.567,89",
+ "space_comma": "1 234 567,89",
+ "none": "None"
+ }
+ },
"themes": {
"header": "Theme",
"error_no_theme": "No themes available.",
diff --git a/src/types.ts b/src/types.ts
index 3c9e872e43..5b9b6ed980 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -9,7 +9,10 @@ import {
} from "home-assistant-js-websocket";
import { LocalizeFunc } from "./common/translations/localize";
import { CoreFrontendUserData } from "./data/frontend";
-import { getHassTranslations } from "./data/translation";
+import {
+ FrontendTranslationData,
+ getHassTranslations,
+} from "./data/translation";
import { Themes } from "./data/ws-themes";
import { ExternalMessaging } from "./external_app/external_messaging";
@@ -193,9 +196,8 @@ export interface HomeAssistant {
selectedTheme?: ThemeSettings | null;
panels: Panels;
panelUrl: string;
-
// i18n
- // current effective language, in that order:
+ // current effective language in that order:
// - backend saved user selected lanugage
// - language in local appstorage
// - browser language
@@ -203,6 +205,7 @@ export interface HomeAssistant {
language: string;
// local stored language, keep that name for backward compability
selectedLanguage: string | null;
+ locale: FrontendTranslationData;
resources: Resources;
localize: LocalizeFunc;
translationMetadata: TranslationMetadata;
diff --git a/src/util/hass-translation.ts b/src/util/hass-translation.ts
index e0be32bd0a..694af2ee4e 100644
--- a/src/util/hass-translation.ts
+++ b/src/util/hass-translation.ts
@@ -1,4 +1,7 @@
-import { fetchTranslationPreferences } from "../data/translation";
+import {
+ fetchTranslationPreferences,
+ FrontendTranslationData,
+} from "../data/translation";
import { translationMetadata } from "../resources/translations-metadata";
import { HomeAssistant } from "../types";
import { getTranslation as commonGetTranslation } from "./common-translation";
@@ -19,7 +22,7 @@ const LOCALE_LOOKUP = {
/**
* Search for a matching translation from most specific to general
*/
-function findAvailableLanguage(language: string) {
+export function findAvailableLanguage(language: string) {
// In most case, the language has the same format with our translation meta data
if (language in translationMetadata.translations) {
return language;
@@ -39,18 +42,26 @@ function findAvailableLanguage(language: string) {
}
/**
- * Get user selected language from backend
+ * Get user selected locale data from backend
*/
-export async function getUserLanguage(hass: HomeAssistant) {
+export async function getUserLocale(
+ hass: HomeAssistant
+): Promise> {
const result = await fetchTranslationPreferences(hass);
- const language = result ? result.language : null;
+ const language = result?.language;
+ const number_format = result?.number_format;
if (language) {
const availableLanguage = findAvailableLanguage(language);
if (availableLanguage) {
- return availableLanguage;
+ return {
+ language: availableLanguage,
+ number_format,
+ };
}
}
- return null;
+ return {
+ number_format,
+ };
}
/**
diff --git a/test-mocha/common/datetime/format_date.ts b/test-mocha/common/datetime/format_date.ts
index dab6dd5188..851c18f99d 100644
--- a/test-mocha/common/datetime/format_date.ts
+++ b/test-mocha/common/datetime/format_date.ts
@@ -1,11 +1,18 @@
import { assert } from "chai";
import { formatDate } from "../../../src/common/datetime/format_date";
+import { NumberFormat } from "../../../src/data/translation";
describe("formatDate", () => {
const dateObj = new Date(2017, 10, 18, 11, 12, 13, 1400);
it("Formats English dates", () => {
- assert.strictEqual(formatDate(dateObj, "en"), "November 18, 2017");
+ assert.strictEqual(
+ formatDate(dateObj, {
+ language: "en",
+ number_format: NumberFormat.language,
+ }),
+ "November 18, 2017"
+ );
});
});
diff --git a/test-mocha/common/datetime/format_date_time.ts b/test-mocha/common/datetime/format_date_time.ts
index 3cc331251e..beead0d442 100644
--- a/test-mocha/common/datetime/format_date_time.ts
+++ b/test-mocha/common/datetime/format_date_time.ts
@@ -4,13 +4,17 @@ import {
formatDateTime,
formatDateTimeWithSeconds,
} from "../../../src/common/datetime/format_date_time";
+import { NumberFormat } from "../../../src/data/translation";
describe("formatDateTime", () => {
const dateObj = new Date(2017, 10, 18, 11, 12, 13, 400);
it("Formats English date times", () => {
assert.strictEqual(
- formatDateTime(dateObj, "en"),
+ formatDateTime(dateObj, {
+ language: "en",
+ number_format: NumberFormat.language,
+ }),
"November 18, 2017, 11:12 AM"
);
});
@@ -21,7 +25,10 @@ describe("formatDateTimeWithSeconds", () => {
it("Formats English date times with seconds", () => {
assert.strictEqual(
- formatDateTimeWithSeconds(dateObj, "en"),
+ formatDateTimeWithSeconds(dateObj, {
+ language: "en",
+ number_format: NumberFormat.language,
+ }),
"November 18, 2017, 11:12:13 AM"
);
});
diff --git a/test-mocha/common/datetime/format_time.ts b/test-mocha/common/datetime/format_time.ts
index 98dfbb0568..2b1ac03aba 100644
--- a/test-mocha/common/datetime/format_time.ts
+++ b/test-mocha/common/datetime/format_time.ts
@@ -4,12 +4,19 @@ import {
formatTime,
formatTimeWithSeconds,
} from "../../../src/common/datetime/format_time";
+import { NumberFormat } from "../../../src/data/translation";
describe("formatTime", () => {
const dateObj = new Date(2017, 10, 18, 11, 12, 13, 1400);
it("Formats English times", () => {
- assert.strictEqual(formatTime(dateObj, "en"), "11:12 AM");
+ assert.strictEqual(
+ formatTime(dateObj, {
+ language: "en",
+ number_format: NumberFormat.language,
+ }),
+ "11:12 AM"
+ );
});
});
@@ -17,6 +24,12 @@ describe("formatTimeWithSeconds", () => {
const dateObj = new Date(2017, 10, 18, 11, 12, 13, 400);
it("Formats English times with seconds", () => {
- assert.strictEqual(formatTimeWithSeconds(dateObj, "en"), "11:12:13 AM");
+ assert.strictEqual(
+ formatTimeWithSeconds(dateObj, {
+ language: "en",
+ number_format: NumberFormat.language,
+ }),
+ "11:12:13 AM"
+ );
});
});
diff --git a/test-mocha/common/entity/compute_state_display.ts b/test-mocha/common/entity/compute_state_display.ts
index b4822774f4..eb0157956f 100644
--- a/test-mocha/common/entity/compute_state_display.ts
+++ b/test-mocha/common/entity/compute_state_display.ts
@@ -1,6 +1,15 @@
import { assert } from "chai";
import { computeStateDisplay } from "../../../src/common/entity/compute_state_display";
import { UNKNOWN } from "../../../src/data/entity";
+import {
+ FrontendTranslationData,
+ NumberFormat,
+} from "../../../src/data/translation";
+
+const localeData: FrontendTranslationData = {
+ language: "en",
+ number_format: NumberFormat.comma_decimal,
+};
describe("computeStateDisplay", () => {
// Mock Localize function for testing
@@ -14,7 +23,7 @@ describe("computeStateDisplay", () => {
attributes: {},
};
assert.strictEqual(
- computeStateDisplay(localize, stateObj, "en"),
+ computeStateDisplay(localize, stateObj, localeData),
"component.binary_sensor.state._.off"
);
});
@@ -28,7 +37,7 @@ describe("computeStateDisplay", () => {
},
};
assert.strictEqual(
- computeStateDisplay(localize, stateObj, "en"),
+ computeStateDisplay(localize, stateObj, localeData),
"component.binary_sensor.state.moisture.off"
);
});
@@ -48,7 +57,7 @@ describe("computeStateDisplay", () => {
},
};
assert.strictEqual(
- computeStateDisplay(altLocalize, stateObj, "en"),
+ computeStateDisplay(altLocalize, stateObj, localeData),
"component.binary_sensor.state.invalid_device_class.off"
);
});
@@ -61,7 +70,10 @@ describe("computeStateDisplay", () => {
unit_of_measurement: "m",
},
};
- assert.strictEqual(computeStateDisplay(localize, stateObj, "en"), "123 m");
+ assert.strictEqual(
+ computeStateDisplay(localize, stateObj, localeData),
+ "123 m"
+ );
});
it("Localizes and formats numeric sensor value with units", () => {
@@ -73,7 +85,7 @@ describe("computeStateDisplay", () => {
},
};
assert.strictEqual(
- computeStateDisplay(localize, stateObj, "en"),
+ computeStateDisplay(localize, stateObj, localeData),
"1,234.5 m"
);
});
@@ -93,7 +105,7 @@ describe("computeStateDisplay", () => {
},
};
assert.strictEqual(
- computeStateDisplay(altLocalize, stateObj, "en"),
+ computeStateDisplay(altLocalize, stateObj, localeData),
"state.default.unknown"
);
});
@@ -113,7 +125,7 @@ describe("computeStateDisplay", () => {
},
};
assert.strictEqual(
- computeStateDisplay(altLocalize, stateObj, "en"),
+ computeStateDisplay(altLocalize, stateObj, localeData),
"state.default.unavailable"
);
});
@@ -131,7 +143,7 @@ describe("computeStateDisplay", () => {
attributes: {},
};
assert.strictEqual(
- computeStateDisplay(altLocalize, stateObj, "en"),
+ computeStateDisplay(altLocalize, stateObj, localeData),
"component.sensor.state._.custom_state"
);
});
@@ -152,7 +164,7 @@ describe("computeStateDisplay", () => {
},
};
assert.strictEqual(
- computeStateDisplay(localize, stateObj, "en"),
+ computeStateDisplay(localize, stateObj, localeData),
"November 18, 2017, 11:12 AM"
);
});
@@ -173,7 +185,7 @@ describe("computeStateDisplay", () => {
},
};
assert.strictEqual(
- computeStateDisplay(localize, stateObj, "en"),
+ computeStateDisplay(localize, stateObj, localeData),
"November 18, 2017"
);
});
@@ -194,7 +206,7 @@ describe("computeStateDisplay", () => {
},
};
assert.strictEqual(
- computeStateDisplay(localize, stateObj, "en"),
+ computeStateDisplay(localize, stateObj, localeData),
"11:12 AM"
);
});
@@ -212,7 +224,7 @@ describe("computeStateDisplay", () => {
attributes: {},
};
assert.strictEqual(
- computeStateDisplay(altLocalize, stateObj, "en"),
+ computeStateDisplay(altLocalize, stateObj, localeData),
"state.default.unavailable"
);
});
@@ -228,7 +240,7 @@ describe("computeStateDisplay", () => {
attributes: {},
};
assert.strictEqual(
- computeStateDisplay(altLocalize, stateObj, "en"),
+ computeStateDisplay(altLocalize, stateObj, localeData),
"My Custom State"
);
});
diff --git a/test-mocha/common/string/format_number.ts b/test-mocha/common/string/format_number.ts
new file mode 100644
index 0000000000..efd1461bc8
--- /dev/null
+++ b/test-mocha/common/string/format_number.ts
@@ -0,0 +1,33 @@
+import { assert } from "chai";
+
+import { formatNumber } from "../../../src/common/string/format_number";
+import { NumberFormat } from "../../../src/data/translation";
+
+describe("formatNumber", () => {
+ // Node only ships with English support for `Intl`, so we can not test for other number formats here.
+ it("Formats English numbers", () => {
+ assert.strictEqual(
+ formatNumber(1234.5, {
+ language: "en",
+ number_format: NumberFormat.language,
+ }),
+ "1,234.5"
+ );
+ });
+
+ it("Formats number with options", () => {
+ assert.strictEqual(
+ formatNumber(
+ 1234.5,
+ {
+ language: "en",
+ number_format: NumberFormat.language,
+ },
+ {
+ minimumFractionDigits: 2,
+ }
+ ),
+ "1,234.50"
+ );
+ });
+});