[FIX] Auto Translate (#19599)

Co-authored-by: Diego Sampaio <chinello@gmail.com>
pull/19642/head
Guilherme Gazzo 5 years ago committed by GitHub
parent a11435caaa
commit 91170184a6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 168
      app/autotranslate/server/autotranslate.js
  2. 86
      app/autotranslate/server/deeplTranslate.js
  3. 67
      app/autotranslate/server/googleTranslate.js
  4. 14
      app/autotranslate/server/methods/getProviderUiMetadata.js
  5. 2
      app/autotranslate/server/methods/getSupportedLanguages.js
  6. 7
      app/autotranslate/server/methods/translateMessage.js
  7. 21
      app/autotranslate/server/msTranslate.js

@ -8,11 +8,20 @@ import { Subscriptions, Messages } from '../../models';
import { Markdown } from '../../markdown/server';
import { Logger } from '../../logger';
const Providers = Symbol('Providers');
const Provider = Symbol('Provider');
/**
* This class allows translation providers to
* register,load and also returns the active provider.
*/
export class TranslationProviderRegistry {
static [Providers] = {};
static enabled = false;
static [Provider] = null;
/**
* Registers the translation provider into the registry.
* @param {*} provider
@ -20,25 +29,73 @@ export class TranslationProviderRegistry {
static registerProvider(provider) {
// get provider information
const metadata = provider._getProviderMetadata();
if (!TranslationProviderRegistry._providers) {
TranslationProviderRegistry._providers = {};
}
TranslationProviderRegistry._providers[metadata.name] = provider;
TranslationProviderRegistry[Providers][metadata.name] = provider;
}
/**
* Return the active Translation provider
*/
static getActiveProvider() {
return TranslationProviderRegistry._providers[TranslationProviderRegistry._activeProvider];
return TranslationProviderRegistry.enabled ? TranslationProviderRegistry[Providers][TranslationProviderRegistry[Provider]] : undefined;
}
static getSupportedLanguages(...args) {
return TranslationProviderRegistry.enabled ? TranslationProviderRegistry.getActiveProvider()?.getSupportedLanguages(...args) : undefined;
}
static translateMessage(...args) {
return TranslationProviderRegistry.enabled ? TranslationProviderRegistry.getActiveProvider()?.translateMessage(...args) : undefined;
}
static getProviders() {
return Object.values(TranslationProviderRegistry[Providers]);
}
static setCurrentProvider(provider) {
if (provider === TranslationProviderRegistry[Provider]) {
return;
}
TranslationProviderRegistry[Provider] = provider;
TranslationProviderRegistry.registerCallbacks();
}
static setEnable(enabled) {
TranslationProviderRegistry.enabled = enabled;
TranslationProviderRegistry.registerCallbacks();
}
static registerCallbacks() {
if (!TranslationProviderRegistry.enabled) {
callbacks.remove('afterSaveMessage', 'autotranslate');
return;
}
const provider = TranslationProviderRegistry.getActiveProvider();
if (!provider) {
return;
}
callbacks.add('afterSaveMessage', provider.translateMessage.bind(provider), callbacks.priority.MEDIUM, 'autotranslate');
}
/**
* Make the activated provider by setting as the active.
*/
static loadActiveServiceProvider() {
settings.get('AutoTranslate_ServiceProvider', (key, value) => {
TranslationProviderRegistry._activeProvider = value;
/** Register the active service provider on the 'AfterSaveMessage' callback.
* So the registered provider will be invoked when a message is saved.
* All the other inactive service provider must be deactivated.
*/
settings.get('AutoTranslate_ServiceProvider', (key, providerName) => {
TranslationProviderRegistry.setCurrentProvider(providerName);
});
// Get Auto Translate Active flag
settings.get('AutoTranslate_Enabled', (key, value) => {
TranslationProviderRegistry.setEnable(value);
});
}
}
@ -59,23 +116,6 @@ export class AutoTranslate {
this.name = '';
this.languages = [];
this.supportedLanguages = {};
// Get Auto Translate Active flag
settings.get('AutoTranslate_Enabled', (key, value) => {
this.autoTranslateEnabled = value;
});
/** Register the active service provider on the 'AfterSaveMessage' callback.
* So the registered provider will be invoked when a message is saved.
* All the other inactive service provider must be deactivated.
*/
settings.get('AutoTranslate_ServiceProvider', (key, value) => {
if (this.name === value) {
this.registerAfterSaveMsgCallBack(this.name);
} else {
this.unRegisterAfterSaveMsgCallBack(this.name);
}
});
}
/**
@ -224,65 +264,43 @@ export class AutoTranslate {
* @returns {object} unmodified message object.
*/
translateMessage(message, room, targetLanguage) {
if (this.autoTranslateEnabled && this.apiKey) {
let targetLanguages;
if (targetLanguage) {
targetLanguages = [targetLanguage];
} else {
targetLanguages = Subscriptions.getAutoTranslateLanguagesByRoomAndNotUser(room._id, message.u && message.u._id);
}
if (message.msg) {
Meteor.defer(() => {
let targetMessage = Object.assign({}, message);
targetMessage.html = s.escapeHTML(String(targetMessage.msg));
targetMessage = this.tokenize(targetMessage);
const translations = this._translateMessage(targetMessage, targetLanguages);
if (!_.isEmpty(translations)) {
Messages.addTranslations(message._id, translations, TranslationProviderRegistry._activeProvider);
}
});
}
let targetLanguages;
if (targetLanguage) {
targetLanguages = [targetLanguage];
} else {
targetLanguages = Subscriptions.getAutoTranslateLanguagesByRoomAndNotUser(room._id, message.u && message.u._id);
}
if (message.msg) {
Meteor.defer(() => {
let targetMessage = Object.assign({}, message);
targetMessage.html = s.escapeHTML(String(targetMessage.msg));
targetMessage = this.tokenize(targetMessage);
const translations = this._translateMessage(targetMessage, targetLanguages);
if (!_.isEmpty(translations)) {
Messages.addTranslations(message._id, translations, TranslationProviderRegistry[Provider]);
}
});
}
if (message.attachments && message.attachments.length > 0) {
Meteor.defer(() => {
for (const index in message.attachments) {
if (message.attachments.hasOwnProperty(index)) {
const attachment = message.attachments[index];
if (attachment.description || attachment.text) {
const translations = this._translateAttachmentDescriptions(attachment, targetLanguages);
if (!_.isEmpty(translations)) {
Messages.addAttachmentTranslations(message._id, index, translations);
}
if (message.attachments && message.attachments.length > 0) {
Meteor.defer(() => {
for (const index in message.attachments) {
if (message.attachments.hasOwnProperty(index)) {
const attachment = message.attachments[index];
if (attachment.description || attachment.text) {
const translations = this._translateAttachmentDescriptions(attachment, targetLanguages);
if (!_.isEmpty(translations)) {
Messages.addAttachmentTranslations(message._id, index, translations);
}
}
}
});
}
}
});
}
return Messages.findOneById(message._id);
}
/**
* On changing the service provider, the callback in which the translation
* is being requested needs to be switched to the new provider
* @protected
* @param {string} provider
*/
registerAfterSaveMsgCallBack(provider) {
callbacks.add('afterSaveMessage', this.translateMessage.bind(this), callbacks.priority.MEDIUM, provider);
}
/**
* On changing the service provider, the callback in which the translation
* is being requested needs to be deactivated for the all other translation providers
* @protected
* @param {string} provider
*/
unRegisterAfterSaveMsgCallBack(provider) {
callbacks.remove('afterSaveMessage', provider);
}
/**
* Returns metadata information about the service provider which is used by
* the generic implementation

@ -67,51 +67,53 @@ class DeeplAutoTranslate extends AutoTranslate {
* @returns {object} code : value pair
*/
getSupportedLanguages(target) {
if (this.autoTranslateEnabled && this.apiKey) {
if (this.supportedLanguages[target]) {
return this.supportedLanguages[target];
}
this.supportedLanguages[target] = [
{
language: 'en',
name: TAPi18n.__('Language_English', { lng: target }),
},
{
language: 'de',
name: TAPi18n.__('Language_German', { lng: target }),
},
{
language: 'fr',
name: TAPi18n.__('Language_French', { lng: target }),
},
{
language: 'es',
name: TAPi18n.__('Language_Spanish', { lng: target }),
},
{
language: 'it',
name: TAPi18n.__('Language_Italian', { lng: target }),
},
{
language: 'nl',
name: TAPi18n.__('Language_Dutch', { lng: target }),
},
{
language: 'pl',
name: TAPi18n.__('Language_Polish', { lng: target }),
},
{
language: 'pt',
name: TAPi18n.__('Language_Portuguese', { lng: target }),
},
{
language: 'ru',
name: TAPi18n.__('Language_Russian', { lng: target }),
},
];
if (!this.apiKey) {
return;
}
if (this.supportedLanguages[target]) {
return this.supportedLanguages[target];
}
this.supportedLanguages[target] = [
{
language: 'en',
name: TAPi18n.__('Language_English', { lng: target }),
},
{
language: 'de',
name: TAPi18n.__('Language_German', { lng: target }),
},
{
language: 'fr',
name: TAPi18n.__('Language_French', { lng: target }),
},
{
language: 'es',
name: TAPi18n.__('Language_Spanish', { lng: target }),
},
{
language: 'it',
name: TAPi18n.__('Language_Italian', { lng: target }),
},
{
language: 'nl',
name: TAPi18n.__('Language_Dutch', { lng: target }),
},
{
language: 'pl',
name: TAPi18n.__('Language_Polish', { lng: target }),
},
{
language: 'pt',
name: TAPi18n.__('Language_Portuguese', { lng: target }),
},
{
language: 'ru',
name: TAPi18n.__('Language_Russian', { lng: target }),
},
];
return this.supportedLanguages[target];
}
/**

@ -15,6 +15,7 @@ import { settings } from '../../settings';
* @class
* @augments AutoTranslate
*/
class GoogleAutoTranslate extends AutoTranslate {
/**
* setup api reference to Google translate to be used as message translation provider.
@ -63,47 +64,45 @@ class GoogleAutoTranslate extends AutoTranslate {
* @returns {object} code : value pair
*/
getSupportedLanguages(target) {
let supportedLanguages = {};
if (this.autoTranslateEnabled && this.apiKey) {
if (this.supportedLanguages[target]) {
return this.supportedLanguages[target];
}
if (!this.apiKey) {
return [];
}
let result;
const params = {
key: this.apiKey,
};
if (this.supportedLanguages[target]) {
return this.supportedLanguages[target];
}
if (target) {
params.target = target;
}
let result;
const params = {
key: this.apiKey,
};
try {
result = HTTP.get('https://translation.googleapis.com/language/translate/v2/languages', {
params,
});
} catch (e) {
// Fallback: Get the English names of the target languages
if (e.response && e.response.statusCode === 400 && e.response.data && e.response.data.error && e.response.data.error.status === 'INVALID_ARGUMENT') {
params.target = 'en';
target = 'en';
if (!this.supportedLanguages[target]) {
result = HTTP.get('https://translation.googleapis.com/language/translate/v2/languages', {
params,
});
}
}
}
if (target) {
params.target = target;
}
if (this.supportedLanguages[target]) {
supportedLanguages = this.supportedLanguages[target];
} else {
this.supportedLanguages[target || 'en'] = result && result.data && result.data.data && result.data.data.languages;
supportedLanguages = this.supportedLanguages[target || 'en'];
try {
result = HTTP.get('https://translation.googleapis.com/language/translate/v2/languages', {
params,
});
} catch (e) {
// Fallback: Get the English names of the target languages
if (e.response && e.response.statusCode === 400 && e.response.data && e.response.data.error && e.response.data.error.status === 'INVALID_ARGUMENT') {
params.target = 'en';
target = 'en';
if (!this.supportedLanguages[target]) {
result = HTTP.get('https://translation.googleapis.com/language/translate/v2/languages', {
params,
});
}
}
}
return supportedLanguages;
if (this.supportedLanguages[target]) {
return this.supportedLanguages[target];
}
this.supportedLanguages[target || 'en'] = result?.data?.data?.languages;
return this.supportedLanguages[target || 'en'];
}
/**

@ -4,19 +4,13 @@ import { TranslationProviderRegistry } from '../autotranslate';
Meteor.methods({
'autoTranslate.getProviderUiMetadata'() {
const providersMetadata = {};
if (!Meteor.userId()) {
throw new Meteor.Error('error-action-not-allowed', 'Login neccessary', { method: 'autoTranslate.getProviderUiMetadata' });
}
for (const provider in TranslationProviderRegistry._providers) {
if (TranslationProviderRegistry._providers.hasOwnProperty(provider)) {
const { name, displayName } = TranslationProviderRegistry._providers[provider]._getProviderMetadata();
providersMetadata[provider] = { name, displayName };
}
}
return providersMetadata;
return Object.fromEntries(TranslationProviderRegistry.getProviders().map((provider) => {
const { name, displayName } = provider._getProviderMetadata();
return [name, { name, displayName }];
}));
},
});

@ -10,7 +10,7 @@ Meteor.methods({
throw new Meteor.Error('error-action-not-allowed', 'Auto-Translate is not allowed', { method: 'autoTranslate.saveSettings' });
}
return TranslationProviderRegistry.getActiveProvider().getSupportedLanguages(targetLanguage);
return TranslationProviderRegistry.getSupportedLanguages(targetLanguage);
},
});

@ -5,9 +5,12 @@ import { TranslationProviderRegistry } from '..';
Meteor.methods({
'autoTranslate.translateMessage'(message, targetLanguage) {
if (!TranslationProviderRegistry.enabled) {
return;
}
const room = Rooms.findOneById(message && message.rid);
if (message && room && TranslationProviderRegistry) {
TranslationProviderRegistry.getActiveProvider().translateMessage(message, room, targetLanguage);
if (message && room) {
TranslationProviderRegistry.translateMessage(message, room, targetLanguage);
}
},
});

@ -70,17 +70,18 @@ class MsAutoTranslate extends AutoTranslate {
* @returns {object} code : value pair
*/
getSupportedLanguages(target) {
if (this.autoTranslateEnabled && this.apiKey) {
if (this.supportedLanguages[target]) {
return this.supportedLanguages[target];
}
const languages = HTTP.get(this.apiGetLanguages);
this.supportedLanguages[target] = Object.keys(languages.data.translation).map((language) => ({
language,
name: languages.data.translation[language].name,
}));
return this.supportedLanguages[target || 'en'];
if (!this.apiKey) {
return;
}
if (this.supportedLanguages[target]) {
return this.supportedLanguages[target];
}
const languages = HTTP.get(this.apiGetLanguages);
this.supportedLanguages[target] = Object.keys(languages.data.translation).map((language) => ({
language,
name: languages.data.translation[language].name,
}));
return this.supportedLanguages[target || 'en'];
}
/**

Loading…
Cancel
Save