From b422c8ce52ee556869b7ca1b67b9b87fbc478fda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 30 Nov 2022 22:02:06 -0500 Subject: [PATCH] breaking: refactor topic events for proper i18n support both `user locked this topic 3 hours ago` & `user locked this topic on 19 Oct 2022, 11:42` --- install/package.json | 4 +- public/language/en-GB/topic.json | 30 +++++++++----- src/topics/events.js | 68 ++++++++++++++++++++++++++------ 3 files changed, 78 insertions(+), 24 deletions(-) diff --git a/install/package.json b/install/package.json index 05c004cd08..d5d178740f 100644 --- a/install/package.json +++ b/install/package.json @@ -100,8 +100,8 @@ "nodebb-plugin-spam-be-gone": "2.0.4", "nodebb-rewards-essentials": "0.2.1", "nodebb-theme-lavender": "7.0.2", - "nodebb-theme-peace": "2.0.8", - "nodebb-theme-persona": "13.0.28", + "nodebb-theme-peace": "2.0.9", + "nodebb-theme-persona": "13.0.30", "nodebb-widget-essentials": "7.0.2", "nodemailer": "6.8.0", "nprogress": "0.2.0", diff --git a/public/language/en-GB/topic.json b/public/language/en-GB/topic.json index 94a3b5db3f..0e6be457d8 100644 --- a/public/language/en-GB/topic.json +++ b/public/language/en-GB/topic.json @@ -43,16 +43,26 @@ "ban-ip": "Ban IP", "view-history": "Edit History", - "locked-by": "Locked by", - "unlocked-by": "Unlocked by", - "pinned-by": "Pinned by", - "unpinned-by": "Unpinned by", - "deleted-by": "Deleted by", - "restored-by": "Restored by", - "moved-from-by": "Moved from %1 by", - "queued-by": "Post queued for approval →", - "backlink": "Referenced by", - "forked-by": "Forked by", + "user-locked-topic-ago": "%1 locked this topic %2", + "user-locked-topic-on": "%1 locked this topic on %2", + "user-unlocked-topic-ago": "%1 unlocked this topic %2", + "user-unlocked-topic-on": "%1 unlocked this topic on %2", + "user-pinned-topic-ago": "%1 pinned this topic %2", + "user-pinned-topic-on": "%1 pinned this topic on %2", + "user-unpinned-topic-ago": "%1 unpinned this topic %2", + "user-unpinned-topic-on": "%1 unpinned this topic on %2", + "user-deleted-topic-ago": "%1 deleted this topic %2", + "user-deleted-topic-on": "%1 deleted this topic on %2", + "user-restored-topic-ago": "%1 restored this topic %2", + "user-restored-topic-on": "%1 restored this topic on %2", + "user-moved-topic-from-ago": "%1 moved this topic from %2 %3", + "user-moved-topic-from-on": "%1 moved this topic from %2 on %3", + "user-queued-post-ago": "%1 queued post for approval %3", + "user-queued-post-on": "%1 queued post for approval on %3", + "user-referenced-topic-ago": "%1 referenced this topic %3", + "user-referenced-topic-on": "%1 referenced this topic on %3", + "user-forked-topic-ago": "%1 forked this topic %3", + "user-forked-topic-on": "%1 forked this topic on %3", "bookmark_instructions" : "Click here to return to the last read post in this thread.", diff --git a/src/topics/events.js b/src/topics/events.js index 8e2b8e04e1..95f3d772d3 100644 --- a/src/topics/events.js +++ b/src/topics/events.js @@ -1,6 +1,7 @@ 'use strict'; const _ = require('lodash'); +const nconf = require('nconf'); const db = require('../database'); const meta = require('../meta'); const user = require('../user'); @@ -9,6 +10,10 @@ const categories = require('../categories'); const plugins = require('../plugins'); const translator = require('../translator'); const privileges = require('../privileges'); +const utils = require('../utils'); +const helpers = require('../helpers'); + +const relative_path = nconf.get('relative_path'); const Events = module.exports; @@ -20,49 +25,49 @@ const Events = module.exports; * You can then log a custom topic event by calling `topics.events.log(tid, { type, uid });` * `uid` is optional; if you pass in a valid uid in the payload, * the user avatar/username will be rendered as part of the event text - * + * see https://github.com/NodeBB/nodebb-plugin-question-and-answer/blob/master/library.js#L288-L306 */ Events._types = { pin: { icon: 'fa-thumb-tack', - text: '[[topic:pinned-by]]', + translation: async event => translateSimple(event, 'topic:user-pinned-topic'), }, unpin: { icon: 'fa-thumb-tack fa-rotate-90', - text: '[[topic:unpinned-by]]', + translation: async event => translateSimple(event, 'topic:user-unpinned-topic'), }, lock: { icon: 'fa-lock', - text: '[[topic:locked-by]]', + translation: async event => translateSimple(event, 'topic:user-locked-topic'), }, unlock: { icon: 'fa-unlock', - text: '[[topic:unlocked-by]]', + translation: async event => translateSimple(event, 'topic:user-unlocked-topic'), }, delete: { icon: 'fa-trash', - text: '[[topic:deleted-by]]', + translation: async event => translateSimple(event, 'topic:user-deleted-topic'), }, restore: { icon: 'fa-trash-o', - text: '[[topic:restored-by]]', + translation: async event => translateSimple(event, 'topic:user-restored-topic'), }, move: { icon: 'fa-arrow-circle-right', - // text: '[[topic:moved-from-by]]', + translation: async event => translateEventArgs(event, 'topic:user-moved-topic-from', renderUser(event), `${event.fromCategory.name}`, renderTimeago(event)), }, 'post-queue': { icon: 'fa-history', - text: '[[topic:queued-by]]', href: '/post-queue', + translation: async event => translateEventArgs(event, 'topic:user-queued-post', renderUser(event), `/post-queue`, renderTimeago(event)), }, backlink: { icon: 'fa-link', - text: '[[topic:backlink]]', + translation: async event => translateEventArgs(event, 'topic:user-referenced-topic', renderUser(event), `${relative_path}${event.href}`, renderTimeago(event)), }, fork: { icon: 'fa-code-fork', - text: '[[topic:forked-by]]', + translation: async event => translateEventArgs(event, 'topic:user-forked-topic', renderUser(event), `${relative_path}${event.href}`, renderTimeago(event)), }, }; @@ -72,6 +77,40 @@ Events.init = async () => { Events._types = types; }; +async function translateEventArgs(event, prefix, ...args) { + const key = getTranslationKey(event, prefix); + const compiled = translator.compile.apply(null, [key, ...args]); + return utils.decodeHTMLEntities(await translator.translate(compiled)); +} + +async function translateSimple(event, prefix) { + return await translateEventArgs(event, prefix, renderUser(event), renderTimeago(event)); +} + +Events.translateSimple = translateSimple; // so plugins can perform translate +Events.translateEventArgs = translateEventArgs; // so plugins can perform translate + +// generate `user-locked-topic-ago` or `user-locked-topic-on` based on timeago cutoff setting +function getTranslationKey(event, prefix) { + const cutoffMs = 1000 * 60 * 60 * 24 * Math.max(0, parseInt(meta.config.timeagoCutoff, 10)); + let translationSuffix = 'ago'; + if (cutoffMs > 0 && Date.now() - event.timestamp > cutoffMs) { + translationSuffix = 'on'; + } + return `${prefix}-${translationSuffix}`; +} + +function renderUser(event) { + if (!event.user || event.user.system) { + return '[[global:system-user]]'; + } + return `${helpers.buildAvatar(event.user, '16px', true)} ${event.user.username}`; +} + +function renderTimeago(event) { + return ``; +} + Events.get = async (tid, uid, reverse = false) => { const topics = require('.'); @@ -154,12 +193,17 @@ async function modifyEvent({ tid, uid, eventIds, timestamps, events }) { } if (event.hasOwnProperty('fromCid')) { event.fromCategory = fromCategories[event.fromCid]; - event.text = translator.compile('topic:moved-from-by', event.fromCategory.name); } Object.assign(event, Events._types[event.type]); }); + await Promise.all(events.map(async (event) => { + if (Events._types[event.type].translation) { + event.text = await Events._types[event.type].translation(event); + } + })); + // Sort events events.sort((a, b) => a.timestamp - b.timestamp);