Use browser default time and number formatting with polyfills if needed (#9481)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
This commit is contained in:
parent
d7f00df391
commit
1bccbd4173
|
@ -12,7 +12,7 @@ on:
|
|||
|
||||
env:
|
||||
NODE_VERSION: 14
|
||||
NODE_OPTIONS: --max_old_space_size=4096
|
||||
NODE_OPTIONS: --max_old_space_size=6144
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
|
@ -53,8 +53,8 @@ jobs:
|
|||
run: yarn install
|
||||
env:
|
||||
CI: true
|
||||
- name: Run Mocha
|
||||
run: yarn run mocha
|
||||
- name: Run Tests
|
||||
run: yarn run test
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [lint, test]
|
||||
|
|
|
@ -7,7 +7,7 @@ on:
|
|||
|
||||
env:
|
||||
NODE_VERSION: 14
|
||||
NODE_OPTIONS: --max_old_space_size=4096
|
||||
NODE_OPTIONS: --max_old_space_size=6144
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
|
|
|
@ -8,7 +8,7 @@ on:
|
|||
env:
|
||||
PYTHON_VERSION: 3.8
|
||||
NODE_VERSION: 14
|
||||
NODE_OPTIONS: --max_old_space_size=4096
|
||||
NODE_OPTIONS: --max_old_space_size=6144
|
||||
|
||||
jobs:
|
||||
release:
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
module.exports = {
|
||||
require: "test-mocha/testconf.js",
|
||||
timeout: 10000,
|
||||
};
|
|
@ -82,6 +82,7 @@ module.exports.babelOptions = ({ latestBuild }) => ({
|
|||
// Only support the syntax, Webpack will handle it.
|
||||
"@babel/plugin-syntax-import-meta",
|
||||
"@babel/plugin-syntax-dynamic-import",
|
||||
"@babel/plugin-syntax-top-level-await",
|
||||
"@babel/plugin-proposal-optional-chaining",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator",
|
||||
["@babel/plugin-proposal-decorators", { decoratorsBeforeExport: true }],
|
||||
|
|
|
@ -6,6 +6,7 @@ const { WebpackManifestPlugin } = require("webpack-manifest-plugin");
|
|||
const paths = require("./paths.js");
|
||||
const bundle = require("./bundle.js");
|
||||
const log = require("fancy-log");
|
||||
const WebpackBar = require("webpackbar");
|
||||
|
||||
class LogStartCompilePlugin {
|
||||
ignoredFirst = false;
|
||||
|
@ -74,6 +75,7 @@ const createWebpackConfig = ({
|
|||
chunkIds: isProdBuild && !isStatsBuild ? "deterministic" : "named",
|
||||
},
|
||||
plugins: [
|
||||
new WebpackBar({ fancy: !isProdBuild }),
|
||||
new WebpackManifestPlugin({
|
||||
// Only include the JS of entrypoints
|
||||
filter: (file) => file.isInitial && !file.name.endsWith(".map"),
|
||||
|
@ -149,6 +151,9 @@ const createWebpackConfig = ({
|
|||
// To silence warning in worker plugin
|
||||
globalObject: "self",
|
||||
},
|
||||
experiments: {
|
||||
topLevelAwait: true,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ import { customElement, property, query, state } from "lit/decorators";
|
|||
import { classMap } from "lit/directives/class-map";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { atLeastVersion } from "../../../src/common/config/version";
|
||||
import relativeTime from "../../../src/common/datetime/relative_time";
|
||||
import { relativeTime } from "../../../src/common/datetime/relative_time";
|
||||
import { HASSDomEvent } from "../../../src/common/dom/fire_event";
|
||||
import {
|
||||
DataTableColumnContainer,
|
||||
|
@ -133,7 +133,7 @@ export class HassioBackups extends LitElement {
|
|||
filterable: true,
|
||||
sortable: true,
|
||||
template: (entry: string) =>
|
||||
relativeTime(new Date(entry), this.hass.localize),
|
||||
relativeTime(new Date(entry), this.hass.locale),
|
||||
},
|
||||
secondary: {
|
||||
title: "",
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
[build.environment]
|
||||
YARN_VERSION = "1.22.11"
|
||||
NODE_OPTIONS = "--max_old_space_size=4096"
|
||||
NODE_OPTIONS = "--max_old_space_size=6144"
|
||||
|
|
26
package.json
26
package.json
|
@ -16,8 +16,7 @@
|
|||
"lint:lit": "lit-analyzer \"**/src/**/*.ts\" --format markdown --outFile result.md",
|
||||
"lint": "yarn run lint:eslint && yarn run lint:prettier && yarn run lint:types",
|
||||
"format": "yarn run format:eslint && yarn run format:prettier",
|
||||
"mocha": "ts-mocha -p test-mocha/tsconfig.test.json \"test-mocha/**/*.ts\"",
|
||||
"test": "yarn run mocha"
|
||||
"test": "instant-mocha --webpack-config ./test/webpack.config.js --require ./test/setup.js \"test/**/*.ts\""
|
||||
},
|
||||
"author": "Paulus Schoutsen <Paulus@PaulusSchoutsen.nl> (http://paulusschoutsen.nl)",
|
||||
"license": "Apache-2.0",
|
||||
|
@ -34,9 +33,13 @@
|
|||
"@codemirror/stream-parser": "^0.19.1",
|
||||
"@codemirror/text": "^0.19.2",
|
||||
"@codemirror/view": "^0.19.4",
|
||||
"@formatjs/intl-datetimeformat": "^4.2.4",
|
||||
"@formatjs/intl-getcanonicallocales": "^1.7.3",
|
||||
"@formatjs/intl-locale": "^2.4.37",
|
||||
"@formatjs/intl-pluralrules": "^4.1.3",
|
||||
"@formatjs/intl-locale": "^2.4.38",
|
||||
"@formatjs/intl-numberformat": "^7.2.4",
|
||||
"@formatjs/intl-pluralrules": "^4.1.4",
|
||||
"@formatjs/intl-relativetimeformat": "^9.3.1",
|
||||
"@formatjs/intl-utils": "^3.8.4",
|
||||
"@fullcalendar/common": "5.9.0",
|
||||
"@fullcalendar/core": "5.9.0",
|
||||
"@fullcalendar/daygrid": "5.9.0",
|
||||
|
@ -103,7 +106,6 @@
|
|||
"date-fns": "^2.23.0",
|
||||
"deep-clone-simple": "^1.1.1",
|
||||
"deep-freeze": "^0.0.1",
|
||||
"fecha": "^4.2.0",
|
||||
"fuse.js": "^6.0.0",
|
||||
"google-timezones-json": "^1.0.2",
|
||||
"hls.js": "^1.0.10",
|
||||
|
@ -152,6 +154,7 @@
|
|||
"@babel/plugin-proposal-optional-chaining": "^7.14.5",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
||||
"@babel/plugin-syntax-import-meta": "^7.10.4",
|
||||
"@babel/plugin-syntax-top-level-await": "^7.14.5",
|
||||
"@babel/preset-env": "^7.14.7",
|
||||
"@babel/preset-typescript": "^7.14.5",
|
||||
"@koa/cors": "^3.1.0",
|
||||
|
@ -197,6 +200,7 @@
|
|||
"gulp-zopfli-green": "^3.0.1",
|
||||
"html-minifier": "^4.0.0",
|
||||
"husky": "^1.3.1",
|
||||
"instant-mocha": "^1.3.1",
|
||||
"lint-staged": "^11.0.1",
|
||||
"lit-analyzer": "^1.2.1",
|
||||
"lodash.template": "^4.5.0",
|
||||
|
@ -216,16 +220,16 @@
|
|||
"sinon": "^11.0.0",
|
||||
"source-map-url": "^0.4.0",
|
||||
"systemjs": "^6.3.2",
|
||||
"terser-webpack-plugin": "^5.1.4",
|
||||
"terser-webpack-plugin": "^5.2.4",
|
||||
"ts-lit-plugin": "^1.2.1",
|
||||
"ts-mocha": "^8.0.0",
|
||||
"typescript": "^4.3.5",
|
||||
"vinyl-buffer": "^1.0.1",
|
||||
"vinyl-source-stream": "^2.0.0",
|
||||
"webpack": "^5.43.0",
|
||||
"webpack-cli": "^4.7.2",
|
||||
"webpack-dev-server": "^3.11.2",
|
||||
"webpack-manifest-plugin": "^3.1.1",
|
||||
"webpack": "^5.55.1",
|
||||
"webpack-cli": "^4.8.0",
|
||||
"webpack-dev-server": "^4.3.0",
|
||||
"webpack-manifest-plugin": "^4.0.2",
|
||||
"webpackbar": "^5.0.0-3",
|
||||
"workbox-build": "^6.1.5"
|
||||
},
|
||||
"_comment": "Polymer 3.2 contained a bug, fixed in https://github.com/Polymer/polymer/pull/5569, add as patch",
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
// Check for support of native locale string options
|
||||
function checkToLocaleDateStringSupportsOptions() {
|
||||
try {
|
||||
new Date().toLocaleDateString("i");
|
||||
} catch (e) {
|
||||
return e.name === "RangeError";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function checkToLocaleTimeStringSupportsOptions() {
|
||||
try {
|
||||
new Date().toLocaleTimeString("i");
|
||||
} catch (e) {
|
||||
return e.name === "RangeError";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function checkToLocaleStringSupportsOptions() {
|
||||
try {
|
||||
new Date().toLocaleString("i");
|
||||
} catch (e) {
|
||||
return e.name === "RangeError";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export const toLocaleDateStringSupportsOptions =
|
||||
checkToLocaleDateStringSupportsOptions();
|
||||
export const toLocaleTimeStringSupportsOptions =
|
||||
checkToLocaleTimeStringSupportsOptions();
|
||||
export const toLocaleStringSupportsOptions =
|
||||
checkToLocaleStringSupportsOptions();
|
|
@ -1,13 +1,15 @@
|
|||
import { format } from "fecha";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { FrontendLocaleData } from "../../data/translation";
|
||||
import { toLocaleDateStringSupportsOptions } from "./check_options_support";
|
||||
import { polyfillsLoaded } from "../translations/localize";
|
||||
|
||||
if (__BUILD__ === "latest" && polyfillsLoaded) {
|
||||
await polyfillsLoaded;
|
||||
}
|
||||
|
||||
// Tuesday, August 10
|
||||
export const formatDateWeekday = toLocaleDateStringSupportsOptions
|
||||
? (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
formatDateWeekdayMem(locale).format(dateObj)
|
||||
: (dateObj: Date) => format(dateObj, "dddd, MMMM D");
|
||||
export const formatDateWeekday = (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
formatDateWeekdayMem(locale).format(dateObj);
|
||||
|
||||
const formatDateWeekdayMem = memoizeOne(
|
||||
(locale: FrontendLocaleData) =>
|
||||
new Intl.DateTimeFormat(locale.language, {
|
||||
|
@ -18,10 +20,9 @@ const formatDateWeekdayMem = memoizeOne(
|
|||
);
|
||||
|
||||
// August 10, 2021
|
||||
export const formatDate = toLocaleDateStringSupportsOptions
|
||||
? (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
formatDateMem(locale).format(dateObj)
|
||||
: (dateObj: Date) => format(dateObj, "MMMM D, YYYY");
|
||||
export const formatDate = (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
formatDateMem(locale).format(dateObj);
|
||||
|
||||
const formatDateMem = memoizeOne(
|
||||
(locale: FrontendLocaleData) =>
|
||||
new Intl.DateTimeFormat(locale.language, {
|
||||
|
@ -32,10 +33,9 @@ const formatDateMem = memoizeOne(
|
|||
);
|
||||
|
||||
// 10/08/2021
|
||||
export const formatDateNumeric = toLocaleDateStringSupportsOptions
|
||||
? (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
formatDateNumericMem(locale).format(dateObj)
|
||||
: (dateObj: Date) => format(dateObj, "M/D/YYYY");
|
||||
export const formatDateNumeric = (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
formatDateNumericMem(locale).format(dateObj);
|
||||
|
||||
const formatDateNumericMem = memoizeOne(
|
||||
(locale: FrontendLocaleData) =>
|
||||
new Intl.DateTimeFormat(locale.language, {
|
||||
|
@ -46,10 +46,9 @@ const formatDateNumericMem = memoizeOne(
|
|||
);
|
||||
|
||||
// Aug 10
|
||||
export const formatDateShort = toLocaleDateStringSupportsOptions
|
||||
? (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
formatDateShortMem(locale).format(dateObj)
|
||||
: (dateObj: Date) => format(dateObj, "MMM D");
|
||||
export const formatDateShort = (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
formatDateShortMem(locale).format(dateObj);
|
||||
|
||||
const formatDateShortMem = memoizeOne(
|
||||
(locale: FrontendLocaleData) =>
|
||||
new Intl.DateTimeFormat(locale.language, {
|
||||
|
@ -59,10 +58,11 @@ const formatDateShortMem = memoizeOne(
|
|||
);
|
||||
|
||||
// August 2021
|
||||
export const formatDateMonthYear = toLocaleDateStringSupportsOptions
|
||||
? (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
formatDateMonthYearMem(locale).format(dateObj)
|
||||
: (dateObj: Date) => format(dateObj, "MMMM YYYY");
|
||||
export const formatDateMonthYear = (
|
||||
dateObj: Date,
|
||||
locale: FrontendLocaleData
|
||||
) => formatDateMonthYearMem(locale).format(dateObj);
|
||||
|
||||
const formatDateMonthYearMem = memoizeOne(
|
||||
(locale: FrontendLocaleData) =>
|
||||
new Intl.DateTimeFormat(locale.language, {
|
||||
|
@ -72,10 +72,9 @@ const formatDateMonthYearMem = memoizeOne(
|
|||
);
|
||||
|
||||
// August
|
||||
export const formatDateMonth = toLocaleDateStringSupportsOptions
|
||||
? (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
formatDateMonthMem(locale).format(dateObj)
|
||||
: (dateObj: Date) => format(dateObj, "MMMM");
|
||||
export const formatDateMonth = (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
formatDateMonthMem(locale).format(dateObj);
|
||||
|
||||
const formatDateMonthMem = memoizeOne(
|
||||
(locale: FrontendLocaleData) =>
|
||||
new Intl.DateTimeFormat(locale.language, {
|
||||
|
@ -84,10 +83,9 @@ const formatDateMonthMem = memoizeOne(
|
|||
);
|
||||
|
||||
// 2021
|
||||
export const formatDateYear = toLocaleDateStringSupportsOptions
|
||||
? (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
formatDateYearMem(locale).format(dateObj)
|
||||
: (dateObj: Date) => format(dateObj, "YYYY");
|
||||
export const formatDateYear = (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
formatDateYearMem(locale).format(dateObj);
|
||||
|
||||
const formatDateYearMem = memoizeOne(
|
||||
(locale: FrontendLocaleData) =>
|
||||
new Intl.DateTimeFormat(locale.language, {
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
import { format } from "fecha";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { FrontendLocaleData } from "../../data/translation";
|
||||
import { toLocaleStringSupportsOptions } from "./check_options_support";
|
||||
import { useAmPm } from "./use_am_pm";
|
||||
import { polyfillsLoaded } from "../translations/localize";
|
||||
|
||||
if (__BUILD__ === "latest" && polyfillsLoaded) {
|
||||
await polyfillsLoaded;
|
||||
}
|
||||
|
||||
// August 9, 2021, 8:23 AM
|
||||
export const formatDateTime = toLocaleStringSupportsOptions
|
||||
? (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
formatDateTimeMem(locale).format(dateObj)
|
||||
: (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
format(dateObj, "MMMM D, YYYY, HH:mm" + useAmPm(locale) ? " A" : "");
|
||||
export const formatDateTime = (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
formatDateTimeMem(locale).format(dateObj);
|
||||
|
||||
const formatDateTimeMem = memoizeOne(
|
||||
(locale: FrontendLocaleData) =>
|
||||
new Intl.DateTimeFormat(locale.language, {
|
||||
|
@ -23,11 +24,11 @@ const formatDateTimeMem = memoizeOne(
|
|||
);
|
||||
|
||||
// August 9, 2021, 8:23:15 AM
|
||||
export const formatDateTimeWithSeconds = toLocaleStringSupportsOptions
|
||||
? (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
formatDateTimeWithSecondsMem(locale).format(dateObj)
|
||||
: (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
format(dateObj, "MMMM D, YYYY, HH:mm:ss" + useAmPm(locale) ? " A" : "");
|
||||
export const formatDateTimeWithSeconds = (
|
||||
dateObj: Date,
|
||||
locale: FrontendLocaleData
|
||||
) => formatDateTimeWithSecondsMem(locale).format(dateObj);
|
||||
|
||||
const formatDateTimeWithSecondsMem = memoizeOne(
|
||||
(locale: FrontendLocaleData) =>
|
||||
new Intl.DateTimeFormat(locale.language, {
|
||||
|
@ -42,11 +43,11 @@ const formatDateTimeWithSecondsMem = memoizeOne(
|
|||
);
|
||||
|
||||
// 9/8/2021, 8:23 AM
|
||||
export const formatDateTimeNumeric = toLocaleStringSupportsOptions
|
||||
? (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
formatDateTimeNumericMem(locale).format(dateObj)
|
||||
: (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
format(dateObj, "M/D/YYYY, HH:mm" + useAmPm(locale) ? " A" : "");
|
||||
export const formatDateTimeNumeric = (
|
||||
dateObj: Date,
|
||||
locale: FrontendLocaleData
|
||||
) => formatDateTimeNumericMem(locale).format(dateObj);
|
||||
|
||||
const formatDateTimeNumericMem = memoizeOne(
|
||||
(locale: FrontendLocaleData) =>
|
||||
new Intl.DateTimeFormat(locale.language, {
|
||||
|
|
|
@ -1,30 +1,31 @@
|
|||
import { format } from "fecha";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { FrontendLocaleData } from "../../data/translation";
|
||||
import { toLocaleTimeStringSupportsOptions } from "./check_options_support";
|
||||
import { useAmPm } from "./use_am_pm";
|
||||
import { polyfillsLoaded } from "../translations/localize";
|
||||
|
||||
if (__BUILD__ === "latest" && polyfillsLoaded) {
|
||||
await polyfillsLoaded;
|
||||
}
|
||||
|
||||
// 9:15 PM || 21:15
|
||||
export const formatTime = toLocaleTimeStringSupportsOptions
|
||||
? (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
formatTimeMem(locale).format(dateObj)
|
||||
: (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
format(dateObj, "shortTime" + useAmPm(locale) ? " A" : "");
|
||||
export const formatTime = (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
formatTimeMem(locale).format(dateObj);
|
||||
|
||||
const formatTimeMem = memoizeOne(
|
||||
(locale: FrontendLocaleData) =>
|
||||
new Intl.DateTimeFormat(locale.language, {
|
||||
hour: useAmPm(locale) ? "numeric" : "2-digit",
|
||||
hour: "numeric",
|
||||
minute: "2-digit",
|
||||
hour12: useAmPm(locale),
|
||||
})
|
||||
);
|
||||
|
||||
// 9:15:24 PM || 21:15:24
|
||||
export const formatTimeWithSeconds = toLocaleTimeStringSupportsOptions
|
||||
? (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
formatTimeWithSecondsMem(locale).format(dateObj)
|
||||
: (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
format(dateObj, "mediumTime" + useAmPm(locale) ? " A" : "");
|
||||
export const formatTimeWithSeconds = (
|
||||
dateObj: Date,
|
||||
locale: FrontendLocaleData
|
||||
) => formatTimeWithSecondsMem(locale).format(dateObj);
|
||||
|
||||
const formatTimeWithSecondsMem = memoizeOne(
|
||||
(locale: FrontendLocaleData) =>
|
||||
new Intl.DateTimeFormat(locale.language, {
|
||||
|
@ -36,17 +37,15 @@ const formatTimeWithSecondsMem = memoizeOne(
|
|||
);
|
||||
|
||||
// Tuesday 7:00 PM || Tuesday 19:00
|
||||
export const formatTimeWeekday = toLocaleTimeStringSupportsOptions
|
||||
? (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
formatTimeWeekdayMem(locale).format(dateObj)
|
||||
: (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
format(dateObj, "dddd, HH:mm" + useAmPm(locale) ? " A" : "");
|
||||
export const formatTimeWeekday = (dateObj: Date, locale: FrontendLocaleData) =>
|
||||
formatTimeWeekdayMem(locale).format(dateObj);
|
||||
|
||||
const formatTimeWeekdayMem = memoizeOne(
|
||||
(locale: FrontendLocaleData) =>
|
||||
new Intl.DateTimeFormat(locale.language, {
|
||||
weekday: "long",
|
||||
hour: useAmPm(locale) ? "numeric" : "2-digit",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
hour12: useAmPm(locale),
|
||||
})
|
||||
);
|
||||
|
|
|
@ -1,48 +1,32 @@
|
|||
import { LocalizeFunc } from "../translations/localize";
|
||||
import { selectUnit } from "@formatjs/intl-utils";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { FrontendLocaleData } from "../../data/translation";
|
||||
import { polyfillsLoaded } from "../translations/localize";
|
||||
|
||||
/**
|
||||
* Calculate a string representing a date object as relative time from now.
|
||||
*
|
||||
* Example output: 5 minutes ago, in 3 days.
|
||||
*/
|
||||
const tests = [60, 60, 24, 7];
|
||||
const langKey = ["second", "minute", "hour", "day"];
|
||||
|
||||
export default function relativeTime(
|
||||
dateObj: Date,
|
||||
localize: LocalizeFunc,
|
||||
options: {
|
||||
compareTime?: Date;
|
||||
includeTense?: boolean;
|
||||
} = {}
|
||||
): string {
|
||||
const compareTime = options.compareTime || new Date();
|
||||
let delta = (compareTime.getTime() - dateObj.getTime()) / 1000;
|
||||
const tense = delta >= 0 ? "past" : "future";
|
||||
delta = Math.abs(delta);
|
||||
let roundedDelta = Math.round(delta);
|
||||
|
||||
if (roundedDelta === 0) {
|
||||
return localize("ui.components.relative_time.just_now");
|
||||
}
|
||||
|
||||
let unit = "week";
|
||||
|
||||
for (let i = 0; i < tests.length; i++) {
|
||||
if (roundedDelta < tests[i]) {
|
||||
unit = langKey[i];
|
||||
break;
|
||||
}
|
||||
|
||||
delta /= tests[i];
|
||||
roundedDelta = Math.round(delta);
|
||||
}
|
||||
|
||||
return localize(
|
||||
options.includeTense === false
|
||||
? `ui.components.relative_time.duration.${unit}`
|
||||
: `ui.components.relative_time.${tense}_duration.${unit}`,
|
||||
"count",
|
||||
roundedDelta
|
||||
);
|
||||
if (__BUILD__ === "latest" && polyfillsLoaded) {
|
||||
await polyfillsLoaded;
|
||||
}
|
||||
|
||||
const formatRelTimeMem = memoizeOne(
|
||||
(locale: FrontendLocaleData) =>
|
||||
// @ts-expect-error
|
||||
new Intl.RelativeTimeFormat(locale.language, { numeric: "auto" })
|
||||
);
|
||||
|
||||
export const relativeTime = (
|
||||
from: Date,
|
||||
locale: FrontendLocaleData,
|
||||
to?: Date,
|
||||
includeTense = true
|
||||
): string => {
|
||||
const diff = selectUnit(from, to);
|
||||
if (includeTense) {
|
||||
return formatRelTimeMem(locale).format(diff.value, diff.unit);
|
||||
}
|
||||
return Intl.NumberFormat(locale.language, {
|
||||
style: "unit",
|
||||
// @ts-expect-error
|
||||
unit: diff.unit,
|
||||
unitDisplay: "long",
|
||||
}).format(Math.abs(diff.value));
|
||||
};
|
||||
|
|
|
@ -4,7 +4,7 @@ import { FrontendLocaleData } from "../../data/translation";
|
|||
import { formatDate } from "../datetime/format_date";
|
||||
import { formatDateTime } from "../datetime/format_date_time";
|
||||
import { formatTime } from "../datetime/format_time";
|
||||
import { formatNumber } from "../string/format_number";
|
||||
import { formatNumber } from "../number/format_number";
|
||||
import { LocalizeFunc } from "../translations/localize";
|
||||
import { computeStateDomain } from "./compute_state_domain";
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { FrontendLocaleData, NumberFormat } from "../../data/translation";
|
||||
import { round } from "../number/round";
|
||||
import { round } from "./round";
|
||||
|
||||
export const numberFormatToLocale = (
|
||||
localeOptions: FrontendLocaleData
|
|
@ -1,4 +1,7 @@
|
|||
import { shouldPolyfill } from "@formatjs/intl-pluralrules/lib/should-polyfill";
|
||||
import { shouldPolyfill as shouldPolyfillLocale } from "@formatjs/intl-locale/lib/should-polyfill";
|
||||
import { shouldPolyfill as shouldPolyfillPluralRules } from "@formatjs/intl-pluralrules/lib/should-polyfill";
|
||||
import { shouldPolyfill as shouldPolyfillRelativeTime } from "@formatjs/intl-relativetimeformat/lib/should-polyfill";
|
||||
import { shouldPolyfill as shouldPolyfillDateTime } from "@formatjs/intl-datetimeformat/lib/should-polyfill";
|
||||
import IntlMessageFormat from "intl-messageformat";
|
||||
import { Resources } from "../../types";
|
||||
|
||||
|
@ -14,15 +17,35 @@ export interface FormatsType {
|
|||
|
||||
let loadedPolyfillLocale: Set<string> | undefined;
|
||||
|
||||
let polyfillLoaded = !shouldPolyfill();
|
||||
const polyfillProm = polyfillLoaded
|
||||
const polyfillPluralRules = shouldPolyfillPluralRules();
|
||||
const polyfillRelativeTime = shouldPolyfillRelativeTime();
|
||||
const polyfillDateTime = shouldPolyfillDateTime();
|
||||
|
||||
const polyfills: Promise<any>[] = [];
|
||||
if (__BUILD__ === "latest") {
|
||||
if (shouldPolyfillLocale()) {
|
||||
polyfills.push(import("@formatjs/intl-locale/polyfill"));
|
||||
}
|
||||
if (polyfillPluralRules) {
|
||||
polyfills.push(import("@formatjs/intl-pluralrules/polyfill"));
|
||||
}
|
||||
if (polyfillRelativeTime) {
|
||||
polyfills.push(import("@formatjs/intl-relativetimeformat/polyfill"));
|
||||
}
|
||||
if (polyfillDateTime) {
|
||||
polyfills.push(import("@formatjs/intl-datetimeformat/polyfill"));
|
||||
}
|
||||
}
|
||||
|
||||
let polyfillLoaded = polyfills.length === 0;
|
||||
export const polyfillsLoaded = polyfillLoaded
|
||||
? undefined
|
||||
: import("@formatjs/intl-locale/polyfill")
|
||||
.then(() => import("@formatjs/intl-pluralrules/polyfill"))
|
||||
.then(() => {
|
||||
loadedPolyfillLocale = new Set();
|
||||
polyfillLoaded = true;
|
||||
});
|
||||
: Promise.all(polyfills).then(() => {
|
||||
loadedPolyfillLocale = new Set();
|
||||
polyfillLoaded = true;
|
||||
// Load English so it becomes the default
|
||||
return loadPolyfillLocales("en");
|
||||
});
|
||||
|
||||
/**
|
||||
* Adapted from Polymer app-localize-behavior.
|
||||
|
@ -52,17 +75,10 @@ export const computeLocalize = async (
|
|||
formats?: FormatsType
|
||||
): Promise<LocalizeFunc> => {
|
||||
if (!polyfillLoaded) {
|
||||
await polyfillProm;
|
||||
await polyfillsLoaded;
|
||||
}
|
||||
|
||||
if (loadedPolyfillLocale && !loadedPolyfillLocale.has(language)) {
|
||||
try {
|
||||
loadedPolyfillLocale.add(language);
|
||||
await import("@formatjs/intl-pluralrules/locale-data/en");
|
||||
} catch (_e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
loadPolyfillLocales(language);
|
||||
|
||||
// Everytime any of the parameters change, invalidate the strings cache.
|
||||
cache._localizationCache = {};
|
||||
|
@ -114,3 +130,23 @@ export const computeLocalize = async (
|
|||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const loadPolyfillLocales = async (language: string) => {
|
||||
if (!loadedPolyfillLocale || loadedPolyfillLocale.has(language)) {
|
||||
return;
|
||||
}
|
||||
loadedPolyfillLocale.add(language);
|
||||
try {
|
||||
if (polyfillPluralRules) {
|
||||
await import(`@formatjs/intl-pluralrules/locale-data/${language}`);
|
||||
}
|
||||
if (polyfillRelativeTime) {
|
||||
await import(`@formatjs/intl-relativetimeformat/locale-data/${language}`);
|
||||
}
|
||||
if (polyfillDateTime) {
|
||||
await import(`@formatjs/intl-datetimeformat/locale-data/${language}`);
|
||||
}
|
||||
} catch (_e) {
|
||||
// Ignore
|
||||
}
|
||||
};
|
||||
|
|
|
@ -5,7 +5,7 @@ import { getColorByIndex } from "../../common/color/colors";
|
|||
import {
|
||||
formatNumber,
|
||||
numberFormatToLocale,
|
||||
} from "../../common/string/format_number";
|
||||
} from "../../common/number/format_number";
|
||||
import { LineChartEntity, LineChartState } from "../../data/history";
|
||||
import { HomeAssistant } from "../../types";
|
||||
import "./ha-chart-base";
|
||||
|
|
|
@ -5,7 +5,7 @@ import { customElement, property, state } from "lit/decorators";
|
|||
import { getColorByIndex } from "../../common/color/colors";
|
||||
import { formatDateTimeWithSeconds } from "../../common/datetime/format_date_time";
|
||||
import { computeDomain } from "../../common/entity/compute_domain";
|
||||
import { numberFormatToLocale } from "../../common/string/format_number";
|
||||
import { numberFormatToLocale } from "../../common/number/format_number";
|
||||
import { computeRTL } from "../../common/util/compute_rtl";
|
||||
import { TimelineEntity } from "../../data/history";
|
||||
import { HomeAssistant } from "../../types";
|
||||
|
|
|
@ -19,7 +19,7 @@ import { computeStateName } from "../../common/entity/compute_state_name";
|
|||
import {
|
||||
formatNumber,
|
||||
numberFormatToLocale,
|
||||
} from "../../common/string/format_number";
|
||||
} from "../../common/number/format_number";
|
||||
import {
|
||||
getStatisticIds,
|
||||
Statistics,
|
||||
|
|
|
@ -15,7 +15,7 @@ import { computeStateDomain } from "../../common/entity/compute_state_domain";
|
|||
import { computeStateName } from "../../common/entity/compute_state_name";
|
||||
import { stateIcon } from "../../common/entity/state_icon";
|
||||
import { timerTimeRemaining } from "../../data/timer";
|
||||
import { formatNumber } from "../../common/string/format_number";
|
||||
import { formatNumber } from "../../common/number/format_number";
|
||||
import { UNAVAILABLE, UNKNOWN } from "../../data/entity";
|
||||
import { HomeAssistant } from "../../types";
|
||||
import "../ha-label-badge";
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { formatNumber } from "../common/string/format_number";
|
||||
import { formatNumber } from "../common/number/format_number";
|
||||
import { CLIMATE_PRESET_NONE } from "../data/climate";
|
||||
import type { HomeAssistant } from "../types";
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import { css, LitElement, PropertyValues, svg, TemplateResult } from "lit";
|
|||
import { customElement, property, state } from "lit/decorators";
|
||||
import { ifDefined } from "lit/directives/if-defined";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import { formatNumber } from "../common/string/format_number";
|
||||
import { formatNumber } from "../common/number/format_number";
|
||||
import { afterNextRender } from "../common/util/render-status";
|
||||
import { FrontendLocaleData } from "../data/translation";
|
||||
import { getValueInPercentage, normalize } from "../util/calculate";
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { PropertyValues, ReactiveElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import relativeTime from "../common/datetime/relative_time";
|
||||
import { relativeTime } from "../common/datetime/relative_time";
|
||||
import type { HomeAssistant } from "../types";
|
||||
|
||||
@customElement("ha-relative-time")
|
||||
|
@ -55,10 +55,7 @@ class HaRelativeTime extends ReactiveElement {
|
|||
if (!this.datetime) {
|
||||
this.innerHTML = this.hass.localize("ui.components.relative_time.never");
|
||||
} else {
|
||||
this.innerHTML = relativeTime(
|
||||
new Date(this.datetime),
|
||||
this.hass.localize
|
||||
);
|
||||
this.innerHTML = relativeTime(new Date(this.datetime), this.hass.locale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|||
/* eslint-plugin-disable lit */
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import { computeStateDisplay } from "../common/entity/compute_state_display";
|
||||
import { formatNumber } from "../common/string/format_number";
|
||||
import { formatNumber } from "../common/number/format_number";
|
||||
import LocalizeMixin from "../mixins/localize-mixin";
|
||||
|
||||
/*
|
||||
|
|
|
@ -17,7 +17,7 @@ import {
|
|||
import { customElement, property } from "lit/decorators";
|
||||
import { ifDefined } from "lit/directives/if-defined";
|
||||
import { formatDateTimeWithSeconds } from "../../common/datetime/format_date_time";
|
||||
import relativeTime from "../../common/datetime/relative_time";
|
||||
import { relativeTime } from "../../common/datetime/relative_time";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { toggleAttribute } from "../../common/dom/toggle_attribute";
|
||||
import { LogbookEntry } from "../../data/logbook";
|
||||
|
@ -66,11 +66,7 @@ class RenderedTimeTracker {
|
|||
renderTime(from: Date, to: Date): void {
|
||||
this.entries.push(html`
|
||||
<ha-timeline label>
|
||||
${relativeTime(from, this.hass.localize, {
|
||||
compareTime: to,
|
||||
includeTense: false,
|
||||
})}
|
||||
later
|
||||
${relativeTime(from, this.hass.locale, to, false)} later
|
||||
</ha-timeline>
|
||||
`);
|
||||
this.lastReportedTime = to;
|
||||
|
|
|
@ -11,7 +11,7 @@ import {
|
|||
} from "home-assistant-js-websocket";
|
||||
import { css, html, svg, SVGTemplateResult, TemplateResult } from "lit";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import { formatNumber } from "../common/string/format_number";
|
||||
import { formatNumber } from "../common/number/format_number";
|
||||
import "../components/ha-icon";
|
||||
import "../components/ha-svg-icon";
|
||||
import type { HomeAssistant } from "../types";
|
||||
|
|
|
@ -2,7 +2,7 @@ import { HassEntity } from "home-assistant-js-websocket";
|
|||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { formatTime } from "../../../common/datetime/format_time";
|
||||
import { formatNumber } from "../../../common/string/format_number";
|
||||
import { formatNumber } from "../../../common/number/format_number";
|
||||
import "../../../components/ha-relative-time";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ import {
|
|||
import { customElement, property } from "lit/decorators";
|
||||
import { formatDateWeekday } from "../../../common/datetime/format_date";
|
||||
import { formatTimeWeekday } from "../../../common/datetime/format_time";
|
||||
import { formatNumber } from "../../../common/string/format_number";
|
||||
import { formatNumber } from "../../../common/number/format_number";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import { getWeatherUnit, getWind } from "../../../data/weather";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
|
|
|
@ -17,7 +17,7 @@ import { computeStateName } from "../../../../common/entity/compute_state_name";
|
|||
import {
|
||||
formatNumber,
|
||||
numberFormatToLocale,
|
||||
} from "../../../../common/string/format_number";
|
||||
} from "../../../../common/number/format_number";
|
||||
import "../../../../components/chart/ha-chart-base";
|
||||
import type HaChartBase from "../../../../components/chart/ha-chart-base";
|
||||
import "../../../../components/ha-card";
|
||||
|
|
|
@ -15,7 +15,7 @@ import { css, html, LitElement, svg } from "lit";
|
|||
import { customElement, property, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import "@material/mwc-button";
|
||||
import { formatNumber } from "../../../../common/string/format_number";
|
||||
import { formatNumber } from "../../../../common/number/format_number";
|
||||
import "../../../../components/ha-card";
|
||||
import "../../../../components/ha-svg-icon";
|
||||
import {
|
||||
|
|
|
@ -32,7 +32,7 @@ import "../../../../components/chart/ha-chart-base";
|
|||
import {
|
||||
formatNumber,
|
||||
numberFormatToLocale,
|
||||
} from "../../../../common/string/format_number";
|
||||
} from "../../../../common/number/format_number";
|
||||
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
|
||||
import { FrontendLocaleData } from "../../../../data/translation";
|
||||
import {
|
||||
|
|
|
@ -3,7 +3,7 @@ import "@polymer/paper-tooltip";
|
|||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { formatNumber } from "../../../../common/string/format_number";
|
||||
import { formatNumber } from "../../../../common/number/format_number";
|
||||
import "../../../../components/ha-card";
|
||||
import "../../../../components/ha-svg-icon";
|
||||
import "../../../../components/ha-gauge";
|
||||
|
|
|
@ -33,7 +33,7 @@ import "../../../../components/chart/ha-chart-base";
|
|||
import {
|
||||
formatNumber,
|
||||
numberFormatToLocale,
|
||||
} from "../../../../common/string/format_number";
|
||||
} from "../../../../common/number/format_number";
|
||||
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
|
||||
import { FrontendLocaleData } from "../../../../data/translation";
|
||||
import {
|
||||
|
|
|
@ -19,7 +19,7 @@ import {
|
|||
} from "../../../../common/color/convert-color";
|
||||
import { labDarken } from "../../../../common/color/lab";
|
||||
import { computeStateName } from "../../../../common/entity/compute_state_name";
|
||||
import { formatNumber } from "../../../../common/string/format_number";
|
||||
import { formatNumber } from "../../../../common/number/format_number";
|
||||
import "../../../../components/chart/statistics-chart";
|
||||
import "../../../../components/ha-card";
|
||||
import {
|
||||
|
|
|
@ -17,7 +17,7 @@ import { computeStateName } from "../../../../common/entity/compute_state_name";
|
|||
import {
|
||||
formatNumber,
|
||||
numberFormatToLocale,
|
||||
} from "../../../../common/string/format_number";
|
||||
} from "../../../../common/number/format_number";
|
||||
import "../../../../components/chart/ha-chart-base";
|
||||
import "../../../../components/ha-card";
|
||||
import { EnergyData, getEnergyDataCollection } from "../../../../data/energy";
|
||||
|
|
|
@ -16,7 +16,7 @@ import { computeStateDomain } from "../../../common/entity/compute_state_domain"
|
|||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||
import { stateIcon } from "../../../common/entity/state_icon";
|
||||
import { isValidEntityId } from "../../../common/entity/valid_entity_id";
|
||||
import { formatNumber } from "../../../common/string/format_number";
|
||||
import { formatNumber } from "../../../common/number/format_number";
|
||||
import { iconColorCSS } from "../../../common/style/icon_color_css";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-icon";
|
||||
|
|
|
@ -9,7 +9,7 @@ import {
|
|||
import { customElement, property, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { ifDefined } from "lit/directives/if-defined";
|
||||
import relativeTime from "../../../common/datetime/relative_time";
|
||||
import { relativeTime } from "../../../common/datetime/relative_time";
|
||||
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { computeStateDisplay } from "../../../common/entity/compute_state_display";
|
||||
|
@ -330,7 +330,7 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard {
|
|||
: entityConf.show_last_changed
|
||||
? relativeTime(
|
||||
new Date(stateObj.last_changed),
|
||||
this.hass!.localize
|
||||
this.hass!.locale
|
||||
)
|
||||
: computeStateDisplay(
|
||||
this.hass!.localize,
|
||||
|
|
|
@ -16,7 +16,7 @@ import { UNIT_F } from "../../../common/const";
|
|||
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||
import { formatNumber } from "../../../common/string/format_number";
|
||||
import { formatNumber } from "../../../common/number/format_number";
|
||||
import "../../../components/ha-card";
|
||||
import type { HaCard } from "../../../components/ha-card";
|
||||
import "../../../components/ha-icon-button";
|
||||
|
|
|
@ -14,7 +14,7 @@ import { computeStateDisplay } from "../../../common/entity/compute_state_displa
|
|||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||
import { stateIcon } from "../../../common/entity/state_icon";
|
||||
import { isValidEntityId } from "../../../common/entity/valid_entity_id";
|
||||
import { formatNumber } from "../../../common/string/format_number";
|
||||
import { formatNumber } from "../../../common/number/format_number";
|
||||
import { debounce } from "../../../common/util/debounce";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-icon";
|
||||
|
|
|
@ -3,7 +3,7 @@ import { customElement, property, state } from "lit/decorators";
|
|||
import { formatDate } from "../../../common/datetime/format_date";
|
||||
import { formatDateTime } from "../../../common/datetime/format_date_time";
|
||||
import { formatTime } from "../../../common/datetime/format_time";
|
||||
import relativeTime from "../../../common/datetime/relative_time";
|
||||
import { relativeTime } from "../../../common/datetime/relative_time";
|
||||
import { FrontendLocaleData } from "../../../data/translation";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { TimestampRenderingFormat } from "./types";
|
||||
|
@ -103,11 +103,13 @@ class HuiTimestampDisplay extends LitElement {
|
|||
if (this.ts && this.hass!.localize) {
|
||||
this._relative =
|
||||
this._format === "relative"
|
||||
? relativeTime(this.ts, this.hass!.localize)
|
||||
: (this._relative = relativeTime(new Date(), this.hass!.localize, {
|
||||
compareTime: this.ts,
|
||||
includeTense: false,
|
||||
}));
|
||||
? relativeTime(this.ts, this.hass!.locale)
|
||||
: (this._relative = relativeTime(
|
||||
new Date(),
|
||||
this.hass!.locale,
|
||||
this.ts,
|
||||
false
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ import { computeDomain } from "../../../common/entity/compute_domain";
|
|||
import { computeStateDisplay } from "../../../common/entity/compute_state_display";
|
||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||
import { stateIcon } from "../../../common/entity/state_icon";
|
||||
import { formatNumber } from "../../../common/string/format_number";
|
||||
import { formatNumber } from "../../../common/number/format_number";
|
||||
import "../../../components/entity/state-badge";
|
||||
import { UNAVAILABLE_STATES } from "../../../data/entity";
|
||||
import { ActionHandlerEvent } from "../../../data/lovelace";
|
||||
|
|
|
@ -8,7 +8,7 @@ import {
|
|||
} from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import checkValidDate from "../../../common/datetime/check_valid_date";
|
||||
import { formatNumber } from "../../../common/string/format_number";
|
||||
import { formatNumber } from "../../../common/number/format_number";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { formatAttributeValue } from "../../../util/hass-attributes-util";
|
||||
import { hasConfigOrEntityChanged } from "../common/has-changed";
|
||||
|
|
|
@ -4,7 +4,7 @@ import { mdiDelete } from "@mdi/js";
|
|||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import relativeTime from "../../common/datetime/relative_time";
|
||||
import { relativeTime } from "../../common/datetime/relative_time";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import "../../components/ha-card";
|
||||
import "../../components/ha-settings-row";
|
||||
|
@ -69,10 +69,7 @@ class HaLongLivedTokens extends LitElement {
|
|||
${this.hass.localize(
|
||||
"ui.panel.profile.long_lived_access_tokens.created",
|
||||
"date",
|
||||
relativeTime(
|
||||
new Date(token.created_at),
|
||||
this.hass.localize
|
||||
)
|
||||
relativeTime(new Date(token.created_at), this.hass.locale)
|
||||
)}
|
||||
</div>
|
||||
<mwc-icon-button
|
||||
|
|
|
@ -3,7 +3,7 @@ import "@polymer/paper-listbox/paper-listbox";
|
|||
import { html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { formatNumber } from "../../common/string/format_number";
|
||||
import { formatNumber } from "../../common/number/format_number";
|
||||
import "../../components/ha-card";
|
||||
import "../../components/ha-paper-dropdown-menu";
|
||||
import "../../components/ha-settings-row";
|
||||
|
|
|
@ -4,7 +4,7 @@ import "@polymer/paper-tooltip/paper-tooltip";
|
|||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import relativeTime from "../../common/datetime/relative_time";
|
||||
import { relativeTime } from "../../common/datetime/relative_time";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import "../../components/ha-card";
|
||||
import "../../components/ha-settings-row";
|
||||
|
@ -64,7 +64,7 @@ class HaRefreshTokens extends LitElement {
|
|||
{
|
||||
date: relativeTime(
|
||||
new Date(token.created_at),
|
||||
this.hass.localize
|
||||
this.hass.locale
|
||||
),
|
||||
}
|
||||
)}
|
||||
|
@ -76,7 +76,7 @@ class HaRefreshTokens extends LitElement {
|
|||
{
|
||||
date: relativeTime(
|
||||
new Date(token.last_used_at),
|
||||
this.hass.localize
|
||||
this.hass.locale
|
||||
),
|
||||
location: token.last_used_ip,
|
||||
}
|
||||
|
|
|
@ -1,8 +1,19 @@
|
|||
import "core-js";
|
||||
import "regenerator-runtime/runtime";
|
||||
import "lit/polyfill-support";
|
||||
// For localize
|
||||
|
||||
// For localize & formatting
|
||||
import "@formatjs/intl-getcanonicallocales/polyfill";
|
||||
import "@formatjs/intl-locale/polyfill";
|
||||
import "@formatjs/intl-pluralrules/polyfill";
|
||||
import "@formatjs/intl-pluralrules/locale-data/en";
|
||||
import "@formatjs/intl-numberformat/polyfill";
|
||||
import "@formatjs/intl-numberformat/locale-data/en";
|
||||
import "@formatjs/intl-relativetimeformat/polyfill";
|
||||
import "@formatjs/intl-relativetimeformat/locale-data/en";
|
||||
import "@formatjs/intl-datetimeformat/polyfill";
|
||||
import "@formatjs/intl-datetimeformat/locale-data/en";
|
||||
|
||||
// To use comlink under ES5
|
||||
import "proxy-polyfill";
|
||||
import "unfetch/polyfill";
|
||||
|
|
|
@ -436,29 +436,7 @@
|
|||
}
|
||||
},
|
||||
"relative_time": {
|
||||
"never": "Never",
|
||||
"just_now": "Just now",
|
||||
"duration": {
|
||||
"second": "{count} {count, plural,\n one {second}\n other {seconds}\n}",
|
||||
"minute": "{count} {count, plural,\n one {minute}\n other {minutes}\n}",
|
||||
"hour": "{count} {count, plural,\n one {hour}\n other {hours}\n}",
|
||||
"day": "{count} {count, plural,\n one {day}\n other {days}\n}",
|
||||
"week": "{count} {count, plural,\n one {week}\n other {weeks}\n}"
|
||||
},
|
||||
"past_duration": {
|
||||
"second": "{count} {count, plural,\n one {second}\n other {seconds}\n} ago",
|
||||
"minute": "{count} {count, plural,\n one {minute}\n other {minutes}\n} ago",
|
||||
"hour": "{count} {count, plural,\n one {hour}\n other {hours}\n} ago",
|
||||
"day": "{count} {count, plural,\n one {day}\n other {days}\n} ago",
|
||||
"week": "{count} {count, plural,\n one {week}\n other {weeks}\n} ago"
|
||||
},
|
||||
"future_duration": {
|
||||
"second": "In {count} {count, plural,\n one {second}\n other {seconds}\n}",
|
||||
"minute": "In {count} {count, plural,\n one {minute}\n other {minutes}\n}",
|
||||
"hour": "In {count} {count, plural,\n one {hour}\n other {hours}\n}",
|
||||
"day": "In {count} {count, plural,\n one {day}\n other {days}\n}",
|
||||
"week": "In {count} {count, plural,\n one {week}\n other {weeks}\n}"
|
||||
}
|
||||
"never": "Never"
|
||||
},
|
||||
"history_charts": {
|
||||
"history_disabled": "History integration disabled",
|
||||
|
|
|
@ -1,217 +0,0 @@
|
|||
import { assert } from "chai";
|
||||
|
||||
import relativeTime from "../../../src/common/datetime/relative_time";
|
||||
|
||||
describe("relativeTime", () => {
|
||||
// Mock localize function for testing
|
||||
const localize = (message, ...args) =>
|
||||
message + (args.length ? ": " + args.join(",") : "");
|
||||
|
||||
it("now", () => {
|
||||
const now = new Date();
|
||||
assert.strictEqual(
|
||||
relativeTime(now, localize, { compareTime: now }),
|
||||
"ui.components.relative_time.just_now"
|
||||
);
|
||||
});
|
||||
|
||||
it("past_second", () => {
|
||||
const inputdt = new Date("2021-02-03T11:22:00+00:00");
|
||||
const compare = new Date("2021-02-03T11:22:33+00:00");
|
||||
assert.strictEqual(
|
||||
relativeTime(inputdt, localize, { compareTime: compare }),
|
||||
"ui.components.relative_time.past_duration.second: count,33"
|
||||
);
|
||||
assert.strictEqual(
|
||||
relativeTime(inputdt, localize, {
|
||||
compareTime: compare,
|
||||
includeTense: false,
|
||||
}),
|
||||
"ui.components.relative_time.duration.second: count,33"
|
||||
);
|
||||
});
|
||||
|
||||
it("past_minute", () => {
|
||||
const inputdt = new Date("2021-02-03T11:20:33+00:00");
|
||||
const compare = new Date("2021-02-03T11:22:33+00:00");
|
||||
assert.strictEqual(
|
||||
relativeTime(inputdt, localize, { compareTime: compare }),
|
||||
"ui.components.relative_time.past_duration.minute: count,2"
|
||||
);
|
||||
assert.strictEqual(
|
||||
relativeTime(inputdt, localize, {
|
||||
compareTime: compare,
|
||||
includeTense: false,
|
||||
}),
|
||||
"ui.components.relative_time.duration.minute: count,2"
|
||||
);
|
||||
});
|
||||
|
||||
it("past_hour", () => {
|
||||
const inputdt = new Date("2021-02-03T09:22:33+00:00");
|
||||
const compare = new Date("2021-02-03T11:22:33+00:00");
|
||||
assert.strictEqual(
|
||||
relativeTime(inputdt, localize, { compareTime: compare }),
|
||||
"ui.components.relative_time.past_duration.hour: count,2"
|
||||
);
|
||||
assert.strictEqual(
|
||||
relativeTime(inputdt, localize, {
|
||||
compareTime: compare,
|
||||
includeTense: false,
|
||||
}),
|
||||
"ui.components.relative_time.duration.hour: count,2"
|
||||
);
|
||||
});
|
||||
|
||||
it("past_day", () => {
|
||||
let inputdt = new Date("2021-02-01T11:22:33+00:00");
|
||||
let compare = new Date("2021-02-03T11:22:33+00:00");
|
||||
assert.strictEqual(
|
||||
relativeTime(inputdt, localize, { compareTime: compare }),
|
||||
"ui.components.relative_time.past_duration.day: count,2"
|
||||
);
|
||||
assert.strictEqual(
|
||||
relativeTime(inputdt, localize, {
|
||||
compareTime: compare,
|
||||
includeTense: false,
|
||||
}),
|
||||
"ui.components.relative_time.duration.day: count,2"
|
||||
);
|
||||
|
||||
// Test switch from days to weeks
|
||||
inputdt = new Date("2021-01-28T11:22:33+00:00");
|
||||
compare = new Date("2021-02-03T11:22:33+00:00");
|
||||
assert.strictEqual(
|
||||
relativeTime(inputdt, localize, {
|
||||
compareTime: compare,
|
||||
includeTense: false,
|
||||
}),
|
||||
"ui.components.relative_time.duration.day: count,6"
|
||||
);
|
||||
inputdt = new Date("2021-01-27T11:22:33+00:00");
|
||||
compare = new Date("2021-02-03T11:22:33+00:00");
|
||||
assert.notStrictEqual(
|
||||
relativeTime(inputdt, localize, {
|
||||
compareTime: compare,
|
||||
includeTense: false,
|
||||
}),
|
||||
"ui.components.relative_time.duration.day: count,7"
|
||||
);
|
||||
});
|
||||
|
||||
it("past_week", () => {
|
||||
const inputdt = new Date("2021-01-03T11:22:33+00:00");
|
||||
const compare = new Date("2021-02-03T11:22:33+00:00");
|
||||
assert.strictEqual(
|
||||
relativeTime(inputdt, localize, { compareTime: compare }),
|
||||
"ui.components.relative_time.past_duration.week: count,4"
|
||||
);
|
||||
assert.strictEqual(
|
||||
relativeTime(inputdt, localize, {
|
||||
compareTime: compare,
|
||||
includeTense: false,
|
||||
}),
|
||||
"ui.components.relative_time.duration.week: count,4"
|
||||
);
|
||||
});
|
||||
|
||||
it("future_second", () => {
|
||||
const inputdt = new Date("2021-02-03T11:22:55+00:00");
|
||||
const compare = new Date("2021-02-03T11:22:33+00:00");
|
||||
assert.strictEqual(
|
||||
relativeTime(inputdt, localize, { compareTime: compare }),
|
||||
"ui.components.relative_time.future_duration.second: count,22"
|
||||
);
|
||||
assert.strictEqual(
|
||||
relativeTime(inputdt, localize, {
|
||||
compareTime: compare,
|
||||
includeTense: false,
|
||||
}),
|
||||
"ui.components.relative_time.duration.second: count,22"
|
||||
);
|
||||
});
|
||||
|
||||
it("future_minute", () => {
|
||||
const inputdt = new Date("2021-02-03T11:24:33+00:00");
|
||||
const compare = new Date("2021-02-03T11:22:33+00:00");
|
||||
assert.strictEqual(
|
||||
relativeTime(inputdt, localize, { compareTime: compare }),
|
||||
"ui.components.relative_time.future_duration.minute: count,2"
|
||||
);
|
||||
assert.strictEqual(
|
||||
relativeTime(inputdt, localize, {
|
||||
compareTime: compare,
|
||||
includeTense: false,
|
||||
}),
|
||||
"ui.components.relative_time.duration.minute: count,2"
|
||||
);
|
||||
});
|
||||
|
||||
it("future_hour", () => {
|
||||
const inputdt = new Date("2021-02-03T13:22:33+00:00");
|
||||
const compare = new Date("2021-02-03T11:22:33+00:00");
|
||||
assert.strictEqual(
|
||||
relativeTime(inputdt, localize, { compareTime: compare }),
|
||||
"ui.components.relative_time.future_duration.hour: count,2"
|
||||
);
|
||||
assert.strictEqual(
|
||||
relativeTime(inputdt, localize, {
|
||||
compareTime: compare,
|
||||
includeTense: false,
|
||||
}),
|
||||
"ui.components.relative_time.duration.hour: count,2"
|
||||
);
|
||||
});
|
||||
|
||||
it("future_day", () => {
|
||||
let inputdt = new Date("2021-02-05T11:22:33+00:00");
|
||||
let compare = new Date("2021-02-03T11:22:33+00:00");
|
||||
assert.strictEqual(
|
||||
relativeTime(inputdt, localize, { compareTime: compare }),
|
||||
"ui.components.relative_time.future_duration.day: count,2"
|
||||
);
|
||||
assert.strictEqual(
|
||||
relativeTime(inputdt, localize, {
|
||||
compareTime: compare,
|
||||
includeTense: false,
|
||||
}),
|
||||
"ui.components.relative_time.duration.day: count,2"
|
||||
);
|
||||
|
||||
// Test switch from days to weeks
|
||||
inputdt = new Date("2021-02-09T11:22:33+00:00");
|
||||
compare = new Date("2021-02-03T11:22:33+00:00");
|
||||
assert.strictEqual(
|
||||
relativeTime(inputdt, localize, {
|
||||
compareTime: compare,
|
||||
includeTense: false,
|
||||
}),
|
||||
"ui.components.relative_time.duration.day: count,6"
|
||||
);
|
||||
inputdt = new Date("2021-02-10T11:22:33+00:00");
|
||||
compare = new Date("2021-02-03T11:22:33+00:00");
|
||||
assert.notStrictEqual(
|
||||
relativeTime(inputdt, localize, {
|
||||
compareTime: compare,
|
||||
includeTense: false,
|
||||
}),
|
||||
"ui.components.relative_time.duration.day: count,7"
|
||||
);
|
||||
});
|
||||
|
||||
it("future_week", () => {
|
||||
const inputdt = new Date("2021-03-03T11:22:33+00:00");
|
||||
const compare = new Date("2021-02-03T11:22:33+00:00");
|
||||
assert.strictEqual(
|
||||
relativeTime(inputdt, localize, { compareTime: compare }),
|
||||
"ui.components.relative_time.future_duration.week: count,4"
|
||||
);
|
||||
assert.strictEqual(
|
||||
relativeTime(inputdt, localize, {
|
||||
compareTime: compare,
|
||||
includeTense: false,
|
||||
}),
|
||||
"ui.components.relative_time.duration.week: count,4"
|
||||
);
|
||||
});
|
||||
});
|
|
@ -16,7 +16,7 @@ describe("formatDateTime", () => {
|
|||
number_format: NumberFormat.language,
|
||||
time_format: TimeFormat.am_pm,
|
||||
}),
|
||||
"November 18, 2017, 11:12 PM"
|
||||
"November 18, 2017 at 11:12 PM"
|
||||
);
|
||||
assert.strictEqual(
|
||||
formatDateTime(dateObj, {
|
||||
|
@ -24,7 +24,7 @@ describe("formatDateTime", () => {
|
|||
number_format: NumberFormat.language,
|
||||
time_format: TimeFormat.twenty_four,
|
||||
}),
|
||||
"November 18, 2017, 23:12"
|
||||
"November 18, 2017 at 23:12"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -39,7 +39,7 @@ describe("formatDateTimeWithSeconds", () => {
|
|||
number_format: NumberFormat.language,
|
||||
time_format: TimeFormat.am_pm,
|
||||
}),
|
||||
"November 18, 2017, 11:12:13 PM"
|
||||
"November 18, 2017 at 11:12:13 PM"
|
||||
);
|
||||
assert.strictEqual(
|
||||
formatDateTimeWithSeconds(dateObj, {
|
||||
|
@ -47,7 +47,7 @@ describe("formatDateTimeWithSeconds", () => {
|
|||
number_format: NumberFormat.language,
|
||||
time_format: TimeFormat.twenty_four,
|
||||
}),
|
||||
"November 18, 2017, 23:12:13"
|
||||
"November 18, 2017 at 23:12:13"
|
||||
);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,115 @@
|
|||
import { assert } from "chai";
|
||||
|
||||
import { relativeTime } from "../../../src/common/datetime/relative_time";
|
||||
import { NumberFormat, TimeFormat } from "../../../src/data/translation";
|
||||
|
||||
describe("relativeTime", () => {
|
||||
const locale = {
|
||||
language: "en",
|
||||
number_format: NumberFormat.language,
|
||||
time_format: TimeFormat.language,
|
||||
};
|
||||
|
||||
it("now", () => {
|
||||
const now = new Date();
|
||||
assert.strictEqual(relativeTime(now, locale, now), "now");
|
||||
assert.strictEqual(relativeTime(now, locale, now, false), "0 seconds");
|
||||
});
|
||||
|
||||
it("past_second", () => {
|
||||
const inputdt = new Date("2021-02-03T11:22:00+00:00");
|
||||
const compare = new Date("2021-02-03T11:22:33+00:00");
|
||||
assert.strictEqual(
|
||||
relativeTime(inputdt, locale, compare),
|
||||
"33 seconds ago"
|
||||
);
|
||||
assert.strictEqual(
|
||||
relativeTime(inputdt, locale, compare, false),
|
||||
"33 seconds"
|
||||
);
|
||||
});
|
||||
|
||||
it("past_minute", () => {
|
||||
const inputdt = new Date("2021-02-03T11:20:33+00:00");
|
||||
const compare = new Date("2021-02-03T11:22:33+00:00");
|
||||
assert.strictEqual(relativeTime(inputdt, locale, compare), "2 minutes ago");
|
||||
assert.strictEqual(
|
||||
relativeTime(inputdt, locale, compare, false),
|
||||
"2 minutes"
|
||||
);
|
||||
});
|
||||
|
||||
it("past_hour", () => {
|
||||
const inputdt = new Date("2021-02-03T09:22:33+00:00");
|
||||
const compare = new Date("2021-02-03T11:22:33+00:00");
|
||||
assert.strictEqual(relativeTime(inputdt, locale, compare), "2 hours ago");
|
||||
assert.strictEqual(
|
||||
relativeTime(inputdt, locale, compare, false),
|
||||
"2 hours"
|
||||
);
|
||||
});
|
||||
|
||||
it("past_day", () => {
|
||||
const inputdt = new Date("2021-02-01T11:22:33+00:00");
|
||||
const compare = new Date("2021-02-03T11:22:33+00:00");
|
||||
assert.strictEqual(relativeTime(inputdt, locale, compare), "2 days ago");
|
||||
assert.strictEqual(relativeTime(inputdt, locale, compare, false), "2 days");
|
||||
});
|
||||
|
||||
it("future_second", () => {
|
||||
const inputdt = new Date("2021-02-03T11:22:55+00:00");
|
||||
const compare = new Date("2021-02-03T11:22:33+00:00");
|
||||
assert.strictEqual(relativeTime(inputdt, locale, compare), "in 22 seconds");
|
||||
assert.strictEqual(
|
||||
relativeTime(inputdt, locale, compare, false),
|
||||
"22 seconds"
|
||||
);
|
||||
});
|
||||
|
||||
it("future_minute", () => {
|
||||
const inputdt = new Date("2021-02-03T11:24:33+00:00");
|
||||
const compare = new Date("2021-02-03T11:22:33+00:00");
|
||||
assert.strictEqual(relativeTime(inputdt, locale, compare), "in 2 minutes");
|
||||
assert.strictEqual(
|
||||
relativeTime(inputdt, locale, compare, false),
|
||||
"2 minutes"
|
||||
);
|
||||
});
|
||||
|
||||
it("future_hour", () => {
|
||||
const inputdt = new Date("2021-02-03T13:22:33+00:00");
|
||||
const compare = new Date("2021-02-03T11:22:33+00:00");
|
||||
assert.strictEqual(relativeTime(inputdt, locale, compare), "in 2 hours");
|
||||
assert.strictEqual(
|
||||
relativeTime(inputdt, locale, compare, false),
|
||||
"2 hours"
|
||||
);
|
||||
});
|
||||
|
||||
it("future_day", () => {
|
||||
const inputdt = new Date("2021-02-05T11:22:33+00:00");
|
||||
const compare = new Date("2021-02-03T11:22:33+00:00");
|
||||
assert.strictEqual(relativeTime(inputdt, locale, compare), "in 2 days");
|
||||
assert.strictEqual(relativeTime(inputdt, locale, compare, false), "2 days");
|
||||
});
|
||||
|
||||
it("future_week", () => {
|
||||
const inputdt = new Date("2021-03-24T11:22:33+00:00");
|
||||
const compare = new Date("2021-03-03T11:22:33+00:00");
|
||||
assert.strictEqual(relativeTime(inputdt, locale, compare), "in 3 weeks");
|
||||
assert.strictEqual(
|
||||
relativeTime(inputdt, locale, compare, false),
|
||||
"3 weeks"
|
||||
);
|
||||
});
|
||||
|
||||
it("future_month", () => {
|
||||
const inputdt = new Date("2021-03-03T11:22:33+00:00");
|
||||
const compare = new Date("2021-02-03T11:22:33+00:00");
|
||||
assert.strictEqual(relativeTime(inputdt, locale, compare), "next month");
|
||||
assert.strictEqual(
|
||||
relativeTime(inputdt, locale, compare, false),
|
||||
"1 month"
|
||||
);
|
||||
});
|
||||
});
|
|
@ -172,14 +172,14 @@ describe("computeStateDisplay", () => {
|
|||
it("Uses am/pm time format", () => {
|
||||
assert.strictEqual(
|
||||
computeStateDisplay(localize, stateObj, localeData),
|
||||
"November 18, 2017, 11:12 PM"
|
||||
"November 18, 2017 at 11:12 PM"
|
||||
);
|
||||
});
|
||||
it("Uses 24h time format", () => {
|
||||
localeData.time_format = TimeFormat.twenty_four;
|
||||
assert.strictEqual(
|
||||
computeStateDisplay(localize, stateObj, localeData),
|
||||
"November 18, 2017, 23:12"
|
||||
"November 18, 2017 at 23:12"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -259,7 +259,7 @@ describe("computeStateDisplay", () => {
|
|||
localeData,
|
||||
"2021-07-04 15:40:03"
|
||||
),
|
||||
"July 4, 2021, 3:40 PM"
|
||||
"July 4, 2021 at 3:40 PM"
|
||||
);
|
||||
});
|
||||
it("Uses 24h time format", () => {
|
||||
|
@ -271,7 +271,7 @@ describe("computeStateDisplay", () => {
|
|||
localeData,
|
||||
"2021-07-04 15:40:03"
|
||||
),
|
||||
"July 4, 2021, 15:40"
|
||||
"July 4, 2021 at 15:40"
|
||||
);
|
||||
});
|
||||
});
|
|
@ -1,6 +1,6 @@
|
|||
import { assert } from "chai";
|
||||
|
||||
import { formatNumber } from "../../../src/common/string/format_number";
|
||||
import { formatNumber } from "../../../src/common/number/format_number";
|
||||
import {
|
||||
FrontendLocaleData,
|
||||
NumberFormat,
|
|
@ -0,0 +1,12 @@
|
|||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
process.env.TZ = "Etc/UTC";
|
||||
process.env.IS_TEST = "true";
|
||||
|
||||
const MDI_OUTPUT_DIR = path.resolve(__dirname, "../build/mdi");
|
||||
|
||||
if (!fs.existsSync(MDI_OUTPUT_DIR)) {
|
||||
fs.mkdirSync(MDI_OUTPUT_DIR, { recursive: true });
|
||||
fs.writeFileSync(path.resolve(MDI_OUTPUT_DIR, "iconMetadata.json"), "{}");
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
const { createAppConfig } = require("../build-scripts/webpack.js");
|
||||
|
||||
module.exports = createAppConfig({
|
||||
isProdBuild: false,
|
||||
latestBuild: true,
|
||||
isStatsBuild: false,
|
||||
});
|
Loading…
Reference in New Issue