Make relatives times coherent with web (#9062)

* Make relatives times coherent with web

* Fix test
This commit is contained in:
Daniel Espino García 2025-08-12 14:28:25 +02:00 committed by GitHub
parent 71cec5a3e2
commit 4f27a022c3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 133 additions and 177 deletions

View file

@ -9,118 +9,157 @@ import FriendlyDate from './index';
describe('Friendly Date', () => {
it('should render correctly', () => {
jest.useFakeTimers();
jest.setSystemTime(new Date('2020-05-15T00:00:00.000Z'));
const justNow = new Date();
justNow.setSeconds(justNow.getSeconds() - 10);
let value = justNow.getTime();
const justNowText = renderWithIntl(
<FriendlyDate value={justNow}/>,
<FriendlyDate value={value}/>,
);
expect(justNowText.getByText('Now')).toBeTruthy();
const minutesAgo = new Date();
minutesAgo.setMinutes(minutesAgo.getMinutes() - 1);
value = minutesAgo.getTime();
const minutesAgoText = renderWithIntl(
<FriendlyDate value={minutesAgo}/>,
<FriendlyDate value={value}/>,
);
expect(minutesAgoText.getByText('1 min ago')).toBeTruthy();
expect(minutesAgoText.getByText('1 min. ago')).toBeTruthy();
const hoursAgo = new Date();
hoursAgo.setHours(hoursAgo.getHours() - 4);
value = hoursAgo.getTime();
const hoursAgoText = renderWithIntl(
<FriendlyDate value={hoursAgo}/>,
<FriendlyDate value={value}/>,
);
expect(hoursAgoText.getByText('4 hours ago')).toBeTruthy();
const yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1);
value = yesterday.getTime();
const yesterdayText = renderWithIntl(
<FriendlyDate value={yesterday}/>,
<FriendlyDate value={value}/>,
);
expect(yesterdayText.getByText('Yesterday')).toBeTruthy();
expect(yesterdayText.getByText('yesterday')).toBeTruthy();
const daysAgo = new Date();
daysAgo.setDate(daysAgo.getDate() - 10);
value = daysAgo.getTime();
const daysAgoText = renderWithIntl(
<FriendlyDate value={daysAgo}/>,
<FriendlyDate value={value}/>,
);
expect(daysAgoText.getByText('10 days ago')).toBeTruthy();
// Difference is less than 30 days
const daysEdgeCase = new Date(2020, 3, 28);
const daysEdgeCaseTodayDate = new Date(2020, 4, 28);
jest.setSystemTime(daysEdgeCaseTodayDate);
value = daysEdgeCase.getTime();
const daysEdgeCaseText = renderWithIntl(
<FriendlyDate
sourceDate={daysEdgeCaseTodayDate}
value={daysEdgeCase}
/>,
<FriendlyDate value={value}/>,
);
expect(daysEdgeCaseText.getByText('1 month ago')).toBeTruthy();
expect(daysEdgeCaseText.getByText('last month')).toBeTruthy();
const daysAgoMax = new Date(2020, 4, 6);
const daysAgoMaxTodayDate = new Date(2020, 5, 5);
jest.setSystemTime(daysAgoMaxTodayDate);
value = daysAgoMax.getTime();
const daysAgoMaxText = renderWithIntl(
<FriendlyDate
sourceDate={daysAgoMaxTodayDate}
value={daysAgoMax}
/>,
<FriendlyDate value={value}/>,
);
expect(daysAgoMaxText.getByText('30 days ago')).toBeTruthy();
const monthsAgo = new Date();
monthsAgo.setMonth(monthsAgo.getMonth() - 2);
value = monthsAgo.getTime();
const monthsAgoText = renderWithIntl(
<FriendlyDate value={monthsAgo}/>,
<FriendlyDate value={value}/>,
);
expect(monthsAgoText.getByText('2 months ago')).toBeTruthy();
const yearsAgo = new Date();
yearsAgo.setFullYear(yearsAgo.getFullYear() - 2);
value = yearsAgo.getTime();
const yearsAgoText = renderWithIntl(
<FriendlyDate value={yearsAgo}/>,
<FriendlyDate value={value}/>,
);
expect(yearsAgoText.getByText('2 years ago')).toBeTruthy();
jest.useRealTimers();
});
it('should render correctly with times in the future', () => {
jest.useFakeTimers();
jest.setSystemTime(new Date('2020-05-15T00:00:00.000Z'));
const justNow = new Date();
justNow.setSeconds(justNow.getSeconds() + 10);
let value = justNow.getTime();
const justNowText = renderWithIntl(
<FriendlyDate value={justNow}/>,
<FriendlyDate value={value}/>,
);
expect(justNowText.getByText('Now')).toBeTruthy();
const inMinutes = new Date();
inMinutes.setMinutes(inMinutes.getMinutes() + 2);
value = inMinutes.getTime();
const inMinutesText = renderWithIntl(
<FriendlyDate value={inMinutes}/>,
<FriendlyDate value={value}/>,
);
expect(inMinutesText.getByText('in 2 mins')).toBeTruthy();
expect(inMinutesText.getByText('in 2 min.')).toBeTruthy();
const inHours = new Date();
inHours.setHours(inHours.getHours() + 2);
value = inHours.getTime();
const inHoursText = renderWithIntl(
<FriendlyDate value={inHours}/>,
<FriendlyDate value={value}/>,
);
expect(inHoursText.getByText('in 2 hours')).toBeTruthy();
const inDays = new Date();
inDays.setDate(inDays.getDate() + 2);
value = inDays.getTime();
const inDaysText = renderWithIntl(
<FriendlyDate value={inDays}/>,
<FriendlyDate value={value}/>,
);
expect(inDaysText.getByText('in 2 days')).toBeTruthy();
const inDaysEdgeCase = new Date(2020, 5, 28);
const inDaysEdgeCaseTodayDate = new Date(2020, 4, 28);
jest.setSystemTime(inDaysEdgeCaseTodayDate);
value = inDaysEdgeCase.getTime();
const inDaysEdgeCaseText = renderWithIntl(
<FriendlyDate value={value}/>,
);
expect(inDaysEdgeCaseText.getByText('next month')).toBeTruthy();
const inDaysMax = new Date(2020, 5, 4);
const inDaysMaxTodayDate = new Date(2020, 4, 5);
jest.setSystemTime(inDaysMaxTodayDate);
value = inDaysMax.getTime();
const inDaysMaxText = renderWithIntl(
<FriendlyDate value={value}/>,
);
expect(inDaysMaxText.getByText('in 30 days')).toBeTruthy();
const inMonths = new Date();
inMonths.setMonth(inMonths.getMonth() + 2);
value = inMonths.getTime();
const inMonthsText = renderWithIntl(
<FriendlyDate value={inMonths}/>,
<FriendlyDate value={value}/>,
);
expect(inMonthsText.getByText('in 2 months')).toBeTruthy();
const inYears = new Date();
inYears.setFullYear(inYears.getFullYear() + 2);
value = inYears.getTime();
const inYearsText = renderWithIntl(
<FriendlyDate value={inYears}/>,
<FriendlyDate value={value}/>,
);
expect(inYearsText.getByText('in 2 years')).toBeTruthy();
jest.useRealTimers();
});
});

View file

@ -1,43 +1,49 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import moment from 'moment';
import React from 'react';
import {type IntlShape, useIntl} from 'react-intl';
import {type StyleProp, Text, type TextStyle} from 'react-native';
import {DateTime} from '@constants';
import {isYesterday} from '@utils/datetime';
const {SECONDS} = DateTime;
type Props = {
style?: StyleProp<TextStyle>;
sourceDate?: number | Date;
value: number | Date;
value: number;
};
function FriendlyDate({style, sourceDate, value}: Props) {
function FriendlyDate({style, value}: Props) {
const intl = useIntl();
const formattedTime = getFriendlyDate(intl, value, sourceDate);
const formattedTime = getFriendlyDate(intl, value);
return (
<Text style={style}>{formattedTime}</Text>
);
}
export function getFriendlyDate(intl: IntlShape, inputDate: number | Date, sourceDate?: number | Date): string {
const today = sourceDate ? new Date(sourceDate) : new Date();
function getDiff(inputDate: number, unit: moment.unitOfTime.Diff) {
const input = new Date(inputDate);
const today = new Date();
const momentA = moment.utc(input.getTime());
const momentB = moment.utc(today.getTime());
const diff = momentA.startOf(unit).diff(momentB.startOf(unit), unit);
return diff;
}
export function getFriendlyDate(intl: IntlShape, inputDate: number): string {
const today = new Date();
const date = new Date(inputDate);
const difference = (today.getTime() - date.getTime()) / 1000;
if (difference < 0) {
return getFriendlyDateAfter(intl, -difference, date, today);
}
const difference = (date.getTime() - today.getTime()) / 1000;
const absoluteDifference = Math.abs(difference);
const sign = Math.sign(difference);
return getFriendlyDateBefore(intl, difference, date, today);
}
function getFriendlyDateAfter(intl: IntlShape, difference: number, date: Date, today: Date): string {
// Message: Now
if (difference < SECONDS.MINUTE) {
if (absoluteDifference < SECONDS.MINUTE) {
return intl.formatMessage({
id: 'friendly_date.now',
defaultMessage: 'Now',
@ -45,143 +51,31 @@ function getFriendlyDateAfter(intl: IntlShape, difference: number, date: Date, t
}
// Message: Minutes
if (difference < SECONDS.HOUR) {
const minutes = Math.floor(Math.round((10 * difference) / SECONDS.MINUTE) / 10);
return intl.formatMessage({
id: 'friendly_date.mins',
defaultMessage: 'in {count} {count, plural, one {min} other {mins}}',
}, {
count: minutes,
});
if (absoluteDifference < SECONDS.HOUR) {
return intl.formatRelativeTime(getDiff(inputDate, 'minute'), 'minute', {numeric: 'auto', style: 'short'});
}
// Message: Hours
if (difference < SECONDS.DAY) {
const hours = Math.floor(Math.round((10 * difference) / SECONDS.HOUR) / 10);
return intl.formatMessage({
id: 'friendly_date.hours',
defaultMessage: 'in {count} {count, plural, one {hour} other {hours}}',
}, {
count: hours,
});
if (absoluteDifference < SECONDS.DAY) {
return intl.formatRelativeTime(getDiff(inputDate, 'hour'), 'hour', {numeric: 'auto'});
}
// Message: Days
if (difference < SECONDS.DAYS_31) {
if (today.getDate() + 1 === date.getDate() && today.getMonth() === date.getMonth()) {
return intl.formatMessage({
id: 'friendly_date.tomorrow',
defaultMessage: 'Tomorrow',
});
}
const completedAMonth = today.getMonth() !== date.getMonth() && today.getDate() <= date.getDate();
if (absoluteDifference < SECONDS.DAYS_31) {
const passedDate = sign === 1 ? today.getDate() <= date.getDate() : today.getDate() >= date.getDate();
const completedAMonth = today.getMonth() !== date.getMonth() && passedDate;
if (!completedAMonth) {
const days = Math.floor(Math.round((10 * difference) / SECONDS.DAY) / 10) || 1;
return intl.formatMessage({
id: 'friendly_date.days',
defaultMessage: 'in {count} {count, plural, one {day} other {days}}',
}, {
count: days,
});
return intl.formatRelativeTime(getDiff(inputDate, 'day'), 'day', {numeric: 'auto'});
}
}
// Message: Months
if (difference < SECONDS.DAYS_366) {
const months = Math.floor(Math.round((10 * difference) / SECONDS.DAYS_30) / 10) || 1;
return intl.formatMessage({
id: 'friendly_date.months',
defaultMessage: 'in {count} {count, plural, one {month} other {months}}',
}, {
count: months,
});
if (absoluteDifference < SECONDS.DAYS_366) {
return intl.formatRelativeTime(getDiff(inputDate, 'month'), 'month', {numeric: 'auto'});
}
// Message: Years
const years = Math.floor(Math.round((10 * difference) / SECONDS.DAYS_365) / 10) || 1;
return intl.formatMessage({
id: 'friendly_date.years',
defaultMessage: 'in {count} {count, plural, one {year} other {years}}',
}, {
count: years,
});
}
function getFriendlyDateBefore(intl: IntlShape, difference: number, date: Date, today: Date): string {
// Message: Now
if (difference < SECONDS.MINUTE) {
return intl.formatMessage({
id: 'friendly_date.now',
defaultMessage: 'Now',
});
}
// Message: Minutes Ago
if (difference < SECONDS.HOUR) {
const minutes = Math.floor(Math.round((10 * difference) / SECONDS.MINUTE) / 10);
return intl.formatMessage({
id: 'friendly_date.minsAgo',
defaultMessage: '{count} {count, plural, one {min} other {mins}} ago',
}, {
count: minutes,
});
}
// Message: Hours Ago
if (difference < SECONDS.DAY) {
const hours = Math.floor(Math.round((10 * difference) / SECONDS.HOUR) / 10);
return intl.formatMessage({
id: 'friendly_date.hoursAgo',
defaultMessage: '{count} {count, plural, one {hour} other {hours}} ago',
}, {
count: hours,
});
}
// Message: Days Ago
if (difference < SECONDS.DAYS_31) {
if (isYesterday(date)) {
return intl.formatMessage({
id: 'friendly_date.yesterday',
defaultMessage: 'Yesterday',
});
}
const completedAMonth = today.getMonth() !== date.getMonth() && today.getDate() >= date.getDate();
if (!completedAMonth) {
const days = Math.floor(Math.round((10 * difference) / SECONDS.DAY) / 10) || 1;
return intl.formatMessage({
id: 'friendly_date.daysAgo',
defaultMessage: '{count} {count, plural, one {day} other {days}} ago',
}, {
count: days,
});
}
}
// Message: Months Ago
if (difference < SECONDS.DAYS_366) {
const completedAnYear = today.getFullYear() !== date.getFullYear() &&
today.getMonth() >= date.getMonth() &&
today.getDate() >= date.getDate();
if (!completedAnYear) {
const months = Math.floor(Math.round((10 * difference) / SECONDS.DAYS_30) / 10) || 1;
return intl.formatMessage({
id: 'friendly_date.monthsAgo',
defaultMessage: '{count} {count, plural, one {month} other {months}} ago',
}, {
count: months,
});
}
}
// Message: Years Ago
const years = Math.floor(Math.round((10 * difference) / SECONDS.DAYS_365) / 10) || 1;
return intl.formatMessage({
id: 'friendly_date.yearsAgo',
defaultMessage: '{count} {count, plural, one {year} other {years}} ago',
}, {
count: years,
});
return intl.formatRelativeTime(getDiff(inputDate, 'year'), 'year', {numeric: 'auto'});
}
export default FriendlyDate;

View file

@ -24,6 +24,7 @@ function loadTranslation(locale?: string): {[x: string]: string} {
require('@formatjs/intl-numberformat/locale-data/bg');
require('@formatjs/intl-datetimeformat/locale-data/bg');
require('@formatjs/intl-listformat/locale-data/bg');
require('@formatjs/intl-relativetimeformat/locale-data/bg');
translations = require('@assets/i18n/bg.json');
break;
@ -32,6 +33,7 @@ function loadTranslation(locale?: string): {[x: string]: string} {
require('@formatjs/intl-numberformat/locale-data/de');
require('@formatjs/intl-datetimeformat/locale-data/de');
require('@formatjs/intl-listformat/locale-data/de');
require('@formatjs/intl-relativetimeformat/locale-data/de');
translations = require('@assets/i18n/de.json');
break;
@ -40,6 +42,7 @@ function loadTranslation(locale?: string): {[x: string]: string} {
require('@formatjs/intl-numberformat/locale-data/en');
require('@formatjs/intl-datetimeformat/locale-data/en');
require('@formatjs/intl-listformat/locale-data/en');
require('@formatjs/intl-relativetimeformat/locale-data/en');
translations = require('@assets/i18n/en_AU.json');
break;
@ -48,6 +51,7 @@ function loadTranslation(locale?: string): {[x: string]: string} {
require('@formatjs/intl-numberformat/locale-data/es');
require('@formatjs/intl-datetimeformat/locale-data/es');
require('@formatjs/intl-listformat/locale-data/es');
require('@formatjs/intl-relativetimeformat/locale-data/es');
translations = require('@assets/i18n/es.json');
break;
@ -56,6 +60,7 @@ function loadTranslation(locale?: string): {[x: string]: string} {
require('@formatjs/intl-numberformat/locale-data/fa');
require('@formatjs/intl-datetimeformat/locale-data/fa');
require('@formatjs/intl-listformat/locale-data/fa');
require('@formatjs/intl-relativetimeformat/locale-data/fa');
translations = require('@assets/i18n/fa.json');
break;
@ -64,6 +69,7 @@ function loadTranslation(locale?: string): {[x: string]: string} {
require('@formatjs/intl-numberformat/locale-data/fr');
require('@formatjs/intl-datetimeformat/locale-data/fr');
require('@formatjs/intl-listformat/locale-data/fr');
require('@formatjs/intl-relativetimeformat/locale-data/fr');
translations = require('@assets/i18n/fr.json');
break;
@ -72,6 +78,7 @@ function loadTranslation(locale?: string): {[x: string]: string} {
require('@formatjs/intl-numberformat/locale-data/hu');
require('@formatjs/intl-datetimeformat/locale-data/hu');
require('@formatjs/intl-listformat/locale-data/hu');
require('@formatjs/intl-relativetimeformat/locale-data/hu');
translations = require('@assets/i18n/hu.json');
break;
@ -80,6 +87,7 @@ function loadTranslation(locale?: string): {[x: string]: string} {
require('@formatjs/intl-numberformat/locale-data/it');
require('@formatjs/intl-datetimeformat/locale-data/it');
require('@formatjs/intl-listformat/locale-data/it');
require('@formatjs/intl-relativetimeformat/locale-data/it');
translations = require('@assets/i18n/it.json');
break;
@ -88,6 +96,7 @@ function loadTranslation(locale?: string): {[x: string]: string} {
require('@formatjs/intl-numberformat/locale-data/ja');
require('@formatjs/intl-datetimeformat/locale-data/ja');
require('@formatjs/intl-listformat/locale-data/ja');
require('@formatjs/intl-relativetimeformat/locale-data/ja');
translations = require('@assets/i18n/ja.json');
break;
@ -96,6 +105,7 @@ function loadTranslation(locale?: string): {[x: string]: string} {
require('@formatjs/intl-numberformat/locale-data/ko');
require('@formatjs/intl-datetimeformat/locale-data/ko');
require('@formatjs/intl-listformat/locale-data/ko');
require('@formatjs/intl-relativetimeformat/locale-data/ko');
translations = require('@assets/i18n/ko.json');
break;
@ -104,6 +114,7 @@ function loadTranslation(locale?: string): {[x: string]: string} {
require('@formatjs/intl-numberformat/locale-data/nl');
require('@formatjs/intl-datetimeformat/locale-data/nl');
require('@formatjs/intl-listformat/locale-data/nl');
require('@formatjs/intl-relativetimeformat/locale-data/nl');
translations = require('@assets/i18n/nl.json');
break;
@ -112,6 +123,7 @@ function loadTranslation(locale?: string): {[x: string]: string} {
require('@formatjs/intl-numberformat/locale-data/pl');
require('@formatjs/intl-datetimeformat/locale-data/pl');
require('@formatjs/intl-listformat/locale-data/pl');
require('@formatjs/intl-relativetimeformat/locale-data/pl');
translations = require('@assets/i18n/pl.json');
break;
@ -120,6 +132,7 @@ function loadTranslation(locale?: string): {[x: string]: string} {
require('@formatjs/intl-numberformat/locale-data/pt');
require('@formatjs/intl-datetimeformat/locale-data/pt');
require('@formatjs/intl-listformat/locale-data/pt');
require('@formatjs/intl-relativetimeformat/locale-data/pt');
translations = require('@assets/i18n/pt-BR.json');
break;
@ -128,6 +141,7 @@ function loadTranslation(locale?: string): {[x: string]: string} {
require('@formatjs/intl-numberformat/locale-data/ro');
require('@formatjs/intl-datetimeformat/locale-data/ro');
require('@formatjs/intl-listformat/locale-data/ro');
require('@formatjs/intl-relativetimeformat/locale-data/ro');
translations = require('@assets/i18n/ro.json');
break;
@ -136,6 +150,7 @@ function loadTranslation(locale?: string): {[x: string]: string} {
require('@formatjs/intl-numberformat/locale-data/ru');
require('@formatjs/intl-datetimeformat/locale-data/ru');
require('@formatjs/intl-listformat/locale-data/ru');
require('@formatjs/intl-relativetimeformat/locale-data/ru');
translations = require('@assets/i18n/ru.json');
break;
@ -144,6 +159,7 @@ function loadTranslation(locale?: string): {[x: string]: string} {
require('@formatjs/intl-numberformat/locale-data/sv');
require('@formatjs/intl-datetimeformat/locale-data/sv');
require('@formatjs/intl-listformat/locale-data/sv');
require('@formatjs/intl-relativetimeformat/locale-data/sv');
translations = require('@assets/i18n/sv.json');
break;
@ -152,6 +168,7 @@ function loadTranslation(locale?: string): {[x: string]: string} {
require('@formatjs/intl-numberformat/locale-data/tr');
require('@formatjs/intl-datetimeformat/locale-data/tr');
require('@formatjs/intl-listformat/locale-data/tr');
require('@formatjs/intl-relativetimeformat/locale-data/tr');
translations = require('@assets/i18n/tr.json');
break;
@ -160,6 +177,7 @@ function loadTranslation(locale?: string): {[x: string]: string} {
require('@formatjs/intl-numberformat/locale-data/uk');
require('@formatjs/intl-datetimeformat/locale-data/uk');
require('@formatjs/intl-listformat/locale-data/uk');
require('@formatjs/intl-relativetimeformat/locale-data/uk');
translations = require('@assets/i18n/uk.json');
break;
@ -168,6 +186,7 @@ function loadTranslation(locale?: string): {[x: string]: string} {
require('@formatjs/intl-numberformat/locale-data/vi');
require('@formatjs/intl-datetimeformat/locale-data/vi');
require('@formatjs/intl-listformat/locale-data/vi');
require('@formatjs/intl-relativetimeformat/locale-data/vi');
translations = require('@assets/i18n/vi.json');
break;
@ -184,6 +203,7 @@ function loadTranslation(locale?: string): {[x: string]: string} {
require('@formatjs/intl-numberformat/locale-data/en');
require('@formatjs/intl-datetimeformat/locale-data/en');
require('@formatjs/intl-listformat/locale-data/en');
require('@formatjs/intl-relativetimeformat/locale-data/en');
translations = en;
break;
@ -201,6 +221,7 @@ function loadChinesePolyfills() {
require('@formatjs/intl-numberformat/locale-data/zh');
require('@formatjs/intl-datetimeformat/locale-data/zh');
require('@formatjs/intl-listformat/locale-data/zh');
require('@formatjs/intl-relativetimeformat/locale-data/zh');
}
export function getLocaleFromLanguage(lang: string) {

View file

@ -239,7 +239,7 @@ describe('ChecklistItem', () => {
const {getByTestId, rerender, queryByTestId} = renderWithIntl(<ChecklistItem {...props}/>);
let chip = getByTestId('base-chip-component');
expect(chip).toBeVisible();
expect(chip.props.label).toBe('Due Tomorrow');
expect(chip.props.label).toBe('Due tomorrow');
expect(chip.props.type).toBe('normal');
expect(chip.props.boldText).toBe(false);
expect(chip.props.prefix).toBeDefined();
@ -263,7 +263,7 @@ describe('ChecklistItem', () => {
rerender(<ChecklistItem {...props}/>);
chip = getByTestId('base-chip-component');
expect(chip).toBeVisible();
expect(chip.props.label).toBe('Due 1 year ago');
expect(chip.props.label).toBe('Due last year');
expect(chip.props.type).toBe('danger');
expect(chip.props.boldText).toBe(true);
expect(chip.props.prefix).toBeDefined();

View file

@ -367,19 +367,7 @@
"find_channels.new_channel": "New Channel",
"find_channels.open_dm": "Open a DM",
"find_channels.title": "Find Channels",
"friendly_date.days": "in {count} {count, plural, one {day} other {days}}",
"friendly_date.daysAgo": "{count} {count, plural, one {day} other {days}} ago",
"friendly_date.hours": "in {count} {count, plural, one {hour} other {hours}}",
"friendly_date.hoursAgo": "{count} {count, plural, one {hour} other {hours}} ago",
"friendly_date.mins": "in {count} {count, plural, one {min} other {mins}}",
"friendly_date.minsAgo": "{count} {count, plural, one {min} other {mins}} ago",
"friendly_date.months": "in {count} {count, plural, one {month} other {months}}",
"friendly_date.monthsAgo": "{count} {count, plural, one {month} other {months}} ago",
"friendly_date.now": "Now",
"friendly_date.tomorrow": "Tomorrow",
"friendly_date.years": "in {count} {count, plural, one {year} other {years}}",
"friendly_date.yearsAgo": "{count} {count, plural, one {year} other {years}} ago",
"friendly_date.yesterday": "Yesterday",
"gallery.copy_link.failed": "Failed to copy link to clipboard",
"gallery.downloading": "Downloading...",
"gallery.footer.channel_name": "Shared in {channelName}",

View file

@ -48,6 +48,7 @@ if (global.HermesInternal) {
require('@formatjs/intl-datetimeformat/polyfill-force');
require('@formatjs/intl-datetimeformat/add-all-tz');
require('@formatjs/intl-listformat/polyfill-force');
require('@formatjs/intl-relativetimeformat/polyfill-force');
}
if (Platform.OS === 'android') {

12
package-lock.json generated
View file

@ -16,6 +16,7 @@
"@formatjs/intl-locale": "4.2.11",
"@formatjs/intl-numberformat": "8.15.4",
"@formatjs/intl-pluralrules": "5.4.4",
"@formatjs/intl-relativetimeformat": "11.4.11",
"@gorhom/bottom-sheet": "5.1.2",
"@mattermost/calls": "github:mattermost/calls-common#ab53c24053b89e4d3d853bbe1244892899517f45",
"@mattermost/compass-icons": "0.1.48",
@ -4219,6 +4220,17 @@
"tslib": "^2.8.0"
}
},
"node_modules/@formatjs/intl-relativetimeformat": {
"version": "11.4.11",
"resolved": "https://registry.npmjs.org/@formatjs/intl-relativetimeformat/-/intl-relativetimeformat-11.4.11.tgz",
"integrity": "sha512-EiknGq2z8ZcIDUhf3kkizrPmHHdD0lO6B3OXG6u0o0lAvusSc9gkr33F2S/vzOHM9iTh6oHmzTl21klGsLR9LA==",
"license": "MIT",
"dependencies": {
"@formatjs/ecma402-abstract": "2.3.4",
"@formatjs/intl-localematcher": "0.6.1",
"tslib": "^2.8.0"
}
},
"node_modules/@gorhom/bottom-sheet": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/@gorhom/bottom-sheet/-/bottom-sheet-5.1.2.tgz",

View file

@ -17,6 +17,7 @@
"@formatjs/intl-locale": "4.2.11",
"@formatjs/intl-numberformat": "8.15.4",
"@formatjs/intl-pluralrules": "5.4.4",
"@formatjs/intl-relativetimeformat": "11.4.11",
"@gorhom/bottom-sheet": "5.1.2",
"@mattermost/calls": "github:mattermost/calls-common#ab53c24053b89e4d3d853bbe1244892899517f45",
"@mattermost/compass-icons": "0.1.48",