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 9 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
2 changes: 1 addition & 1 deletion .run/Backend localhost.run.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@
<option name="Make" enabled="true" />
</method>
</configuration>
</component>
</component>
stepan662 marked this conversation as resolved.
Show resolved Hide resolved
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 Down Expand Up @@ -45,6 +44,16 @@ class MtServiceManager(
val provider = params.serviceInfo.serviceType.getProvider()
validate(provider, params)

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

if (!provider.isLanguageFormalitySupported(params.targetLanguageTag) && requiresFormality) {
// disable formality, if it's not supported for the language
params.serviceInfo.formality = null
}

if (internalProperties.fakeMtProviders) {
logger.debug("Fake MT provider is enabled")
return getFaked(params)
Expand Down Expand Up @@ -114,15 +123,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 @@ -146,12 +146,16 @@ class MtServiceManager(
}

private fun getFaked(params: TranslationParams): TranslateResult {
val formalityIndicator =
if ((params.serviceInfo.formality ?: Formality.DEFAULT) !== Formality.DEFAULT) {
"${params.serviceInfo.formality} "
} else {
""
}
var fakedText =
"${params.text} translated with ${params.serviceInfo.serviceType.name} " +
"${params.text} translated ${formalityIndicator}with ${params.serviceInfo.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,
Expand Down
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
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ import io.tolgee.model.mtServiceConfig.Formality

data class MtServiceInfo(
val serviceType: MtServiceType,
val formality: Formality? = null,
var formality: Formality? = null,
stepan662 marked this conversation as resolved.
Show resolved Hide resolved
)
12 changes: 10 additions & 2 deletions e2e/cypress/e2e/translations/mtSettings.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ describe('Machine translation settings', () => {
cy.gcy('machine-translations-settings-language-options').first().click();
getEnableCheckbox('GOOGLE').click();
getPrimaryRadio('AWS').click();
getFormalitySelect('AWS').click();
cy.gcy('mt-language-dialog-formality-select-item')
.contains('Formal')
.click();
cy.gcy('mt-language-dialog-auto-for-import').click();
cy.gcy('mt-language-dialog-auto-machine-translation').click();
cy.gcy('mt-language-dialog-auto-translation-memory').click();
Expand All @@ -44,6 +48,7 @@ describe('Machine translation settings', () => {
cy.gcy('translation-tools-machine-translation-item')
.contains('Cool translated text 1 translated with AWS from en to cs')
.should('be.visible');

cy.gcy('translation-tools-machine-translation-item').should(
'have.length',
1
Expand All @@ -53,6 +58,9 @@ describe('Machine translation settings', () => {
cy.contains('test translation translated with AWS from en to cs').should(
'be.visible'
);
cy.contains(
'test translation translated FORMAL with AWS from en to es'
).should('be.visible');
});

it('will update language specific settings', { retries: 5 }, () => {
Expand Down Expand Up @@ -86,7 +94,7 @@ describe('Machine translation settings', () => {
);
cy.gcy('translation-tools-machine-translation-item')
.contains(
'Cool translated text 1 translated with AWS from en to es FORMAL'
'Cool translated text 1 translated FORMAL with AWS from en to es'
)
.should('be.visible');
cy.gcy('global-editor').type('{esc}');
Expand All @@ -100,7 +108,7 @@ describe('Machine translation settings', () => {

createTranslation({ key: 'aaa_key', translation: 'test translation' });
cy.contains(
'test translation translated with AWS from en to es FORMAL'
'test translation translated FORMAL with AWS from en to es'
).should('be.visible');
cy.contains('from en to cs').should('not.exist');
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { FormalityType, RowData, ServiceType } from './types';
import { ServiceLabel } from './ServiceLabel';
import { PrimaryServiceLabel } from './PrimaryServiceLabel';
import { SuggestionsLabel } from './SuggestionsLabel';
import { supportsFormality } from './supportsFormality';

const StyledSubtitle = styled('div')`
font-size: 14px;
Expand All @@ -56,6 +57,11 @@ const StyledHint = styled(Typography)`
color: ${({ theme }) => theme.palette.text.secondary};
`;

const StyledFormalityHint = styled(Typography)`
color: ${({ theme }) => theme.palette.text.secondary};
font-size: 15px;
`;

const StyledSettings = styled('div')`
align-items: flex-start;
display: flex;
Expand Down Expand Up @@ -103,7 +109,7 @@ export const LanguageSettingsDialog = ({
const [reseting, setReseting] = useState(false);
async function handleResetToDefault() {
setReseting(true);
return onChange(settings.language?.id, null)
return onChange(settings.info!, null)
.then(() => {
onClose();
})
Expand Down Expand Up @@ -136,7 +142,7 @@ export const LanguageSettingsDialog = ({
formality: primaryFormality,
};

await onChange(settings.id || undefined, {
await onChange(settings.info!, {
machineTranslation: {
targetLanguageId: settings.language?.id,
primaryServiceInfo: primaryServiceInfo,
Expand Down Expand Up @@ -223,7 +229,10 @@ export const LanguageSettingsDialog = ({
(s) => s.serviceType === service
);
const languageSupported = Boolean(serviceInfo) || isDefault;
const formalitySupported = serviceInfo?.formalitySupported;
const formalitySupported = supportsFormality(
settings.info!,
service
);
return (
<React.Fragment key={service}>
<Box className={TABLE_FIRST_CELL} key={service}>
Expand Down Expand Up @@ -257,7 +266,7 @@ export const LanguageSettingsDialog = ({
)}
</div>
<div className={TABLE_CENTERED}>
{formalitySupported && (
{formalitySupported ? (
<Select
data-cy="mt-language-dialog-formality-select"
data-cy-service={service}
Expand All @@ -283,6 +292,10 @@ export const LanguageSettingsDialog = ({
</MenuItem>
))}
</Select>
) : (
<StyledFormalityHint>
{t('project_mt_dialog_formality_not_supported')}
</StyledFormalityHint>
)}
</div>
</React.Fragment>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import clsx from 'clsx';

import { LanguageRow } from './LanguageRow';
import { TABLE_CENTERED, TABLE_DIVIDER, TABLE_TOP_ROW } from '../tableStyles';
import { LanguageRow } from './LanguageRow';
import { LanguageCombinedSetting, OnMtChange } from './types';
import { PrimaryServiceLabel } from './PrimaryServiceLabel';
import { SuggestionsLabel } from './SuggestionsLabel';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { LanguageInfoModel, ServiceType } from './types';

export function supportsFormality(
langInfo: LanguageInfoModel,
serviceType: ServiceType
) {
return langInfo.supportedServices.find((i) => i.serviceType === serviceType)
?.formalitySupported;
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,6 @@ export type AutoTranslationSettings = {
usingTranslationMemory: boolean;
};

export type LanguageData = {
languageId: number | undefined;
primaryServiceInfo: LanguageConfigItemModel['primaryServiceInfo'];
enabledServicesInfo: LanguageCombinedSetting[];
};

export type LanguageCombinedSetting = {
id: number | null;
info: LanguageInfoModel | undefined;
Expand All @@ -42,7 +36,7 @@ export type RowData = {
};

export type OnMtChange = (
languageId: number | undefined,
langInfo: LanguageInfoModel,
value: OnRowChangeData | null
) => Promise<void>;

Expand Down
Loading
Loading