diff --git a/framework/core/js/src/common/Translator.tsx b/framework/core/js/src/common/Translator.tsx index 29498fb3a5..3aee69c72d 100644 --- a/framework/core/js/src/common/Translator.tsx +++ b/framework/core/js/src/common/Translator.tsx @@ -1,3 +1,4 @@ +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'; @@ -88,4 +89,16 @@ export default class Translator { return id; } + + /** + * Formats the time. + * + * The format of the time will be chosen by the following order: + * - The format ID defined in current locale. + * - The provided fallback format. + * - DayJS default format. + */ + format(time: Dayjs, id?: string, fallback?: string): string { + return time.format(id && (this.translations[id] ?? fallback)); + } } diff --git a/framework/core/js/src/common/index.ts b/framework/core/js/src/common/index.ts index f3308c7dfc..c3bff0978d 100644 --- a/framework/core/js/src/common/index.ts +++ b/framework/core/js/src/common/index.ts @@ -10,14 +10,13 @@ import 'bootstrap/js/transition'; import 'jquery.hotkeys/jquery.hotkeys'; import relativeTime from 'dayjs/plugin/relativeTime'; +import localizedFormat from "dayjs/plugin/localizedFormat"; dayjs.extend(relativeTime); +dayjs.extend(localizedFormat); import './registry'; -import { customFormats } from './utils/localizedFormat'; -dayjs.extend(customFormats); - import patchMithril from './utils/patchMithril'; patchMithril(window); diff --git a/framework/core/js/src/common/utils/humanTime.ts b/framework/core/js/src/common/utils/humanTime.ts index d450c7df4c..0ecb8614be 100644 --- a/framework/core/js/src/common/utils/humanTime.ts +++ b/framework/core/js/src/common/utils/humanTime.ts @@ -1,3 +1,4 @@ +import app from '../app'; import dayjs from 'dayjs'; /** @@ -21,9 +22,9 @@ export default function humanTime(time: dayjs.ConfigType): string { // in the string. If it wasn't this year, we'll show the year as well. if (d.diff(now, 'day') < -30) { if (d.isSame(now, 'year')) { - ago = d.format('f'); + ago = app.translator.format(d, 'core.lib.datetime_formats.human_time_short'); } else { - ago = d.format('ll'); + ago = app.translator.format(d, 'core.lib.datetime_formats.human_time_full'); } } else { ago = d.fromNow(); diff --git a/framework/core/js/src/common/utils/localizedFormat.ts b/framework/core/js/src/common/utils/localizedFormat.ts deleted file mode 100644 index 937a578814..0000000000 --- a/framework/core/js/src/common/utils/localizedFormat.ts +++ /dev/null @@ -1,84 +0,0 @@ -import app from '../../common/app'; -import ItemList from './ItemList'; - -export type LocalizedFormat = { - /** Regexes that matches the formats. */ - regexes: string[]; - /** The map that turns matched localized formats to original DayJS formats. */ - formats: Record -} - -const defaultFormats = new ItemList(); - -defaultFormats.add("flarum-date-formats", { - regexes: ["f{1,2}", "F{1,2}"], - formats: { - F: "DD MMMM", - FF: "MMMM YYYY" - } -}, 10); - -defaultFormats.add("dayjs-formats", { - regexes: ["LTS?", "l{1,4}", "L{1,4}"], - formats: { - LTS: 'h:mm:ss A', - LT: 'h:mm A', - L: 'MM/DD/YYYY', - LL: 'MMMM D, YYYY', - LLL: 'MMMM D, YYYY h:mm A', - LLLL: 'dddd, MMMM D, YYYY h:mm A' - } -}, 20); - -const exports = { - /** - * Returns a list of regexes that matches all localized formats and a map that maps the format to the original DayJS format. - */ - formatList: function() { - const formats = new ItemList(); - formats.merge(defaultFormats); - return formats; - } -}; - -/** - * Flarum's localized format plugin. - * @see https://day.js.org/docs/en/plugin/plugin - */ -export const customFormats: import('dayjs').PluginFunc = function (_option, c, _factory) { - const proto = c.prototype; - const oldFormat = proto.format; - - /** Converts the long date to short date. */ - const t = (format?: string) => format?.replace(/(\[[^\]]+])|(MMMM|MM|DD|dddd)/g, (_, a, b) => a || b.slice(1)); - - proto.format = function (template) { - const { formats = {} } = (this as any).$locale(); - - const config = exports.formatList().toArray(); - const regexes: string[] = []; - let defFormats: Record = {}; - config.forEach((v) => { - regexes.push(...v.regexes); - defFormats = { ...defFormats, ...v.formats }; - }); - - const result = template?.replace(new RegExp(`(\\[[^\\]]+])|(${regexes.join("|")})`, "g"), (_, a, b) => { - const B = b && b.toUpperCase(); - // The format is fetched in the following order: Translator > DayJS locale > formatList. - console.log(regexes, defFormats); - return a || - // short dates - app.translator.translations[`core.forum.date-format.${b}`] || - formats[b] || - defFormats[b] || - // long dates - t(app.translator.translations[`core.forum.date-format.${B}`]) || - t(formats[B]) || - t(defFormats[B]); - }); - return oldFormat.call(this, result); - }; -}; - -export default exports; \ No newline at end of file diff --git a/framework/core/js/src/forum/components/PostStream.js b/framework/core/js/src/forum/components/PostStream.js index 78251423a4..cdb66128eb 100644 --- a/framework/core/js/src/forum/components/PostStream.js +++ b/framework/core/js/src/forum/components/PostStream.js @@ -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('FF'); + if (period) this.stream.description = app.translator.format(dayjs(period), 'core.lib.datetime_formats.post_stream_scrubber'); } /** diff --git a/framework/core/locale/core.yml b/framework/core/locale/core.yml index edb62d0d72..4d424a8ccd 100644 --- a/framework/core/locale/core.yml +++ b/framework/core/locale/core.yml @@ -843,6 +843,12 @@ core: username: deleted_text: "[deleted]" + # These are DayJS formats used in core. + datetime_formats: + 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.