From b69b284efa25640c69c7e5a33382bc458021f81e Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 13 Jan 2020 21:20:01 -0700 Subject: [PATCH] Humanize the recent DM rooms ourselves for translations --- package.json | 1 - .../views/dialogs/DMInviteDialog.js | 6 +- src/i18n/strings/en_EN.json | 14 +++++ src/utils/humanize.js | 57 +++++++++++++++++++ yarn.lock | 5 -- 5 files changed, 73 insertions(+), 10 deletions(-) create mode 100644 src/utils/humanize.js diff --git a/package.json b/package.json index 4cbfd84882..df354d3ff6 100644 --- a/package.json +++ b/package.json @@ -82,7 +82,6 @@ "glob-to-regexp": "^0.4.1", "highlight.js": "^9.15.8", "html-entities": "^1.2.1", - "humanize": "^0.0.9", "is-ip": "^2.0.0", "isomorphic-fetch": "^2.2.1", "linkifyjs": "^2.1.6", diff --git a/src/components/views/dialogs/DMInviteDialog.js b/src/components/views/dialogs/DMInviteDialog.js index 371768eb4e..54d3ab8337 100644 --- a/src/components/views/dialogs/DMInviteDialog.js +++ b/src/components/views/dialogs/DMInviteDialog.js @@ -22,7 +22,6 @@ import MatrixClientPeg from "../../../MatrixClientPeg"; import {makeUserPermalink} from "../../../utils/permalinks/Permalinks"; import DMRoomMap from "../../../utils/DMRoomMap"; import {RoomMember} from "matrix-js-sdk/lib/matrix"; -import * as humanize from "humanize"; import SdkConfig from "../../../SdkConfig"; import {getHttpUriForMxc} from "matrix-js-sdk/lib/content-repo"; import * as Email from "../../../email"; @@ -31,6 +30,7 @@ import {abbreviateUrl} from "../../../utils/UrlUtils"; import dis from "../../../dispatcher"; import IdentityAuthClient from "../../../IdentityAuthClient"; import Modal from "../../../Modal"; +import {humanizeTime} from "../../../utils/humanize"; // TODO: [TravisR] Make this generic for all kinds of invites @@ -226,9 +226,7 @@ class DMRoomTile extends React.PureComponent { let timestamp = null; if (this.props.lastActiveTs) { - // TODO: [TravisR] Figure out how to i18n this - // `humanize` wants seconds for a timestamp, so divide by 1000 - const humanTs = humanize.relativeTime(this.props.lastActiveTs / 1000); + const humanTs = humanizeTime(this.props.lastActiveTs); timestamp = {humanTs}; } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index e3aaf044c3..b1c8b12d86 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -309,6 +309,20 @@ "%(items)s and %(count)s others|other": "%(items)s and %(count)s others", "%(items)s and %(count)s others|one": "%(items)s and one other", "%(items)s and %(lastItem)s": "%(items)s and %(lastItem)s", + "a few seconds ago": "a few seconds ago", + "about a minute ago": "about a minute ago", + "%(num)s minutes ago": "%(num)s minutes ago", + "about an hour ago": "about an hour ago", + "%(num)s hours ago": "%(num)s hours ago", + "about a day ago": "about a day ago", + "%(num)s days ago": "%(num)s days ago", + "a few seconds from now": "a few seconds from now", + "about a minute from now": "about a minute from now", + "%(num)s minutes from now": "%(num)s minutes from now", + "about an hour from now": "about an hour from now", + "%(num)s hours from now": "%(num)s hours from now", + "about a day from now": "about a day from now", + "%(num)s days from now": "%(num)s days from now", "%(name)s (%(userId)s)": "%(name)s (%(userId)s)", "Your browser does not support the required cryptography extensions": "Your browser does not support the required cryptography extensions", "Not a valid Riot keyfile": "Not a valid Riot keyfile", diff --git a/src/utils/humanize.js b/src/utils/humanize.js new file mode 100644 index 0000000000..6e6f17f8f4 --- /dev/null +++ b/src/utils/humanize.js @@ -0,0 +1,57 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import {_t} from "../languageHandler"; + +/** + * Converts a timestamp into human-readable, translated, text. + * @param {number} timeMillis The time in millis to compare against. + * @returns {string} The humanized time. + */ +export function humanizeTime(timeMillis) { + // These are the constants we use for when to break the text + const MILLISECONDS_RECENT = 15000; + const MILLISECONDS_1_MIN = 75000; + const MINUTES_UNDER_1_HOUR = 45; + const MINUTES_1_HOUR = 75; + const HOURS_UNDER_1_DAY = 23; + const HOURS_1_DAY = 26; + + const now = (new Date()).getTime(); + let msAgo = now - timeMillis; + const minutes = Math.abs(Math.ceil(msAgo / 60000)); + const hours = Math.ceil(minutes / 60); + const days = Math.ceil(hours / 24); + + if (msAgo >= 0) { // Past + if (msAgo <= MILLISECONDS_RECENT) return _t("a few seconds ago"); + if (msAgo <= MILLISECONDS_1_MIN) return _t("about a minute ago"); + if (minutes <= MINUTES_UNDER_1_HOUR) return _t("%(num)s minutes ago", {num: minutes}); + if (minutes <= MINUTES_1_HOUR) return _t("about an hour ago"); + if (hours <= HOURS_UNDER_1_DAY) return _t("%(num)s hours ago", {num: hours}); + if (hours <= HOURS_1_DAY) return _t("about a day ago"); + return _t("%(num)s days ago", {num: days}); + } else { // Future + msAgo = Math.abs(msAgo); + if (msAgo <= MILLISECONDS_RECENT) return _t("a few seconds from now"); + if (msAgo <= MILLISECONDS_1_MIN) return _t("about a minute from now"); + if (minutes <= MINUTES_UNDER_1_HOUR) return _t("%(num)s minutes from now", {num: minutes}); + if (minutes <= MINUTES_1_HOUR) return _t("about an hour from now"); + if (hours <= HOURS_UNDER_1_DAY) return _t("%(num)s hours from now", {num: hours}); + if (hours <= HOURS_1_DAY) return _t("about a day from now"); + return _t("%(num)s days from now", {num: days}); + } +} diff --git a/yarn.lock b/yarn.lock index 0ea3190258..b31cd3ab4c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4258,11 +4258,6 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" -humanize@^0.0.9: - version "0.0.9" - resolved "https://registry.yarnpkg.com/humanize/-/humanize-0.0.9.tgz#1994ffaecdfe9c441ed2bdac7452b7bb4c9e41a4" - integrity sha1-GZT/rs3+nEQe0r2sdFK3u0yeQaQ= - iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"