Files
blap/packages/shared-components/src/utils/humanize.ts
T
David Baker 5370f25870 Introduce i18nContext (#31347)
* Introduce i18nContext

 * Adds a context that holds the module i1n API
 * Switches shared components to use that instead of importing it directly
 * Adds the context to MatrixChat and BaseDalog so it should be available most places in EW

This is a relatively small PR but does change the way the shared components do i18n so
just doing this one by itself (it stands by itself anyway).

This will allow shared components to use i18n when used in modules.

* Add the file

* Fix import lint

* Name the translate function _t

Then it should continue to get picked up by the script

This seems a bit flaky and ew but I'm not sure I want to get into
changing this in this PR.

* Put humanize back to calling something called _t too

* Missed one

* Add i18n context wrapper to stories

* Unused import

* Fix imports

* wrap richitem

* Wrap other richitem & richlist

* One day I will get my head around this syntax

* Fix import spacing

* Add wrapper to test

* unused import

* Hack around dependency cycle

* Make a moduleapi instance for tests

* Add i18n wrapper to jest-matrix-react

* Simple test for i18napi

* Import type

* Move i18n context wrapper to storybook template

* Unused imports & fix pill story

* Move i18n to its own provider

* Add i18ncontext wrapper to jest tests

* imports

* Bump module api to 1.7.0

* tsdoc
2025-12-02 13:47:14 +00:00

56 lines
2.2 KiB
TypeScript

/*
Copyright 2024 New Vector Ltd.
Copyright 2020, 2021 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import { type I18nApi } from "@element-hq/element-web-module-api";
import { _t as _tFromModule } from "./i18n";
// 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;
/**
* 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: number, i18nApi?: I18nApi): string {
const now = Date.now();
let msAgo = now - timeMillis;
const minutes = Math.abs(Math.ceil(msAgo / 60000));
const hours = Math.ceil(minutes / 60);
const days = Math.ceil(hours / 24);
const _t = i18nApi?.translate ?? _tFromModule;
if (msAgo >= 0) {
// Past
if (msAgo <= MILLISECONDS_RECENT) return _t("time|few_seconds_ago");
if (msAgo <= MILLISECONDS_1_MIN) return _t("time|about_minute_ago");
if (minutes <= MINUTES_UNDER_1_HOUR) return _t("time|n_minutes_ago", { num: minutes });
if (minutes <= MINUTES_1_HOUR) return _t("time|about_hour_ago");
if (hours <= HOURS_UNDER_1_DAY) return _t("time|n_hours_ago", { num: hours });
if (hours <= HOURS_1_DAY) return _t("time|about_day_ago");
return _t("time|n_days_ago", { num: days });
} else {
// Future
msAgo = Math.abs(msAgo);
if (msAgo <= MILLISECONDS_RECENT) return _t("time|in_few_seconds");
if (msAgo <= MILLISECONDS_1_MIN) return _t("time|in_about_minute");
if (minutes <= MINUTES_UNDER_1_HOUR) return _t("time|in_n_minutes", { num: minutes });
if (minutes <= MINUTES_1_HOUR) return _t("time|in_about_hour");
if (hours <= HOURS_UNDER_1_DAY) return _t("time|in_n_hours", { num: hours });
if (hours <= HOURS_1_DAY) return _t("time|in_about_day");
return _t("time|in_n_days", { num: days });
}
}