Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: date time formats from locales #4029

Open
wants to merge 17 commits into
base: 2.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions framework/core/js/src/common/Translator.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
import type { Dayjs } from 'dayjs';
import { RichMessageFormatter, mithrilRichHandler, NestedStringArray } from '@askvortsov/rich-icu-message-formatter';
import { pluralTypeHandler, selectTypeHandler } from '@ultraq/icu-message-formatter';
import username from './helpers/username';
import User from './models/User';
import extract from './utils/extract';
import extractText from './utils/extractText';
import ItemList from './utils/ItemList';

type Translations = Record<string, string>;
type TranslatorParameters = Record<string, unknown>;
type DateTimeFormatCallback = (format?: string) => string | void;
YUCLing marked this conversation as resolved.
Show resolved Hide resolved

export default class Translator {
/**
* A map of translation keys to their translated values.
*/
translations: Translations = {};

/**
* A item list of date time format callbacks.
*/
dateTimeFormats: ItemList<DateTimeFormatCallback> = new ItemList();

/**
* The underlying ICU MessageFormatter util.
*/
Expand Down Expand Up @@ -88,4 +96,22 @@ export default class Translator {

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 format = id && (this.translations[id] ?? id);
const formatCallback = id && this.dateTimeFormats.has(id) && this.dateTimeFormats.get(id);
if (formatCallback) {
const result = formatCallback.apply(this, [format]);
if (result) return result;
}
return time.format(format);
}
}
3 changes: 2 additions & 1 deletion framework/core/js/src/common/helpers/fullTime.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import dayjs from 'dayjs';
import type Mithril from 'mithril';
import app from '../app';

/**
* The `fullTime` helper displays a formatted time string wrapped in a <time>
Expand All @@ -9,7 +10,7 @@ export default function fullTime(time: Date): Mithril.Vnode {
const d = dayjs(time);

const datetime = d.format();
const full = d.format('LLLL');
const full = app.translator.formatDateTime(d, 'core.lib.datetime_formats.full_time');

return (
<time pubdate datetime={datetime}>
Expand Down
3 changes: 2 additions & 1 deletion framework/core/js/src/common/helpers/humanTime.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import dayjs from 'dayjs';
import type Mithril from 'mithril';
import app from '../app';
import humanTimeUtil from '../utils/humanTime';

/**
Expand All @@ -11,7 +12,7 @@ export default function humanTime(time: Date): Mithril.Vnode {
const d = dayjs(time);

const datetime = d.format();
const full = d.format('LLLL');
const full = app.translator.formatDateTime(d, 'core.lib.datetime_formats.full_time');
const ago = humanTimeUtil(time);

return (
Expand Down
11 changes: 5 additions & 6 deletions framework/core/js/src/common/utils/humanTime.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import app from '../app';
import dayjs from 'dayjs';

/**
Expand All @@ -15,17 +16,15 @@ export default function humanTime(time: dayjs.ConfigType): string {
d = now;
}

const day = 864e5;
const diff = d.diff(dayjs());
let ago: string;

// 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.
if (diff < -30 * day) {
if (d.year() === dayjs().year()) {
ago = d.format('D MMM');
if (d.diff(now, 'day') < -30) {
if (d.isSame(now, 'year')) {
ago = app.translator.formatDateTime(d, 'core.lib.datetime_formats.human_time_short');
} else {
ago = d.format('ll');
ago = app.translator.formatDateTime(d, 'core.lib.datetime_formats.human_time_full');
}
} else {
ago = d.fromNow();
Expand Down
2 changes: 1 addition & 1 deletion framework/core/js/src/common/utils/liveHumanTimes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
export default function liveHumanTimes() {
Expand Down
2 changes: 1 addition & 1 deletion framework/core/js/src/forum/components/PostStream.js
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ export default class PostStream extends Component {
// set the index to the last post.
this.stream.index = indexFromViewPort !== null ? indexFromViewPort + 1 : this.stream.count();
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.post_stream_scrubber');
}

/**
Expand Down
7 changes: 7 additions & 0 deletions framework/core/locale/core.yml
Original file line number Diff line number Diff line change
Expand Up @@ -843,6 +843,13 @@ core:
username:
deleted_text: "[deleted]"

# These are DayJS formats used in core.
datetime_formats:
full_time: LLLL
human_time_short: D MMM
human_time_full: ll
post_stream_scrubber: MMMM YYYY

# Translations in this namespace are used in views other than Flarum's normal JS client.
views:
# Translations in this namespace are displayed by the basic HTML admin index.
Expand Down
Loading