feat: date time formats from locales (#4029)

Co-authored-by: Sami Mazouz <sychocouldy@gmail.com>
This commit is contained in:
2024-10-23 18:11:07 +08:00 committed by GitHub
parent 983d42160d
commit d041515e19
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 47 additions and 11 deletions

View File

@ -273,7 +273,7 @@ export default class UserListPage extends AdminPage {
name: app.translator.trans('core.admin.users.grid.columns.join_time.title'), name: app.translator.trans('core.admin.users.grid.columns.join_time.title'),
content: (user: User) => ( content: (user: User) => (
<span className="UserList-joinDate" title={user.joinTime()}> <span className="UserList-joinDate" title={user.joinTime()}>
{dayjs(user.joinTime()).format('LLL')} {app.translator.formatDateTime(dayjs(user.joinTime()), 'core.lib.datetime_formats.userListJoinDate')}
</span> </span>
), ),
}, },

View File

@ -1,12 +1,15 @@
import type { Dayjs } from 'dayjs';
import { RichMessageFormatter, mithrilRichHandler, NestedStringArray } from '@askvortsov/rich-icu-message-formatter'; import { RichMessageFormatter, mithrilRichHandler, NestedStringArray } from '@askvortsov/rich-icu-message-formatter';
import { pluralTypeHandler, selectTypeHandler } from '@ultraq/icu-message-formatter'; import { pluralTypeHandler, selectTypeHandler } from '@ultraq/icu-message-formatter';
import username from './helpers/username'; import username from './helpers/username';
import User from './models/User'; import User from './models/User';
import extract from './utils/extract'; import extract from './utils/extract';
import extractText from './utils/extractText'; import extractText from './utils/extractText';
import ItemList from './utils/ItemList';
type Translations = Record<string, string>; type Translations = Record<string, string>;
type TranslatorParameters = Record<string, unknown>; type TranslatorParameters = Record<string, unknown>;
type DateTimeFormatCallback = (id?: string) => string | void;
export default class Translator { export default class Translator {
/** /**
@ -14,6 +17,11 @@ export default class Translator {
*/ */
translations: Translations = {}; translations: Translations = {};
/**
* A item list of date time format callbacks.
*/
dateTimeFormats: ItemList<DateTimeFormatCallback> = new ItemList();
/** /**
* The underlying ICU MessageFormatter util. * The underlying ICU MessageFormatter util.
*/ */
@ -88,4 +96,23 @@ export default class Translator {
return id; return id;
} }
/**
* Formats the time.
*
* The format of the time will be chosen by the following order:
* - Custom format defined in the item list.
* - The format defined in current locale.
* - DayJS default format.
*/
formatDateTime(time: Dayjs, id: string): string {
const formatCallback = this.dateTimeFormats.has(id) && this.dateTimeFormats.get(id);
if (formatCallback) {
const result = formatCallback.apply(this, [id]);
if (result) return result;
}
return time.format(this.translations[id]);
}
} }

View File

@ -1,5 +1,6 @@
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import type Mithril from 'mithril'; import type Mithril from 'mithril';
import app from '../app';
/** /**
* The `fullTime` helper displays a formatted time string wrapped in a <time> * The `fullTime` helper displays a formatted time string wrapped in a <time>
@ -9,7 +10,7 @@ export default function fullTime(time: Date): Mithril.Vnode {
const d = dayjs(time); const d = dayjs(time);
const datetime = d.format(); const datetime = d.format();
const full = d.format('LLLL'); const full = app.translator.formatDateTime(d, 'core.lib.datetime_formats.fullTime');
return ( return (
<time pubdate datetime={datetime}> <time pubdate datetime={datetime}>

View File

@ -1,5 +1,6 @@
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import type Mithril from 'mithril'; import type Mithril from 'mithril';
import app from '../app';
import humanTimeUtil from '../utils/humanTime'; import humanTimeUtil from '../utils/humanTime';
/** /**
@ -11,7 +12,7 @@ export default function humanTime(time: Date): Mithril.Vnode {
const d = dayjs(time); const d = dayjs(time);
const datetime = d.format(); const datetime = d.format();
const full = d.format('LLLL'); const full = app.translator.formatDateTime(d, 'core.lib.datetime_formats.fullTime');
const ago = humanTimeUtil(time); const ago = humanTimeUtil(time);
return ( return (

View File

@ -1,3 +1,4 @@
import app from '../app';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
/** /**
@ -15,17 +16,15 @@ export default function humanTime(time: dayjs.ConfigType): string {
d = now; d = now;
} }
const day = 864e5;
const diff = d.diff(dayjs());
let ago: string; let ago: string;
// If this date was more than a month ago, we'll show the name of the month // If this date was more than a month ago, we'll show the name of the month
// in the string. If it wasn't this year, we'll show the year as well. // in the string. If it wasn't this year, we'll show the year as well.
if (diff < -30 * day) { if (d.diff(now, 'day') < -30) {
if (d.year() === dayjs().year()) { if (d.isSame(now, 'year')) {
ago = d.format('D MMM'); ago = app.translator.formatDateTime(d, 'core.lib.datetime_formats.humanTimeShort');
} else { } else {
ago = d.format('ll'); ago = app.translator.formatDateTime(d, 'core.lib.datetime_formats.humanTimeFull');
} }
} else { } else {
ago = d.fromNow(); ago = d.fromNow();

View File

@ -10,7 +10,7 @@ function updateHumanTimes() {
} }
/** /**
* The `liveHumanTimes` initializer sets up a loop every 1 second to update * The `liveHumanTimes` initializer sets up a loop every 10 seconds to update
* timestamps rendered with the `humanTime` helper. * timestamps rendered with the `humanTime` helper.
*/ */
export default function liveHumanTimes() { export default function liveHumanTimes() {

View File

@ -270,7 +270,7 @@ export default class PostStream extends Component {
// set the index to the last post. // set the index to the last post.
this.stream.index = indexFromViewPort !== null ? indexFromViewPort + 1 : this.stream.count(); this.stream.index = indexFromViewPort !== null ? indexFromViewPort + 1 : this.stream.count();
this.stream.visible = visible; this.stream.visible = visible;
if (period) this.stream.description = dayjs(period).format('MMMM YYYY'); if (period) this.stream.description = app.translator.formatDateTime(dayjs(period), 'core.lib.datetime_formats.scrubber');
} }
/** /**

View File

@ -843,6 +843,14 @@ core:
username: username:
deleted_text: "[deleted]" deleted_text: "[deleted]"
# These are DayJS formats used in core.
datetime_formats:
fullTime: LLLL
humanTimeShort: D MMM
humanTimeLong: ll
scrubber: MMMM YYYY
userListJoinDate: LLL
# Translations in this namespace are used in views other than Flarum's normal JS client. # Translations in this namespace are used in views other than Flarum's normal JS client.
views: views:
# Translations in this namespace are displayed by the basic HTML admin index. # Translations in this namespace are displayed by the basic HTML admin index.