Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: allow formality to be set globally #2698

Merged
merged 15 commits into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .run/Backend localhost.run.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
<configuration default="false" name="Backend localhost" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot" nameIsGenerated="true">
<option name="ACTIVE_PROFILES" value="dev" />
<option name="FRAME_DEACTIVATION_UPDATE_POLICY" value="UpdateClassesAndResources" />
<module name="tolgee-platform.server-app.main" />
<option name="SPRING_BOOT_MAIN_CLASS" value="io.tolgee.Application" />
<method v="2">
<option name="Make" enabled="true" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ class MachineTranslationSettingsController(
return CollectionModel.of(
data.map {
LanguageInfoModel(
it.language.id,
it.language.tag,
it.language?.id,
it.language?.tag,
supportedServices = it.supportedServices,
)
}.sortedBy { it.languageId },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import java.io.Serializable
@Suppress("unused")
@Relation(collectionRelation = "languageInfos", itemRelation = "languageInfo")
class LanguageInfoModel(
val languageId: Long,
val languageTag: String,
val languageId: Long?,
val languageTag: String?,
val supportedServices: List<MtSupportedService>,
) : RepresentationModel<LanguageInfoModel>(), Serializable
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,34 @@ class MachineTranslationSettingsControllerTest : ProjectAuthControllerTest() {
}
}

@Test
@ProjectJWTAuthTestMethod
fun `formality can be set for default`() {
performAuthPut(
"/v2/projects/${project.id}/machine-translation-service-settings",
SetMachineTranslationSettingsDto(
listOf(
MachineTranslationLanguagePropsDto(
targetLanguageId = null,
primaryService = MtServiceType.GOOGLE,
enabledServicesInfo =
setOf(
MtServiceInfo(MtServiceType.GOOGLE, null),
MtServiceInfo(MtServiceType.AWS, Formality.FORMAL),
),
),
),
),
)

executeInNewTransaction {
val germanSetting =
mtServiceConfigService.getProjectSettings(testData.projectBuilder.self)
.find { it.targetLanguage?.id == null }
germanSetting!!.awsFormality.assert.isEqualTo(Formality.FORMAL)
}
}

@Test
@ProjectJWTAuthTestMethod
fun `it sets primary service via info`() {
Expand Down Expand Up @@ -263,7 +291,21 @@ class MachineTranslationSettingsControllerTest : ProjectAuthControllerTest() {
).andPrettyPrint.andAssertThatJson {
node("_embedded.languageInfos") {
isArray
node("[1]") {
node("[0]") {
node("languageTag").isEqualTo(null)
node("supportedServices") {
isArray
node("[0]") {
node("serviceType").isEqualTo("GOOGLE")
node("formalitySupported").isEqualTo(false)
}
node("[1]") {
node("serviceType").isEqualTo("AWS")
node("formalitySupported").isEqualTo(true)
}
}
}
node("[2]") {
node("languageTag").isEqualTo("de")
node("supportedServices") {
isArray
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import io.tolgee.component.machineTranslation.providers.ProviderTranslateParams
import io.tolgee.configuration.tolgee.InternalProperties
import io.tolgee.constants.Caches
import io.tolgee.constants.MtServiceType
import io.tolgee.exceptions.FormalityNotSupportedException
import io.tolgee.exceptions.LanguageNotSupportedException
import io.tolgee.model.mtServiceConfig.Formality
import org.slf4j.LoggerFactory
Expand All @@ -29,13 +28,16 @@ class MtServiceManager(
) {
private val logger = LoggerFactory.getLogger(this::class.java)

private fun findInCache(params: TranslationParams): TranslateResult? {
return params.findInCacheByParams()?.let {
private fun findInCache(
params: ProviderTranslateParams,
serviceType: MtServiceType,
): TranslateResult? {
return params.findInCacheByParams(serviceType)?.let {
TranslateResult(
translatedText = it.translatedText,
contextDescription = it.contextDescription,
actualPrice = 0,
usedService = params.serviceInfo.serviceType,
usedService = serviceType,
params.textRaw.isEmpty(),
)
}
Expand All @@ -45,9 +47,25 @@ class MtServiceManager(
val provider = params.serviceInfo.serviceType.getProvider()
validate(provider, params)

val supportsFormality = provider.isLanguageFormalitySupported(params.targetLanguageTag)

val translateParams =
ProviderTranslateParams(
params.text,
params.textRaw,
params.keyName,
params.sourceLanguageTag,
params.targetLanguageTag,
params.metadata,
if (supportsFormality) params.serviceInfo.formality else null,
params.isBatch,
pluralFormExamples = params.pluralFormExamples,
pluralForms = params.pluralForms,
)

if (internalProperties.fakeMtProviders) {
logger.debug("Fake MT provider is enabled")
return getFaked(params)
return getFaked(translateParams, params.serviceInfo.serviceType)
}

if (params.textRaw.isBlank()) {
Expand All @@ -60,27 +78,14 @@ class MtServiceManager(
)
}

val foundInCache = findInCache(params)
val foundInCache = findInCache(translateParams, params.serviceInfo.serviceType)
if (foundInCache != null) {
return foundInCache
}

return try {
val translated =
provider.translate(
ProviderTranslateParams(
params.text,
params.textRaw,
params.keyName,
params.sourceLanguageTag,
params.targetLanguageTag,
params.metadata,
params.serviceInfo.formality,
params.isBatch,
pluralFormExamples = params.pluralFormExamples,
pluralForms = params.pluralForms,
),
)
provider.translate(translateParams)

val translateResult =
TranslateResult(
Expand All @@ -91,7 +96,7 @@ class MtServiceManager(
params.textRaw.isBlank(),
)

params.cacheResult(translateResult)
translateParams.cacheResult(translateResult, params.serviceInfo.serviceType)

return translateResult
} catch (e: Exception) {
Expand All @@ -114,15 +119,6 @@ class MtServiceManager(
if (!provider.isLanguageSupported(params.targetLanguageTag)) {
throw LanguageNotSupportedException(params.targetLanguageTag, params.serviceInfo.serviceType)
}

val formality = params.serviceInfo.formality
val requiresFormality =
formality != null &&
formality != Formality.DEFAULT

if (!provider.isLanguageFormalitySupported(params.targetLanguageTag) && requiresFormality) {
throw FormalityNotSupportedException(params.targetLanguageTag, params.serviceInfo.serviceType)
}
}

private fun handleSilentFail(
Expand All @@ -145,32 +141,42 @@ class MtServiceManager(
}
}

private fun getFaked(params: TranslationParams): TranslateResult {
var fakedText =
"${params.text} translated with ${params.serviceInfo.serviceType.name} " +
private fun getFaked(
params: ProviderTranslateParams,
serviceType: MtServiceType,
): TranslateResult {
val formalityIndicator =
if ((params.formality ?: Formality.DEFAULT) !== Formality.DEFAULT) {
"${params.formality} "
} else {
""
}
val fakedText =
"${params.text} translated ${formalityIndicator}with ${serviceType.name} " +
"from ${params.sourceLanguageTag} to ${params.targetLanguageTag}"
if ((params.serviceInfo.formality ?: Formality.DEFAULT) !== Formality.DEFAULT) {
fakedText += " ${params.serviceInfo.formality}"
}

return TranslateResult(
translatedText = fakedText,
contextDescription = null,
actualPrice = params.text.length * 100,
usedService = params.serviceInfo.serviceType,
usedService = serviceType,
baseBlank = params.textRaw.isEmpty(),
)
}

private fun TranslationParams.findInCacheByParams(): TranslateResult? {
private fun ProviderTranslateParams.findInCacheByParams(serviceType: MtServiceType): TranslateResult? {
return getCache()?.let { cache ->
val result = cache.get(this.cacheKey)?.get() as? TranslateResult
val result = cache.get(this.cacheKey(serviceType.name))?.get() as? TranslateResult
result?.actualPrice = 0
return result
}
}

private fun TranslationParams.cacheResult(result: TranslateResult) {
getCache()?.put(this.cacheKey, result)
private fun ProviderTranslateParams.cacheResult(
result: TranslateResult,
serviceType: MtServiceType,
) {
getCache()?.put(this.cacheKey(serviceType.name), result)
}

private fun getCache() = cacheManager.getCache(Caches.MACHINE_TRANSLATIONS)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package io.tolgee.component.machineTranslation

import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import io.tolgee.component.machineTranslation.metadata.Metadata
import io.tolgee.service.machineTranslation.MtServiceInfo

Expand All @@ -15,11 +14,4 @@ data class TranslationParams(
val isBatch: Boolean,
var pluralForms: Map<String, String>? = null,
val pluralFormExamples: Map<String, String>? = null,
) {
val cacheKey: String
get() =
jacksonObjectMapper()
.writeValueAsString(
listOf(text, textRaw, pluralForms, sourceLanguageTag, targetLanguageTag, serviceInfo, metadata),
)
}
)
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.tolgee.component.machineTranslation.providers

import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import io.tolgee.component.machineTranslation.metadata.Metadata
import io.tolgee.model.mtServiceConfig.Formality

Expand All @@ -23,4 +24,22 @@ data class ProviderTranslateParams(
* Only for translators supporting plurals
*/
val pluralFormExamples: Map<String, String>? = null,
)
) {
fun cacheKey(provider: String): String {
return jacksonObjectMapper()
.writeValueAsString(
listOf(
text,
textRaw,
keyName,
sourceLanguageTag,
targetLanguageTag,
metadata,
formality,
pluralForms,
pluralFormExamples,
provider,
),
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,9 @@ class TolgeeTranslationProvider(
return metadata
}

override val supportedLanguages = null
override val formalitySupportingLanguages = null
// empty array meaning all is supported
override val supportedLanguages = arrayOf<String>()
override val formalitySupportingLanguages = arrayOf<String>()

override fun isLanguageSupported(tag: String): Boolean = true

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ package io.tolgee.service.machineTranslation
import io.tolgee.dtos.cacheable.LanguageDto

data class MtLanguageInfo(
val language: LanguageDto,
val language: LanguageDto?,
val supportedServices: List<MtSupportedService>,
)
Original file line number Diff line number Diff line change
Expand Up @@ -252,12 +252,7 @@ class MtServiceConfigService(
MtServiceType.AWS -> entity.awsFormality = it.formality ?: Formality.DEFAULT
MtServiceType.DEEPL -> entity.deeplFormality = it.formality ?: Formality.DEFAULT
MtServiceType.TOLGEE -> entity.tolgeeFormality = it.formality ?: Formality.DEFAULT
else -> {
if (it.formality == null) {
return@forEach
}
throw BadRequestException(Message.FORMALITY_NOT_SUPPORTED_BY_SERVICE, listOf(it.serviceType))
}
else -> {}
}
}
}
Expand Down Expand Up @@ -330,13 +325,26 @@ class MtServiceConfigService(
}

fun getLanguageInfo(project: ProjectDto): List<MtLanguageInfo> {
return languageService.findAll(project.id).map { language ->
val result: MutableList<MtLanguageInfo> = mutableListOf()
result.add(
MtLanguageInfo(
language = null,
supportedServices =
services.filter {
it.value.second.isEnabled
}.map {
MtSupportedService(it.key, it.value.second.formalitySupportingLanguages !== null)
},
),
)
languageService.findAll(project.id).forEach { language ->
val supportedServices =
services.filter { it.value.second.isLanguageSupported(language.tag) && it.value.second.isEnabled }.map {
MtSupportedService(it.key, it.value.second.isLanguageFormalitySupported(language.tag))
}
MtLanguageInfo(language = language, supportedServices)
result.add(MtLanguageInfo(language = language, supportedServices))
}
return result
}

val services by lazy {
Expand Down
Loading
Loading