mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-25 18:26:35 +00:00
Create number formatting options on the profile panel (#7925)
This commit is contained in:
parent
0393970a80
commit
f43c420d59
@ -39,7 +39,7 @@ class HcLovelace extends LitElement {
|
|||||||
urlPath: this.urlPath!,
|
urlPath: this.urlPath!,
|
||||||
enableFullEditMode: () => undefined,
|
enableFullEditMode: () => undefined,
|
||||||
mode: "storage",
|
mode: "storage",
|
||||||
language: "en",
|
locale: this.hass.locale,
|
||||||
saveConfig: async () => undefined,
|
saveConfig: async () => undefined,
|
||||||
deleteConfig: async () => undefined,
|
deleteConfig: async () => undefined,
|
||||||
setEditMode: () => undefined,
|
setEditMode: () => undefined,
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import { format } from "fecha";
|
import { format } from "fecha";
|
||||||
|
import { FrontendTranslationData } from "../../data/translation";
|
||||||
import { toLocaleDateStringSupportsOptions } from "./check_options_support";
|
import { toLocaleDateStringSupportsOptions } from "./check_options_support";
|
||||||
|
|
||||||
export const formatDate = toLocaleDateStringSupportsOptions
|
export const formatDate = toLocaleDateStringSupportsOptions
|
||||||
? (dateObj: Date, locales: string) =>
|
? (dateObj: Date, locales: FrontendTranslationData) =>
|
||||||
dateObj.toLocaleDateString(locales, {
|
dateObj.toLocaleDateString(locales.language, {
|
||||||
year: "numeric",
|
year: "numeric",
|
||||||
month: "long",
|
month: "long",
|
||||||
day: "numeric",
|
day: "numeric",
|
||||||
@ -11,8 +12,8 @@ export const formatDate = toLocaleDateStringSupportsOptions
|
|||||||
: (dateObj: Date) => format(dateObj, "longDate");
|
: (dateObj: Date) => format(dateObj, "longDate");
|
||||||
|
|
||||||
export const formatDateWeekday = toLocaleDateStringSupportsOptions
|
export const formatDateWeekday = toLocaleDateStringSupportsOptions
|
||||||
? (dateObj: Date, locales: string) =>
|
? (dateObj: Date, locales: FrontendTranslationData) =>
|
||||||
dateObj.toLocaleDateString(locales, {
|
dateObj.toLocaleDateString(locales.language, {
|
||||||
weekday: "long",
|
weekday: "long",
|
||||||
month: "short",
|
month: "short",
|
||||||
day: "numeric",
|
day: "numeric",
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import { format } from "fecha";
|
import { format } from "fecha";
|
||||||
|
import { FrontendTranslationData } from "../../data/translation";
|
||||||
import { toLocaleStringSupportsOptions } from "./check_options_support";
|
import { toLocaleStringSupportsOptions } from "./check_options_support";
|
||||||
|
|
||||||
export const formatDateTime = toLocaleStringSupportsOptions
|
export const formatDateTime = toLocaleStringSupportsOptions
|
||||||
? (dateObj: Date, locales: string) =>
|
? (dateObj: Date, locales: FrontendTranslationData) =>
|
||||||
dateObj.toLocaleString(locales, {
|
dateObj.toLocaleString(locales.language, {
|
||||||
year: "numeric",
|
year: "numeric",
|
||||||
month: "long",
|
month: "long",
|
||||||
day: "numeric",
|
day: "numeric",
|
||||||
@ -13,8 +14,8 @@ export const formatDateTime = toLocaleStringSupportsOptions
|
|||||||
: (dateObj: Date) => format(dateObj, "MMMM D, YYYY, HH:mm");
|
: (dateObj: Date) => format(dateObj, "MMMM D, YYYY, HH:mm");
|
||||||
|
|
||||||
export const formatDateTimeWithSeconds = toLocaleStringSupportsOptions
|
export const formatDateTimeWithSeconds = toLocaleStringSupportsOptions
|
||||||
? (dateObj: Date, locales: string) =>
|
? (dateObj: Date, locales: FrontendTranslationData) =>
|
||||||
dateObj.toLocaleString(locales, {
|
dateObj.toLocaleString(locales.language, {
|
||||||
year: "numeric",
|
year: "numeric",
|
||||||
month: "long",
|
month: "long",
|
||||||
day: "numeric",
|
day: "numeric",
|
||||||
|
@ -1,17 +1,18 @@
|
|||||||
import { format } from "fecha";
|
import { format } from "fecha";
|
||||||
|
import { FrontendTranslationData } from "../../data/translation";
|
||||||
import { toLocaleTimeStringSupportsOptions } from "./check_options_support";
|
import { toLocaleTimeStringSupportsOptions } from "./check_options_support";
|
||||||
|
|
||||||
export const formatTime = toLocaleTimeStringSupportsOptions
|
export const formatTime = toLocaleTimeStringSupportsOptions
|
||||||
? (dateObj: Date, locales: string) =>
|
? (dateObj: Date, locales: FrontendTranslationData) =>
|
||||||
dateObj.toLocaleTimeString(locales, {
|
dateObj.toLocaleTimeString(locales.language, {
|
||||||
hour: "numeric",
|
hour: "numeric",
|
||||||
minute: "2-digit",
|
minute: "2-digit",
|
||||||
})
|
})
|
||||||
: (dateObj: Date) => format(dateObj, "shortTime");
|
: (dateObj: Date) => format(dateObj, "shortTime");
|
||||||
|
|
||||||
export const formatTimeWithSeconds = toLocaleTimeStringSupportsOptions
|
export const formatTimeWithSeconds = toLocaleTimeStringSupportsOptions
|
||||||
? (dateObj: Date, locales: string) =>
|
? (dateObj: Date, locales: FrontendTranslationData) =>
|
||||||
dateObj.toLocaleTimeString(locales, {
|
dateObj.toLocaleTimeString(locales.language, {
|
||||||
hour: "numeric",
|
hour: "numeric",
|
||||||
minute: "2-digit",
|
minute: "2-digit",
|
||||||
second: "2-digit",
|
second: "2-digit",
|
||||||
@ -19,8 +20,8 @@ export const formatTimeWithSeconds = toLocaleTimeStringSupportsOptions
|
|||||||
: (dateObj: Date) => format(dateObj, "mediumTime");
|
: (dateObj: Date) => format(dateObj, "mediumTime");
|
||||||
|
|
||||||
export const formatTimeWeekday = toLocaleTimeStringSupportsOptions
|
export const formatTimeWeekday = toLocaleTimeStringSupportsOptions
|
||||||
? (dateObj: Date, locales: string) =>
|
? (dateObj: Date, locales: FrontendTranslationData) =>
|
||||||
dateObj.toLocaleTimeString(locales, {
|
dateObj.toLocaleTimeString(locales.language, {
|
||||||
weekday: "long",
|
weekday: "long",
|
||||||
hour: "numeric",
|
hour: "numeric",
|
||||||
minute: "2-digit",
|
minute: "2-digit",
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { HassEntity } from "home-assistant-js-websocket";
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
import { UNAVAILABLE, UNKNOWN } from "../../data/entity";
|
import { UNAVAILABLE, UNKNOWN } from "../../data/entity";
|
||||||
|
import { FrontendTranslationData } from "../../data/translation";
|
||||||
import { formatDate } from "../datetime/format_date";
|
import { formatDate } from "../datetime/format_date";
|
||||||
import { formatDateTime } from "../datetime/format_date_time";
|
import { formatDateTime } from "../datetime/format_date_time";
|
||||||
import { formatTime } from "../datetime/format_time";
|
import { formatTime } from "../datetime/format_time";
|
||||||
@ -10,7 +11,7 @@ import { computeStateDomain } from "./compute_state_domain";
|
|||||||
export const computeStateDisplay = (
|
export const computeStateDisplay = (
|
||||||
localize: LocalizeFunc,
|
localize: LocalizeFunc,
|
||||||
stateObj: HassEntity,
|
stateObj: HassEntity,
|
||||||
language: string,
|
locale: FrontendTranslationData,
|
||||||
state?: string
|
state?: string
|
||||||
): string => {
|
): string => {
|
||||||
const compareState = state !== undefined ? state : stateObj.state;
|
const compareState = state !== undefined ? state : stateObj.state;
|
||||||
@ -20,7 +21,7 @@ export const computeStateDisplay = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (stateObj.attributes.unit_of_measurement) {
|
if (stateObj.attributes.unit_of_measurement) {
|
||||||
return `${formatNumber(compareState, language)} ${
|
return `${formatNumber(compareState, locale)} ${
|
||||||
stateObj.attributes.unit_of_measurement
|
stateObj.attributes.unit_of_measurement
|
||||||
}`;
|
}`;
|
||||||
}
|
}
|
||||||
@ -35,7 +36,7 @@ export const computeStateDisplay = (
|
|||||||
stateObj.attributes.month - 1,
|
stateObj.attributes.month - 1,
|
||||||
stateObj.attributes.day
|
stateObj.attributes.day
|
||||||
);
|
);
|
||||||
return formatDate(date, language);
|
return formatDate(date, locale);
|
||||||
}
|
}
|
||||||
if (!stateObj.attributes.has_date) {
|
if (!stateObj.attributes.has_date) {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
@ -48,7 +49,7 @@ export const computeStateDisplay = (
|
|||||||
stateObj.attributes.hour,
|
stateObj.attributes.hour,
|
||||||
stateObj.attributes.minute
|
stateObj.attributes.minute
|
||||||
);
|
);
|
||||||
return formatTime(date, language);
|
return formatTime(date, locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
date = new Date(
|
date = new Date(
|
||||||
@ -58,7 +59,7 @@ export const computeStateDisplay = (
|
|||||||
stateObj.attributes.hour,
|
stateObj.attributes.hour,
|
||||||
stateObj.attributes.minute
|
stateObj.attributes.minute
|
||||||
);
|
);
|
||||||
return formatDateTime(date, language);
|
return formatDateTime(date, locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (domain === "humidifier") {
|
if (domain === "humidifier") {
|
||||||
@ -67,8 +68,9 @@ export const computeStateDisplay = (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (domain === "counter") {
|
// `counter` and `number` domains do not have a unit of measurement but should still use `formatNumber`
|
||||||
return formatNumber(compareState, language);
|
if (domain === "counter" || domain === "number") {
|
||||||
|
return formatNumber(compareState, locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -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 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 = (
|
export const formatNumber = (
|
||||||
num: string | number,
|
num: string | number,
|
||||||
language: string,
|
locale?: FrontendTranslationData,
|
||||||
options?: Intl.NumberFormatOptions
|
options?: Intl.NumberFormatOptions
|
||||||
): string => {
|
): 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()
|
// Polyfill for Number.isNaN, which is more reliable than the global isNaN()
|
||||||
Number.isNaN =
|
Number.isNaN =
|
||||||
Number.isNaN ||
|
Number.isNaN ||
|
||||||
@ -16,13 +38,27 @@ export const formatNumber = (
|
|||||||
return typeof input === "number" && isNaN(input);
|
return typeof input === "number" && isNaN(input);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!Number.isNaN(Number(num)) && Intl) {
|
if (
|
||||||
return new Intl.NumberFormat(
|
!Number.isNaN(Number(num)) &&
|
||||||
language,
|
Intl &&
|
||||||
getDefaultFormatOptions(num, options)
|
locale?.number_format !== NumberFormat.none
|
||||||
).format(Number(num));
|
) {
|
||||||
|
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() : "";
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -371,7 +371,7 @@ class HaChartBase extends mixinBehaviors(
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
const date = new Date(values[index].value);
|
const date = new Date(values[index].value);
|
||||||
return formatTime(date, this.hass.language);
|
return formatTime(date, this.hass.locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
drawChart() {
|
drawChart() {
|
||||||
|
@ -116,12 +116,8 @@ export class HaStateLabelBadge extends LitElement {
|
|||||||
: state.state === UNKNOWN
|
: state.state === UNKNOWN
|
||||||
? "-"
|
? "-"
|
||||||
: state.attributes.unit_of_measurement
|
: state.attributes.unit_of_measurement
|
||||||
? formatNumber(state.state, this.hass!.language)
|
? formatNumber(state.state, this.hass!.locale)
|
||||||
: computeStateDisplay(
|
: computeStateDisplay(this.hass!.localize, state, this.hass!.locale);
|
||||||
this.hass!.localize,
|
|
||||||
state,
|
|
||||||
this.hass!.language
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,7 +84,7 @@ class StateInfo extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
|
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);
|
this.rtl = computeRTL(this.hass);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,14 +53,14 @@ class HaClimateState extends LitElement {
|
|||||||
if (this.stateObj.attributes.current_temperature != null) {
|
if (this.stateObj.attributes.current_temperature != null) {
|
||||||
return `${formatNumber(
|
return `${formatNumber(
|
||||||
this.stateObj.attributes.current_temperature,
|
this.stateObj.attributes.current_temperature,
|
||||||
this.hass!.language
|
this.hass.locale
|
||||||
)} ${this.hass.config.unit_system.temperature}`;
|
)} ${this.hass.config.unit_system.temperature}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.stateObj.attributes.current_humidity != null) {
|
if (this.stateObj.attributes.current_humidity != null) {
|
||||||
return `${formatNumber(
|
return `${formatNumber(
|
||||||
this.stateObj.attributes.current_humidity,
|
this.stateObj.attributes.current_humidity,
|
||||||
this.hass!.language
|
this.hass.locale
|
||||||
)} %`;
|
)} %`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,17 +78,17 @@ class HaClimateState extends LitElement {
|
|||||||
) {
|
) {
|
||||||
return `${formatNumber(
|
return `${formatNumber(
|
||||||
this.stateObj.attributes.target_temp_low,
|
this.stateObj.attributes.target_temp_low,
|
||||||
this.hass!.language
|
this.hass.locale
|
||||||
)}-${formatNumber(
|
)}-${formatNumber(
|
||||||
this.stateObj.attributes.target_temp_high,
|
this.stateObj.attributes.target_temp_high,
|
||||||
this.hass!.language
|
this.hass.locale
|
||||||
)} ${this.hass.config.unit_system.temperature}`;
|
)} ${this.hass.config.unit_system.temperature}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.stateObj.attributes.temperature != null) {
|
if (this.stateObj.attributes.temperature != null) {
|
||||||
return `${formatNumber(
|
return `${formatNumber(
|
||||||
this.stateObj.attributes.temperature,
|
this.stateObj.attributes.temperature,
|
||||||
this.hass!.language
|
this.hass.locale
|
||||||
)} ${this.hass.config.unit_system.temperature}`;
|
)} ${this.hass.config.unit_system.temperature}`;
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
@ -97,17 +97,17 @@ class HaClimateState extends LitElement {
|
|||||||
) {
|
) {
|
||||||
return `${formatNumber(
|
return `${formatNumber(
|
||||||
this.stateObj.attributes.target_humidity_low,
|
this.stateObj.attributes.target_humidity_low,
|
||||||
this.hass!.language
|
this.hass.locale
|
||||||
)}-${formatNumber(
|
)}-${formatNumber(
|
||||||
this.stateObj.attributes.target_humidity_high,
|
this.stateObj.attributes.target_humidity_high,
|
||||||
this.hass!.language
|
this.hass.locale
|
||||||
)} %`;
|
)} %`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.stateObj.attributes.humidity != null) {
|
if (this.stateObj.attributes.humidity != null) {
|
||||||
return `${formatNumber(
|
return `${formatNumber(
|
||||||
this.stateObj.attributes.humidity,
|
this.stateObj.attributes.humidity,
|
||||||
this.hass!.language
|
this.hass.locale
|
||||||
)} %`;
|
)} %`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ export class HaDateRangePicker extends LitElement {
|
|||||||
protected updated(changedProps: PropertyValues) {
|
protected updated(changedProps: PropertyValues) {
|
||||||
if (changedProps.has("hass")) {
|
if (changedProps.has("hass")) {
|
||||||
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
|
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._hour24format = this._compute24hourFormat();
|
||||||
this._rtlDirection = computeRTLDirection(this.hass);
|
this._rtlDirection = computeRTLDirection(this.hass);
|
||||||
}
|
}
|
||||||
@ -62,7 +62,7 @@ export class HaDateRangePicker extends LitElement {
|
|||||||
<div slot="input" class="date-range-inputs">
|
<div slot="input" class="date-range-inputs">
|
||||||
<ha-svg-icon .path=${mdiCalendar}></ha-svg-icon>
|
<ha-svg-icon .path=${mdiCalendar}></ha-svg-icon>
|
||||||
<paper-input
|
<paper-input
|
||||||
.value=${formatDateTime(this.startDate, this.hass.language)}
|
.value=${formatDateTime(this.startDate, this.hass.locale)}
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.components.date-range-picker.start_date"
|
"ui.components.date-range-picker.start_date"
|
||||||
)}
|
)}
|
||||||
@ -71,7 +71,7 @@ export class HaDateRangePicker extends LitElement {
|
|||||||
readonly
|
readonly
|
||||||
></paper-input>
|
></paper-input>
|
||||||
<paper-input
|
<paper-input
|
||||||
.value=${formatDateTime(this.endDate, this.hass.language)}
|
.value=${formatDateTime(this.endDate, this.hass.locale)}
|
||||||
label=${this.hass.localize(
|
label=${this.hass.localize(
|
||||||
"ui.components.date-range-picker.end_date"
|
"ui.components.date-range-picker.end_date"
|
||||||
)}
|
)}
|
||||||
|
@ -11,6 +11,7 @@ import { ifDefined } from "lit-html/directives/if-defined";
|
|||||||
import { styleMap } from "lit-html/directives/style-map";
|
import { styleMap } from "lit-html/directives/style-map";
|
||||||
import { formatNumber } from "../common/string/format_number";
|
import { formatNumber } from "../common/string/format_number";
|
||||||
import { afterNextRender } from "../common/util/render-status";
|
import { afterNextRender } from "../common/util/render-status";
|
||||||
|
import { FrontendTranslationData } from "../data/translation";
|
||||||
import { getValueInPercentage, normalize } from "../util/calculate";
|
import { getValueInPercentage, normalize } from "../util/calculate";
|
||||||
|
|
||||||
const getAngle = (value: number, min: number, max: number) => {
|
const getAngle = (value: number, min: number, max: number) => {
|
||||||
@ -29,7 +30,7 @@ export class Gauge extends LitElement {
|
|||||||
|
|
||||||
@property({ type: Number }) public value = 0;
|
@property({ type: Number }) public value = 0;
|
||||||
|
|
||||||
@property({ type: String }) public language = "";
|
@property() public locale!: FrontendTranslationData;
|
||||||
|
|
||||||
@property() public label = "";
|
@property() public label = "";
|
||||||
|
|
||||||
@ -90,7 +91,7 @@ export class Gauge extends LitElement {
|
|||||||
</svg>
|
</svg>
|
||||||
<svg class="text">
|
<svg class="text">
|
||||||
<text class="value-text">
|
<text class="value-text">
|
||||||
${formatNumber(this.value, this.language)} ${this.label}
|
${formatNumber(this.value, this.locale)} ${this.label}
|
||||||
</text>
|
</text>
|
||||||
</svg>`;
|
</svg>`;
|
||||||
}
|
}
|
||||||
|
@ -245,7 +245,7 @@ class HaSidebar extends LitElement {
|
|||||||
hass.panelUrl !== oldHass.panelUrl ||
|
hass.panelUrl !== oldHass.panelUrl ||
|
||||||
hass.user !== oldHass.user ||
|
hass.user !== oldHass.user ||
|
||||||
hass.localize !== oldHass.localize ||
|
hass.localize !== oldHass.localize ||
|
||||||
hass.language !== oldHass.language ||
|
hass.locale !== oldHass.locale ||
|
||||||
hass.states !== oldHass.states ||
|
hass.states !== oldHass.states ||
|
||||||
hass.defaultPanel !== oldHass.defaultPanel
|
hass.defaultPanel !== oldHass.defaultPanel
|
||||||
);
|
);
|
||||||
@ -281,7 +281,7 @@ class HaSidebar extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
|
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);
|
this.rtl = computeRTL(this.hass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|||||||
/* eslint-plugin-disable lit */
|
/* eslint-plugin-disable lit */
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
import { computeStateDisplay } from "../common/entity/compute_state_display";
|
import { computeStateDisplay } from "../common/entity/compute_state_display";
|
||||||
|
import { formatNumber } from "../common/string/format_number";
|
||||||
import LocalizeMixin from "../mixins/localize-mixin";
|
import LocalizeMixin from "../mixins/localize-mixin";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -55,21 +56,31 @@ class HaWaterHeaterState extends LocalizeMixin(PolymerElement) {
|
|||||||
computeTarget(hass, stateObj) {
|
computeTarget(hass, stateObj) {
|
||||||
if (!hass || !stateObj) return null;
|
if (!hass || !stateObj) return null;
|
||||||
// We're using "!= null" on purpose so that we match both null and undefined.
|
// We're using "!= null" on purpose so that we match both null and undefined.
|
||||||
|
|
||||||
if (
|
if (
|
||||||
stateObj.attributes.target_temp_low != null &&
|
stateObj.attributes.target_temp_low != null &&
|
||||||
stateObj.attributes.target_temp_high != null
|
stateObj.attributes.target_temp_high != null
|
||||||
) {
|
) {
|
||||||
return `${stateObj.attributes.target_temp_low} - ${stateObj.attributes.target_temp_high} ${hass.config.unit_system.temperature}`;
|
return `${formatNumber(
|
||||||
|
stateObj.attributes.target_temp_low,
|
||||||
|
this.hass.locale
|
||||||
|
)} - ${formatNumber(
|
||||||
|
stateObj.attributes.target_temp_high,
|
||||||
|
this.hass.locale
|
||||||
|
)} ${hass.config.unit_system.temperature}`;
|
||||||
}
|
}
|
||||||
if (stateObj.attributes.temperature != null) {
|
if (stateObj.attributes.temperature != null) {
|
||||||
return `${stateObj.attributes.temperature} ${hass.config.unit_system.temperature}`;
|
return `${formatNumber(
|
||||||
|
stateObj.attributes.temperature,
|
||||||
|
this.hass.locale
|
||||||
|
)} ${hass.config.unit_system.temperature}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
_localizeState(stateObj) {
|
_localizeState(stateObj) {
|
||||||
return computeStateDisplay(this.hass.localize, stateObj);
|
return computeStateDisplay(this.hass.localize, stateObj, this.hass.locale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
customElements.define("ha-water_heater-state", HaWaterHeaterState);
|
customElements.define("ha-water_heater-state", HaWaterHeaterState);
|
||||||
|
@ -361,7 +361,7 @@ class StateHistoryChartLine extends LocalizeMixin(PolymerElement) {
|
|||||||
const item = items[0];
|
const item = items[0];
|
||||||
const date = data.datasets[item.datasetIndex].data[item.index].x;
|
const date = data.datasets[item.datasetIndex].data[item.index].x;
|
||||||
|
|
||||||
return formatDateTimeWithSeconds(date, this.hass.language);
|
return formatDateTimeWithSeconds(date, this.hass.locale);
|
||||||
};
|
};
|
||||||
|
|
||||||
const chartOptions = {
|
const chartOptions = {
|
||||||
|
@ -201,8 +201,8 @@ class StateHistoryChartTimeline extends LocalizeMixin(PolymerElement) {
|
|||||||
const formatTooltipLabel = (item, data) => {
|
const formatTooltipLabel = (item, data) => {
|
||||||
const values = data.datasets[item.datasetIndex].data[item.index];
|
const values = data.datasets[item.datasetIndex].data[item.index];
|
||||||
|
|
||||||
const start = formatDateTimeWithSeconds(values[0], this.hass.language);
|
const start = formatDateTimeWithSeconds(values[0], this.hass.locale);
|
||||||
const end = formatDateTimeWithSeconds(values[1], this.hass.language);
|
const end = formatDateTimeWithSeconds(values[1], this.hass.locale);
|
||||||
const state = values[2];
|
const state = values[2];
|
||||||
|
|
||||||
return [state, start, end];
|
return [state, start, end];
|
||||||
|
@ -366,7 +366,7 @@ export class HaAutomationTracer extends LitElement {
|
|||||||
Triggered by the ${this.trace.variables.trigger.description} at
|
Triggered by the ${this.trace.variables.trigger.description} at
|
||||||
${formatDateTimeWithSeconds(
|
${formatDateTimeWithSeconds(
|
||||||
new Date(this.trace.timestamp.start),
|
new Date(this.trace.timestamp.start),
|
||||||
this.hass.language
|
this.hass.locale
|
||||||
)}
|
)}
|
||||||
</ha-timeline>
|
</ha-timeline>
|
||||||
`,
|
`,
|
||||||
@ -433,7 +433,7 @@ export class HaAutomationTracer extends LitElement {
|
|||||||
? html`Finished at
|
? html`Finished at
|
||||||
${formatDateTimeWithSeconds(
|
${formatDateTimeWithSeconds(
|
||||||
new Date(this.trace.timestamp.finish),
|
new Date(this.trace.timestamp.finish),
|
||||||
this.hass.language
|
this.hass.locale
|
||||||
)}
|
)}
|
||||||
(runtime:
|
(runtime:
|
||||||
${(
|
${(
|
||||||
|
@ -54,7 +54,7 @@ export const getRecent = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const prom = fetchRecent(hass, entityId, startTime, endTime).then(
|
const prom = fetchRecent(hass, entityId, startTime, endTime).then(
|
||||||
(stateHistory) => computeHistory(hass, stateHistory, localize, language),
|
(stateHistory) => computeHistory(hass, stateHistory, localize),
|
||||||
(err) => {
|
(err) => {
|
||||||
delete RECENT_CACHE[entityId];
|
delete RECENT_CACHE[entityId];
|
||||||
throw err;
|
throw err;
|
||||||
@ -140,12 +140,7 @@ export const getRecentWithCache = (
|
|||||||
delete stateHistoryCache[cacheKey];
|
delete stateHistoryCache[cacheKey];
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
const stateHistory = computeHistory(
|
const stateHistory = computeHistory(hass, fetchedHistory, localize);
|
||||||
hass,
|
|
||||||
fetchedHistory,
|
|
||||||
localize,
|
|
||||||
language
|
|
||||||
);
|
|
||||||
if (appendingToCache) {
|
if (appendingToCache) {
|
||||||
mergeLine(stateHistory.line, cache.data.line);
|
mergeLine(stateHistory.line, cache.data.line);
|
||||||
mergeTimeline(stateHistory.timeline, cache.data.timeline);
|
mergeTimeline(stateHistory.timeline, cache.data.timeline);
|
||||||
|
@ -4,6 +4,7 @@ import { computeStateDomain } from "../common/entity/compute_state_domain";
|
|||||||
import { computeStateName } from "../common/entity/compute_state_name";
|
import { computeStateName } from "../common/entity/compute_state_name";
|
||||||
import { LocalizeFunc } from "../common/translations/localize";
|
import { LocalizeFunc } from "../common/translations/localize";
|
||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
|
import { FrontendTranslationData } from "./translation";
|
||||||
|
|
||||||
const DOMAINS_USE_LAST_UPDATED = ["climate", "humidifier", "water_heater"];
|
const DOMAINS_USE_LAST_UPDATED = ["climate", "humidifier", "water_heater"];
|
||||||
const LINE_ATTRIBUTES_TO_KEEP = [
|
const LINE_ATTRIBUTES_TO_KEEP = [
|
||||||
@ -109,7 +110,7 @@ const equalState = (obj1: LineChartState, obj2: LineChartState) =>
|
|||||||
|
|
||||||
const processTimelineEntity = (
|
const processTimelineEntity = (
|
||||||
localize: LocalizeFunc,
|
localize: LocalizeFunc,
|
||||||
language: string,
|
language: FrontendTranslationData,
|
||||||
states: HassEntity[]
|
states: HassEntity[]
|
||||||
): TimelineEntity => {
|
): TimelineEntity => {
|
||||||
const data: TimelineState[] = [];
|
const data: TimelineState[] = [];
|
||||||
@ -203,8 +204,7 @@ const processLineChartEntities = (
|
|||||||
export const computeHistory = (
|
export const computeHistory = (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
stateHistory: HassEntity[][],
|
stateHistory: HassEntity[][],
|
||||||
localize: LocalizeFunc,
|
localize: LocalizeFunc
|
||||||
language: string
|
|
||||||
): HistoryResult => {
|
): HistoryResult => {
|
||||||
const lineChartDevices: { [unit: string]: HassEntity[][] } = {};
|
const lineChartDevices: { [unit: string]: HassEntity[][] } = {};
|
||||||
const timelineDevices: TimelineEntity[] = [];
|
const timelineDevices: TimelineEntity[] = [];
|
||||||
@ -235,7 +235,7 @@ export const computeHistory = (
|
|||||||
|
|
||||||
if (!unit) {
|
if (!unit) {
|
||||||
timelineDevices.push(
|
timelineDevices.push(
|
||||||
processTimelineEntity(localize, language, stateInfo)
|
processTimelineEntity(localize, hass.locale, stateInfo)
|
||||||
);
|
);
|
||||||
} else if (unit in lineChartDevices) {
|
} else if (unit in lineChartDevices) {
|
||||||
lineChartDevices[unit].push(stateInfo);
|
lineChartDevices[unit].push(stateInfo);
|
||||||
|
@ -314,7 +314,7 @@ export const getLogbookMessage = (
|
|||||||
`${LOGBOOK_LOCALIZE_PATH}.changed_to_state`,
|
`${LOGBOOK_LOCALIZE_PATH}.changed_to_state`,
|
||||||
"state",
|
"state",
|
||||||
stateObj
|
stateObj
|
||||||
? computeStateDisplay(hass.localize, stateObj, hass.language, state)
|
? computeStateDisplay(hass.localize, stateObj, hass.locale, state)
|
||||||
: state
|
: state
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,8 +1,18 @@
|
|||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
import { fetchFrontendUserData, saveFrontendUserData } from "./frontend";
|
import { fetchFrontendUserData, saveFrontendUserData } from "./frontend";
|
||||||
|
|
||||||
|
export enum NumberFormat {
|
||||||
|
language = "language",
|
||||||
|
system = "system",
|
||||||
|
comma_decimal = "comma_decimal",
|
||||||
|
decimal_comma = "decimal_comma",
|
||||||
|
space_comma = "space_comma",
|
||||||
|
none = "none",
|
||||||
|
}
|
||||||
|
|
||||||
export interface FrontendTranslationData {
|
export interface FrontendTranslationData {
|
||||||
language: string;
|
language: string;
|
||||||
|
number_format: NumberFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
@ -133,7 +133,7 @@ export const getWind = (
|
|||||||
speed: string,
|
speed: string,
|
||||||
bearing: string
|
bearing: string
|
||||||
): string => {
|
): string => {
|
||||||
const speedText = `${formatNumber(speed, hass!.language)} ${getWeatherUnit(
|
const speedText = `${formatNumber(speed, hass.locale)} ${getWeatherUnit(
|
||||||
hass!,
|
hass!,
|
||||||
"wind_speed"
|
"wind_speed"
|
||||||
)}`;
|
)}`;
|
||||||
@ -206,7 +206,7 @@ export const getSecondaryWeatherAttribute = (
|
|||||||
<ha-svg-icon class="attr-icon" .path=${weatherAttrIcon}></ha-svg-icon>
|
<ha-svg-icon class="attr-icon" .path=${weatherAttrIcon}></ha-svg-icon>
|
||||||
`
|
`
|
||||||
: hass!.localize(`ui.card.weather.attributes.${attribute}`)}
|
: hass!.localize(`ui.card.weather.attributes.${attribute}`)}
|
||||||
${formatNumber(value, hass!.language, { maximumFractionDigits: 1 })}
|
${formatNumber(value, hass.locale, { maximumFractionDigits: 1 })}
|
||||||
${getWeatherUnit(hass!, attribute)}
|
${getWeatherUnit(hass!, attribute)}
|
||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
@ -50,7 +50,7 @@ class MoreInfoSun extends LitElement {
|
|||||||
<div class="value">
|
<div class="value">
|
||||||
${formatTime(
|
${formatTime(
|
||||||
item === "ris" ? risingDate : settingDate,
|
item === "ris" ? risingDate : settingDate,
|
||||||
this.hass.language
|
this.hass.locale
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -61,10 +61,7 @@ class MoreInfoSun extends LitElement {
|
|||||||
${this.hass.localize("ui.dialogs.more_info_control.sun.elevation")}
|
${this.hass.localize("ui.dialogs.more_info_control.sun.elevation")}
|
||||||
</div>
|
</div>
|
||||||
<div class="value">
|
<div class="value">
|
||||||
${formatNumber(
|
${formatNumber(this.stateObj.attributes.elevation, this.hass.locale)}
|
||||||
this.stateObj.attributes.elevation,
|
|
||||||
this.hass!.language
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
@ -68,7 +68,7 @@ class MoreInfoWeather extends LitElement {
|
|||||||
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
|
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
|
||||||
if (
|
if (
|
||||||
!oldHass ||
|
!oldHass ||
|
||||||
oldHass.language !== this.hass.language ||
|
oldHass.locale !== this.hass.locale ||
|
||||||
oldHass.config.unit_system !== this.hass.config.unit_system
|
oldHass.config.unit_system !== this.hass.config.unit_system
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
@ -91,7 +91,7 @@ class MoreInfoWeather extends LitElement {
|
|||||||
<div>
|
<div>
|
||||||
${formatNumber(
|
${formatNumber(
|
||||||
this.stateObj.attributes.temperature,
|
this.stateObj.attributes.temperature,
|
||||||
this.hass!.language
|
this.hass.locale
|
||||||
)}
|
)}
|
||||||
${getWeatherUnit(this.hass, "temperature")}
|
${getWeatherUnit(this.hass, "temperature")}
|
||||||
</div>
|
</div>
|
||||||
@ -106,7 +106,7 @@ class MoreInfoWeather extends LitElement {
|
|||||||
<div>
|
<div>
|
||||||
${formatNumber(
|
${formatNumber(
|
||||||
this.stateObj.attributes.pressure,
|
this.stateObj.attributes.pressure,
|
||||||
this.hass!.language
|
this.hass.locale
|
||||||
)}
|
)}
|
||||||
${getWeatherUnit(this.hass, "air_pressure")}
|
${getWeatherUnit(this.hass, "air_pressure")}
|
||||||
</div>
|
</div>
|
||||||
@ -123,7 +123,7 @@ class MoreInfoWeather extends LitElement {
|
|||||||
<div>
|
<div>
|
||||||
${formatNumber(
|
${formatNumber(
|
||||||
this.stateObj.attributes.humidity,
|
this.stateObj.attributes.humidity,
|
||||||
this.hass!.language
|
this.hass.locale
|
||||||
)}
|
)}
|
||||||
%
|
%
|
||||||
</div>
|
</div>
|
||||||
@ -157,7 +157,7 @@ class MoreInfoWeather extends LitElement {
|
|||||||
<div>
|
<div>
|
||||||
${formatNumber(
|
${formatNumber(
|
||||||
this.stateObj.attributes.visibility,
|
this.stateObj.attributes.visibility,
|
||||||
this.hass!.language
|
this.hass.locale
|
||||||
)}
|
)}
|
||||||
${getWeatherUnit(this.hass, "length")}
|
${getWeatherUnit(this.hass, "length")}
|
||||||
</div>
|
</div>
|
||||||
@ -184,7 +184,7 @@ class MoreInfoWeather extends LitElement {
|
|||||||
<div class="main">
|
<div class="main">
|
||||||
${formatTimeWeekday(
|
${formatTimeWeekday(
|
||||||
new Date(item.datetime),
|
new Date(item.datetime),
|
||||||
this.hass.language
|
this.hass.locale
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
@ -194,17 +194,17 @@ class MoreInfoWeather extends LitElement {
|
|||||||
<div class="main">
|
<div class="main">
|
||||||
${formatDateWeekday(
|
${formatDateWeekday(
|
||||||
new Date(item.datetime),
|
new Date(item.datetime),
|
||||||
this.hass.language
|
this.hass.locale
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div class="templow">
|
<div class="templow">
|
||||||
${formatNumber(item.templow, this.hass!.language)}
|
${formatNumber(item.templow, this.hass.locale)}
|
||||||
${getWeatherUnit(this.hass, "temperature")}
|
${getWeatherUnit(this.hass, "temperature")}
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
<div class="temp">
|
<div class="temp">
|
||||||
${formatNumber(item.temperature, this.hass!.language)}
|
${formatNumber(item.temperature, this.hass.locale)}
|
||||||
${getWeatherUnit(this.hass, "temperature")}
|
${getWeatherUnit(this.hass, "temperature")}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -42,7 +42,7 @@ export class HuiConfiguratorNotificationItem extends LitElement {
|
|||||||
>${computeStateDisplay(
|
>${computeStateDisplay(
|
||||||
this.hass.localize,
|
this.hass.localize,
|
||||||
this.notification,
|
this.notification,
|
||||||
this.hass.language
|
this.hass.locale
|
||||||
)}</mwc-button
|
)}</mwc-button
|
||||||
>
|
>
|
||||||
</notification-item-template>
|
</notification-item-template>
|
||||||
|
@ -9,6 +9,7 @@ import {
|
|||||||
property,
|
property,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
|
import { formatDateTime } from "../../common/datetime/format_date_time";
|
||||||
import "../../components/ha-markdown";
|
import "../../components/ha-markdown";
|
||||||
import "../../components/ha-relative-time";
|
import "../../components/ha-relative-time";
|
||||||
import { PersistentNotification } from "../../data/persistent_notification";
|
import { PersistentNotification } from "../../data/persistent_notification";
|
||||||
@ -92,13 +93,7 @@ export class HuiPersistentNotificationItem extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const d = new Date(notification.created_at!);
|
const d = new Date(notification.created_at!);
|
||||||
return d.toLocaleDateString(hass.language, {
|
return formatDateTime(d, hass.locale);
|
||||||
year: "numeric",
|
|
||||||
month: "short",
|
|
||||||
day: "numeric",
|
|
||||||
minute: "numeric",
|
|
||||||
hour: "numeric",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import {
|
|||||||
} from "../common/dom/apply_themes_on_element";
|
} from "../common/dom/apply_themes_on_element";
|
||||||
import { computeLocalize } from "../common/translations/localize";
|
import { computeLocalize } from "../common/translations/localize";
|
||||||
import { DEFAULT_PANEL } from "../data/panel";
|
import { DEFAULT_PANEL } from "../data/panel";
|
||||||
|
import { NumberFormat } from "../data/translation";
|
||||||
import { translationMetadata } from "../resources/translations-metadata";
|
import { translationMetadata } from "../resources/translations-metadata";
|
||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
import { getTranslation, getLocalLanguage } from "../util/hass-translation";
|
import { getTranslation, getLocalLanguage } from "../util/hass-translation";
|
||||||
@ -198,9 +199,12 @@ export const provideHass = (
|
|||||||
},
|
},
|
||||||
panelUrl: "lovelace",
|
panelUrl: "lovelace",
|
||||||
defaultPanel: DEFAULT_PANEL,
|
defaultPanel: DEFAULT_PANEL,
|
||||||
|
|
||||||
language: localLanguage,
|
language: localLanguage,
|
||||||
selectedLanguage: localLanguage,
|
selectedLanguage: localLanguage,
|
||||||
|
locale: {
|
||||||
|
language: localLanguage,
|
||||||
|
number_format: NumberFormat.language,
|
||||||
|
},
|
||||||
resources: null as any,
|
resources: null as any,
|
||||||
localize: () => "",
|
localize: () => "",
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ class HaAutomationPicker extends LitElement {
|
|||||||
});
|
});
|
||||||
|
|
||||||
private _columns = memoizeOne(
|
private _columns = memoizeOne(
|
||||||
(narrow: boolean, _language): DataTableColumnContainer => {
|
(narrow: boolean, _locale): DataTableColumnContainer => {
|
||||||
const columns: DataTableColumnContainer = {
|
const columns: DataTableColumnContainer = {
|
||||||
toggle: {
|
toggle: {
|
||||||
title: "",
|
title: "",
|
||||||
@ -83,7 +83,7 @@ class HaAutomationPicker extends LitElement {
|
|||||||
${automation.attributes.last_triggered
|
${automation.attributes.last_triggered
|
||||||
? formatDateTime(
|
? formatDateTime(
|
||||||
new Date(automation.attributes.last_triggered),
|
new Date(automation.attributes.last_triggered),
|
||||||
this.hass.language
|
this.hass.locale
|
||||||
)
|
)
|
||||||
: this.hass.localize("ui.components.relative_time.never")}
|
: this.hass.localize("ui.components.relative_time.never")}
|
||||||
</div>
|
</div>
|
||||||
@ -192,7 +192,7 @@ class HaAutomationPicker extends LitElement {
|
|||||||
back-path="/config"
|
back-path="/config"
|
||||||
.route=${this.route}
|
.route=${this.route}
|
||||||
.tabs=${configSections.automation}
|
.tabs=${configSections.automation}
|
||||||
.columns=${this._columns(this.narrow, this.hass.language)}
|
.columns=${this._columns(this.narrow, this.hass.locale)}
|
||||||
.data=${this._automations(this.automations)}
|
.data=${this._automations(this.automations)}
|
||||||
id="entity_id"
|
id="entity_id"
|
||||||
.noDataText=${this.hass.localize(
|
.noDataText=${this.hass.localize(
|
||||||
|
@ -85,7 +85,7 @@ export class HaAutomationTrace extends LitElement {
|
|||||||
html`<option value=${trace.run_id}
|
html`<option value=${trace.run_id}
|
||||||
>${formatDateTimeWithSeconds(
|
>${formatDateTimeWithSeconds(
|
||||||
new Date(trace.timestamp.start),
|
new Date(trace.timestamp.start),
|
||||||
this.hass.language
|
this.hass.locale
|
||||||
)}</option
|
)}</option
|
||||||
>`
|
>`
|
||||||
)}
|
)}
|
||||||
|
@ -221,7 +221,7 @@ class CloudAccount extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
|||||||
"{periodEnd}",
|
"{periodEnd}",
|
||||||
formatDateTime(
|
formatDateTime(
|
||||||
new Date(subInfo.plan_renewal_date * 1000),
|
new Date(subInfo.plan_renewal_date * 1000),
|
||||||
this.hass.language
|
this.hass.locale
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ class DialogCloudCertificate extends LitElement {
|
|||||||
)}
|
)}
|
||||||
${formatDateTime(
|
${formatDateTime(
|
||||||
new Date(certificateInfo.expire_date),
|
new Date(certificateInfo.expire_date),
|
||||||
this.hass!.language
|
this.hass!.locale
|
||||||
)}<br />
|
)}<br />
|
||||||
(${this.hass!.localize(
|
(${this.hass!.localize(
|
||||||
"ui.panel.config.cloud.dialog_certificate.will_be_auto_renewed"
|
"ui.panel.config.cloud.dialog_certificate.will_be_auto_renewed"
|
||||||
|
@ -49,8 +49,8 @@ export abstract class HaDeviceAutomationCard<
|
|||||||
if (changedProps.has("deviceId") || changedProps.has("automations")) {
|
if (changedProps.has("deviceId") || changedProps.has("automations")) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
const oldHass = changedProps.get("hass");
|
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
|
||||||
if (!oldHass || this.hass.language !== oldHass.language) {
|
if (!oldHass || oldHass.language !== this.hass.language) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -61,7 +61,7 @@ class MQTTMessages extends LitElement {
|
|||||||
Received
|
Received
|
||||||
${formatTimeWithSeconds(
|
${formatTimeWithSeconds(
|
||||||
new Date(message.time),
|
new Date(message.time),
|
||||||
this.hass.language
|
this.hass.locale
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
${this._renderSingleMessage(message)}
|
${this._renderSingleMessage(message)}
|
||||||
|
@ -101,7 +101,7 @@ class SystemHealthCard extends LitElement {
|
|||||||
`}
|
`}
|
||||||
`;
|
`;
|
||||||
} else if (info.type === "date") {
|
} else if (info.type === "date") {
|
||||||
value = formatDateTime(new Date(info.value), this.hass.language);
|
value = formatDateTime(new Date(info.value), this.hass.locale);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
value = domainInfo.info[key];
|
value = domainInfo.info[key];
|
||||||
@ -228,7 +228,7 @@ class SystemHealthCard extends LitElement {
|
|||||||
} else if (info.type === "failed") {
|
} else if (info.type === "failed") {
|
||||||
value = `failed to load: ${info.error}`;
|
value = `failed to load: ${info.error}`;
|
||||||
} else if (info.type === "date") {
|
} else if (info.type === "date") {
|
||||||
value = formatDateTime(new Date(info.value), this.hass.language);
|
value = formatDateTime(new Date(info.value), this.hass.locale);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
value = domainInfo.info[key];
|
value = domainInfo.info[key];
|
||||||
|
@ -77,7 +77,7 @@ class MqttSubscribeCard extends LitElement {
|
|||||||
"topic",
|
"topic",
|
||||||
msg.message.topic,
|
msg.message.topic,
|
||||||
"time",
|
"time",
|
||||||
formatTime(msg.time, this.hass!.language)
|
formatTime(msg.time, this.hass!.locale)
|
||||||
)}
|
)}
|
||||||
<pre>${msg.payload}</pre>
|
<pre>${msg.payload}</pre>
|
||||||
<div class="bottom">
|
<div class="bottom">
|
||||||
|
@ -116,15 +116,12 @@ class DialogSystemLogDetail extends LitElement {
|
|||||||
${item.count > 0
|
${item.count > 0
|
||||||
? html`
|
? html`
|
||||||
First occurred:
|
First occurred:
|
||||||
${formatSystemLogTime(
|
${formatSystemLogTime(item.first_occurred, this.hass!.locale)}
|
||||||
item.first_occurred,
|
|
||||||
this.hass!.language
|
|
||||||
)}
|
|
||||||
(${item.count} occurrences) <br />
|
(${item.count} occurrences) <br />
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
Last logged:
|
Last logged:
|
||||||
${formatSystemLogTime(item.timestamp, this.hass!.language)}
|
${formatSystemLogTime(item.timestamp, this.hass!.locale)}
|
||||||
</p>
|
</p>
|
||||||
${item.message.length > 1
|
${item.message.length > 1
|
||||||
? html`
|
? html`
|
||||||
|
@ -68,7 +68,7 @@ export class SystemLogCard extends LitElement {
|
|||||||
<div secondary>
|
<div secondary>
|
||||||
${formatSystemLogTime(
|
${formatSystemLogTime(
|
||||||
item.timestamp,
|
item.timestamp,
|
||||||
this.hass!.language
|
this.hass!.locale
|
||||||
)}
|
)}
|
||||||
–
|
–
|
||||||
${html`(<span class="${item.level.toLowerCase()}"
|
${html`(<span class="${item.level.toLowerCase()}"
|
||||||
@ -88,7 +88,7 @@ export class SystemLogCard extends LitElement {
|
|||||||
"time",
|
"time",
|
||||||
formatSystemLogTime(
|
formatSystemLogTime(
|
||||||
item.first_occurred,
|
item.first_occurred,
|
||||||
this.hass!.language
|
this.hass!.locale
|
||||||
),
|
),
|
||||||
"counter",
|
"counter",
|
||||||
item.count
|
item.count
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import { formatDateTimeWithSeconds } from "../../../common/datetime/format_date_time";
|
import { formatDateTimeWithSeconds } from "../../../common/datetime/format_date_time";
|
||||||
import { formatTimeWithSeconds } from "../../../common/datetime/format_time";
|
import { formatTimeWithSeconds } from "../../../common/datetime/format_time";
|
||||||
|
import { FrontendTranslationData } from "../../../data/translation";
|
||||||
|
|
||||||
export const formatSystemLogTime = (date, language: string) => {
|
export const formatSystemLogTime = (date, locale: FrontendTranslationData) => {
|
||||||
const today = new Date().setHours(0, 0, 0, 0);
|
const today = new Date().setHours(0, 0, 0, 0);
|
||||||
const dateTime = new Date(date * 1000);
|
const dateTime = new Date(date * 1000);
|
||||||
const dateTimeDay = new Date(date * 1000).setHours(0, 0, 0, 0);
|
const dateTimeDay = new Date(date * 1000).setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
return dateTimeDay < today
|
return dateTimeDay < today
|
||||||
? formatDateTimeWithSeconds(dateTime, language)
|
? formatDateTimeWithSeconds(dateTime, locale)
|
||||||
: formatTimeWithSeconds(dateTime, language);
|
: formatTimeWithSeconds(dateTime, locale);
|
||||||
};
|
};
|
||||||
|
@ -89,7 +89,7 @@ class HaScriptPicker extends LitElement {
|
|||||||
${script.attributes.last_triggered
|
${script.attributes.last_triggered
|
||||||
? formatDateTime(
|
? formatDateTime(
|
||||||
new Date(script.attributes.last_triggered),
|
new Date(script.attributes.last_triggered),
|
||||||
this.hass.language
|
this.hass.locale
|
||||||
)
|
)
|
||||||
: this.hass.localize("ui.components.relative_time.never")}
|
: this.hass.localize("ui.components.relative_time.never")}
|
||||||
</div>
|
</div>
|
||||||
|
@ -82,10 +82,7 @@ class EventSubscribeCard extends LitElement {
|
|||||||
"name",
|
"name",
|
||||||
ev.id
|
ev.id
|
||||||
)}
|
)}
|
||||||
${formatTime(
|
${formatTime(new Date(ev.event.time_fired), this.hass!.locale)}:
|
||||||
new Date(ev.event.time_fired),
|
|
||||||
this.hass!.language
|
|
||||||
)}:
|
|
||||||
<pre>${JSON.stringify(ev.event, null, 4)}</pre>
|
<pre>${JSON.stringify(ev.event, null, 4)}</pre>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
|
@ -462,14 +462,14 @@ class HaPanelDevState extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
|||||||
lastChangedString(entity) {
|
lastChangedString(entity) {
|
||||||
return formatDateTimeWithSeconds(
|
return formatDateTimeWithSeconds(
|
||||||
new Date(entity.last_changed),
|
new Date(entity.last_changed),
|
||||||
this.hass.language
|
this.hass.locale
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
lastUpdatedString(entity) {
|
lastUpdatedString(entity) {
|
||||||
return formatDateTimeWithSeconds(
|
return formatDateTimeWithSeconds(
|
||||||
new Date(entity.last_updated),
|
new Date(entity.last_updated),
|
||||||
this.hass.language
|
this.hass.locale
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,8 +181,7 @@ class HaPanelHistory extends LitElement {
|
|||||||
this._stateHistory = computeHistory(
|
this._stateHistory = computeHistory(
|
||||||
this.hass,
|
this.hass,
|
||||||
dateHistory,
|
dateHistory,
|
||||||
this.hass.localize,
|
this.hass.localize
|
||||||
this.hass.language
|
|
||||||
);
|
);
|
||||||
this._isLoading = false;
|
this._isLoading = false;
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,7 @@ class HaLogbook extends LitElement {
|
|||||||
protected shouldUpdate(changedProps: PropertyValues<this>) {
|
protected shouldUpdate(changedProps: PropertyValues<this>) {
|
||||||
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
|
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
|
||||||
const languageChanged =
|
const languageChanged =
|
||||||
oldHass === undefined || oldHass.language !== this.hass.language;
|
oldHass === undefined || oldHass.locale !== this.hass.locale;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
changedProps.has("entries") ||
|
changedProps.has("entries") ||
|
||||||
@ -139,7 +139,7 @@ class HaLogbook extends LitElement {
|
|||||||
new Date(previous.when).toDateString())
|
new Date(previous.when).toDateString())
|
||||||
? html`
|
? html`
|
||||||
<h4 class="date">
|
<h4 class="date">
|
||||||
${formatDate(new Date(item.when), this.hass.language)}
|
${formatDate(new Date(item.when), this.hass.locale)}
|
||||||
</h4>
|
</h4>
|
||||||
`
|
`
|
||||||
: html``}
|
: html``}
|
||||||
@ -204,7 +204,7 @@ class HaLogbook extends LitElement {
|
|||||||
<span
|
<span
|
||||||
>${formatTimeWithSeconds(
|
>${formatTimeWithSeconds(
|
||||||
new Date(item.when),
|
new Date(item.when),
|
||||||
this.hass.language
|
this.hass.locale
|
||||||
)}</span
|
)}</span
|
||||||
>
|
>
|
||||||
-
|
-
|
||||||
|
@ -132,7 +132,7 @@ class HuiAlarmPanelCard extends LitElement implements LovelaceCard {
|
|||||||
if (
|
if (
|
||||||
!oldHass ||
|
!oldHass ||
|
||||||
oldHass.themes !== this.hass!.themes ||
|
oldHass.themes !== this.hass!.themes ||
|
||||||
oldHass.language !== this.hass!.language
|
oldHass.locale !== this.hass!.locale
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -113,7 +113,7 @@ export class HuiButtonCard extends LitElement implements LovelaceCard {
|
|||||||
if (
|
if (
|
||||||
!oldHass ||
|
!oldHass ||
|
||||||
oldHass.themes !== this.hass!.themes ||
|
oldHass.themes !== this.hass!.themes ||
|
||||||
oldHass.language !== this.hass!.language
|
oldHass.locale !== this.hass!.locale
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -196,7 +196,7 @@ export class HuiButtonCard extends LitElement implements LovelaceCard {
|
|||||||
${computeStateDisplay(
|
${computeStateDisplay(
|
||||||
this.hass.localize,
|
this.hass.localize,
|
||||||
stateObj,
|
stateObj,
|
||||||
this.hass.language
|
this.hass.locale
|
||||||
)}
|
)}
|
||||||
</span>`
|
</span>`
|
||||||
: ""}
|
: ""}
|
||||||
|
@ -129,11 +129,11 @@ export class HuiEntityCard extends LitElement implements LovelaceCard {
|
|||||||
? stateObj.attributes[this._config.attribute!] ??
|
? stateObj.attributes[this._config.attribute!] ??
|
||||||
this.hass.localize("state.default.unknown")
|
this.hass.localize("state.default.unknown")
|
||||||
: stateObj.attributes.unit_of_measurement
|
: stateObj.attributes.unit_of_measurement
|
||||||
? formatNumber(stateObj.state, this.hass!.language)
|
? formatNumber(stateObj.state, this.hass.locale)
|
||||||
: computeStateDisplay(
|
: computeStateDisplay(
|
||||||
this.hass.localize,
|
this.hass.localize,
|
||||||
stateObj,
|
stateObj,
|
||||||
this.hass.language
|
this.hass.locale
|
||||||
)}</span
|
)}</span
|
||||||
>${showUnit
|
>${showUnit
|
||||||
? html`
|
? html`
|
||||||
|
@ -130,7 +130,7 @@ class HuiGaugeCard extends LitElement implements LovelaceCard {
|
|||||||
.min=${this._config.min!}
|
.min=${this._config.min!}
|
||||||
.max=${this._config.max!}
|
.max=${this._config.max!}
|
||||||
.value=${stateObj.state}
|
.value=${stateObj.state}
|
||||||
.language=${this.hass!.language}
|
.locale=${this.hass!.locale}
|
||||||
.label=${this._config!.unit ||
|
.label=${this._config!.unit ||
|
||||||
this.hass?.states[this._config!.entity].attributes
|
this.hass?.states[this._config!.entity].attributes
|
||||||
.unit_of_measurement ||
|
.unit_of_measurement ||
|
||||||
|
@ -134,7 +134,7 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard {
|
|||||||
!this._configEntities ||
|
!this._configEntities ||
|
||||||
!oldHass ||
|
!oldHass ||
|
||||||
oldHass.themes !== this.hass!.themes ||
|
oldHass.themes !== this.hass!.themes ||
|
||||||
oldHass.language !== this.hass!.language
|
oldHass.locale !== this.hass!.locale
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -301,7 +301,7 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard {
|
|||||||
: computeStateDisplay(
|
: computeStateDisplay(
|
||||||
this.hass!.localize,
|
this.hass!.localize,
|
||||||
stateObj,
|
stateObj,
|
||||||
this.hass!.language
|
this.hass!.locale
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
|
@ -158,7 +158,7 @@ export class HuiLightCard extends LitElement implements LovelaceCard {
|
|||||||
${computeStateDisplay(
|
${computeStateDisplay(
|
||||||
this.hass.localize,
|
this.hass.localize,
|
||||||
stateObj,
|
stateObj,
|
||||||
this.hass.language
|
this.hass.locale
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
|
@ -101,7 +101,7 @@ export class HuiLogbookCard extends LitElement implements LovelaceCard {
|
|||||||
!this._configEntities ||
|
!this._configEntities ||
|
||||||
!oldHass ||
|
!oldHass ||
|
||||||
oldHass.themes !== this.hass!.themes ||
|
oldHass.themes !== this.hass!.themes ||
|
||||||
oldHass.language !== this.hass!.language
|
oldHass.locale !== this.hass!.locale
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -125,7 +125,7 @@ class HuiPictureEntityCard extends LitElement implements LovelaceCard {
|
|||||||
const state = computeStateDisplay(
|
const state = computeStateDisplay(
|
||||||
this.hass!.localize,
|
this.hass!.localize,
|
||||||
stateObj,
|
stateObj,
|
||||||
this.hass.language
|
this.hass.locale
|
||||||
);
|
);
|
||||||
|
|
||||||
let footer: TemplateResult | string = "";
|
let footer: TemplateResult | string = "";
|
||||||
|
@ -118,7 +118,7 @@ class HuiPictureGlanceCard extends LitElement implements LovelaceCard {
|
|||||||
if (
|
if (
|
||||||
!oldHass ||
|
!oldHass ||
|
||||||
oldHass.themes !== this.hass!.themes ||
|
oldHass.themes !== this.hass!.themes ||
|
||||||
oldHass.language !== this.hass!.language
|
oldHass.locale !== this.hass!.locale
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -259,7 +259,7 @@ class HuiPictureGlanceCard extends LitElement implements LovelaceCard {
|
|||||||
${computeStateName(stateObj)} : ${computeStateDisplay(
|
${computeStateName(stateObj)} : ${computeStateDisplay(
|
||||||
this.hass!.localize,
|
this.hass!.localize,
|
||||||
stateObj,
|
stateObj,
|
||||||
this.hass!.language
|
this.hass!.locale
|
||||||
)}
|
)}
|
||||||
`}
|
`}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
@ -276,7 +276,7 @@ class HuiPictureGlanceCard extends LitElement implements LovelaceCard {
|
|||||||
: computeStateDisplay(
|
: computeStateDisplay(
|
||||||
this.hass!.localize,
|
this.hass!.localize,
|
||||||
stateObj,
|
stateObj,
|
||||||
this.hass!.language
|
this.hass!.locale
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
`}
|
`}
|
||||||
|
@ -146,7 +146,7 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
|
|||||||
!isNaN(stateObj.attributes.current_temperature)
|
!isNaN(stateObj.attributes.current_temperature)
|
||||||
? svg`${formatNumber(
|
? svg`${formatNumber(
|
||||||
stateObj.attributes.current_temperature,
|
stateObj.attributes.current_temperature,
|
||||||
this.hass!.language
|
this.hass.locale
|
||||||
)}
|
)}
|
||||||
<tspan dx="-3" dy="-6.5" style="font-size: 4px;">
|
<tspan dx="-3" dy="-6.5" style="font-size: 4px;">
|
||||||
${this.hass.config.unit_system.temperature}
|
${this.hass.config.unit_system.temperature}
|
||||||
@ -169,31 +169,31 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
|
|||||||
: Array.isArray(this._setTemp)
|
: Array.isArray(this._setTemp)
|
||||||
? this._stepSize === 1
|
? this._stepSize === 1
|
||||||
? svg`
|
? svg`
|
||||||
${formatNumber(this._setTemp[0], this.hass!.language, {
|
${formatNumber(this._setTemp[0], this.hass.locale, {
|
||||||
maximumFractionDigits: 0,
|
maximumFractionDigits: 0,
|
||||||
})} -
|
})} -
|
||||||
${formatNumber(this._setTemp[1], this.hass!.language, {
|
${formatNumber(this._setTemp[1], this.hass.locale, {
|
||||||
maximumFractionDigits: 0,
|
maximumFractionDigits: 0,
|
||||||
})}
|
})}
|
||||||
`
|
`
|
||||||
: svg`
|
: svg`
|
||||||
${formatNumber(this._setTemp[0], this.hass!.language, {
|
${formatNumber(this._setTemp[0], this.hass.locale, {
|
||||||
minimumFractionDigits: 1,
|
minimumFractionDigits: 1,
|
||||||
maximumFractionDigits: 1,
|
maximumFractionDigits: 1,
|
||||||
})} -
|
})} -
|
||||||
${formatNumber(this._setTemp[1], this.hass!.language, {
|
${formatNumber(this._setTemp[1], this.hass.locale, {
|
||||||
minimumFractionDigits: 1,
|
minimumFractionDigits: 1,
|
||||||
maximumFractionDigits: 1,
|
maximumFractionDigits: 1,
|
||||||
})}
|
})}
|
||||||
`
|
`
|
||||||
: this._stepSize === 1
|
: this._stepSize === 1
|
||||||
? svg`
|
? svg`
|
||||||
${formatNumber(this._setTemp, this.hass!.language, {
|
${formatNumber(this._setTemp, this.hass.locale, {
|
||||||
maximumFractionDigits: 0,
|
maximumFractionDigits: 0,
|
||||||
})}
|
})}
|
||||||
`
|
`
|
||||||
: svg`
|
: svg`
|
||||||
${formatNumber(this._setTemp, this.hass!.language, {
|
${formatNumber(this._setTemp, this.hass.locale, {
|
||||||
minimumFractionDigits: 1,
|
minimumFractionDigits: 1,
|
||||||
maximumFractionDigits: 1,
|
maximumFractionDigits: 1,
|
||||||
})}
|
})}
|
||||||
|
@ -219,7 +219,7 @@ class HuiWeatherForecastCard extends LitElement implements LovelaceCard {
|
|||||||
${computeStateDisplay(
|
${computeStateDisplay(
|
||||||
this.hass.localize,
|
this.hass.localize,
|
||||||
stateObj,
|
stateObj,
|
||||||
this.hass.language
|
this.hass.locale
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div class="name">
|
<div class="name">
|
||||||
@ -230,7 +230,7 @@ class HuiWeatherForecastCard extends LitElement implements LovelaceCard {
|
|||||||
<div class="temp">
|
<div class="temp">
|
||||||
${formatNumber(
|
${formatNumber(
|
||||||
stateObj.attributes.temperature,
|
stateObj.attributes.temperature,
|
||||||
this.hass!.language
|
this.hass.locale
|
||||||
)} <span>${getWeatherUnit(this.hass, "temperature")}</span>
|
)} <span>${getWeatherUnit(this.hass, "temperature")}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="attribute">
|
<div class="attribute">
|
||||||
@ -260,7 +260,7 @@ class HuiWeatherForecastCard extends LitElement implements LovelaceCard {
|
|||||||
stateObj.attributes[
|
stateObj.attributes[
|
||||||
this._config.secondary_info_attribute
|
this._config.secondary_info_attribute
|
||||||
],
|
],
|
||||||
this.hass!.language
|
this.hass.locale
|
||||||
)}
|
)}
|
||||||
${getWeatherUnit(
|
${getWeatherUnit(
|
||||||
this.hass,
|
this.hass,
|
||||||
@ -298,7 +298,7 @@ class HuiWeatherForecastCard extends LitElement implements LovelaceCard {
|
|||||||
? html`
|
? html`
|
||||||
${formatTime(
|
${formatTime(
|
||||||
new Date(item.datetime),
|
new Date(item.datetime),
|
||||||
this.hass!.language
|
this.hass!.locale
|
||||||
)}
|
)}
|
||||||
`
|
`
|
||||||
: html`
|
: html`
|
||||||
@ -325,7 +325,7 @@ class HuiWeatherForecastCard extends LitElement implements LovelaceCard {
|
|||||||
<div class="temp">
|
<div class="temp">
|
||||||
${formatNumber(
|
${formatNumber(
|
||||||
item.temperature,
|
item.temperature,
|
||||||
this.hass!.language
|
this.hass!.locale
|
||||||
)}°
|
)}°
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
@ -333,10 +333,7 @@ class HuiWeatherForecastCard extends LitElement implements LovelaceCard {
|
|||||||
${item.templow !== undefined && item.templow !== null
|
${item.templow !== undefined && item.templow !== null
|
||||||
? html`
|
? html`
|
||||||
<div class="templow">
|
<div class="templow">
|
||||||
${formatNumber(
|
${formatNumber(item.templow, this.hass!.locale)}°
|
||||||
item.templow,
|
|
||||||
this.hass!.language
|
|
||||||
)}°
|
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
|
@ -15,7 +15,7 @@ function hasConfigChanged(element: any, changedProps: PropertyValues): boolean {
|
|||||||
if (
|
if (
|
||||||
oldHass.connected !== element.hass!.connected ||
|
oldHass.connected !== element.hass!.connected ||
|
||||||
oldHass.themes !== element.hass!.themes ||
|
oldHass.themes !== element.hass!.themes ||
|
||||||
oldHass.language !== element.hass!.language ||
|
oldHass.locale !== element.hass!.locale ||
|
||||||
oldHass.localize !== element.hass.localize ||
|
oldHass.localize !== element.hass.localize ||
|
||||||
oldHass.config.state !== element.hass.config.state
|
oldHass.config.state !== element.hass.config.state
|
||||||
) {
|
) {
|
||||||
|
@ -11,10 +11,13 @@ import { formatDate } from "../../../common/datetime/format_date";
|
|||||||
import { formatDateTime } from "../../../common/datetime/format_date_time";
|
import { formatDateTime } from "../../../common/datetime/format_date_time";
|
||||||
import { formatTime } from "../../../common/datetime/format_time";
|
import { formatTime } from "../../../common/datetime/format_time";
|
||||||
import relativeTime from "../../../common/datetime/relative_time";
|
import relativeTime from "../../../common/datetime/relative_time";
|
||||||
|
import { FrontendTranslationData } from "../../../data/translation";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import { TimestampRenderingFormats } 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,
|
date: formatDate,
|
||||||
datetime: formatDateTime,
|
datetime: formatDateTime,
|
||||||
time: formatTime,
|
time: formatTime,
|
||||||
@ -64,7 +67,7 @@ class HuiTimestampDisplay extends LitElement {
|
|||||||
return html` ${this._relative} `;
|
return html` ${this._relative} `;
|
||||||
}
|
}
|
||||||
if (format in FORMATS) {
|
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(
|
return html`${this.hass.localize(
|
||||||
"ui.panel.lovelace.components.timestamp-display.invalid_format"
|
"ui.panel.lovelace.components.timestamp-display.invalid_format"
|
||||||
|
@ -147,7 +147,7 @@ export class HuiCardPicker extends LitElement {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oldHass.language !== this.hass!.language) {
|
if (oldHass.locale !== this.hass!.locale) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,11 +85,7 @@ class HuiStateLabelElement extends LitElement implements LovelaceElement {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
${this._config.prefix}${!this._config.attribute
|
${this._config.prefix}${!this._config.attribute
|
||||||
? computeStateDisplay(
|
? computeStateDisplay(this.hass.localize, stateObj, this.hass.locale)
|
||||||
this.hass.localize,
|
|
||||||
stateObj,
|
|
||||||
this.hass.language
|
|
||||||
)
|
|
||||||
: stateObj.attributes[this._config.attribute]}${this._config.suffix}
|
: stateObj.attributes[this._config.attribute]}${this._config.suffix}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
@ -76,7 +76,7 @@ class HuiGroupEntityRow extends LitElement implements LovelaceRow {
|
|||||||
${computeStateDisplay(
|
${computeStateDisplay(
|
||||||
this.hass!.localize,
|
this.hass!.localize,
|
||||||
stateObj,
|
stateObj,
|
||||||
this.hass.language
|
this.hass.locale
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
`}
|
`}
|
||||||
|
@ -10,6 +10,7 @@ import {
|
|||||||
PropertyValues,
|
PropertyValues,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
|
import { formatNumber } from "../../../common/string/format_number";
|
||||||
import { computeRTLDirection } from "../../../common/util/compute_rtl";
|
import { computeRTLDirection } from "../../../common/util/compute_rtl";
|
||||||
import "../../../components/ha-slider";
|
import "../../../components/ha-slider";
|
||||||
import { UNAVAILABLE_STATES } from "../../../data/entity";
|
import { UNAVAILABLE_STATES } from "../../../data/entity";
|
||||||
@ -88,7 +89,7 @@ class HuiInputNumberEntityRow extends LitElement implements LovelaceRow {
|
|||||||
id="input"
|
id="input"
|
||||||
></ha-slider>
|
></ha-slider>
|
||||||
<span class="state">
|
<span class="state">
|
||||||
${Number(stateObj.state)}
|
${formatNumber(Number(stateObj.state), this.hass.locale)}
|
||||||
${stateObj.attributes.unit_of_measurement}
|
${stateObj.attributes.unit_of_measurement}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -136,7 +136,7 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow {
|
|||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.config=${this._config}
|
.config=${this._config}
|
||||||
.secondaryText=${mediaDescription ||
|
.secondaryText=${mediaDescription ||
|
||||||
computeStateDisplay(this.hass.localize, stateObj, this.hass.language)}
|
computeStateDisplay(this.hass.localize, stateObj, this.hass.locale)}
|
||||||
>
|
>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
${supportsFeature(stateObj, SUPPORT_TURN_ON) &&
|
${supportsFeature(stateObj, SUPPORT_TURN_ON) &&
|
||||||
|
@ -84,7 +84,7 @@ class HuiSensorEntityRow extends LitElement implements LovelaceRow {
|
|||||||
: computeStateDisplay(
|
: computeStateDisplay(
|
||||||
this.hass!.localize,
|
this.hass!.localize,
|
||||||
stateObj,
|
stateObj,
|
||||||
this.hass.language
|
this.hass.locale
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</hui-generic-entity-row>
|
</hui-generic-entity-row>
|
||||||
|
@ -76,7 +76,7 @@ class HuiTextEntityRow extends LitElement implements LovelaceRow {
|
|||||||
${computeStateDisplay(
|
${computeStateDisplay(
|
||||||
this.hass!.localize,
|
this.hass!.localize,
|
||||||
stateObj,
|
stateObj,
|
||||||
this.hass.language
|
this.hass.locale
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</hui-generic-entity-row>
|
</hui-generic-entity-row>
|
||||||
|
@ -64,7 +64,7 @@ class HuiToggleEntityRow extends LitElement implements LovelaceRow {
|
|||||||
${computeStateDisplay(
|
${computeStateDisplay(
|
||||||
this.hass!.localize,
|
this.hass!.localize,
|
||||||
stateObj,
|
stateObj,
|
||||||
this.hass!.language
|
this.hass!.locale
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
`}
|
`}
|
||||||
|
@ -16,6 +16,7 @@ import { computeDomain } from "../../../common/entity/compute_domain";
|
|||||||
import { computeStateDisplay } from "../../../common/entity/compute_state_display";
|
import { computeStateDisplay } from "../../../common/entity/compute_state_display";
|
||||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||||
import { stateIcon } from "../../../common/entity/state_icon";
|
import { stateIcon } from "../../../common/entity/state_icon";
|
||||||
|
import { formatNumber } from "../../../common/string/format_number";
|
||||||
import "../../../components/entity/state-badge";
|
import "../../../components/entity/state-badge";
|
||||||
import { UNAVAILABLE_STATES } from "../../../data/entity";
|
import { UNAVAILABLE_STATES } from "../../../data/entity";
|
||||||
import { ActionHandlerEvent } from "../../../data/lovelace";
|
import { ActionHandlerEvent } from "../../../data/lovelace";
|
||||||
@ -111,10 +112,13 @@ class HuiWeatherEntityRow extends LitElement implements LovelaceRow {
|
|||||||
? computeStateDisplay(
|
? computeStateDisplay(
|
||||||
this.hass.localize,
|
this.hass.localize,
|
||||||
stateObj,
|
stateObj,
|
||||||
this.hass.language
|
this.hass.locale
|
||||||
)
|
)
|
||||||
: html`
|
: html`
|
||||||
${stateObj.attributes.temperature}
|
${formatNumber(
|
||||||
|
stateObj.attributes.temperature,
|
||||||
|
this.hass.locale
|
||||||
|
)}
|
||||||
${getWeatherUnit(this.hass, "temperature")}
|
${getWeatherUnit(this.hass, "temperature")}
|
||||||
`}
|
`}
|
||||||
</div>
|
</div>
|
||||||
|
@ -68,7 +68,7 @@ class LovelacePanel extends LitElement {
|
|||||||
if (
|
if (
|
||||||
this.lovelace &&
|
this.lovelace &&
|
||||||
this.hass &&
|
this.hass &&
|
||||||
this.lovelace.language !== this.hass.language
|
this.lovelace.locale !== this.hass.locale
|
||||||
) {
|
) {
|
||||||
// language has been changed, rebuild UI
|
// language has been changed, rebuild UI
|
||||||
this._setLovelaceConfig(this.lovelace.config, this.lovelace.mode);
|
this._setLovelaceConfig(this.lovelace.config, this.lovelace.mode);
|
||||||
@ -285,7 +285,7 @@ class LovelacePanel extends LitElement {
|
|||||||
mode,
|
mode,
|
||||||
urlPath: this.urlPath,
|
urlPath: this.urlPath,
|
||||||
editMode: this.lovelace ? this.lovelace.editMode : false,
|
editMode: this.lovelace ? this.lovelace.editMode : false,
|
||||||
language: this.hass!.language,
|
locale: this.hass!.locale,
|
||||||
enableFullEditMode: () => {
|
enableFullEditMode: () => {
|
||||||
if (!editorLoaded) {
|
if (!editorLoaded) {
|
||||||
editorLoaded = true;
|
editorLoaded = true;
|
||||||
|
@ -3,6 +3,7 @@ import {
|
|||||||
LovelaceCardConfig,
|
LovelaceCardConfig,
|
||||||
LovelaceConfig,
|
LovelaceConfig,
|
||||||
} from "../../data/lovelace";
|
} from "../../data/lovelace";
|
||||||
|
import { FrontendTranslationData } from "../../data/translation";
|
||||||
import { Constructor, HomeAssistant } from "../../types";
|
import { Constructor, HomeAssistant } from "../../types";
|
||||||
import { LovelaceRow, LovelaceRowConfig } from "./entity-rows/types";
|
import { LovelaceRow, LovelaceRowConfig } from "./entity-rows/types";
|
||||||
import { LovelaceHeaderFooterConfig } from "./header-footer/types";
|
import { LovelaceHeaderFooterConfig } from "./header-footer/types";
|
||||||
@ -20,7 +21,7 @@ export interface Lovelace {
|
|||||||
editMode: boolean;
|
editMode: boolean;
|
||||||
urlPath: string | null;
|
urlPath: string | null;
|
||||||
mode: "generated" | "yaml" | "storage";
|
mode: "generated" | "yaml" | "storage";
|
||||||
language: string;
|
locale: FrontendTranslationData;
|
||||||
enableFullEditMode: () => void;
|
enableFullEditMode: () => void;
|
||||||
setEditMode: (editMode: boolean) => void;
|
setEditMode: (editMode: boolean) => void;
|
||||||
saveConfig: (newConfig: LovelaceConfig) => Promise<void>;
|
saveConfig: (newConfig: LovelaceConfig) => Promise<void>;
|
||||||
|
@ -214,7 +214,7 @@ class HaPanelMailbox extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
|||||||
for (let i = 0; i < arrayLength; i++) {
|
for (let i = 0; i < arrayLength; i++) {
|
||||||
const datetime = formatDateTime(
|
const datetime = formatDateTime(
|
||||||
new Date(values[i].info.origtime * 1000),
|
new Date(values[i].info.origtime * 1000),
|
||||||
this.hass.language
|
this.hass.locale
|
||||||
);
|
);
|
||||||
platformItems.push({
|
platformItems.push({
|
||||||
timestamp: datetime,
|
timestamp: datetime,
|
||||||
|
@ -50,6 +50,7 @@ class AdvancedModeRow extends LitElement {
|
|||||||
|
|
||||||
private async _advancedToggled(ev) {
|
private async _advancedToggled(ev) {
|
||||||
getOptimisticFrontendUserDataCollection(this.hass.connection, "core").save({
|
getOptimisticFrontendUserDataCollection(this.hass.connection, "core").save({
|
||||||
|
...this.coreUserData,
|
||||||
showAdvanced: ev.currentTarget.checked,
|
showAdvanced: ev.currentTarget.checked,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,7 @@ import "./ha-long-lived-access-tokens-card";
|
|||||||
import "./ha-mfa-modules-card";
|
import "./ha-mfa-modules-card";
|
||||||
import "./ha-pick-dashboard-row";
|
import "./ha-pick-dashboard-row";
|
||||||
import "./ha-pick-language-row";
|
import "./ha-pick-language-row";
|
||||||
|
import "./ha-pick-number-format-row";
|
||||||
import "./ha-pick-theme-row";
|
import "./ha-pick-theme-row";
|
||||||
import "./ha-push-notifications-row";
|
import "./ha-push-notifications-row";
|
||||||
import "./ha-refresh-tokens-card";
|
import "./ha-refresh-tokens-card";
|
||||||
@ -100,6 +101,10 @@ class HaPanelProfile extends LitElement {
|
|||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
></ha-pick-language-row>
|
></ha-pick-language-row>
|
||||||
|
<ha-pick-number-format-row
|
||||||
|
.narrow=${this.narrow}
|
||||||
|
.hass=${this.hass}
|
||||||
|
></ha-pick-number-format-row>
|
||||||
<ha-pick-theme-row
|
<ha-pick-theme-row
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
|
@ -96,14 +96,14 @@ class HaPickLanguageRow extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
|||||||
// Only fire event if language was changed. This prevents select updates when
|
// Only fire event if language was changed. This prevents select updates when
|
||||||
// responding to hass changes.
|
// responding to hass changes.
|
||||||
if (newVal !== this.hass.language) {
|
if (newVal !== this.hass.language) {
|
||||||
this.fire("hass-language-select", { language: newVal });
|
this.fire("hass-language-select", newVal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ready() {
|
ready() {
|
||||||
super.ready();
|
super.ready();
|
||||||
if (this.hass && this.hass.language) {
|
if (this.hass && this.hass.locale && this.hass.locale.language) {
|
||||||
this.setLanguageSelection(this.hass.language);
|
this.setLanguageSelection(this.hass.locale.language);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
81
src/panels/profile/ha-pick-number-format-row.ts
Normal file
81
src/panels/profile/ha-pick-number-format-row.ts
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import "@polymer/paper-item/paper-item";
|
||||||
|
import "@polymer/paper-listbox/paper-listbox";
|
||||||
|
import {
|
||||||
|
customElement,
|
||||||
|
html,
|
||||||
|
LitElement,
|
||||||
|
property,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit-element";
|
||||||
|
import "../../components/ha-card";
|
||||||
|
import "../../components/ha-paper-dropdown-menu";
|
||||||
|
import { HomeAssistant } from "../../types";
|
||||||
|
import "../../components/ha-settings-row";
|
||||||
|
import { formatNumber } from "../../common/string/format_number";
|
||||||
|
import { NumberFormat } from "../../data/translation";
|
||||||
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
|
|
||||||
|
@customElement("ha-pick-number-format-row")
|
||||||
|
class NumberFormatRow extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property() public narrow!: boolean;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<ha-settings-row .narrow=${this.narrow}>
|
||||||
|
<span slot="heading">
|
||||||
|
${this.hass.localize("ui.panel.profile.number_format.header")}
|
||||||
|
</span>
|
||||||
|
<span slot="description">
|
||||||
|
${this.hass.localize("ui.panel.profile.number_format.description")}
|
||||||
|
</span>
|
||||||
|
<ha-paper-dropdown-menu
|
||||||
|
label=${this.hass.localize(
|
||||||
|
"ui.panel.profile.number_format.dropdown_label"
|
||||||
|
)}
|
||||||
|
dynamic-align
|
||||||
|
.disabled=${this.hass.locale === undefined}
|
||||||
|
>
|
||||||
|
<paper-listbox
|
||||||
|
slot="dropdown-content"
|
||||||
|
.selected=${this.hass.locale.number_format}
|
||||||
|
@iron-select=${this._handleFormatSelection}
|
||||||
|
attr-for-selected="format"
|
||||||
|
>
|
||||||
|
${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`
|
||||||
|
<paper-item .format=${format}>
|
||||||
|
<paper-item-body ?two-line=${twoLine}>
|
||||||
|
<div>${value}</div>
|
||||||
|
${twoLine
|
||||||
|
? html`<div secondary>${formattedNumber}</div>`
|
||||||
|
: ""}
|
||||||
|
</paper-item-body>
|
||||||
|
</paper-item>
|
||||||
|
`;
|
||||||
|
})}
|
||||||
|
</paper-listbox>
|
||||||
|
</ha-paper-dropdown-menu>
|
||||||
|
</ha-settings-row>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -58,11 +58,7 @@ class StateCardConfigurator extends LocalizeMixin(PolymerElement) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_localizeState(stateObj) {
|
_localizeState(stateObj) {
|
||||||
return computeStateDisplay(
|
return computeStateDisplay(this.hass.localize, stateObj, this.hass.locale);
|
||||||
this.hass.localize,
|
|
||||||
stateObj,
|
|
||||||
this.hass.language
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
customElements.define("state-card-configurator", StateCardConfigurator);
|
customElements.define("state-card-configurator", StateCardConfigurator);
|
||||||
|
@ -56,7 +56,7 @@ export class StateCardDisplay extends LitElement {
|
|||||||
: computeStateDisplay(
|
: computeStateDisplay(
|
||||||
this.hass!.localize,
|
this.hass!.localize,
|
||||||
this.stateObj,
|
this.stateObj,
|
||||||
this.hass.language
|
this.hass.locale
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -86,7 +86,7 @@ class StateCardMediaPlayer extends LocalizeMixin(PolymerElement) {
|
|||||||
computePrimaryText(localize, playerObj) {
|
computePrimaryText(localize, playerObj) {
|
||||||
return (
|
return (
|
||||||
playerObj.primaryTitle ||
|
playerObj.primaryTitle ||
|
||||||
computeStateDisplay(localize, playerObj.stateObj, this.hass.language)
|
computeStateDisplay(localize, playerObj.stateObj, this.hass.locale)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ import { broadcastConnectionStatus } from "../data/connection-status";
|
|||||||
import { subscribeFrontendUserData } from "../data/frontend";
|
import { subscribeFrontendUserData } from "../data/frontend";
|
||||||
import { forwardHaptic } from "../data/haptics";
|
import { forwardHaptic } from "../data/haptics";
|
||||||
import { DEFAULT_PANEL } from "../data/panel";
|
import { DEFAULT_PANEL } from "../data/panel";
|
||||||
|
import { NumberFormat } from "../data/translation";
|
||||||
import { subscribePanels } from "../data/ws-panels";
|
import { subscribePanels } from "../data/ws-panels";
|
||||||
import { translationMetadata } from "../resources/translations-metadata";
|
import { translationMetadata } from "../resources/translations-metadata";
|
||||||
import { Constructor, ServiceCallResponse } from "../types";
|
import { Constructor, ServiceCallResponse } from "../types";
|
||||||
@ -27,6 +28,8 @@ export const connectionMixin = <T extends Constructor<HassBaseEl>>(
|
|||||||
) =>
|
) =>
|
||||||
class extends superClass {
|
class extends superClass {
|
||||||
protected initializeHass(auth: Auth, conn: Connection) {
|
protected initializeHass(auth: Auth, conn: Connection) {
|
||||||
|
const language = getLocalLanguage();
|
||||||
|
|
||||||
this.hass = {
|
this.hass = {
|
||||||
auth,
|
auth,
|
||||||
connection: conn,
|
connection: conn,
|
||||||
@ -39,8 +42,12 @@ export const connectionMixin = <T extends Constructor<HassBaseEl>>(
|
|||||||
user: null as any,
|
user: null as any,
|
||||||
panelUrl: (this as any)._panelUrl,
|
panelUrl: (this as any)._panelUrl,
|
||||||
defaultPanel: DEFAULT_PANEL,
|
defaultPanel: DEFAULT_PANEL,
|
||||||
language: getLocalLanguage(),
|
language,
|
||||||
selectedLanguage: null,
|
selectedLanguage: null,
|
||||||
|
locale: {
|
||||||
|
language,
|
||||||
|
number_format: NumberFormat.language,
|
||||||
|
},
|
||||||
resources: null as any,
|
resources: null as any,
|
||||||
localize: () => "",
|
localize: () => "",
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import { debounce } from "../common/util/debounce";
|
|||||||
import {
|
import {
|
||||||
getHassTranslations,
|
getHassTranslations,
|
||||||
getHassTranslationsPre109,
|
getHassTranslationsPre109,
|
||||||
|
NumberFormat,
|
||||||
saveTranslationPreferences,
|
saveTranslationPreferences,
|
||||||
TranslationCategory,
|
TranslationCategory,
|
||||||
} from "../data/translation";
|
} from "../data/translation";
|
||||||
@ -14,10 +15,22 @@ import { storeState } from "../util/ha-pref-storage";
|
|||||||
import {
|
import {
|
||||||
getTranslation,
|
getTranslation,
|
||||||
getLocalLanguage,
|
getLocalLanguage,
|
||||||
getUserLanguage,
|
getUserLocale,
|
||||||
} from "../util/hass-translation";
|
} from "../util/hass-translation";
|
||||||
import { HassBaseEl } from "./hass-base-mixin";
|
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 {
|
interface LoadedTranslationCategory {
|
||||||
// individual integrations loaded for this category
|
// individual integrations loaded for this category
|
||||||
integrations: string[];
|
integrations: string[];
|
||||||
@ -45,9 +58,12 @@ export default <T extends Constructor<HassBaseEl>>(superClass: T) =>
|
|||||||
|
|
||||||
protected firstUpdated(changedProps) {
|
protected firstUpdated(changedProps) {
|
||||||
super.firstUpdated(changedProps);
|
super.firstUpdated(changedProps);
|
||||||
this.addEventListener("hass-language-select", (e) =>
|
this.addEventListener("hass-language-select", (e) => {
|
||||||
this._selectLanguage((e as CustomEvent).detail.language, true)
|
this._selectLanguage((e as CustomEvent).detail, true);
|
||||||
);
|
});
|
||||||
|
this.addEventListener("hass-number-format-select", (e) => {
|
||||||
|
this._selectNumberFormat((e as CustomEvent).detail, true);
|
||||||
|
});
|
||||||
this._loadCoreTranslations(getLocalLanguage());
|
this._loadCoreTranslations(getLocalLanguage());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,20 +72,31 @@ export default <T extends Constructor<HassBaseEl>>(superClass: T) =>
|
|||||||
if (!changedProps.has("hass")) {
|
if (!changedProps.has("hass")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const oldHass = changedProps.get("hass");
|
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
|
||||||
if (this.hass?.panels && oldHass.panels !== this.hass.panels) {
|
if (
|
||||||
|
this.hass?.panels &&
|
||||||
|
(!oldHass || oldHass.panels !== this.hass.panels)
|
||||||
|
) {
|
||||||
this._loadFragmentTranslations(this.hass.language, this.hass.panelUrl);
|
this._loadFragmentTranslations(this.hass.language, this.hass.panelUrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected hassConnected() {
|
protected hassConnected() {
|
||||||
super.hassConnected();
|
super.hassConnected();
|
||||||
getUserLanguage(this.hass!).then((language) => {
|
getUserLocale(this.hass!).then((locale) => {
|
||||||
if (language && this.hass!.language !== language) {
|
if (locale?.language && this.hass!.language !== locale.language) {
|
||||||
// We just get language from backend, no need to save back
|
// We just got language from backend, no need to save back
|
||||||
this._selectLanguage(language, false);
|
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(
|
this.hass!.connection.subscribeEvents(
|
||||||
debounce(() => {
|
debounce(() => {
|
||||||
this._refetchCachedHassTranslations(false, false);
|
this._refetchCachedHassTranslations(false, false);
|
||||||
@ -94,6 +121,18 @@ export default <T extends Constructor<HassBaseEl>>(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) {
|
private _selectLanguage(language: string, saveToBackend: boolean) {
|
||||||
if (!this.hass) {
|
if (!this.hass) {
|
||||||
// should not happen, do it to avoid use this.hass!
|
// should not happen, do it to avoid use this.hass!
|
||||||
@ -101,10 +140,14 @@ export default <T extends Constructor<HassBaseEl>>(superClass: T) =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// update selectedLanguage so that it can be saved to local storage
|
// 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);
|
storeState(this.hass);
|
||||||
if (saveToBackend) {
|
if (saveToBackend) {
|
||||||
saveTranslationPreferences(this.hass, { language });
|
saveTranslationPreferences(this.hass, this.hass.locale);
|
||||||
}
|
}
|
||||||
this._applyTranslations(this.hass);
|
this._applyTranslations(this.hass);
|
||||||
this._refetchCachedHassTranslations(true, true);
|
this._refetchCachedHassTranslations(true, true);
|
||||||
|
@ -3110,6 +3110,19 @@
|
|||||||
"link_promo": "Help translating",
|
"link_promo": "Help translating",
|
||||||
"dropdown_label": "Language"
|
"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": {
|
"themes": {
|
||||||
"header": "Theme",
|
"header": "Theme",
|
||||||
"error_no_theme": "No themes available.",
|
"error_no_theme": "No themes available.",
|
||||||
|
@ -9,7 +9,10 @@ import {
|
|||||||
} from "home-assistant-js-websocket";
|
} from "home-assistant-js-websocket";
|
||||||
import { LocalizeFunc } from "./common/translations/localize";
|
import { LocalizeFunc } from "./common/translations/localize";
|
||||||
import { CoreFrontendUserData } from "./data/frontend";
|
import { CoreFrontendUserData } from "./data/frontend";
|
||||||
import { getHassTranslations } from "./data/translation";
|
import {
|
||||||
|
FrontendTranslationData,
|
||||||
|
getHassTranslations,
|
||||||
|
} from "./data/translation";
|
||||||
import { Themes } from "./data/ws-themes";
|
import { Themes } from "./data/ws-themes";
|
||||||
import { ExternalMessaging } from "./external_app/external_messaging";
|
import { ExternalMessaging } from "./external_app/external_messaging";
|
||||||
|
|
||||||
@ -193,9 +196,8 @@ export interface HomeAssistant {
|
|||||||
selectedTheme?: ThemeSettings | null;
|
selectedTheme?: ThemeSettings | null;
|
||||||
panels: Panels;
|
panels: Panels;
|
||||||
panelUrl: string;
|
panelUrl: string;
|
||||||
|
|
||||||
// i18n
|
// i18n
|
||||||
// current effective language, in that order:
|
// current effective language in that order:
|
||||||
// - backend saved user selected lanugage
|
// - backend saved user selected lanugage
|
||||||
// - language in local appstorage
|
// - language in local appstorage
|
||||||
// - browser language
|
// - browser language
|
||||||
@ -203,6 +205,7 @@ export interface HomeAssistant {
|
|||||||
language: string;
|
language: string;
|
||||||
// local stored language, keep that name for backward compability
|
// local stored language, keep that name for backward compability
|
||||||
selectedLanguage: string | null;
|
selectedLanguage: string | null;
|
||||||
|
locale: FrontendTranslationData;
|
||||||
resources: Resources;
|
resources: Resources;
|
||||||
localize: LocalizeFunc;
|
localize: LocalizeFunc;
|
||||||
translationMetadata: TranslationMetadata;
|
translationMetadata: TranslationMetadata;
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
import { fetchTranslationPreferences } from "../data/translation";
|
import {
|
||||||
|
fetchTranslationPreferences,
|
||||||
|
FrontendTranslationData,
|
||||||
|
} from "../data/translation";
|
||||||
import { translationMetadata } from "../resources/translations-metadata";
|
import { translationMetadata } from "../resources/translations-metadata";
|
||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
import { getTranslation as commonGetTranslation } from "./common-translation";
|
import { getTranslation as commonGetTranslation } from "./common-translation";
|
||||||
@ -19,7 +22,7 @@ const LOCALE_LOOKUP = {
|
|||||||
/**
|
/**
|
||||||
* Search for a matching translation from most specific to general
|
* 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
|
// In most case, the language has the same format with our translation meta data
|
||||||
if (language in translationMetadata.translations) {
|
if (language in translationMetadata.translations) {
|
||||||
return language;
|
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<Partial<FrontendTranslationData>> {
|
||||||
const result = await fetchTranslationPreferences(hass);
|
const result = await fetchTranslationPreferences(hass);
|
||||||
const language = result ? result.language : null;
|
const language = result?.language;
|
||||||
|
const number_format = result?.number_format;
|
||||||
if (language) {
|
if (language) {
|
||||||
const availableLanguage = findAvailableLanguage(language);
|
const availableLanguage = findAvailableLanguage(language);
|
||||||
if (availableLanguage) {
|
if (availableLanguage) {
|
||||||
return availableLanguage;
|
return {
|
||||||
|
language: availableLanguage,
|
||||||
|
number_format,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return {
|
||||||
|
number_format,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,11 +1,18 @@
|
|||||||
import { assert } from "chai";
|
import { assert } from "chai";
|
||||||
|
|
||||||
import { formatDate } from "../../../src/common/datetime/format_date";
|
import { formatDate } from "../../../src/common/datetime/format_date";
|
||||||
|
import { NumberFormat } from "../../../src/data/translation";
|
||||||
|
|
||||||
describe("formatDate", () => {
|
describe("formatDate", () => {
|
||||||
const dateObj = new Date(2017, 10, 18, 11, 12, 13, 1400);
|
const dateObj = new Date(2017, 10, 18, 11, 12, 13, 1400);
|
||||||
|
|
||||||
it("Formats English dates", () => {
|
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"
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -4,13 +4,17 @@ import {
|
|||||||
formatDateTime,
|
formatDateTime,
|
||||||
formatDateTimeWithSeconds,
|
formatDateTimeWithSeconds,
|
||||||
} from "../../../src/common/datetime/format_date_time";
|
} from "../../../src/common/datetime/format_date_time";
|
||||||
|
import { NumberFormat } from "../../../src/data/translation";
|
||||||
|
|
||||||
describe("formatDateTime", () => {
|
describe("formatDateTime", () => {
|
||||||
const dateObj = new Date(2017, 10, 18, 11, 12, 13, 400);
|
const dateObj = new Date(2017, 10, 18, 11, 12, 13, 400);
|
||||||
|
|
||||||
it("Formats English date times", () => {
|
it("Formats English date times", () => {
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
formatDateTime(dateObj, "en"),
|
formatDateTime(dateObj, {
|
||||||
|
language: "en",
|
||||||
|
number_format: NumberFormat.language,
|
||||||
|
}),
|
||||||
"November 18, 2017, 11:12 AM"
|
"November 18, 2017, 11:12 AM"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -21,7 +25,10 @@ describe("formatDateTimeWithSeconds", () => {
|
|||||||
|
|
||||||
it("Formats English date times with seconds", () => {
|
it("Formats English date times with seconds", () => {
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
formatDateTimeWithSeconds(dateObj, "en"),
|
formatDateTimeWithSeconds(dateObj, {
|
||||||
|
language: "en",
|
||||||
|
number_format: NumberFormat.language,
|
||||||
|
}),
|
||||||
"November 18, 2017, 11:12:13 AM"
|
"November 18, 2017, 11:12:13 AM"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -4,12 +4,19 @@ import {
|
|||||||
formatTime,
|
formatTime,
|
||||||
formatTimeWithSeconds,
|
formatTimeWithSeconds,
|
||||||
} from "../../../src/common/datetime/format_time";
|
} from "../../../src/common/datetime/format_time";
|
||||||
|
import { NumberFormat } from "../../../src/data/translation";
|
||||||
|
|
||||||
describe("formatTime", () => {
|
describe("formatTime", () => {
|
||||||
const dateObj = new Date(2017, 10, 18, 11, 12, 13, 1400);
|
const dateObj = new Date(2017, 10, 18, 11, 12, 13, 1400);
|
||||||
|
|
||||||
it("Formats English times", () => {
|
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);
|
const dateObj = new Date(2017, 10, 18, 11, 12, 13, 400);
|
||||||
|
|
||||||
it("Formats English times with seconds", () => {
|
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"
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,15 @@
|
|||||||
import { assert } from "chai";
|
import { assert } from "chai";
|
||||||
import { computeStateDisplay } from "../../../src/common/entity/compute_state_display";
|
import { computeStateDisplay } from "../../../src/common/entity/compute_state_display";
|
||||||
import { UNKNOWN } from "../../../src/data/entity";
|
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", () => {
|
describe("computeStateDisplay", () => {
|
||||||
// Mock Localize function for testing
|
// Mock Localize function for testing
|
||||||
@ -14,7 +23,7 @@ describe("computeStateDisplay", () => {
|
|||||||
attributes: {},
|
attributes: {},
|
||||||
};
|
};
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
computeStateDisplay(localize, stateObj, "en"),
|
computeStateDisplay(localize, stateObj, localeData),
|
||||||
"component.binary_sensor.state._.off"
|
"component.binary_sensor.state._.off"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -28,7 +37,7 @@ describe("computeStateDisplay", () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
computeStateDisplay(localize, stateObj, "en"),
|
computeStateDisplay(localize, stateObj, localeData),
|
||||||
"component.binary_sensor.state.moisture.off"
|
"component.binary_sensor.state.moisture.off"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -48,7 +57,7 @@ describe("computeStateDisplay", () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
computeStateDisplay(altLocalize, stateObj, "en"),
|
computeStateDisplay(altLocalize, stateObj, localeData),
|
||||||
"component.binary_sensor.state.invalid_device_class.off"
|
"component.binary_sensor.state.invalid_device_class.off"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -61,7 +70,10 @@ describe("computeStateDisplay", () => {
|
|||||||
unit_of_measurement: "m",
|
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", () => {
|
it("Localizes and formats numeric sensor value with units", () => {
|
||||||
@ -73,7 +85,7 @@ describe("computeStateDisplay", () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
computeStateDisplay(localize, stateObj, "en"),
|
computeStateDisplay(localize, stateObj, localeData),
|
||||||
"1,234.5 m"
|
"1,234.5 m"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -93,7 +105,7 @@ describe("computeStateDisplay", () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
computeStateDisplay(altLocalize, stateObj, "en"),
|
computeStateDisplay(altLocalize, stateObj, localeData),
|
||||||
"state.default.unknown"
|
"state.default.unknown"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -113,7 +125,7 @@ describe("computeStateDisplay", () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
computeStateDisplay(altLocalize, stateObj, "en"),
|
computeStateDisplay(altLocalize, stateObj, localeData),
|
||||||
"state.default.unavailable"
|
"state.default.unavailable"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -131,7 +143,7 @@ describe("computeStateDisplay", () => {
|
|||||||
attributes: {},
|
attributes: {},
|
||||||
};
|
};
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
computeStateDisplay(altLocalize, stateObj, "en"),
|
computeStateDisplay(altLocalize, stateObj, localeData),
|
||||||
"component.sensor.state._.custom_state"
|
"component.sensor.state._.custom_state"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -152,7 +164,7 @@ describe("computeStateDisplay", () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
computeStateDisplay(localize, stateObj, "en"),
|
computeStateDisplay(localize, stateObj, localeData),
|
||||||
"November 18, 2017, 11:12 AM"
|
"November 18, 2017, 11:12 AM"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -173,7 +185,7 @@ describe("computeStateDisplay", () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
computeStateDisplay(localize, stateObj, "en"),
|
computeStateDisplay(localize, stateObj, localeData),
|
||||||
"November 18, 2017"
|
"November 18, 2017"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -194,7 +206,7 @@ describe("computeStateDisplay", () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
computeStateDisplay(localize, stateObj, "en"),
|
computeStateDisplay(localize, stateObj, localeData),
|
||||||
"11:12 AM"
|
"11:12 AM"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -212,7 +224,7 @@ describe("computeStateDisplay", () => {
|
|||||||
attributes: {},
|
attributes: {},
|
||||||
};
|
};
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
computeStateDisplay(altLocalize, stateObj, "en"),
|
computeStateDisplay(altLocalize, stateObj, localeData),
|
||||||
"state.default.unavailable"
|
"state.default.unavailable"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -228,7 +240,7 @@ describe("computeStateDisplay", () => {
|
|||||||
attributes: {},
|
attributes: {},
|
||||||
};
|
};
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
computeStateDisplay(altLocalize, stateObj, "en"),
|
computeStateDisplay(altLocalize, stateObj, localeData),
|
||||||
"My Custom State"
|
"My Custom State"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
33
test-mocha/common/string/format_number.ts
Normal file
33
test-mocha/common/string/format_number.ts
Normal file
@ -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"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user