Skip to content

Commit

Permalink
[NEW] Auto-translate (#1012)
Browse files Browse the repository at this point in the history
* Update realm

* View original and translate working

* Read AutoTranslate_Enabled setting

* RocketChat.canAutoTranslate()

* AutoTranslateView

* Save language

* Auto-translate switch

* Translate message
  • Loading branch information
diegolmello authored Jun 28, 2019
1 parent cfa1269 commit b3986b9
Show file tree
Hide file tree
Showing 18 changed files with 330 additions and 32 deletions.
7 changes: 6 additions & 1 deletion app/constants/colors.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isIOS } from '../utils/deviceInfo';
import { isIOS, isAndroid } from '../utils/deviceInfo';

export const COLOR_DANGER = '#f5455c';
export const COLOR_SUCCESS = '#2de0a5';
Expand All @@ -25,3 +25,8 @@ export const HEADER_BACKGROUND = isIOS ? '#f8f8f8' : '#2F343D';
export const HEADER_TITLE = isIOS ? COLOR_TITLE : COLOR_WHITE;
export const HEADER_BACK = isIOS ? COLOR_PRIMARY : COLOR_WHITE;
export const HEADER_TINT = isIOS ? COLOR_PRIMARY : COLOR_WHITE;

export const SWITCH_TRACK_COLOR = {
false: isAndroid ? COLOR_DANGER : null,
true: COLOR_SUCCESS
};
3 changes: 3 additions & 0 deletions app/constants/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,5 +70,8 @@ export default {
},
API_Gitlab_URL: {
type: 'valueAsString'
},
AutoTranslate_Enabled: {
type: 'valueAsBoolean'
}
};
30 changes: 29 additions & 1 deletion app/containers/MessageActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ import {
} from '../actions/messages';
import { vibrate } from '../utils/vibration';
import RocketChat from '../lib/rocketchat';
import database from '../lib/realm';
import I18n from '../i18n';
import log from '../utils/log';
import Navigation from '../lib/Navigation';
import { getMessageTranslation } from './message/utils';

@connect(
state => ({
Expand Down Expand Up @@ -46,7 +48,7 @@ export default class MessageActions extends React.Component {
room: PropTypes.object.isRequired,
actionMessage: PropTypes.object,
toast: PropTypes.element,
// user: PropTypes.object.isRequired,
user: PropTypes.object,
deleteRequest: PropTypes.func.isRequired,
editInit: PropTypes.func.isRequired,
toggleStarRequest: PropTypes.func.isRequired,
Expand Down Expand Up @@ -127,6 +129,12 @@ export default class MessageActions extends React.Component {
this.READ_RECEIPT_INDEX = this.options.length - 1;
}

// Toggle Auto-translate
if (props.room.autoTranslate && props.actionMessage.u && props.actionMessage.u._id !== props.user.id) {
this.options.push(I18n.t(props.actionMessage.autoTranslate ? 'View_Original' : 'Translate'));
this.TOGGLE_TRANSLATION_INDEX = this.options.length - 1;
}

// Report
this.options.push(I18n.t('Report'));
this.REPORT_INDEX = this.options.length - 1;
Expand Down Expand Up @@ -326,6 +334,23 @@ export default class MessageActions extends React.Component {
}
}

handleToggleTranslation = async() => {
const { actionMessage, room } = this.props;
try {
const message = database.objectForPrimaryKey('messages', actionMessage._id);
database.write(() => {
message.autoTranslate = !message.autoTranslate;
message._updatedAt = new Date();
});
const translatedMessage = getMessageTranslation(message, room.autoTranslateLanguage);
if (!translatedMessage) {
await RocketChat.translateMessage(actionMessage, room.autoTranslateLanguage);
}
} catch (err) {
log('err_toggle_translation', err);
}
}

handleActionPress = (actionIndex) => {
if (actionIndex) {
switch (actionIndex) {
Expand Down Expand Up @@ -365,6 +390,9 @@ export default class MessageActions extends React.Component {
case this.READ_RECEIPT_INDEX:
this.handleReadReceipt();
break;
case this.TOGGLE_TRANSLATION_INDEX:
this.handleToggleTranslation();
break;
default:
break;
}
Expand Down
22 changes: 17 additions & 5 deletions app/containers/message/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { KeyboardUtils } from 'react-native-keyboard-input';

import Message from './Message';
import debounce from '../../utils/debounce';
import { SYSTEM_MESSAGES, getCustomEmoji } from './utils';
import { SYSTEM_MESSAGES, getCustomEmoji, getMessageTranslation } from './utils';
import messagesStatus from '../../constants/messagesStatus';

export default class MessageContainer extends React.Component {
Expand All @@ -27,6 +27,8 @@ export default class MessageContainer extends React.Component {
isReadReceiptEnabled: PropTypes.bool,
useRealName: PropTypes.bool,
useMarkdown: PropTypes.bool,
autoTranslateRoom: PropTypes.bool,
autoTranslateLanguage: PropTypes.string,
status: PropTypes.number,
onLongPress: PropTypes.func,
onReactionPress: PropTypes.func,
Expand All @@ -49,12 +51,15 @@ export default class MessageContainer extends React.Component {

shouldComponentUpdate(nextProps) {
const {
status, item, _updatedAt
status, item, _updatedAt, autoTranslateRoom
} = this.props;

if (status !== nextProps.status) {
return true;
}
if (autoTranslateRoom !== nextProps.autoTranslateRoom) {
return true;
}
if (item.tmsg !== nextProps.item.tmsg) {
return true;
}
Expand Down Expand Up @@ -191,16 +196,23 @@ export default class MessageContainer extends React.Component {

render() {
const {
item, user, style, archived, baseUrl, useRealName, broadcast, fetchThreadName, customThreadTimeFormat, onOpenFileModal, timeFormat, useMarkdown, isReadReceiptEnabled
item, user, style, archived, baseUrl, useRealName, broadcast, fetchThreadName, customThreadTimeFormat, onOpenFileModal, timeFormat, useMarkdown, isReadReceiptEnabled, autoTranslateRoom, autoTranslateLanguage
} = this.props;
const {
_id, msg, ts, attachments, urls, reactions, t, avatar, u, alias, editedBy, role, drid, dcount, dlm, tmid, tcount, tlm, tmsg, mentions, channels, unread
_id, msg, ts, attachments, urls, reactions, t, avatar, u, alias, editedBy, role, drid, dcount, dlm, tmid, tcount, tlm, tmsg, mentions, channels, unread, autoTranslate: autoTranslateMessage
} = item;

let message = msg;
// "autoTranslateRoom" and "autoTranslateLanguage" are properties from the subscription
// "autoTranslateMessage" is a toggle between "View Original" and "Translate" state
if (autoTranslateRoom && autoTranslateMessage) {
message = getMessageTranslation(item, autoTranslateLanguage) || message;
}

return (
<Message
id={_id}
msg={msg}
msg={message}
author={u}
ts={ts}
type={t}
Expand Down
12 changes: 12 additions & 0 deletions app/containers/message/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,15 @@ export const getCustomEmoji = (content) => {
});
return findByAlias;
};

export const getMessageTranslation = (message, autoTranslateLanguage) => {
if (!autoTranslateLanguage) {
return null;
}
const { translations } = message;
if (translations) {
const translation = translations.find(trans => trans.language === autoTranslateLanguage);
return translation && translation.value;
}
return null;
};
4 changes: 4 additions & 0 deletions app/i18n/locales/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ export default {
Are_you_sure_question_mark: 'Are you sure?',
Are_you_sure_you_want_to_leave_the_room: 'Are you sure you want to leave the room {{room}}?',
Authenticating: 'Authenticating',
Auto_Translate: 'Auto-Translate',
Avatar_changed_successfully: 'Avatar changed successfully!',
Avatar_Url: 'Avatar URL',
Away: 'Away',
Expand Down Expand Up @@ -155,6 +156,7 @@ export default {
Email_or_password_field_is_empty: 'Email or password field is empty',
Email: 'Email',
email: 'e-mail',
Enable_Auto_Translate: 'Enable Auto-Translate',
Enable_markdown: 'Enable markdown',
Enable_notifications: 'Enable notifications',
Everyone_can_access_this_channel: 'Everyone can access this channel',
Expand Down Expand Up @@ -343,6 +345,7 @@ export default {
Timezone: 'Timezone',
topic: 'topic',
Topic: 'Topic',
Translate: 'Translate',
Try_again: 'Try again',
Two_Factor_Authentication: 'Two-factor Authentication',
Type_the_channel_name_here: 'Type the channel name here',
Expand Down Expand Up @@ -374,6 +377,7 @@ export default {
Username_or_email: 'Username or email',
Validating: 'Validating',
Video_call: 'Video call',
View_Original: 'View Original',
Voice_call: 'Voice call',
Welcome: 'Welcome',
Welcome_to_RocketChat: 'Welcome to Rocket.Chat',
Expand Down
2 changes: 2 additions & 0 deletions app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import SearchMessagesView from './views/SearchMessagesView';
import ReadReceiptsView from './views/ReadReceiptView';
import ThreadMessagesView from './views/ThreadMessagesView';
import MessagesView from './views/MessagesView';
import AutoTranslateView from './views/AutoTranslateView';
import SelectedUsersView from './views/SelectedUsersView';
import CreateChannelView from './views/CreateChannelView';
import LegalView from './views/LegalView';
Expand Down Expand Up @@ -116,6 +117,7 @@ const ChatsStack = createStackNavigator({
SelectedUsersView,
ThreadMessagesView,
MessagesView,
AutoTranslateView,
ReadReceiptsView,
DirectoryView
}, {
Expand Down
3 changes: 3 additions & 0 deletions app/lib/methods/helpers/normalizeMessage.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ export default (msg) => {
if (!Array.isArray(msg.reactions)) {
msg.reactions = Object.keys(msg.reactions).map(key => ({ _id: `${ msg._id }${ key }`, emoji: key, usernames: msg.reactions[key].usernames }));
}
if (msg.translations && Object.keys(msg.translations).length) {
msg.translations = Object.keys(msg.translations).map(key => ({ _id: `${ msg._id }${ key }`, language: key, value: msg.translations[key] }));
}
msg.urls = msg.urls ? parseUrls(msg.urls) : [];
msg._updatedAt = new Date();
// loadHistory returns msg.starred as object
Expand Down
42 changes: 34 additions & 8 deletions app/lib/realm.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,9 @@ const subscriptionSchema = {
broadcast: { type: 'bool', optional: true },
prid: { type: 'string', optional: true },
draftMessage: { type: 'string', optional: true },
lastThreadSync: 'date?'
lastThreadSync: 'date?',
autoTranslate: 'bool?',
autoTranslateLanguage: 'string?'
}
};

Expand Down Expand Up @@ -171,6 +173,16 @@ const messagesReactionsSchema = {
}
};

const messagesTranslationsSchema = {
name: 'messagesTranslations',
primaryKey: '_id',
properties: {
_id: 'string',
language: 'string',
value: 'string'
}
};

const messagesEditedBySchema = {
name: 'messagesEditedBy',
primaryKey: '_id',
Expand Down Expand Up @@ -212,7 +224,9 @@ const messagesSchema = {
replies: 'string[]',
mentions: { type: 'list', objectType: 'users' },
channels: { type: 'list', objectType: 'rooms' },
unread: { type: 'bool', optional: true }
unread: { type: 'bool', optional: true },
autoTranslate: { type: 'bool', default: false },
translations: { type: 'list', objectType: 'messagesTranslations' }
}
};

Expand Down Expand Up @@ -246,6 +260,11 @@ const threadsSchema = {
tcount: { type: 'int', optional: true },
tlm: { type: 'date', optional: true },
replies: 'string[]',
mentions: { type: 'list', objectType: 'users' },
channels: { type: 'list', objectType: 'rooms' },
unread: { type: 'bool', optional: true },
autoTranslate: { type: 'bool', default: false },
translations: { type: 'list', objectType: 'messagesTranslations' },
draftMessage: 'string?'
}
};
Expand All @@ -272,7 +291,13 @@ const threadMessagesSchema = {
starred: { type: 'bool', optional: true },
editedBy: 'messagesEditedBy',
reactions: { type: 'list', objectType: 'messagesReactions' },
role: { type: 'string', optional: true }
role: { type: 'string', optional: true },
replies: 'string[]',
mentions: { type: 'list', objectType: 'users' },
channels: { type: 'list', objectType: 'rooms' },
unread: { type: 'bool', optional: true },
autoTranslate: { type: 'bool', default: false },
translations: { type: 'list', objectType: 'messagesTranslations' }
}
};

Expand Down Expand Up @@ -374,7 +399,8 @@ const schema = [
messagesReactionsSchema,
rolesSchema,
uploadsSchema,
slashCommandSchema
slashCommandSchema,
messagesTranslationsSchema
];

const inMemorySchema = [usersTypingSchema, activeUsersSchema];
Expand All @@ -387,9 +413,9 @@ class DB {
userSchema,
serversSchema
],
schemaVersion: 8,
schemaVersion: 9,
migration: (oldRealm, newRealm) => {
if (oldRealm.schemaVersion >= 1 && newRealm.schemaVersion <= 8) {
if (oldRealm.schemaVersion >= 1 && newRealm.schemaVersion <= 9) {
const newServers = newRealm.objects('servers');

// eslint-disable-next-line no-plusplus
Expand Down Expand Up @@ -444,9 +470,9 @@ class DB {
return this.databases.activeDB = new Realm({
path: `${ path }.realm`,
schema,
schemaVersion: 12,
schemaVersion: 13,
migration: (oldRealm, newRealm) => {
if (oldRealm.schemaVersion >= 3 && newRealm.schemaVersion <= 11) {
if (oldRealm.schemaVersion >= 3 && newRealm.schemaVersion <= 13) {
const newSubs = newRealm.objects('subscriptions');
newRealm.delete(newSubs);
const newMessages = newRealm.objects('messages');
Expand Down
25 changes: 25 additions & 0 deletions app/lib/rocketchat.js
Original file line number Diff line number Diff line change
Expand Up @@ -881,6 +881,31 @@ const RocketChat = {
return this.sdk.get('directory', {
query, count, offset, sort
});
},
canAutoTranslate() {
try {
const AutoTranslate_Enabled = reduxStore.getState().settings && reduxStore.getState().settings.AutoTranslate_Enabled;
if (!AutoTranslate_Enabled) {
return false;
}
const autoTranslatePermission = database.objectForPrimaryKey('permissions', 'auto-translate');
const userRoles = (reduxStore.getState().login.user && reduxStore.getState().login.user.roles) || [];
return autoTranslatePermission.roles.some(role => userRoles.includes(role));
} catch (error) {
log('err_can_auto_translate', error);
return false;
}
},
saveAutoTranslate({
rid, field, value, options
}) {
return this.sdk.methodCall('autoTranslate.saveSettings', rid, field, value, options);
},
getSupportedLanguagesAutoTranslate() {
return this.sdk.methodCall('autoTranslate.getSupportedLanguages', 'en');
},
translateMessage(message, targetLanguage) {
return this.sdk.methodCall('autoTranslate.translateMessage', message, targetLanguage);
}
};

Expand Down
Loading

0 comments on commit b3986b9

Please sign in to comment.