diff --git a/app/autotranslate/server/autotranslate.js b/app/autotranslate/server/autotranslate.js index 6019dfab37a..012cffdc414 100644 --- a/app/autotranslate/server/autotranslate.js +++ b/app/autotranslate/server/autotranslate.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 diff --git a/app/autotranslate/server/deeplTranslate.js b/app/autotranslate/server/deeplTranslate.js index 22c8293d3dd..645fa47935c 100644 --- a/app/autotranslate/server/deeplTranslate.js +++ b/app/autotranslate/server/deeplTranslate.js @@ -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]; } /** diff --git a/app/autotranslate/server/googleTranslate.js b/app/autotranslate/server/googleTranslate.js index 7486ff11859..b2463718e6f 100644 --- a/app/autotranslate/server/googleTranslate.js +++ b/app/autotranslate/server/googleTranslate.js @@ -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']; } /** diff --git a/app/autotranslate/server/methods/getProviderUiMetadata.js b/app/autotranslate/server/methods/getProviderUiMetadata.js index 7446cd5d376..d2149081ebd 100644 --- a/app/autotranslate/server/methods/getProviderUiMetadata.js +++ b/app/autotranslate/server/methods/getProviderUiMetadata.js @@ -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 }]; + })); }, }); diff --git a/app/autotranslate/server/methods/getSupportedLanguages.js b/app/autotranslate/server/methods/getSupportedLanguages.js index 1a5d2bca7d4..158979d3d97 100644 --- a/app/autotranslate/server/methods/getSupportedLanguages.js +++ b/app/autotranslate/server/methods/getSupportedLanguages.js @@ -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); }, }); diff --git a/app/autotranslate/server/methods/translateMessage.js b/app/autotranslate/server/methods/translateMessage.js index 44fe977d09b..bedf6551832 100644 --- a/app/autotranslate/server/methods/translateMessage.js +++ b/app/autotranslate/server/methods/translateMessage.js @@ -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); } }, }); diff --git a/app/autotranslate/server/msTranslate.js b/app/autotranslate/server/msTranslate.js index efe7f6eeb87..a71d450fa72 100644 --- a/app/autotranslate/server/msTranslate.js +++ b/app/autotranslate/server/msTranslate.js @@ -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']; } /**