From 405e49cc87108963f4ce5762edb004af41a1fef0 Mon Sep 17 00:00:00 2001 From: charles_moulhaud Date: Fri, 17 May 2024 16:07:15 +0200 Subject: [PATCH] resolve #1606 [Translator] : init Deepl integration --- translator/deepl-translate/pom.xml | 2 +- .../src/main/kotlin/DeeplClient.kt | 54 ++++--------------- .../src/main/kotlin/DeeplTranslatorEngine.kt | 11 ++-- .../kotlin/DeeplTranslateIntegrationTest.kt | 52 ++---------------- 4 files changed, 18 insertions(+), 101 deletions(-) diff --git a/translator/deepl-translate/pom.xml b/translator/deepl-translate/pom.xml index 8c6c8b1c92..87cacd3b29 100644 --- a/translator/deepl-translate/pom.xml +++ b/translator/deepl-translate/pom.xml @@ -25,7 +25,7 @@ 23.9.3-SNAPSHOT - tock-translator-deepl + tock-deepl-translate Tock Deepl Translator Deepl translator implementation diff --git a/translator/deepl-translate/src/main/kotlin/DeeplClient.kt b/translator/deepl-translate/src/main/kotlin/DeeplClient.kt index 8629b99f14..48668e880a 100644 --- a/translator/deepl-translate/src/main/kotlin/DeeplClient.kt +++ b/translator/deepl-translate/src/main/kotlin/DeeplClient.kt @@ -14,16 +14,14 @@ * limitations under the License. */ -package ai.tock.translator.deepl +package ai.tock import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.RequestBody.Companion.toRequestBody import com.squareup.moshi.Moshi import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory -import mu.KotlinLogging import java.io.IOException -import java.util.regex.Pattern data class TranslationResponse( val translations: List @@ -34,58 +32,24 @@ data class Translation( ) val DEEPL_URL_API = "https://api.deepl.com/v2/translate" -val TAG_HANDLING = "xml" class DeeplClient(private val apiKey: String) { private val client = OkHttpClient() private val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build() private val jsonAdapter = moshi.adapter(TranslationResponse::class.java) - private val logger = KotlinLogging.logger {} + fun translate(text: String, sourceLang: String,targetLang: String): String? { + // TODO Pass placeholders options in parameters + val textWithTags = text.replace("{", "").replace("}", "") - private fun replaceSpecificPlaceholders(text: String): Pair> { - // Store original placeholders for later restoration - val placeholderPattern = Pattern.compile("\\{:([^}]*)}") - val matcher = placeholderPattern.matcher(text) - - val placeholders = mutableListOf() - while (matcher.find()) { - placeholders.add(matcher.group(1)) - } - - // Replace placeholders with '_PLACEHOLDER_' - val replacedText = matcher.replaceAll("_PLACEHOLDER_") - - return Pair(replacedText, placeholders) - } - - private fun revertSpecificPlaceholders(text: String, placeholders: List): String { - var resultText = text - for (placeholder in placeholders) { - resultText = resultText.replaceFirst("_PLACEHOLDER_", "{:$placeholder}") - } - return resultText - } - - fun translate(text: String, sourceLang: String,targetLang: String,preserveFormatting: Boolean,glossaryId:String?): String? { - val (textWithPlaceholders, originalPlaceholders) = replaceSpecificPlaceholders(text) - - val requestBody = buildString { - append("text=$textWithPlaceholders") - append("&source_lang=$sourceLang") - append("&target_lang=$targetLang") - append("&preserve_formatting=$preserveFormatting") - append("&tag_handling=$TAG_HANDLING") - - if (glossaryId != "default") { - append("&glossary=$glossaryId") - } - } + val requestBody = """ + text=$textWithTags&source_lang=$sourceLang&target_lang=$targetLang&tag_handling=xml&non_translatable_tags=x + """.trimIndent().toRequestBody("application/x-www-form-urlencoded".toMediaTypeOrNull()) val request = Request.Builder() .url(DEEPL_URL_API) .addHeader("Authorization", "DeepL-Auth-Key $apiKey") - .post(requestBody.trimIndent().toRequestBody("application/x-www-form-urlencoded".toMediaTypeOrNull())) + .post(requestBody) .build() client.newCall(request).execute().use { response -> @@ -95,7 +59,7 @@ class DeeplClient(private val apiKey: String) { val translationResponse = jsonAdapter.fromJson(responseBody!!) val translatedText = translationResponse?.translations?.firstOrNull()?.text - return translatedText?.let { revertSpecificPlaceholders(it,originalPlaceholders) } + return translatedText?.replace("", "{")?.replace("", "}") } } } \ No newline at end of file diff --git a/translator/deepl-translate/src/main/kotlin/DeeplTranslatorEngine.kt b/translator/deepl-translate/src/main/kotlin/DeeplTranslatorEngine.kt index de16de96c1..43d91dab95 100644 --- a/translator/deepl-translate/src/main/kotlin/DeeplTranslatorEngine.kt +++ b/translator/deepl-translate/src/main/kotlin/DeeplTranslatorEngine.kt @@ -14,25 +14,20 @@ * limitations under the License. */ -package ai.tock.translator.deepl +package ai.tock import ai.tock.shared.property import ai.tock.translator.TranslatorEngine -import mu.KotlinLogging import org.apache.commons.text.StringEscapeUtils import java.util.Locale internal object DeeplTranslatorEngine : TranslatorEngine { - private val logger = KotlinLogging.logger {} - - private val deeplClient = DeeplClient(property ("tock_translator_deepl_api_key", "default")) - private val glossaryId = property ("tock_translator_deepl_glossaryId", "default") + private val deeplClient = DeeplClient(property ("tock_deepl_api_key", "default")) override val supportAdminTranslation: Boolean = true override fun translate(text: String, source: Locale, target: Locale): String { - logger.debug("Try to translate text using deepl") - val translatedText = deeplClient.translate(text, source.language, target.language,true,glossaryId) + val translatedText = deeplClient.translate(text, source.language, target.language) return StringEscapeUtils.unescapeHtml4(translatedText) } } diff --git a/translator/deepl-translate/src/test/kotlin/DeeplTranslateIntegrationTest.kt b/translator/deepl-translate/src/test/kotlin/DeeplTranslateIntegrationTest.kt index 317bb1f794..b41641b399 100644 --- a/translator/deepl-translate/src/test/kotlin/DeeplTranslateIntegrationTest.kt +++ b/translator/deepl-translate/src/test/kotlin/DeeplTranslateIntegrationTest.kt @@ -1,5 +1,4 @@ -import ai.tock.translator.deepl.DeeplTranslatorEngine -import org.junit.jupiter.api.Disabled +import ai.tock.DeeplTranslatorEngine import org.junit.jupiter.api.Test import java.util.Locale import kotlin.test.assertEquals @@ -20,57 +19,16 @@ import kotlin.test.assertEquals * limitations under the License. */ -/** - * All these tests are disabled because it uses Deepl pro api that can be expensive - */ class DeeplTranslateIntegrationTest { @Test - @Disabled fun simpleTest() { - val result = DeeplTranslatorEngine.translate( - "Bonjour, je voudrais me rendre à New-York Mardi prochain", - Locale.FRENCH, - Locale.ENGLISH - ) - assertEquals("Hello, I would like to go to New York next Tuesday.", result) - } - - @Test - @Disabled - fun testWithEmoticonAndAntislash() { - val result = DeeplTranslatorEngine.translate("Bonjour, je suis l'Agent virtuel SNCF Voyageurs! \uD83E\uDD16\n" + - "Je vous informe sur l'état du trafic en temps réel.\n" + - "Dites-moi par exemple \"Mon train 6111 est-il à l'heure ?\", \"Aller à Saint-Lazare\", \"Prochains départs Gare de Lyon\" ...", - Locale.FRENCH, - Locale.ENGLISH - ) - - assertEquals("Hello, I'm the SNCF Voyageurs Virtual Agent! \uD83E\uDD16\n" + - "I inform you about traffic conditions in real time.\n" + - "Tell me for example \"Is my train 6111 on time?\", \"Going to Saint-Lazare\", \"Next departures Gare de Lyon\" ...", - result - ) + val result = DeeplTranslatorEngine.translate("Bonjour, je voudrais me rendre à New-York Mardi prochain", Locale.FRENCH, Locale.ENGLISH) + assertEquals("Hello, I would like to go to New York next Tuesday.",result) } @Test - @Disabled fun testWithParameters() { - val result = DeeplTranslatorEngine.translate( - "Bonjour, je voudrais me rendre à {:city} {:date}", - Locale.FRENCH, - Locale.GERMAN - ) - assertEquals("Hallo, ich möchte nach {:city} {:date} reisen", result) - } - - @Test - @Disabled - fun testWithHTML() { - val result = DeeplTranslatorEngine.translate( - "Bonjour, je voudrais me rendre à Paris

demain soir", - Locale.FRENCH, - Locale.GERMAN - ) - assertEquals("Hallo, ich möchte morgen Abend nach Paris

fahren", result) + val result = DeeplTranslatorEngine.translate("Bonjour, je voudrais me rendre à {:city} {:date}", Locale.FRENCH, Locale.GERMAN) + assertEquals("Hallo, ich möchte nach {:city} {:date} reisen",result) } } \ No newline at end of file