diff --git a/public/openapi/components/schemas/PostObject.yaml b/public/openapi/components/schemas/PostObject.yaml index 3bf0701729..4da9fe96a3 100644 --- a/public/openapi/components/schemas/PostObject.yaml +++ b/public/openapi/components/schemas/PostObject.yaml @@ -31,6 +31,10 @@ PostObject: For posts received via ActivityPub, it is the url of the original piece of content. content: type: string + isEnglish: + type: boolean + translatedContent: + type: string sourceContent: type: string nullable: true @@ -206,6 +210,10 @@ PostDataObject: description: A topic identifier content: type: string + isEnglish: + type: boolean + translatedContent: + type: string timestamp: type: number anonymous: diff --git a/public/openapi/components/schemas/TopicObject.yaml b/public/openapi/components/schemas/TopicObject.yaml index 07adf6197b..05ccf82e6f 100644 --- a/public/openapi/components/schemas/TopicObject.yaml +++ b/public/openapi/components/schemas/TopicObject.yaml @@ -130,6 +130,9 @@ TopicObject: type: string isLocal: type: boolean + translatedContent: + type: string + nullable: true timestampISO: type: string description: An ISO 8601 formatted date string (complementing `timestamp`) @@ -170,6 +173,8 @@ TopicObject: example: "#f44336" index: type: number + isEnglish: + type: boolean nullable: true tags: type: array diff --git a/public/openapi/read/categories.yaml b/public/openapi/read/categories.yaml index 9d8071d8f5..f2369a29d6 100644 --- a/public/openapi/read/categories.yaml +++ b/public/openapi/read/categories.yaml @@ -95,6 +95,10 @@ get: type: number content: type: string + isEnglish: + type: boolean + translatedContent: + type: string timestampISO: type: string description: An ISO 8601 formatted date string (complementing `timestamp`) @@ -171,6 +175,10 @@ get: type: number content: type: string + isEnglish: + type: boolean + translatedContent: + type: string sourceContent: type: string nullable: true diff --git a/public/openapi/read/index.yaml b/public/openapi/read/index.yaml index aff30a31f8..8b58c6c21c 100644 --- a/public/openapi/read/index.yaml +++ b/public/openapi/read/index.yaml @@ -77,6 +77,10 @@ get: type: number content: type: string + isEnglish: + type: boolean + translatedContent: + type: string timestampISO: type: string description: An ISO 8601 formatted date string (complementing `timestamp`) @@ -174,6 +178,10 @@ get: type: number content: type: string + isEnglish: + type: boolean + translatedContent: + type: string sourceContent: type: string nullable: true diff --git a/public/openapi/read/unread.yaml b/public/openapi/read/unread.yaml index 3fd9957e09..fff455fc15 100644 --- a/public/openapi/read/unread.yaml +++ b/public/openapi/read/unread.yaml @@ -162,6 +162,9 @@ get: anonymous: type: number description: Whether this post was made anonymously (1 for anonymous, 0 for not) + translatedContent: + type: string + nullable: true timestampISO: type: string description: An ISO 8601 formatted date string (complementing `timestamp`) @@ -202,6 +205,8 @@ get: example: "#f44336" index: type: number + isEnglish: + type: boolean tags: type: array items: diff --git a/public/openapi/write/posts/pid.yaml b/public/openapi/write/posts/pid.yaml index 2203992b52..c1c572e340 100644 --- a/public/openapi/write/posts/pid.yaml +++ b/public/openapi/write/posts/pid.yaml @@ -35,6 +35,10 @@ get: description: A topic identifier content: type: string + isEnglish: + type: boolean + translatedContent: + type: string timestamp: type: number flagId: diff --git a/public/src/client/topic.js b/public/src/client/topic.js index b54185bf80..30ba5b07c2 100644 --- a/public/src/client/topic.js +++ b/public/src/client/topic.js @@ -72,12 +72,28 @@ define('forum/topic', [ addCrosspostsHandler(); $(window).on('scroll', utils.debounce(updateTopicTitle, 250)); + configurePostToggle(); handleTopicSearch(); hooks.fire('action:topic.loaded', ajaxify.data); }; + function configurePostToggle() { + $('.topic').on('click', '.view-translated-btn', function () { + // Toggle the visibility of the next .translated-content div + $(this).closest('.sensitive-content-message').next('.translated-content').toggle(); + // Optionally, change the button text based on visibility + var isVisible = $(this).closest('.sensitive-content-message').next('.translated-content').is(':visible'); + if (isVisible) { + $(this).text('Hide the translated message.'); + } else { + $(this).text('Click here to view the translated message.'); + } + }); + } + + function handleTopicSearch() { require(['mousetrap'], (mousetrap) => { if (config.topicSearchEnabled) { diff --git a/src/posts/create.js b/src/posts/create.js index 061ebe6174..2e6caa4fbc 100644 --- a/src/posts/create.js +++ b/src/posts/create.js @@ -9,6 +9,7 @@ const categories = require('../categories'); const groups = require('../groups'); const activitypub = require('../activitypub'); const utils = require('../utils'); +const translate = require('../translate'); module.exports = function (Posts) { Posts.create = async function (data) { @@ -18,6 +19,8 @@ module.exports = function (Posts) { const timestamp = data.timestamp || Date.now(); const isMain = data.isMain || false; let hasAttachment = false; + const [isEnglish, translatedContent] = await translate.translate(data); + if (!uid && parseInt(uid, 10) !== 0) { throw new Error('[[error:invalid-uid]]'); @@ -28,7 +31,7 @@ module.exports = function (Posts) { } const pid = data.pid || await db.incrObjectField('global', 'nextPid'); - let postData = { pid, uid, tid, content, sourceContent, timestamp }; + let postData = { pid, uid, tid, content, sourceContent, timestamp, isEnglish, translatedContent}; if (data.toPid) { postData.toPid = data.toPid; diff --git a/src/posts/data.js b/src/posts/data.js index 28d280e752..bb47359722 100644 --- a/src/posts/data.js +++ b/src/posts/data.js @@ -100,5 +100,11 @@ function modifyPost(post, fields) { post.uploads = []; } } + // Mark post as "English" if decided by translator service or if it has no info + post.isEnglish = post.isEnglish == 'true' || post.isEnglish === undefined; + // If translatedContent is undefined, default to empty string (no translation needed for English posts) + if (post.translatedContent === undefined) { + post.translatedContent = ''; + } } } diff --git a/src/posts/summary.js b/src/posts/summary.js index 631195988c..508caf9940 100644 --- a/src/posts/summary.js +++ b/src/posts/summary.js @@ -22,7 +22,7 @@ module.exports = function (Posts) { options.escape = options.hasOwnProperty('escape') ? options.escape : false; options.extraFields = options.hasOwnProperty('extraFields') ? options.extraFields : []; - const fields = ['pid', 'tid', 'toPid', 'url', 'content', 'sourceContent', 'uid', 'timestamp', 'deleted', 'upvotes', 'downvotes', 'replies', 'handle', 'anonymous'].concat(options.extraFields); + const fields = ['pid', 'tid', 'toPid', 'url', 'content', 'sourceContent', 'uid', 'timestamp', 'deleted', 'upvotes', 'downvotes', 'replies', 'handle', 'anonymous', 'isEnglish', 'translatedContent'].concat(options.extraFields); let posts = await Posts.getPostsFields(pids, fields); posts = posts.filter(Boolean); diff --git a/src/translate/index.js b/src/translate/index.js new file mode 100644 index 0000000000..5b103bed79 --- /dev/null +++ b/src/translate/index.js @@ -0,0 +1,18 @@ + +/* eslint-disable strict */ +//var request = require('request'); + +const translatorApi = module.exports; + + + +translatorApi.translate = async function (postData) { + const TRANSLATOR_API = 'http://17313-team11.s3d.cmu.edu:5000/'; + try { + const response = await fetch(TRANSLATOR_API + '/?content=' + postData.content); + const data = await response.json(); + return [data.is_english.toString(), data.translated_content]; + } catch (e) { + return [true, postData.content]; // default as english + } +}; diff --git a/vendor/nodebb-theme-harmony-2.1.35/templates/partials/topic/post.tpl b/vendor/nodebb-theme-harmony-2.1.35/templates/partials/topic/post.tpl index bfb84361b5..87192eea4f 100644 --- a/vendor/nodebb-theme-harmony-2.1.35/templates/partials/topic/post.tpl +++ b/vendor/nodebb-theme-harmony-2.1.35/templates/partials/topic/post.tpl @@ -83,6 +83,14 @@