diff --git a/app/build.gradle b/app/build.gradle
index 64655dd812..ee7b9ea17d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -50,14 +50,17 @@ android {
}
sourceSets {
- github {
- java.srcDirs = ['src/main/java', 'src/iab/java']
+ githubDebug {
+ java.srcDirs = ['src/main/java', 'src/iab/java', 'src/xlat/java']
+ }
+ githubRelease {
+ java.srcDirs = ['src/main/java', 'src/iab/java', 'src/stub/java']
}
fdroid {
- java.srcDirs = ['src/main/java', 'src/fdroid/java']
+ java.srcDirs = ['src/main/java', 'src/fdroid/java', 'src/stub/java']
}
play {
- java.srcDirs = ['src/main/java', 'src/iab/java']
+ java.srcDirs = ['src/main/java', 'src/iab/java', 'src/stub/java']
}
}
}
@@ -247,6 +250,7 @@ dependencies {
def exif_version = "1.3.0-alpha01"
def biometric_version = "1.0.1" // https://issuetracker.google.com/issues/159983244
def textclassifier_version = "1.0.0-alpha03"
+ def mlkit_translate = "16.0.0"
def billingclient_version = "3.0.0"
def javamail_version = "1.6.5"
def jsoup_version = "1.13.1"
@@ -340,6 +344,9 @@ dependencies {
// https://developer.android.com/jetpack/androidx/releases/textclassifier
//implementation "androidx.textclassifier:textclassifier:$textclassifier_version"
+ // https://developers.google.com/ml-kit/language/translation/android
+ debugImplementation "com.google.mlkit:translate:$mlkit_translate"
+
// https://developer.android.com/google/play/billing/billing_library_releases_notes
// https://android-developers.googleblog.com/2020/06/meet-google-play-billing-library.html
githubImplementation "com.android.billingclient:billing:$billingclient_version"
diff --git a/app/src/main/java/eu/faircode/email/FragmentCompose.java b/app/src/main/java/eu/faircode/email/FragmentCompose.java
index a7bf8ded0f..4cedaaf512 100644
--- a/app/src/main/java/eu/faircode/email/FragmentCompose.java
+++ b/app/src/main/java/eu/faircode/email/FragmentCompose.java
@@ -278,9 +278,10 @@ public class FragmentCompose extends FragmentBase {
private static final int REQUEST_COLOR = 10;
private static final int REQUEST_CONTACT_GROUP = 11;
private static final int REQUEST_ANSWER = 12;
- private static final int REQUEST_LINK = 13;
- private static final int REQUEST_DISCARD = 14;
- private static final int REQUEST_SEND = 15;
+ private static final int REQUEST_TRANSLATE = 13;
+ private static final int REQUEST_LINK = 14;
+ private static final int REQUEST_DISCARD = 15;
+ private static final int REQUEST_SEND = 16;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -1213,8 +1214,11 @@ public class FragmentCompose extends FragmentBase {
menu.findItem(R.id.menu_contact_group).setEnabled(
state == State.LOADED && hasPermission(Manifest.permission.READ_CONTACTS));
menu.findItem(R.id.menu_answer).setEnabled(state == State.LOADED);
+ menu.findItem(R.id.menu_translate).setEnabled(state == State.LOADED);
menu.findItem(R.id.menu_clear).setEnabled(state == State.LOADED);
+ menu.findItem(R.id.menu_translate).setVisible(BuildConfig.DEBUG);
+
int colorEncrypt = Helper.resolveColor(getContext(), R.attr.colorEncrypt);
ImageButton ib = (ImageButton) menu.findItem(R.id.menu_encrypt).getActionView();
ib.setEnabled(state == State.LOADED);
@@ -1275,6 +1279,9 @@ public class FragmentCompose extends FragmentBase {
case R.id.menu_answer:
onMenuAnswer();
return true;
+ case R.id.menu_translate:
+ onMenuTranslate();
+ return true;
case R.id.menu_clear:
StyleHelper.apply(R.id.menu_clear, etBody);
return true;
@@ -1444,6 +1451,20 @@ public class FragmentCompose extends FragmentBase {
fragment.show(getParentFragmentManager(), "compose:answer");
}
+ private void onMenuTranslate() {
+ Bundle args = new Bundle();
+
+ CharSequence seq = (etBody.hasSelection()
+ ? etBody.getText().subSequence(etBody.getSelectionStart(), etBody.getSelectionEnd())
+ : etBody.getText());
+ args.putString("text", seq.toString());
+
+ FragmentDialogTranslate fragment = new FragmentDialogTranslate();
+ fragment.setArguments(args);
+ fragment.setTargetFragment(this, REQUEST_TRANSLATE);
+ fragment.show(getParentFragmentManager(), "compose:answer");
+ }
+
private boolean onActionStyle(int action) {
Log.i("Style action=" + action);
@@ -1752,6 +1773,10 @@ public class FragmentCompose extends FragmentBase {
if (resultCode == RESULT_OK && data != null)
onAnswerSelected(data.getBundleExtra("args"));
break;
+ case REQUEST_TRANSLATE:
+ if (resultCode == RESULT_OK && data != null)
+ onTranslated(data.getBundleExtra("args"));
+ break;
case REQUEST_COLOR:
if (resultCode == RESULT_OK && data != null)
onColorSelected(data.getBundleExtra("args"));
@@ -2732,6 +2757,15 @@ public class FragmentCompose extends FragmentBase {
etBody.getText().insert(etBody.getSelectionStart(), spanned);
}
+ private void onTranslated(Bundle args) {
+ String translated = "\n" + args.getString("translated");
+
+ if (etBody.hasSelection())
+ etBody.getEditableText().insert(etBody.getSelectionEnd(), translated);
+ else
+ etBody.getEditableText().append(translated);
+ }
+
private void onColorSelected(Bundle args) {
int color = args.getInt("color");
int start = args.getInt("start");
diff --git a/app/src/main/res/menu/menu_compose.xml b/app/src/main/res/menu/menu_compose.xml
index 227f6fb0f3..9113ffddff 100644
--- a/app/src/main/res/menu/menu_compose.xml
+++ b/app/src/main/res/menu/menu_compose.xml
@@ -50,6 +50,11 @@
android:title="@string/title_insert_template"
app:showAsAction="never" />
+
+
- Více
Nevyžádané
Přesunout
- Kopírovat
+ Kopírovat
Odhlásit odběr
Vytvořit pravidlo
Není spam
diff --git a/app/src/main/res/values-da-rDK/strings.xml b/app/src/main/res/values-da-rDK/strings.xml
index 335a725124..462b8032ce 100644
--- a/app/src/main/res/values-da-rDK/strings.xml
+++ b/app/src/main/res/values-da-rDK/strings.xml
@@ -663,7 +663,7 @@
Mere
Spam
Flyt
- Kopiér
+ Kopiér
Afmeld
Opret regel
Ikke spam
diff --git a/app/src/main/res/values-de-rDE/strings.xml b/app/src/main/res/values-de-rDE/strings.xml
index 61ea20b628..33dda46ff7 100644
--- a/app/src/main/res/values-de-rDE/strings.xml
+++ b/app/src/main/res/values-de-rDE/strings.xml
@@ -663,7 +663,7 @@ Protokolle, die dem Standard nicht entsprechen, wie „Microsoft Exchange Web Se
Mehr
Spam
Verschieben
- Kopieren
+ Kopieren
Aus Liste abmelden
Regel erstellen
Kein Spam
@@ -780,6 +780,7 @@ Protokolle, die dem Standard nicht entsprechen, wie „Microsoft Exchange Web Se
Mediensymbolleiste
Kontaktgruppe einfügen
Vorlage einfügen
+ Übersetzen
Als einfachen Text bearbeiten
Als neu formatierten Text bearbeiten
Öffentlichen Schlüssel auswählen
diff --git a/app/src/main/res/values-es-rES/strings.xml b/app/src/main/res/values-es-rES/strings.xml
index 44858d7ef4..35bdc9dbb4 100644
--- a/app/src/main/res/values-es-rES/strings.xml
+++ b/app/src/main/res/values-es-rES/strings.xml
@@ -664,7 +664,7 @@
Más
Spam
Mover
- Copiar
+ Copiar
Darse de baja
Crear regla
No es Spam
diff --git a/app/src/main/res/values-fi-rFI/strings.xml b/app/src/main/res/values-fi-rFI/strings.xml
index 72ca78a034..4f64098b93 100644
--- a/app/src/main/res/values-fi-rFI/strings.xml
+++ b/app/src/main/res/values-fi-rFI/strings.xml
@@ -6,6 +6,7 @@
Esimerkiksi viestit muotoillaan aina uudelleen vaarallisten elementtien poistamiseksi ja luettavuuden parantamiseksi, ja linkkien avaaminen pitää turvallisuuden takia erikseen vahvistaa.
FairEmailia ei tueta tässä laitteessa, koska bugit Androidissa aiheuttavat kaatumisia
Mene \'takaisin\' uudelleen poistuaksesi
+ Vastaanottaminen
Lähettäminen
Ilmoitukset
Päivitykset
@@ -75,6 +76,7 @@
Toimeton
\'%1$s\' epäonnistui
Palvelimen \'%1$s\' hälytys
+ Lähettäminen vastaanottajalle %1$s epäonnistui
%1$s/%2$s
%1$s (%2$s)
%1$s +%2$d
@@ -620,6 +622,7 @@
Tee viestistä kopio kansioon %1$s
Re: %1$s
Fwd: %1$s
+ Painikkeet
Merkitse luetuksi
Merkitse lukemattomaksi
Piilota
@@ -658,6 +661,9 @@
Lisää
Siirrä roskapostikansioon
Siirrä
+ Kopioi
+ Peru tilaus
+ Luo sääntö
Ei roskaposti
Siirrä kansioon …
Siirrä kansioon …
@@ -772,6 +778,7 @@
Mediatyökalupalkki
Lisää yhteystietoryhmä
Lisää viestipohja
+ Käännä
Muokkaa pelkkänä tekstinä
Muokkaa uudelleenmuotoiltuna tekstinä
Valitse julkinen avain
diff --git a/app/src/main/res/values-fr-rCA/strings.xml b/app/src/main/res/values-fr-rCA/strings.xml
index 18d66ed360..96f90497cf 100644
--- a/app/src/main/res/values-fr-rCA/strings.xml
+++ b/app/src/main/res/values-fr-rCA/strings.xml
@@ -663,7 +663,7 @@
Plus
Indésirables
Déplacer
- Copier
+ Copier
Se désabonner
Créer une règle
Non indésirable
@@ -780,6 +780,7 @@
Barre d’outils média
Insérer un groupe de contacts
Insérer un modèle
+ Traduire
Modifier comme texte brut
Modifier comme texte reformaté
Sélectionner la clé publique
diff --git a/app/src/main/res/values-fr-rFR/strings.xml b/app/src/main/res/values-fr-rFR/strings.xml
index 3932eec86c..3adb225b38 100644
--- a/app/src/main/res/values-fr-rFR/strings.xml
+++ b/app/src/main/res/values-fr-rFR/strings.xml
@@ -663,7 +663,7 @@
Plus
Spam
Déplacer
- Copier
+ Copier
Se désabonner
Créer une règle
Non spam
@@ -780,6 +780,7 @@
Barre d\'outils média
Insérer un groupe de contacts
Insérer un modèle
+ Traduire
Modifier comme texte brut
Modifier comme texte reformaté
Sélectionner la clé publique
diff --git a/app/src/main/res/values-it-rIT/strings.xml b/app/src/main/res/values-it-rIT/strings.xml
index 2a8eba86ad..508f9a0beb 100644
--- a/app/src/main/res/values-it-rIT/strings.xml
+++ b/app/src/main/res/values-it-rIT/strings.xml
@@ -663,7 +663,7 @@
Altro
Posta indesiderata
Sposta
- Copia
+ Copia
Disiscriviti
Crea una regola
Non spam
diff --git a/app/src/main/res/values-nl-rNL/strings.xml b/app/src/main/res/values-nl-rNL/strings.xml
index b478674786..3482bbd231 100644
--- a/app/src/main/res/values-nl-rNL/strings.xml
+++ b/app/src/main/res/values-nl-rNL/strings.xml
@@ -662,7 +662,7 @@
Meer
Spam
Verplaatsen
- Kopiëren
+ Kopiëren
Uitschrijven
Maak regel
Geen spam
@@ -779,6 +779,7 @@
Media werkbalk
Contactgroep toevoegen
Sjabloon invoegen
+ Vertalen
Bewerk als platte tekst
Bewerk als geformatteerde tekst
Selecteer publieke sleutel
diff --git a/app/src/main/res/values-pl-rPL/strings.xml b/app/src/main/res/values-pl-rPL/strings.xml
index 8807371c51..faf96ab8c5 100644
--- a/app/src/main/res/values-pl-rPL/strings.xml
+++ b/app/src/main/res/values-pl-rPL/strings.xml
@@ -684,7 +684,7 @@
Więcej
Spam
Przenieś
- Kopiuj
+ Kopiuj
Anuluj subskrypcję
Utwórz regułę
To nie jest spam
diff --git a/app/src/main/res/values-ru-rRU/strings.xml b/app/src/main/res/values-ru-rRU/strings.xml
index f5ff9faab4..f2e4197343 100644
--- a/app/src/main/res/values-ru-rRU/strings.xml
+++ b/app/src/main/res/values-ru-rRU/strings.xml
@@ -685,7 +685,7 @@
Ещё
Спам
Переместить
- Копировать
+ Копировать
Отписаться
Создать правило
Не спам
@@ -802,6 +802,7 @@
Панель медиа
Добавить группу контактов
Вставить шаблон
+ Перевод
Редактировать как простой текст
Редактировать как исходный текст
Выбор публичного ключа
diff --git a/app/src/main/res/values-sv-rSE/strings.xml b/app/src/main/res/values-sv-rSE/strings.xml
index 16465ddab8..40c7e52ac0 100644
--- a/app/src/main/res/values-sv-rSE/strings.xml
+++ b/app/src/main/res/values-sv-rSE/strings.xml
@@ -662,7 +662,7 @@
Mer
Skräppost
Flytta
- Kopiera
+ Kopiera
Avbryt prenumeration
Skapa regel
Inte skräppost
@@ -779,6 +779,7 @@
Verktygsfält för media
Infoga kontaktgrupp
Infoga mall
+ Översätt
Redigera som oformaterad text
Redigera som formaterad text
Välj publik nyckel
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index b79837bfc2..009f541cca 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -651,7 +651,7 @@
更多
垃圾邮件
移动
- 复制
+ 复制
取消订阅
创建规则
不是垃圾邮件
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 61a2e4f5c6..4aea0d965d 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -887,6 +887,7 @@
Media toolbar
Insert contact group
Insert template
+ Translate
Edit as plain text
Edit as reformatted text
Select public key
diff --git a/app/src/stub/java/eu/faircode/email/FragmentDialogTranslate.java b/app/src/stub/java/eu/faircode/email/FragmentDialogTranslate.java
new file mode 100644
index 0000000000..1220748061
--- /dev/null
+++ b/app/src/stub/java/eu/faircode/email/FragmentDialogTranslate.java
@@ -0,0 +1,18 @@
+package eu.faircode.email;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+public class FragmentDialogTranslate extends FragmentDialogBase {
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+ return new AlertDialog.Builder(getContext())
+ .setTitle(R.string.title_translate)
+ .create();
+ }
+}
diff --git a/app/src/xlat/java/eu/faircode/email/FragmentDialogTranslate.java b/app/src/xlat/java/eu/faircode/email/FragmentDialogTranslate.java
new file mode 100644
index 0000000000..2afe0f4262
--- /dev/null
+++ b/app/src/xlat/java/eu/faircode/email/FragmentDialogTranslate.java
@@ -0,0 +1,90 @@
+package eu.faircode.email;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.google.android.gms.tasks.OnFailureListener;
+import com.google.android.gms.tasks.OnSuccessListener;
+import com.google.mlkit.common.model.DownloadConditions;
+import com.google.mlkit.nl.translate.TranslateLanguage;
+import com.google.mlkit.nl.translate.Translation;
+import com.google.mlkit.nl.translate.Translator;
+import com.google.mlkit.nl.translate.TranslatorOptions;
+
+import java.util.Locale;
+import java.util.Map;
+import java.util.TreeMap;
+
+import static android.app.Activity.RESULT_OK;
+
+public class FragmentDialogTranslate extends FragmentDialogBase {
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+ final String text = getArguments().getString("text");
+
+ Map map = new TreeMap<>();
+ for (String lc : TranslateLanguage.getAllLanguages())
+ map.put(new Locale(lc).getDisplayLanguage(), lc);
+
+ String[] items = map.keySet().toArray(new String[0]);
+
+ return new AlertDialog.Builder(getContext())
+ .setTitle(R.string.title_translate)
+ .setItems(items, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ String language = map.get(items[which]);
+
+ TranslatorOptions options = new TranslatorOptions.Builder()
+ .setSourceLanguage(TranslateLanguage.ENGLISH)
+ .setTargetLanguage(language)
+ .build();
+ Translator translator = Translation.getClient(options);
+ DownloadConditions conditions = new DownloadConditions.Builder()
+ .requireWifi()
+ .build();
+ translator.downloadModelIfNeeded(conditions)
+ .addOnSuccessListener(
+ new OnSuccessListener() {
+ @Override
+ public void onSuccess(Void v) {
+ translator.translate(text)
+ .addOnSuccessListener(
+ new OnSuccessListener() {
+ @Override
+ public void onSuccess(@NonNull String translatedText) {
+ getArguments().putString("translated", translatedText);
+ sendResult(RESULT_OK);
+ }
+ })
+ .addOnFailureListener(
+ new OnFailureListener() {
+ @Override
+ public void onFailure(@NonNull Exception ex) {
+ Log.unexpectedError(getParentFragmentManager(), ex);
+ }
+ });
+ }
+ })
+ .addOnFailureListener(
+ new OnFailureListener() {
+ @Override
+ public void onFailure(@NonNull Exception ex) {
+ Log.unexpectedError(getParentFragmentManager(), ex);
+ }
+ });
+ }
+ })
+ .create();
+ }
+
+ @Override
+ public void onDismiss(@NonNull DialogInterface dialog) {
+ }
+}