From d77ffd86b7ae5c99b68982f5e033a686e504f61e Mon Sep 17 00:00:00 2001 From: Max Inno Date: Mon, 3 Aug 2026 06:50:46 +0300 Subject: [PATCH] feat(translations): add scope and re-translation options to Apply Pre-Translation --- .../api_resources/translations/enums.py | 12 ++++ .../api_resources/translations/resource.py | 13 ++++ .../tests/test_translations_resources.py | 71 +++++++++++++++++++ 3 files changed, 96 insertions(+) diff --git a/crowdin_api/api_resources/translations/enums.py b/crowdin_api/api_resources/translations/enums.py index e817ba6..06d1eae 100644 --- a/crowdin_api/api_resources/translations/enums.py +++ b/crowdin_api/api_resources/translations/enums.py @@ -14,6 +14,18 @@ class PreTranslationAutoApproveOption(Enum): NONE = "none" +class PreTranslationScope(Enum): + UNTRANSLATED = "untranslated" + TRANSLATED = "translated" + ALL = "all" + + +class PreTranslationReplaceTranslationsOption(Enum): + NONE = "none" + AUTO_TRANSLATED = "autoTranslated" + ALL = "all" + + class CharTransformation(Enum): ASIAN = "asian" EUROPEAN = "european" diff --git a/crowdin_api/api_resources/translations/resource.py b/crowdin_api/api_resources/translations/resource.py index a81fa1c..8ba1e92 100644 --- a/crowdin_api/api_resources/translations/resource.py +++ b/crowdin_api/api_resources/translations/resource.py @@ -11,6 +11,8 @@ CharTransformation, PreTranslationApplyMethod, PreTranslationAutoApproveOption, + PreTranslationReplaceTranslationsOption, + PreTranslationScope, ) @@ -87,6 +89,10 @@ def apply_pre_translation( duplicateTranslations: Optional[bool] = None, skipApprovedTranslations: Optional[bool] = None, translateUntranslatedOnly: Optional[bool] = None, + scope: Optional[PreTranslationScope] = None, + translationModifiedBefore: Optional[str] = None, + replaceTranslationsOption: Optional[PreTranslationReplaceTranslationsOption] = None, + resetApprovalStatus: Optional[bool] = None, translateWithPerfectMatchOnly: Optional[bool] = None, fallbackLanguages: Optional[Iterable[FallbackLanguages]] = None, labelIds: Optional[Iterable[int]] = None, @@ -96,6 +102,9 @@ def apply_pre_translation( """ Apply Pre-Translation. + `translateUntranslatedOnly` is deprecated in favor of `scope` and cannot be + combined with it in the same request. + Link to documentation: https://developer.crowdin.com/api/v2/#operation/api.projects.pre-translations.post """ @@ -126,6 +135,10 @@ def apply_pre_translation( "duplicateTranslations": duplicateTranslations, "skipApprovedTranslations": skipApprovedTranslations, "translateUntranslatedOnly": translateUntranslatedOnly, + "scope": scope, + "translationModifiedBefore": translationModifiedBefore, + "replaceTranslationsOption": replaceTranslationsOption, + "resetApprovalStatus": resetApprovalStatus, "translateWithPerfectMatchOnly": translateWithPerfectMatchOnly, "fallbackLanguages": fallbackLanguages, "labelIds": labelIds, diff --git a/crowdin_api/api_resources/translations/tests/test_translations_resources.py b/crowdin_api/api_resources/translations/tests/test_translations_resources.py index 4b7af7f..21fbfc4 100644 --- a/crowdin_api/api_resources/translations/tests/test_translations_resources.py +++ b/crowdin_api/api_resources/translations/tests/test_translations_resources.py @@ -7,6 +7,8 @@ PreTranslationApplyMethod, PreTranslationAutoApproveOption, PreTranslationEditOperation, + PreTranslationReplaceTranslationsOption, + PreTranslationScope, ) from crowdin_api.api_resources.translations.resource import TranslationsResource from crowdin_api.requester import APIRequester @@ -67,6 +69,10 @@ def test_list_pre_translations(self, m_request, base_absolut_url): "duplicateTranslations": None, "skipApprovedTranslations": None, "translateUntranslatedOnly": None, + "scope": None, + "translationModifiedBefore": None, + "replaceTranslationsOption": None, + "resetApprovalStatus": None, "translateWithPerfectMatchOnly": None, "fallbackLanguages": [], "labelIds": [], @@ -85,6 +91,10 @@ def test_list_pre_translations(self, m_request, base_absolut_url): "duplicateTranslations": False, "skipApprovedTranslations": False, "translateUntranslatedOnly": False, + "scope": PreTranslationScope.TRANSLATED, + "translationModifiedBefore": "2026-01-01T00:00:00+00:00", + "replaceTranslationsOption": PreTranslationReplaceTranslationsOption.AUTO_TRANSLATED, + "resetApprovalStatus": True, "translateWithPerfectMatchOnly": False, "fallbackLanguages": ["lang"], "labelIds": [1], @@ -100,6 +110,10 @@ def test_list_pre_translations(self, m_request, base_absolut_url): "duplicateTranslations": False, "skipApprovedTranslations": False, "translateUntranslatedOnly": False, + "scope": PreTranslationScope.TRANSLATED, + "translationModifiedBefore": "2026-01-01T00:00:00+00:00", + "replaceTranslationsOption": PreTranslationReplaceTranslationsOption.AUTO_TRANSLATED, + "resetApprovalStatus": True, "translateWithPerfectMatchOnly": False, "fallbackLanguages": ["lang"], "labelIds": [1], @@ -147,6 +161,10 @@ def test_apply_pre_translation_with_branchids(self, m_request, base_absolut_url) "duplicateTranslations": None, "skipApprovedTranslations": None, "translateUntranslatedOnly": None, + "scope": None, + "translationModifiedBefore": None, + "replaceTranslationsOption": None, + "resetApprovalStatus": None, "translateWithPerfectMatchOnly": None, "fallbackLanguages": [], "labelIds": [], @@ -186,6 +204,10 @@ def test_apply_pre_translation_edge_cases(self, m_request, base_absolut_url): "duplicateTranslations": None, "skipApprovedTranslations": None, "translateUntranslatedOnly": None, + "scope": None, + "translationModifiedBefore": None, + "replaceTranslationsOption": None, + "resetApprovalStatus": None, "translateWithPerfectMatchOnly": None, "fallbackLanguages": [], "labelIds": [], @@ -237,6 +259,10 @@ def test_apply_pre_translation_with_all_optional_params(self, m_request, base_ab "duplicateTranslations": True, "skipApprovedTranslations": False, "translateUntranslatedOnly": True, + "scope": None, + "translationModifiedBefore": None, + "replaceTranslationsOption": None, + "resetApprovalStatus": None, "translateWithPerfectMatchOnly": False, "fallbackLanguages": [{"languageId": "fr", "userId": 789}], "labelIds": [1, 2, 3], @@ -246,6 +272,51 @@ def test_apply_pre_translation_with_all_optional_params(self, m_request, base_ab path="projects/1/pre-translations", ) + @mock.patch("crowdin_api.requester.APIRequester.request") + def test_apply_pre_translation_with_scope_and_retranslation_options( + self, m_request, base_absolut_url + ): + """Test that scope, translationModifiedBefore, replaceTranslationsOption + and resetApprovalStatus are properly handled.""" + m_request.return_value = "response" + + resource = self.get_resource(base_absolut_url) + response = resource.apply_pre_translation( + projectId=1, + languageIds=["en"], + fileIds=[1], + scope=PreTranslationScope.ALL, + translationModifiedBefore="2026-01-01T00:00:00+00:00", + replaceTranslationsOption=PreTranslationReplaceTranslationsOption.ALL, + resetApprovalStatus=True, + ) + + assert response == "response" + m_request.assert_called_once_with( + method="post", + request_data={ + "languageIds": ["en"], + "fileIds": [1], + "method": None, + "engineId": None, + "aiPromptId": None, + "autoApproveOption": None, + "duplicateTranslations": None, + "skipApprovedTranslations": None, + "translateUntranslatedOnly": None, + "scope": PreTranslationScope.ALL, + "translationModifiedBefore": "2026-01-01T00:00:00+00:00", + "replaceTranslationsOption": PreTranslationReplaceTranslationsOption.ALL, + "resetApprovalStatus": True, + "translateWithPerfectMatchOnly": None, + "fallbackLanguages": [], + "labelIds": [], + "excludeLabelIds": [], + "branchIds": [], + }, + path="projects/1/pre-translations", + ) + @mock.patch("crowdin_api.requester.APIRequester.request") def test_pre_translation_report(self, m_request, base_absolut_url): m_request.return_value = "response"