diff --git a/app/internal_packages/preferences/lib/tabs/preferences-identity.tsx b/app/internal_packages/preferences/lib/tabs/preferences-identity.tsx
index 0777b1c44..4df7c6a42 100644
--- a/app/internal_packages/preferences/lib/tabs/preferences-identity.tsx
+++ b/app/internal_packages/preferences/lib/tabs/preferences-identity.tsx
@@ -126,6 +126,14 @@ const ProTourFeatures = [
`Use the Activity tab to get a birds-eye view of your mailbox: open and click rates, subject line effectiveness, and more.`
),
},
+ {
+ link: 'https://foundry376.zendesk.com/hc/en-us/articles/360031102452',
+ icon: `pro-feature-translation.png`,
+ title: localized(`Automatic Translation`),
+ text: localized(
+ `Instantly translate messages you receive into your preferred reading language.`
+ ),
+ },
];
class PreferencesIdentity extends React.Component<{}, { identity: IIdentity }> {
diff --git a/app/internal_packages/translation/README.md b/app/internal_packages/translation/README.md
new file mode 100644
index 000000000..050bb5cb6
--- /dev/null
+++ b/app/internal_packages/translation/README.md
@@ -0,0 +1,11 @@
+## Translate
+
+A package for Mailspring that translates draft text into other languages using the Yandex Translation API.
+
+
+
+#### Enable this plugin
+
+1. Download and run Mailspring
+
+2. Navigate to Preferences > Plugins and click "Enable" beside the plugin.
diff --git a/app/internal_packages/translation/assets/ic-translation-modal@2x.png b/app/internal_packages/translation/assets/ic-translation-modal@2x.png
new file mode 100644
index 000000000..edc6a2324
Binary files /dev/null and b/app/internal_packages/translation/assets/ic-translation-modal@2x.png differ
diff --git a/app/internal_packages/composer-translate/assets/icon-composer-translate@1x.png b/app/internal_packages/translation/assets/icon-composer-translate@1x.png
similarity index 100%
rename from app/internal_packages/composer-translate/assets/icon-composer-translate@1x.png
rename to app/internal_packages/translation/assets/icon-composer-translate@1x.png
diff --git a/app/internal_packages/composer-translate/assets/icon-composer-translate@2x.png b/app/internal_packages/translation/assets/icon-composer-translate@2x.png
similarity index 100%
rename from app/internal_packages/composer-translate/assets/icon-composer-translate@2x.png
rename to app/internal_packages/translation/assets/icon-composer-translate@2x.png
diff --git a/app/internal_packages/composer-translate/icon.png b/app/internal_packages/translation/icon.png
similarity index 100%
rename from app/internal_packages/composer-translate/icon.png
rename to app/internal_packages/translation/icon.png
diff --git a/app/internal_packages/composer-translate/lib/main.tsx b/app/internal_packages/translation/lib/composer-button.tsx
similarity index 50%
rename from app/internal_packages/composer-translate/lib/main.tsx
rename to app/internal_packages/translation/lib/composer-button.tsx
index b49132037..9fa074674 100644
--- a/app/internal_packages/composer-translate/lib/main.tsx
+++ b/app/internal_packages/translation/lib/composer-button.tsx
@@ -1,44 +1,23 @@
-/* eslint global-require: "off" */
-
-// // Translation Plugin
-// Last Revised: Feb. 29, 2016 by Ben Gotow
-
-// TranslateButton is a simple React component that allows you to select
-// a language from a popup menu and translates draft text into that language.
-
import React from 'react';
import ReactDOM from 'react-dom';
import {
PropTypes,
- ComponentRegistry,
- QuotedHTMLTransformer,
localized,
Actions,
Message,
DraftEditingSession,
+ FeatureUsageStore,
} from 'mailspring-exports';
import { Menu, RetinaImg } from 'mailspring-component-kit';
+import { TranslatePopupOptions, translateMessageBody, TranslationsUsedLexicon } from './service';
-const YandexTranslationURL = 'https://translate.yandex.net/api/v1.5/tr.json/translate';
-const YandexTranslationKey =
- 'trnsl.1.1.20150415T044616Z.24814c314120d022.0a339e2bc2d2337461a98d5ec9863fc46e42735e';
-const YandexLanguages = {
- English: 'en',
- Spanish: 'es',
- Russian: 'ru',
- Chinese: 'zh',
- French: 'fr',
- German: 'de',
- Italian: 'it',
- Japanese: 'ja',
- Portuguese: 'pt',
- Korean: 'ko',
-};
-
-class TranslateButton extends React.Component<{ draft: Message; session: DraftEditingSession }> {
+export class TranslateComposerButton extends React.Component<{
+ draft: Message;
+ session: DraftEditingSession;
+}> {
// Adding a `displayName` makes debugging React easier
- static displayName = 'TranslateButton';
+ static displayName = 'TranslateComposerButton';
// Since our button is being injected into the Composer Footer,
// we receive the local id of the current draft as a `prop` (a read-only
@@ -54,47 +33,27 @@ class TranslateButton extends React.Component<{ draft: Message; session: DraftEd
return nextProps.session !== this.props.session;
}
- _onError(error) {
+ _onTranslate = async langName => {
Actions.closePopover();
- const dialog = require('electron').remote.dialog;
- dialog.showErrorBox(localized('Language Conversion Failed'), error.toString());
- }
- _onTranslate = async lang => {
- Actions.closePopover();
+ try {
+ await FeatureUsageStore.markUsedOrUpgrade('translation', TranslationsUsedLexicon);
+ } catch (err) {
+ // user does not have access to this feature
+ return;
+ }
// Obtain the session for the current draft. The draft session provides us
// the draft object and also manages saving changes to the local cache and
// Nilas API as multiple parts of the application touch the draft.
- const draftHtml = this.props.draft.body;
- const text = QuotedHTMLTransformer.removeQuotedHTML(draftHtml);
+ const langCode = TranslatePopupOptions[langName];
+ const translated = await translateMessageBody(this.props.draft.body, langCode);
- const queryParams = new URLSearchParams();
- queryParams.set('key', YandexTranslationKey);
- queryParams.set('lang', YandexLanguages[lang]);
- queryParams.set('text', text);
- queryParams.set('format', 'html');
-
- try {
- const resp = await fetch(YandexTranslationURL, { method: 'POST', body: queryParams });
- if (!resp.ok) {
- throw new Error(localized('Sorry, we were unable to complete the translation request.'));
- }
- const json = await resp.json();
- let translated = json.text.join('');
-
- // The new text of the draft is our translated response, plus any quoted text
- // that we didn't process.
- translated = QuotedHTMLTransformer.appendQuotedHTML(translated, draftHtml);
-
- // To update the draft, we add the new body to it's session. The session object
- // automatically marshalls changes to the database and ensures that others accessing
- // the same draft are notified of changes.
- this.props.session.changes.add({ body: translated });
- this.props.session.changes.commit();
- } catch (error) {
- this._onError(error);
- }
+ // To update the draft, we add the new body to it's session. The session object
+ // automatically marshalls changes to the database and ensures that others accessing
+ // the same draft are notified of changes.
+ this.props.session.changes.add({ body: translated });
+ this.props.session.changes.commit();
};
_onClickTranslateButton = () => {
@@ -108,7 +67,7 @@ class TranslateButton extends React.Component<{ draft: Message; session: DraftEd
return (