Skip to content

Commit

Permalink
feat: [WIP] add DeepLX translation provider
Browse files Browse the repository at this point in the history
Signed-off-by: Next Alone <[email protected]>
  • Loading branch information
NextAlone committed Nov 3, 2024
1 parent a09d7bb commit 27b1f43
Show file tree
Hide file tree
Showing 7 changed files with 229 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ public class GeneralSettingActivity extends BaseActivity {
private int translatorRow;
private int showOriginalRow;
private int deepLFormalityRow;
private int deepLxApiRow;
private int deepLxTokenRow;
private int translatorTypeRow;
private int translationProviderRow;
private int translationTargetRow;
Expand Down Expand Up @@ -306,11 +308,28 @@ else if (position == showBotAPIRow) {
listAdapter.notifyItemChanged(translationTargetRow, PARTIAL);
}
if (!oldProvider.equals(TranslateHelper.getCurrentProviderType())) {
if (oldProvider.equals(ProviderType.DeepLTranslator)) {
boolean wasDeepLTranslator = oldProvider.equals(ProviderType.DeepLTranslator);
boolean isDeepLTranslator = TranslateHelper.getCurrentProviderType().equals(ProviderType.DeepLTranslator);
boolean wasDeepLxTranslator = oldProvider.equals(ProviderType.DeepLxTranslator);
boolean isDeepLxTranslator = TranslateHelper.getCurrentProviderType().equals(ProviderType.DeepLxTranslator);

if (wasDeepLTranslator && !isDeepLxTranslator) {
listAdapter.notifyItemRemoved(deepLFormalityRow);
updateRows();
} else if (TranslateHelper.getCurrentProviderType().equals(ProviderType.DeepLTranslator)) {
updateRows();
} else if (wasDeepLxTranslator) {
listAdapter.notifyItemRemoved(deepLxApiRow);
listAdapter.notifyItemRemoved(deepLxTokenRow);
if (!isDeepLxTranslator) {
listAdapter.notifyItemRemoved(deepLFormalityRow);
}
}

updateRows();

if (isDeepLTranslator) {
listAdapter.notifyItemInserted(deepLFormalityRow);
} else if (isDeepLxTranslator) {
listAdapter.notifyItemInserted(deepLxApiRow);
listAdapter.notifyItemInserted(deepLxTokenRow);
listAdapter.notifyItemInserted(deepLFormalityRow);
}
}
Expand Down Expand Up @@ -338,6 +357,38 @@ else if (position == showBotAPIRow) {
ConfigManager.putInt(Defines.deepLFormality, types.get(i));
listAdapter.notifyItemChanged(deepLFormalityRow, PARTIAL);
});
} else if (position == deepLxApiRow) {
EditTextBoldCursor editText = new EditTextBoldCursor(getParentActivity());
editText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
editText.setHint("DeepLx API Url"); // todo: string resource
editText.setImeOptions(EditorInfo.IME_ACTION_DONE);
editText.setText(Config.getDeepLxApi());
FrameLayout frameLayout = new FrameLayout(getParentActivity());
frameLayout.addView(editText, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP, 16, 0, 16, 0));
AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity());
builder.setTitle("DeepLx API Url"); // todo: string resource
builder.setView(frameLayout);
builder.setPositiveButton(LocaleController.getString("Save", R.string.Save), (dialogInterface, i) -> {
Config.setDeepLxApi(editText.getText().toString());
});
builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null);
builder.show();
} else if (position == deepLxTokenRow) {
EditTextBoldCursor editText = new EditTextBoldCursor(getParentActivity());
editText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
editText.setHint("DeepLx API Token"); // todo: string resource
editText.setImeOptions(EditorInfo.IME_ACTION_DONE);
editText.setText(Config.getDeepLxToken());
FrameLayout frameLayout = new FrameLayout(getParentActivity());
frameLayout.addView(editText, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP, 16, 0, 16, 0));
AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity());
builder.setTitle("DeepLx API Token"); // todo: string resource
builder.setView(frameLayout);
builder.setPositiveButton(LocaleController.getString("Save", R.string.Save), (dialogInterface, i) -> {
Config.setDeepLxToken(editText.getText().toString());
});
builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null);
builder.show();
} else if (position == translatorTypeRow) {
var oldType = TranslateHelper.getCurrentStatus();
TranslateHelper.showTranslatorTypeSelector(getParentActivity(), view, () -> {
Expand Down Expand Up @@ -441,7 +492,9 @@ protected void updateRows() {
if (TranslateHelper.getCurrentStatus() != TranslateHelper.Status.External) {
showOriginalRow = TranslateHelper.getCurrentStatus() == TranslateHelper.Status.InMessage ? addRow("showOriginal") : -1;
translationProviderRow = addRow("translationProvider");
deepLFormalityRow = TranslateHelper.getCurrentProviderType().equals(ProviderType.DeepLTranslator) ? addRow("deepLFormality") : -1;
deepLxApiRow = TranslateHelper.getCurrentProviderType().equals(ProviderType.DeepLxTranslator) ? addRow("deepLxApi") : -1;
deepLxTokenRow = TranslateHelper.getCurrentProviderType().equals(ProviderType.DeepLxTranslator) ? addRow("deepPToken") : -1;
deepLFormalityRow = (TranslateHelper.getCurrentProviderType().equals(ProviderType.DeepLTranslator) || TranslateHelper.getCurrentProviderType().equals(ProviderType.DeepLxTranslator)) ? addRow("deepLFormality") : -1;
translationTargetRow = addRow("translationTarget");
doNotTranslateRow = addRow("doNotTranslate");
autoTranslateRow = addRow("autoTranslate");
Expand Down Expand Up @@ -612,6 +665,10 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position, boole
textCell.setTextAndValue(LocaleController.getString("customTitle", R.string.customTitle), Config.getCustomTitle(), payload, true);
} else if (position == drawerListRow) {
textCell.setText(LocaleController.getString("drawerList", R.string.drawerList), false);
} else if (position == deepLxApiRow) {
textCell.setTextAndValue(LocaleController.getString(R.string.DeepLxApiUrl), Config.getDeepLxApi().substring(0, 25) + "...", payload, true);
} else if (position == deepLxTokenRow) {
textCell.setTextAndValue(LocaleController.getString(R.string.DeepLxApiToken), Config.getDeepLxToken().substring(0, 20) + "...", payload, true);
}
break;
}
Expand Down Expand Up @@ -768,7 +825,7 @@ public int getItemViewType(int position) {
return 1;
} else if (position == tabsTitleTypeRow || position == translationProviderRow || position == deepLFormalityRow || position == translationTargetRow ||
position == translatorTypeRow || position == doNotTranslateRow || position == overrideDevicePerformanceRow || position == customTitleRow ||
position == drawerListRow) {
position == drawerListRow || position == deepLxTokenRow || position == deepLxApiRow) {
return 2;
} else if (position == generalRow || position == translatorRow || position == devicesRow || position == storiesRow) {
return 4;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import xyz.nextalone.nnngram.config.ConfigManager
import xyz.nextalone.nnngram.translate.BaseTranslator
import xyz.nextalone.nnngram.translate.providers.BaiduTranslator
import xyz.nextalone.nnngram.translate.providers.DeepLTranslator
import xyz.nextalone.nnngram.translate.providers.DeepLxTranslator
import xyz.nextalone.nnngram.translate.providers.GoogleTranslator
import xyz.nextalone.nnngram.translate.providers.LingoTranslator
import xyz.nextalone.nnngram.translate.providers.MicrosoftTranslator
Expand Down Expand Up @@ -101,6 +102,7 @@ object TranslateHelper {
YandexTranslator(6),
DeepLTranslator(7),
TranSmartTranslator(8),
DeepLxTranslator(9),
}

@JvmStatic
Expand All @@ -110,6 +112,7 @@ object TranslateHelper {
ProviderType.TelegramTranslator.num -> ProviderType.TelegramTranslator
ProviderType.MicrosoftTranslator.num -> ProviderType.MicrosoftTranslator
ProviderType.DeepLTranslator.num -> ProviderType.DeepLTranslator
ProviderType.DeepLxTranslator.num -> ProviderType.DeepLxTranslator
ProviderType.LingoTranslator.num -> ProviderType.LingoTranslator
ProviderType.BaiduTranslator.num -> ProviderType.BaiduTranslator
ProviderType.TranSmartTranslator.num -> ProviderType.TranSmartTranslator
Expand All @@ -130,6 +133,7 @@ object TranslateHelper {
ProviderType.LingoTranslator -> LingoTranslator
ProviderType.BaiduTranslator -> BaiduTranslator
ProviderType.TranSmartTranslator -> TranSmartTranslator
ProviderType.DeepLxTranslator -> DeepLxTranslator
}

@JvmStatic
Expand All @@ -142,6 +146,7 @@ object TranslateHelper {
ProviderType.LingoTranslator -> LingoTranslator
ProviderType.BaiduTranslator -> BaiduTranslator
ProviderType.TranSmartTranslator -> TranSmartTranslator
ProviderType.DeepLxTranslator -> DeepLxTranslator
}

@JvmStatic
Expand Down Expand Up @@ -252,6 +257,8 @@ object TranslateHelper {
types.add(ProviderType.BaiduTranslator)
names.add(LocaleController.getString("ProviderTranSmartTranslate", R.string.ProviderTranSmartTranslate))
types.add(ProviderType.TranSmartTranslator)
names.add("DeepLx")
types.add(ProviderType.DeepLxTranslator)
return Pair(names, types)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/*
* Copyright (C) 2019-2023 qwq233 <[email protected]>
* https://github.com/qwq233/Nullgram
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this software.
* If not, see
* <https://www.gnu.org/licenses/>
*/

package xyz.nextalone.nnngram.translate.providers

import android.text.TextUtils
import io.ktor.client.request.post
import io.ktor.client.request.setBody
import io.ktor.client.statement.bodyAsText
import io.ktor.http.ContentType
import io.ktor.http.HttpStatusCode
import io.ktor.http.contentType
import org.json.JSONObject
import xyz.nextalone.gen.Config
import xyz.nextalone.nnngram.config.ConfigManager
import xyz.nextalone.nnngram.translate.BaseTranslator
import xyz.nextalone.nnngram.utils.Defines
import xyz.nextalone.nnngram.utils.Log
import java.io.IOException
import java.util.Locale
import java.util.UUID
import java.util.regex.Matcher
import java.util.regex.Pattern

/**
* @author NextAlone
* @date 2024/11/04 01:14
*
*/
object DeepLxTranslator : BaseTranslator() {

private val targetLanguages = listOf(
"bg", "cs", "da", "de", "el", "en-GB", "en-US", "en", "es", "et",
"fi", "fr", "hu", "id", "it", "ja", "lt", "lv", "nl", "pl", "pt-BR",
"pt-PT", "pt", "ro", "ru", "sk", "sl", "sv", "tr", "uk", "zh"
)

override fun getTargetLanguages(): List<String> = targetLanguages

override fun convertLanguageCode(language: String, country: String?): String {
val languageLowerCase: String = language.lowercase(Locale.getDefault())
val code: String = if (!TextUtils.isEmpty(country)) {
val countryUpperCase: String = country!!.uppercase(Locale.getDefault())
if (targetLanguages.contains("$languageLowerCase-$countryUpperCase")) {
"$languageLowerCase-$countryUpperCase"
} else {
languageLowerCase
}
} else {
languageLowerCase
}
return code
}

override suspend fun translateText(text: String, from: String, to: String): RequestResult {
Log.d("text: $text")
Log.d("from: $from")
Log.d("to: $to")
if (from == to) {
return RequestResult(from, text)
}
if (Config.deepLxApi.isEmpty() || Config.deepLxToken.isEmpty()) {
throw IOException("DeepLx API or token is empty")
}

client.post( Config.deepLxApi + "?token=" + Config.deepLxToken) {
contentType(ContentType.Application.Json)
// header("Referer", "https://www.deepl.com/")
// header("User-Agent", "DeepL/1.8(52) Android 13 (Pixel 5;aarch64)")
// header("Client-Id", uuid)
// header("x-instance", uuid)
// header("x-app-os-name", "Android")
// header("x-app-os-version", "13")
// header("x-app-version", "1.8")
// header("x-app-build", "52")
// header("x-app-device", "Pixel 5")
// header("x-app-instance-id", uuid)
setBody(getRequestBody(text, from, to))
}.let {
when (it.status) {
HttpStatusCode.OK -> {
val jsonObject = JSONObject(it.bodyAsText())
if (jsonObject.has("error")) {
throw IOException(jsonObject.getString("message"))
}
return RequestResult(
jsonObject.getString("source_lang"),
jsonObject.getString("data")
)
}

else -> {
Log.w(it.bodyAsText())
return RequestResult(from, null, it.status)
}
}
}
}

const val FORMALITY_DEFAULT = 0
const val FORMALITY_MORE = 1
const val FORMALITY_LESS = 2


private fun getRequestBody(text: String, from: String, to: String): String {
var iCounter = 1
val iMatcher: Matcher = Pattern.compile("[i]").matcher(text)
while (iMatcher.find()) {
iCounter++
}
val params = JSONObject().apply {
put("text", text)
put("split_sentences", 1)
put("source_lang", from)
put("target_lang", to)
put("preserve_formatting", true)
put("formality", getFormalityString())
}

return params.toString()
}

private fun getFormalityString(): String? {
return when (ConfigManager.getIntOrDefault(Defines.deepLFormality, -1)) {
FORMALITY_DEFAULT -> "default"
FORMALITY_MORE -> "more"
FORMALITY_LESS -> "less"
else -> "default"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,9 @@ object Defines {
@BooleanConfig const val showOriginal = "showOriginal"
const val translatorProvider = "translatorProvider"
const val deepLFormality = "deepLFormality"
@StringConfig("") const val deepLxApi = "deepLxApi"
@StringConfig("") const val deepLxToken = "deepLxToken"
const val deepLxPreserveFormatting = "deepLxPreserveFormatting"
const val translatorStatus = "translatorStatus"
const val targetLanguage = "targetLanguage"
const val restrictedLanguages = "restrictedLanguagesFix"
Expand Down
3 changes: 3 additions & 0 deletions TMessagesProj/src/main/res/values-zh-rTW/strings_nullgram.xml
Original file line number Diff line number Diff line change
Expand Up @@ -369,4 +369,7 @@
<string name="hideFilterMuteAll">隱藏文件夾中的\"全部取消靜音\"</string>
<string name="hideKeyboardWhenScrolling">滑動時隱藏鍵盤</string>
<string name="searchInPlace">在当前位置中开始搜索</string>
<string name="ProviderDeepLXTranslate">DeepLX 翻譯</string>
<string name="DeepLxApiUrl">DeepLX API 網址</string>
<string name="DeepLxApiToken">DeepLX API 令牌</string>
</resources>
3 changes: 3 additions & 0 deletions TMessagesProj/src/main/res/values-zh/strings_nullgram.xml
Original file line number Diff line number Diff line change
Expand Up @@ -379,4 +379,7 @@
<string name="hideProxyEntryInTitle">隐藏标题代理入口</string>
<string name="autoMuteAfterJoiningChannel">加入频道后自动静音</string>
<string name="enableXiaomiHyperAi">在输入框中显示小米澎湃AI</string>
<string name="ProviderDeepLXTranslate">DeepLX 翻译</string>
<string name="DeepLxApiUrl">DeepLX API 网址</string>
<string name="DeepLxApiToken">DeepLX API 令牌</string>
</resources>
3 changes: 3 additions & 0 deletions TMessagesProj/src/main/res/values/strings_nullgram.xml
Original file line number Diff line number Diff line change
Expand Up @@ -392,4 +392,7 @@
<string name="hideProxyEntryInTitle">Hide proxy entry in title</string>
<string name="autoMuteAfterJoiningChannel">Auto mute after joining channel</string>
<string name="enableXiaomiHyperAi">Enable Xiaomi HyperAI in editor</string>
<string name="ProviderDeepLXTranslate">DeepLX Translate</string>
<string name="DeepLxApiUrl">DeepLX API URL</string>
<string name="DeepLxApiToken">DeepLX API Token</string>
</resources>

0 comments on commit 27b1f43

Please sign in to comment.