forked from mrsimpson/Rocket.Chat
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feature/RocketChat#175 auto translate with deepl (RocketChat#311)
This brings the ability to use DeepL for autotranslation of messages. Doing that, it also introduces a pluggable service registry pattern for translation providers with an abstract superclass handling (most) of the common parts.
- Loading branch information
1 parent
e28fb6d
commit 4709af2
Showing
23 changed files
with
938 additions
and
132 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
Package.describe({ | ||
name: 'assistify:deepl-translation', | ||
version: '0.0.1', | ||
// Brief, one-line summary of the package. | ||
summary: 'Empowers RocketChat by integrating DEEPL text translation engine', | ||
// URL to the Git repository containing the source code for this package. | ||
git: 'https://github.com/assistify', | ||
// By default, Meteor will default to using README.md for documentation. | ||
// To avoid submitting documentation, set this field to null. | ||
documentation: 'README.md' | ||
}); | ||
|
||
Package.onUse(function(api) { | ||
api.versionsFrom('1.6.0.1'); | ||
api.use('ecmascript'); | ||
api.use('rocketchat:lib'); | ||
api.use('rocketchat:autotranslate'); | ||
api.addFiles('server/deeplTranslate.js', 'server'); | ||
}); | ||
|
182 changes: 182 additions & 0 deletions
182
packages/assistify-deepl-translation/server/deeplTranslate.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
/** | ||
* @author Vigneshwaran Odayappan <vickyokrm@gmail.com> | ||
*/ | ||
|
||
import {TranslationProviderRegistry, AutoTranslate} from 'meteor/rocketchat:autotranslate'; | ||
import {RocketChat} from 'meteor/rocketchat:lib'; | ||
import _ from 'underscore'; | ||
|
||
/** | ||
* Represents DEEPL translate class | ||
* @class | ||
* @augments AutoTranslate | ||
*/ | ||
class DeeplAutoTranslate extends AutoTranslate { | ||
/** | ||
* setup api reference to deepl translate to be used as message translation provider. | ||
* @constructor | ||
*/ | ||
constructor() { | ||
super(); | ||
this.name = 'deepl-translate'; | ||
this.apiEndPointUrl = 'https://api.deepl.com/v1/translate'; | ||
// self register & de-register callback - afterSaveMessage based on the activeProvider | ||
RocketChat.settings.get('AutoTranslate_ServiceProvider', (key, value) => { | ||
if (this.name === value) { | ||
this._registerAfterSaveMsgCallBack(this.name); | ||
} else { | ||
this._unRegisterAfterSaveMsgCallBack(this.name); | ||
} | ||
}); | ||
} | ||
|
||
/** | ||
* Returns metadata information about the service provide | ||
* @private implements super abstract method. | ||
* @return {object} | ||
*/ | ||
_getProviderMetadata() { | ||
return { | ||
name: this.name, | ||
displayName: TAPi18n.__('AutoTranslate_DeepL'), | ||
settings: this._getSettings() | ||
}; | ||
} | ||
|
||
/** | ||
* Returns necessary settings information about the translation service provider. | ||
* @private implements super abstract method. | ||
* @return {object} | ||
*/ | ||
_getSettings() { | ||
return { | ||
apiKey: this.apiKey, | ||
apiEndPointUrl: this.apiEndPointUrl | ||
}; | ||
} | ||
|
||
/** | ||
* Returns supported languages for translation by the active service provider. | ||
* @private implements super abstract method. | ||
* @param {string} target | ||
* @returns {object} code : value pair | ||
*/ | ||
_getSupportedLanguages(target) { | ||
if (this.autoTranslateEnabled && this.apiKey) { | ||
if (this.supportedLanguages[target]) { | ||
return this.supportedLanguages[target]; | ||
} | ||
return this.supportedLanguages[target] = [ | ||
{ | ||
'language': 'EN', | ||
'name': TAPi18n.__('English', { lng: target }) | ||
}, | ||
{ | ||
'language': 'DE', | ||
'name': TAPi18n.__('German', { lng: target }) | ||
}, | ||
{ | ||
'language': 'FR', | ||
'name': TAPi18n.__('French', { lng: target }) | ||
}, | ||
{ | ||
'language': 'ES', | ||
'name': TAPi18n.__('Spanish', { lng: target }) | ||
}, | ||
{ | ||
'language': 'IT', | ||
'name': TAPi18n.__('Italian', { lng: target }) | ||
}, | ||
{ | ||
'language': 'NL', | ||
'name': TAPi18n.__('Dutch', { lng: target }) | ||
}, | ||
{ | ||
'language': 'PL', | ||
'name': TAPi18n.__('Polish', { lng: target }) | ||
} | ||
]; | ||
} | ||
} | ||
|
||
/** | ||
* Send Request REST API call to the service provider. | ||
* Returns translated message for each target language in target languages. | ||
* @private | ||
* @param {object} targetMessage | ||
* @param {object} targetLanguages | ||
* @returns {object} translations: Translated messages for each language | ||
*/ | ||
_sendRequestTranslateMessage(targetMessage, targetLanguages) { | ||
const translations = {}; | ||
let msgs = targetMessage.msg.split('\n'); | ||
msgs = msgs.map(msg => encodeURIComponent(msg)); | ||
const query = `text=${ msgs.join('&text=') }`; | ||
const supportedLanguages = this._getSupportedLanguages('en'); | ||
targetLanguages.forEach(language => { | ||
if (language.indexOf('-') !== -1 && !_.findWhere(supportedLanguages, {language})) { | ||
language = language.substr(0, 2); | ||
} | ||
let result; | ||
try { | ||
result = HTTP.get(this.apiEndPointUrl, { | ||
params: { | ||
auth_key: this.apiKey, | ||
target_lang: language | ||
}, query | ||
}); | ||
} catch (e) { | ||
console.log('Error translating message', e); | ||
} | ||
if (result.statusCode === 200 && result.data && result.data.translations && Array.isArray(result.data.translations) && result.data.translations.length > 0) { | ||
// store translation only when the source and target language are different. | ||
if (result.data.translations.map(translation => translation.detected_source_language).join() !== language) { | ||
const txt = result.data.translations.map(translation => translation.text).join('\n'); | ||
translations[language] = this.deTokenize(Object.assign({}, targetMessage, {msg: txt})); | ||
} | ||
} | ||
}); | ||
return translations; | ||
} | ||
|
||
/** | ||
* Returns translated message attachment description in target languages. | ||
* @private | ||
* @param {object} attachment | ||
* @param {object} targetLanguages | ||
* @returns {object} translated messages for each target language | ||
*/ | ||
_sendRequestTranslateMessageAttachments(attachment, targetLanguages) { | ||
const translations = {}; | ||
const query = `text=${ encodeURIComponent(attachment.description || attachment.text) }`; | ||
const supportedLanguages = this._getSupportedLanguages('en'); | ||
targetLanguages.forEach(language => { | ||
if (language.indexOf('-') !== -1 && !_.findWhere(supportedLanguages, {language})) { | ||
language = language.substr(0, 2); | ||
} | ||
let result; | ||
try { | ||
result = HTTP.get(this.apiEndPointUrl, { | ||
params: { | ||
auth_key: this.apiKey, | ||
target_lang: language | ||
}, query | ||
}); | ||
} catch (e) { | ||
console.log('Error translating message attachment', e); | ||
} | ||
if (result.statusCode === 200 && result.data && result.data.translations && Array.isArray(result.data.translations) && result.data.translations.length > 0) { | ||
if (result.data.translations.map(translation => translation.detected_source_language).join() !== language) { | ||
translations[language] = result.data.translations.map(translation => translation.text).join('\n'); | ||
} | ||
} | ||
}); | ||
return translations; | ||
} | ||
} | ||
|
||
|
||
Meteor.startup(() => { | ||
TranslationProviderRegistry.registerProvider(new DeeplAutoTranslate()); | ||
RocketChat.AutoTranslate = TranslationProviderRegistry.getActiveServiceProvider(); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
packages/rocketchat-autotranslate/client/views/autoTranslateFlexTab.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.