├── gradle.properties ├── settings.gradle.kts ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── .github ├── dependabot.yml └── workflows │ ├── add_issues_to_kanban.yml │ └── run_ci.yml ├── catalog-info.yaml ├── deepl-java ├── src │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── deepl │ │ │ └── api │ │ │ ├── LanguageType.java │ │ │ ├── IGlossary.java │ │ │ ├── utils │ │ │ ├── KeyValuePair.java │ │ │ ├── NamedStream.java │ │ │ ├── StreamUtil.java │ │ │ └── BackoffTimer.java │ │ │ ├── NotFoundException.java │ │ │ ├── AuthorizationException.java │ │ │ ├── QuotaExceededException.java │ │ │ ├── GlossaryNotFoundException.java │ │ │ ├── TooManyRequestsException.java │ │ │ ├── DocumentNotReadyException.java │ │ │ ├── DeepLException.java │ │ │ ├── parsing │ │ │ ├── TextResponse.java │ │ │ ├── WriteResponse.java │ │ │ ├── GlossaryListResponse.java │ │ │ ├── StyleRuleListResponse.java │ │ │ ├── MultilingualGlossaryListResponse.java │ │ │ ├── MultilingualGlossaryDictionaryListResponse.java │ │ │ ├── GlossaryLanguagesResponse.java │ │ │ ├── WriteResultDeserializer.java │ │ │ ├── LanguageDeserializer.java │ │ │ ├── TextResultDeserializer.java │ │ │ ├── ErrorResponse.java │ │ │ ├── UsageDeserializer.java │ │ │ ├── MultilingualGlossaryDictionaryEntriesResponse.java │ │ │ └── Parser.java │ │ │ ├── http │ │ │ ├── HttpResponse.java │ │ │ ├── HttpResponseStream.java │ │ │ └── HttpContent.java │ │ │ ├── AppInfo.java │ │ │ ├── DeepLApiVersion.java │ │ │ ├── Formality.java │ │ │ ├── WritingStyle.java │ │ │ ├── WritingTone.java │ │ │ ├── ConnectionException.java │ │ │ ├── SentenceSplittingMode.java │ │ │ ├── DocumentHandle.java │ │ │ ├── WriteResult.java │ │ │ ├── DocumentTranslationException.java │ │ │ ├── DeepLClientOptions.java │ │ │ ├── BaseRequestOptions.java │ │ │ ├── GlossaryLanguagePair.java │ │ │ ├── TextResult.java │ │ │ ├── TextRephraseOptions.java │ │ │ ├── MultilingualGlossaryDictionaryEntries.java │ │ │ ├── CustomInstruction.java │ │ │ ├── MultilingualGlossaryDictionaryInfo.java │ │ │ ├── MultilingualGlossaryInfo.java │ │ │ ├── Language.java │ │ │ ├── DocumentTranslationOptions.java │ │ │ ├── GlossaryInfo.java │ │ │ ├── ConfiguredRules.java │ │ │ ├── DocumentStatus.java │ │ │ ├── StyleRuleInfo.java │ │ │ ├── Usage.java │ │ │ ├── TranslatorOptions.java │ │ │ ├── LanguageCode.java │ │ │ ├── GlossaryEntries.java │ │ │ ├── HttpClientWrapper.java │ │ │ └── TextTranslationOptions.java │ └── test │ │ └── java │ │ └── com │ │ └── deepl │ │ └── api │ │ ├── GlossaryCleanupUtility.java │ │ ├── MultilingualGlossaryCleanupUtility.java │ │ ├── RephraseTextTest.java │ │ ├── StyleRuleTest.java │ │ ├── SessionOptions.java │ │ ├── TestBase.java │ │ └── TranslateDocumentTest.java └── build.gradle.kts ├── license_checker.sh ├── examples └── maven │ └── deepl-test-app │ ├── src │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── deepl │ │ │ └── deepltestapp │ │ │ ├── annotation │ │ │ └── IntegrationTest.java │ │ │ └── App.java │ └── test │ │ └── java │ │ └── com │ │ └── deepl │ │ └── deepltestapp │ │ └── DeepLIntegrationTest.java │ └── pom.xml ├── .bumpversion.toml ├── LICENSE ├── CONTRIBUTING.md ├── gradlew.bat ├── SECURITY.md ├── CODE_OF_CONDUCT.md ├── .gitlab-ci.yml ├── gradlew └── CHANGELOG.md /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Dfile.encoding=UTF-8 -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | 2 | rootProject.name = "deepl-java" 3 | include("deepl-java") 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeepLcom/deepl-java/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | .classpath 3 | .project 4 | .settings 5 | tags 6 | target 7 | .idea 8 | *.iml 9 | .DS_Store 10 | 11 | # Gradle files 12 | .gradle/* 13 | build/ 14 | out/* 15 | 16 | # compiler output 17 | bin/ -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: gradle 4 | directory: / 5 | schedule: 6 | interval: weekly 7 | - package-ecosystem: maven 8 | directory: /examples/maven/deepl-test-app 9 | schedule: 10 | interval: weekly -------------------------------------------------------------------------------- /catalog-info.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: backstage.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: deepl-java 5 | title: DeepL Java Library 6 | description: Public Java library for interacting with the DeepL API 7 | spec: 8 | type: library 9 | lifecycle: production 10 | owner: api-core-team 11 | system: oss-client-libraries -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/LanguageType.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | /** Enum specifying a source or target language type. */ 7 | public enum LanguageType { 8 | Source, 9 | Target, 10 | } 11 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/IGlossary.java: -------------------------------------------------------------------------------- 1 | // Copyright 2025 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | /** Interface representing a glossary. */ 7 | public interface IGlossary { 8 | /** @return Unique ID assigned to the glossary. */ 9 | String getGlossaryId(); 10 | } 11 | -------------------------------------------------------------------------------- /license_checker.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # Usage: ./license_checker.sh source_code_pattern 4 | # Example: ./license_checker.sh '*.py' 5 | # This will search all .py files, ignoring anything not tracked in your git tree 6 | 7 | git ls-files -z $1 | xargs -0 -I{} sh -c 'RES=$(head -n 3 "{}" | grep "Copyright 20[0-9][0-9] DeepL SE (https://www.deepl.com)"); if [ ! "${RES}" ] ; then echo "Lacking copyright header in" "{}" ; fi' 8 | -------------------------------------------------------------------------------- /examples/maven/deepl-test-app/src/main/java/com/deepl/deepltestapp/annotation/IntegrationTest.java: -------------------------------------------------------------------------------- 1 | // Copyright 2023 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.deepltestapp.annotation; 5 | 6 | /** 7 | * Marks tests as Integration tests, used for DeepLs internal CI pipeline. 8 | */ 9 | public interface IntegrationTest {} 10 | -------------------------------------------------------------------------------- /.github/workflows/add_issues_to_kanban.yml: -------------------------------------------------------------------------------- 1 | name: Add bugs to bugs project 2 | 3 | on: 4 | issues: 5 | types: 6 | - opened 7 | 8 | jobs: 9 | add-to-project: 10 | name: Add issue to project 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/add-to-project@v0.5.0 14 | with: 15 | project-url: https://github.com/orgs/DeepLcom/projects/1 16 | github-token: ${{ secrets.ADD_TO_PROJECT_PAT }} 17 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/utils/KeyValuePair.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api.utils; 5 | 6 | import java.util.AbstractMap; 7 | 8 | public class KeyValuePair extends AbstractMap.SimpleEntry { 9 | 10 | public KeyValuePair(K key, V value) { 11 | super(key, value); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/NotFoundException.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | /** Exception thrown when the specified resource could not be found. */ 7 | public class NotFoundException extends DeepLException { 8 | public NotFoundException(String message) { 9 | super(message); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/AuthorizationException.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | /** Exception thrown when the specified authentication key was invalid. */ 7 | public class AuthorizationException extends DeepLException { 8 | public AuthorizationException(String message) { 9 | super(message); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/QuotaExceededException.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | /** Exception thrown when the DeepL translation quota has been reached. */ 7 | public class QuotaExceededException extends DeepLException { 8 | public QuotaExceededException(String message) { 9 | super(message); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/GlossaryNotFoundException.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | /** Exception thrown when the specified glossary could not be found. */ 7 | public class GlossaryNotFoundException extends NotFoundException { 8 | public GlossaryNotFoundException(String message) { 9 | super(message); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/TooManyRequestsException.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | /** Exception thrown when too many requests are made to the DeepL API too quickly. */ 7 | public class TooManyRequestsException extends DeepLException { 8 | public TooManyRequestsException(String message) { 9 | super(message); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/DocumentNotReadyException.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | /** Exception thrown when attempting to download a translated document before it is ready. */ 7 | public class DocumentNotReadyException extends DeepLException { 8 | public DocumentNotReadyException(String message) { 9 | super(message); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/DeepLException.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | /** Base class for all exceptions thrown by this library. */ 7 | public class DeepLException extends Exception { 8 | public DeepLException(String message) { 9 | super(message); 10 | } 11 | 12 | public DeepLException(String message, Throwable cause) { 13 | super(message, cause); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/parsing/TextResponse.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api.parsing; 5 | 6 | import com.deepl.api.TextResult; 7 | import java.util.List; 8 | 9 | /** 10 | * Class representing text translation responses from the DeepL API. 11 | * 12 | *

This class is internal; you should not use this class directly. 13 | */ 14 | class TextResponse { 15 | public List translations; 16 | } 17 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/parsing/WriteResponse.java: -------------------------------------------------------------------------------- 1 | // Copyright 2025 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api.parsing; 5 | 6 | import com.deepl.api.WriteResult; 7 | import java.util.List; 8 | 9 | /** 10 | * Class representing text rephrase responses from the DeepL API. 11 | * 12 | *

This class is internal; you should not use this class directly. 13 | */ 14 | class WriteResponse { 15 | public List improvements; 16 | } 17 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/http/HttpResponse.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api.http; 5 | 6 | public class HttpResponse { 7 | 8 | private final int code; 9 | 10 | private final String body; 11 | 12 | public HttpResponse(int code, String body) { 13 | this.code = code; 14 | this.body = body; 15 | } 16 | 17 | public int getCode() { 18 | return code; 19 | } 20 | 21 | public String getBody() { 22 | return body; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/AppInfo.java: -------------------------------------------------------------------------------- 1 | // Copyright 2023 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | public class AppInfo { 7 | private String appName; 8 | private String appVersion; 9 | 10 | public AppInfo(String appName, String appVersion) { 11 | this.appName = appName; 12 | this.appVersion = appVersion; 13 | } 14 | 15 | public String getAppName() { 16 | return appName; 17 | } 18 | 19 | public String getAppVersion() { 20 | return appVersion; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/parsing/GlossaryListResponse.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api.parsing; 5 | 6 | import com.deepl.api.*; 7 | import java.util.List; 8 | 9 | /** 10 | * Class representing list-glossaries response by the DeepL API. 11 | * 12 | *

This class is internal; you should not use this class directly. 13 | */ 14 | class GlossaryListResponse { 15 | private List glossaries; 16 | 17 | public List getGlossaries() { 18 | return glossaries; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/parsing/StyleRuleListResponse.java: -------------------------------------------------------------------------------- 1 | // Copyright 2025 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api.parsing; 5 | 6 | import com.deepl.api.StyleRuleInfo; 7 | import java.util.List; 8 | 9 | /** 10 | * Class representing v3 style_rules list response by the DeepL API. 11 | * 12 | *

This class is internal; you should not use this class directly. 13 | */ 14 | class StyleRuleListResponse { 15 | private List style_rules; 16 | 17 | public List getStyleRules() { 18 | return style_rules; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.bumpversion.toml: -------------------------------------------------------------------------------- 1 | # Configuration file for bumpversion 2 | # See https://github.com/callowayproject/bump-my-version 3 | [tool.bumpversion] 4 | current_version = "1.14.0" 5 | parse = "(?P\\d+)\\.(?P\\d+)\\.(?P\\d+)" 6 | serialize = ["{major}.{minor}.{patch}"] 7 | search = "{current_version}" 8 | replace = "{new_version}" 9 | regex = false 10 | ignore_missing_version = false 11 | tag = false 12 | allow_dirty = false 13 | commit = false 14 | 15 | [[tool.bumpversion.files]] 16 | filename = "README.md" 17 | 18 | [[tool.bumpversion.files]] 19 | filename = "deepl-java/build.gradle.kts" 20 | 21 | [[tool.bumpversion.files]] 22 | filename = "deepl-java/src/main/java/com/deepl/api/Translator.java" -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/utils/NamedStream.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api.utils; 5 | 6 | import java.io.*; 7 | 8 | public class NamedStream { 9 | private final String fileName; 10 | private final InputStream inputStream; 11 | 12 | public NamedStream(String fileName, InputStream inputStream) { 13 | this.fileName = fileName; 14 | this.inputStream = inputStream; 15 | } 16 | 17 | public String getFileName() { 18 | return fileName; 19 | } 20 | 21 | public InputStream getInputStream() { 22 | return inputStream; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/DeepLApiVersion.java: -------------------------------------------------------------------------------- 1 | // Copyright 2025 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | public enum DeepLApiVersion { 7 | VERSION_1("v1"), 8 | VERSION_2("v2"); 9 | 10 | /** 11 | * How the version is represented in the URL string. Does not include any slashes (/). Example: 12 | * "v2" 13 | */ 14 | private final String urlRepresentation; 15 | 16 | private DeepLApiVersion(String urlRepresentation) { 17 | this.urlRepresentation = urlRepresentation; 18 | } 19 | 20 | public String toString() { 21 | return this.urlRepresentation; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/parsing/MultilingualGlossaryListResponse.java: -------------------------------------------------------------------------------- 1 | // Copyright 2025 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api.parsing; 5 | 6 | import com.deepl.api.MultilingualGlossaryInfo; 7 | import java.util.List; 8 | 9 | /** 10 | * Class representing v3 list-glossaries response by the DeepL API. 11 | * 12 | *

This class is internal; you should not use this class directly. 13 | */ 14 | class MultilingualGlossaryListResponse { 15 | private List glossaries; 16 | 17 | public List getGlossaries() { 18 | return glossaries; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/parsing/MultilingualGlossaryDictionaryListResponse.java: -------------------------------------------------------------------------------- 1 | // Copyright 2025 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api.parsing; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * Class representing v3 list-glossaries response by the DeepL API. 10 | * 11 | *

This class is internal; you should not use this class directly. 12 | */ 13 | public class MultilingualGlossaryDictionaryListResponse { 14 | private List dictionaries; 15 | 16 | public List getDictionaries() { 17 | return dictionaries; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/Formality.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | /** Desired level of formality for translation. */ 7 | public enum Formality { 8 | /** Standard level of formality. */ 9 | Default, 10 | 11 | /** Less formality, i.e. more informal. */ 12 | Less, 13 | 14 | /** Increased formality. */ 15 | More, 16 | 17 | /** 18 | * Less formality, i.e. more informal, if available for the specified target language, otherwise 19 | * default. 20 | */ 21 | PreferLess, 22 | 23 | /** Increased formality, if available for the specified target language, otherwise default. */ 24 | PreferMore, 25 | } 26 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/parsing/GlossaryLanguagesResponse.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api.parsing; 5 | 6 | import com.deepl.api.*; 7 | import com.google.gson.annotations.*; 8 | import java.util.List; 9 | 10 | /** 11 | * Class representing glossary-languages response from the DeepL API. 12 | * 13 | *

This class is internal; you should not use this class directly. 14 | */ 15 | class GlossaryLanguagesResponse { 16 | @SerializedName("supported_languages") 17 | private List supportedLanguages; 18 | 19 | public List getSupportedLanguages() { 20 | return supportedLanguages; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/WritingStyle.java: -------------------------------------------------------------------------------- 1 | // Copyright 2025 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | /** Represents the style the improved text should be in in a rephrase request. */ 7 | public enum WritingStyle { 8 | Academic("academic"), 9 | Business("business"), 10 | Casual("casual"), 11 | Default("default"), 12 | PreferAcademic("prefer_academic"), 13 | PreferBusiness("prefer_business"), 14 | PreferCasual("prefer_casual"), 15 | PreferSimple("prefer_simple"), 16 | Simple("simple"); 17 | 18 | private final String value; 19 | 20 | WritingStyle(String value) { 21 | this.value = value; 22 | } 23 | 24 | public String getValue() { 25 | return value; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/WritingTone.java: -------------------------------------------------------------------------------- 1 | // Copyright 2025 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | /** Represents the tone the improved text should be in in a rephrase request. */ 7 | public enum WritingTone { 8 | Confident("confident"), 9 | Default("default"), 10 | Diplomatic("diplomatic"), 11 | Enthusiastic("enthusiastic"), 12 | Friendly("friendly"), 13 | PreferConfident("prefer_confident"), 14 | PreferDiplomatic("prefer_diplomatic"), 15 | PreferEnthusiastic("prefer_enthusiastic"), 16 | PreferFriendly("prefer_friendly"); 17 | 18 | private final String value; 19 | 20 | WritingTone(String value) { 21 | this.value = value; 22 | } 23 | 24 | public String getValue() { 25 | return value; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/ConnectionException.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | /** Exception thrown when a connection error occurs while accessing the DeepL API. */ 7 | public class ConnectionException extends DeepLException { 8 | private final boolean shouldRetry; 9 | 10 | public ConnectionException(String message, boolean shouldRetry, Throwable cause) { 11 | super(message, cause); 12 | this.shouldRetry = shouldRetry; 13 | } 14 | 15 | /** 16 | * Returns true if this exception occurred due to transient condition and the request 17 | * should be retried, otherwise false. 18 | */ 19 | public boolean getShouldRetry() { 20 | return shouldRetry; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/SentenceSplittingMode.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | /** Enum controlling how input translation text should be split into sentences. */ 7 | public enum SentenceSplittingMode { 8 | /** 9 | * Input translation text will be split into sentences using both newlines and punctuation, this 10 | * is the default behaviour. 11 | */ 12 | All, 13 | 14 | /** 15 | * Input text will not be split into sentences. This is advisable for applications where each 16 | * input translation text is only one sentence. 17 | */ 18 | Off, 19 | 20 | /** 21 | * Input translation text will be split into sentences using only punctuation but not newlines. 22 | */ 23 | NoNewlines, 24 | } 25 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/parsing/WriteResultDeserializer.java: -------------------------------------------------------------------------------- 1 | // Copyright 2025 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api.parsing; 5 | 6 | import com.deepl.api.WriteResult; 7 | import com.google.gson.*; 8 | import java.lang.reflect.Type; 9 | 10 | /** 11 | * Utility class for deserializing text rephrase results returned by the DeepL API. 12 | * 13 | *

This class is internal; you should not use this class directly. 14 | */ 15 | class WriteResultDeserializer implements JsonDeserializer { 16 | public WriteResult deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) 17 | throws JsonParseException { 18 | JsonObject jsonObject = json.getAsJsonObject(); 19 | return new WriteResult( 20 | jsonObject.get("text").getAsString(), 21 | jsonObject.get("detected_source_language").getAsString(), 22 | jsonObject.get("target_language").getAsString()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/parsing/LanguageDeserializer.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api.parsing; 5 | 6 | import com.deepl.api.Language; 7 | import com.google.gson.*; 8 | import java.lang.reflect.Type; 9 | 10 | /** 11 | * Utility class for deserializing language codes returned by the DeepL API. 12 | * 13 | *

This class is internal; you should not use this class directly. 14 | */ 15 | class LanguageDeserializer implements JsonDeserializer { 16 | public Language deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) 17 | throws JsonParseException { 18 | JsonObject jsonObject = json.getAsJsonObject(); 19 | Boolean supportsFormality = Parser.getAsBooleanOrNull(jsonObject, "supports_formality"); 20 | return new Language( 21 | jsonObject.get("name").getAsString(), 22 | jsonObject.get("language").getAsString(), 23 | supportsFormality); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/DocumentHandle.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | import com.google.gson.annotations.SerializedName; 7 | 8 | /** 9 | * Handle to an in-progress document translation. 10 | * 11 | * @see Translator#translateDocumentStatus(DocumentHandle) 12 | */ 13 | public class DocumentHandle { 14 | @SerializedName(value = "document_id") 15 | private final String documentId; 16 | 17 | @SerializedName(value = "document_key") 18 | private final String documentKey; 19 | 20 | public DocumentHandle(String documentId, String documentKey) { 21 | this.documentId = documentId; 22 | this.documentKey = documentKey; 23 | } 24 | 25 | /** Get the ID of associated document request. */ 26 | public String getDocumentId() { 27 | return documentId; 28 | } 29 | 30 | /** Get the key of associated document request. */ 31 | public String getDocumentKey() { 32 | return documentKey; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright 2022 DeepL SE (https://www.deepl.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/WriteResult.java: -------------------------------------------------------------------------------- 1 | // Copyright 2025 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | /** The result of a text translation. */ 7 | public class WriteResult { 8 | private final String text; 9 | private final String detectedSourceLanguage; 10 | private final String targetLanguage; 11 | 12 | /** Constructs a new instance. */ 13 | public WriteResult(String text, String detectedSourceLanguage, String targetLanguage) { 14 | this.text = text; 15 | this.detectedSourceLanguage = LanguageCode.standardize(detectedSourceLanguage); 16 | this.targetLanguage = targetLanguage; 17 | } 18 | 19 | /** The translated text. */ 20 | public String getText() { 21 | return text; 22 | } 23 | 24 | /** The language code of the source text detected by DeepL. */ 25 | public String getDetectedSourceLanguage() { 26 | return detectedSourceLanguage; 27 | } 28 | 29 | /** The language code of the target language set by the request. */ 30 | public String getTargetLanguage() { 31 | return targetLanguage; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/parsing/TextResultDeserializer.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api.parsing; 5 | 6 | import com.deepl.api.TextResult; 7 | import com.google.gson.*; 8 | import java.lang.reflect.Type; 9 | 10 | /** 11 | * Utility class for deserializing text translation results returned by the DeepL API. 12 | * 13 | *

This class is internal; you should not use this class directly. 14 | */ 15 | class TextResultDeserializer implements JsonDeserializer { 16 | public TextResult deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) 17 | throws JsonParseException { 18 | JsonObject jsonObject = json.getAsJsonObject(); 19 | JsonElement modelType = jsonObject.get("model_type_used"); 20 | return new TextResult( 21 | jsonObject.get("text").getAsString(), 22 | jsonObject.get("detected_source_language").getAsString(), 23 | jsonObject.get("billed_characters").getAsInt(), 24 | modelType != null ? (modelType.getAsString()) : null); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/DocumentTranslationException.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | /** 9 | * Exception thrown when an error occurs during {@link Translator#translateDocument}. If the error 10 | * occurs after the document was successfully uploaded, the {@link DocumentHandle} for the 11 | * associated document is included, to allow later retrieval of the document. 12 | */ 13 | public class DocumentTranslationException extends DeepLException { 14 | 15 | private final @Nullable DocumentHandle handle; 16 | 17 | public DocumentTranslationException( 18 | String message, Throwable throwable, @Nullable DocumentHandle handle) { 19 | super(message, throwable); 20 | this.handle = handle; 21 | } 22 | 23 | /** 24 | * Get the handle to the in-progress document translation, or null if an error 25 | * occurred before uploading the document. 26 | */ 27 | public @Nullable DocumentHandle getHandle() { 28 | return handle; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/parsing/ErrorResponse.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api.parsing; 5 | 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | /** 9 | * Class representing error messages returned by the DeepL API. 10 | * 11 | *

This class is internal; you should not use this class directly. 12 | */ 13 | class ErrorResponse { 14 | @Nullable private String message; 15 | @Nullable private String detail; 16 | 17 | /** Returns a diagnostic string including the message and detail (if available). */ 18 | public String getErrorMessage() { 19 | StringBuilder sb = new StringBuilder(); 20 | if (getMessage() != null) sb.append("message: ").append(getMessage()); 21 | if (getDetail() != null) { 22 | if (sb.length() != 0) sb.append(", "); 23 | sb.append("detail: ").append(getDetail()); 24 | } 25 | return sb.toString(); 26 | } 27 | 28 | public @Nullable String getMessage() { 29 | return message; 30 | } 31 | 32 | public @Nullable String getDetail() { 33 | return detail; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/DeepLClientOptions.java: -------------------------------------------------------------------------------- 1 | // Copyright 2025 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | /** {@inheritDoc} */ 9 | @SuppressWarnings("deprecation") 10 | public class DeepLClientOptions extends TranslatorOptions { 11 | /** 12 | * Set the version of the DeepL API to use. By default, this value is 13 | * DeepLApiVersion.VERSION_2 and the most recent DeepL API version is used. Note that older 14 | * API versions like DeepLApiVersion.VERSION_1 might not support all features of the 15 | * more modern API (eg. document translation is v2-only), and that not all API subscriptions have 16 | * access to one or the other API version. If in doubt, always use the most recent API version you 17 | * have access to. 18 | */ 19 | public DeepLClientOptions setApiVersion(DeepLApiVersion apiVersion) { 20 | this.apiVersion = apiVersion; 21 | return this; 22 | } 23 | 24 | /** Gets the current API version. */ 25 | public @Nullable DeepLApiVersion getApiVersion() { 26 | return apiVersion; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/BaseRequestOptions.java: -------------------------------------------------------------------------------- 1 | // Copyright 2025 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | import java.util.Map; 7 | 8 | /** Base class for request options providing common functionality for all endpoints. */ 9 | public abstract class BaseRequestOptions { 10 | private Map extraBodyParameters; 11 | 12 | /** 13 | * Sets additional parameters to pass in the body of the HTTP request. Can be used to access beta 14 | * features, override built-in parameters, or for testing purposes. Keys in this map will be added 15 | * to the request body and can override existing keys. 16 | * 17 | * @param extraBodyParameters Map of additional parameters to include in the request. 18 | * @return This options object for method chaining. 19 | */ 20 | public BaseRequestOptions setExtraBodyParameters(Map extraBodyParameters) { 21 | this.extraBodyParameters = extraBodyParameters; 22 | return this; 23 | } 24 | 25 | /** Gets the current extra body parameters. */ 26 | public Map getExtraBodyParameters() { 27 | return extraBodyParameters; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/parsing/UsageDeserializer.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api.parsing; 5 | 6 | import com.deepl.api.*; 7 | import com.google.gson.*; 8 | import java.lang.reflect.*; 9 | import org.jetbrains.annotations.*; 10 | 11 | /** 12 | * Class representing usage responses from the DeepL API. 13 | * 14 | *

This class is internal; you should not use this class directly. 15 | */ 16 | class UsageDeserializer implements JsonDeserializer { 17 | public Usage deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) 18 | throws JsonParseException { 19 | JsonObject jsonObject = json.getAsJsonObject(); 20 | 21 | return new Usage( 22 | createDetail(jsonObject, "character_"), 23 | createDetail(jsonObject, "document_"), 24 | createDetail(jsonObject, "team_document_")); 25 | } 26 | 27 | public static @Nullable Usage.Detail createDetail(JsonObject jsonObject, String prefix) { 28 | Long count = Parser.getAsLongOrNull(jsonObject, prefix + "count"); 29 | Long limit = Parser.getAsLongOrNull(jsonObject, prefix + "limit"); 30 | if (count == null || limit == null) return null; 31 | return new Usage.Detail(count, limit); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/GlossaryLanguagePair.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | import com.google.gson.annotations.*; 7 | 8 | /** 9 | * Information about a language pair supported for glossaries. 10 | * 11 | * @see Translator#getGlossaryLanguages() 12 | */ 13 | public class GlossaryLanguagePair { 14 | @SerializedName("source_lang") 15 | private final String sourceLang; 16 | 17 | @SerializedName("target_lang") 18 | private final String targetLang; 19 | 20 | /** 21 | * Initializes a new GlossaryLanguagePair object. 22 | * 23 | * @param sourceLang Language code of the source terms in the glossary. 24 | * @param targetLang Language code of the target terms in the glossary. 25 | */ 26 | public GlossaryLanguagePair(String sourceLang, String targetLang) { 27 | this.sourceLang = LanguageCode.standardize(sourceLang); 28 | this.targetLang = LanguageCode.standardize(targetLang); 29 | } 30 | 31 | /** @return Language code of the source terms in the glossary. */ 32 | public String getSourceLanguage() { 33 | return sourceLang; 34 | } 35 | 36 | /** @return Language code of the target terms in the glossary. */ 37 | public String getTargetLanguage() { 38 | return targetLang; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/http/HttpResponseStream.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api.http; 5 | 6 | import com.deepl.api.*; 7 | import com.deepl.api.utils.*; 8 | import java.io.*; 9 | import org.jetbrains.annotations.*; 10 | 11 | public class HttpResponseStream implements AutoCloseable { 12 | 13 | private final int code; 14 | 15 | @Nullable private final InputStream body; 16 | 17 | public HttpResponseStream(int code, @Nullable InputStream body) { 18 | this.code = code; 19 | this.body = body; 20 | } 21 | 22 | public void close() { 23 | try { 24 | if (this.body != null) { 25 | this.body.close(); 26 | } 27 | } catch (Exception e) { 28 | // ignore 29 | } 30 | } 31 | 32 | public HttpResponse toStringResponse() throws DeepLException { 33 | try { 34 | String content = this.body == null ? "" : StreamUtil.readStream(this.body); 35 | return new HttpResponse(getCode(), content); 36 | } catch (IOException exception) { 37 | throw new DeepLException("Error reading stream", exception); 38 | } finally { 39 | close(); 40 | } 41 | } 42 | 43 | public int getCode() { 44 | return code; 45 | } 46 | 47 | public @Nullable InputStream getBody() { 48 | return body; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing guidelines 2 | 3 | * [Code of Conduct](#code-of-conduct) 4 | * [Issues](#issues) 5 | * [Pull Requests](#pull-requests) 6 | 7 | ## Code of Conduct 8 | 9 | This project has a [Code of Conduct](CODE_OF_CONDUCT.md) to which all 10 | contributors must adhere when participating in the project. Instances of 11 | abusive, harassing, or otherwise unacceptable behavior may be reported by 12 | contacting [open-source@deepl.com](mailto:open-source@deepl.com) and/or a 13 | project maintainer. 14 | 15 | ## Issues 16 | 17 | If you experience problems using the library, or would like to request a new 18 | feature, please open an [issue][issues]. 19 | 20 | Please provide as much context as possible when you open an issue. The 21 | information you provide must be comprehensive enough for us to reproduce the 22 | issue. 23 | 24 | ## Pull Requests 25 | 26 | You are welcome to contribute code and/or documentation in order to fix a bug or 27 | to implement a new feature. Before beginning work, you should create an issue 28 | describing the changes you plan to contribute, to avoid wasting or duplicating 29 | effort. We will then let you know whether we would accept the changes. 30 | 31 | Contributions must be licensed under the same license as the project: 32 | [MIT License](LICENSE). 33 | 34 | Currently automated testing is implemented internally at DeepL, however we plan 35 | to implement publicly visible testing soon. 36 | 37 | [issues]: https://www.github.com/DeepLcom/deepl-java/issues 38 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/TextResult.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | /** The result of a text translation. */ 9 | public class TextResult { 10 | private final String text; 11 | private final String detectedSourceLanguage; 12 | private final int billedCharacters; 13 | private final @Nullable String modelTypeUsed; 14 | 15 | /** Constructs a new instance. */ 16 | public TextResult( 17 | String text, 18 | String detectedSourceLanguage, 19 | int billedCharacters, 20 | @Nullable String modelTypeUsed) { 21 | this.text = text; 22 | this.detectedSourceLanguage = LanguageCode.standardize(detectedSourceLanguage); 23 | this.billedCharacters = billedCharacters; 24 | this.modelTypeUsed = modelTypeUsed; 25 | } 26 | 27 | /** The translated text. */ 28 | public String getText() { 29 | return text; 30 | } 31 | 32 | /** The language code of the source text detected by DeepL. */ 33 | public String getDetectedSourceLanguage() { 34 | return detectedSourceLanguage; 35 | } 36 | 37 | /** Number of characters billed for this text. */ 38 | public int getBilledCharacters() { 39 | return billedCharacters; 40 | } 41 | 42 | /** Model type used for the translation of this text. */ 43 | public @Nullable String getModelTypeUsed() { 44 | return modelTypeUsed; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/TextRephraseOptions.java: -------------------------------------------------------------------------------- 1 | // Copyright 2025 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | /** 7 | * Options to control text rephrasing behaviour. These options may be provided to {@link 8 | * DeepLClient#rephraseText} overloads. 9 | * 10 | *

All properties have corresponding setters in fluent-style, so the following is possible: 11 | * 12 | * TextRephraseOptions options = new TextRephraseOptions() 13 | * .WritingStyle(WritingStyle.Business.getValue()); 14 | * 15 | */ 16 | public class TextRephraseOptions { 17 | private String writingStyle; 18 | private String tone; 19 | 20 | /** 21 | * Sets a style the improved text should be in. Note that only style OR tone can be set. 22 | * 23 | * @see WritingStyle 24 | */ 25 | public TextRephraseOptions setWritingStyle(String style) { 26 | this.writingStyle = style; 27 | return this; 28 | } 29 | 30 | /** 31 | * Sets a tone the improved text should be in. Note that only style OR tone can be set. 32 | * 33 | * @see WritingTone 34 | */ 35 | public TextRephraseOptions setTone(String tone) { 36 | this.tone = tone; 37 | return this; 38 | } 39 | 40 | /** Gets the current style setting. */ 41 | public String getWritingStyle() { 42 | return writingStyle; 43 | } 44 | 45 | /** Gets the current tone setting. */ 46 | public String getTone() { 47 | return tone; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/MultilingualGlossaryDictionaryEntries.java: -------------------------------------------------------------------------------- 1 | // Copyright 2025 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | /** Stores the entries of a glossary. */ 7 | public class MultilingualGlossaryDictionaryEntries { 8 | private final String sourceLanguageCode; 9 | private final String targetLanguageCode; 10 | private final GlossaryEntries entries; 11 | 12 | /** 13 | * Initializes a new {@link MultilingualGlossaryDictionaryInfo} containing information about a 14 | * glossary dictionary. 15 | * 16 | * @param sourceLanguageCode the source language for this dictionary 17 | * @param targetLanguageCode the target language for this dictionary 18 | * @param entries the entries in this dictionary 19 | */ 20 | public MultilingualGlossaryDictionaryEntries( 21 | String sourceLanguageCode, String targetLanguageCode, GlossaryEntries entries) { 22 | this.sourceLanguageCode = sourceLanguageCode; 23 | this.targetLanguageCode = targetLanguageCode; 24 | this.entries = entries; 25 | } 26 | 27 | /** @return the source language code */ 28 | public String getSourceLanguageCode() { 29 | return this.sourceLanguageCode; 30 | } 31 | 32 | /** @return the target language code */ 33 | public String getTargetLanguageCode() { 34 | return this.targetLanguageCode; 35 | } 36 | 37 | /** @return the entry count */ 38 | public GlossaryEntries getEntries() { 39 | return this.entries; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /examples/maven/deepl-test-app/src/test/java/com/deepl/deepltestapp/DeepLIntegrationTest.java: -------------------------------------------------------------------------------- 1 | // Copyright 2023 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.deepltestapp; 5 | 6 | import com.deepl.api.*; 7 | import com.deepl.deepltestapp.*; 8 | import com.deepl.deepltestapp.annotation.IntegrationTest; 9 | import junit.framework.Test; 10 | import junit.framework.TestCase; 11 | import junit.framework.TestSuite; 12 | import org.junit.Assert.*; 13 | import org.junit.experimental.categories.Category; 14 | 15 | /** 16 | * Internal DeepL Integration Test 17 | */ 18 | @Category(IntegrationTest.class) 19 | public class DeepLIntegrationTest extends TestCase { 20 | public DeepLIntegrationTest(String testName) { 21 | super(testName); 22 | } 23 | 24 | public static Test suite() { 25 | return new TestSuite(DeepLIntegrationTest.class); 26 | } 27 | 28 | /** 29 | * Runs the hello world example. Requires a DeepL auth key via the DEEPL_AUTH_KEY 30 | * environment variable. 31 | */ 32 | public void testApp() throws InterruptedException, DeepLException { 33 | String result = App.translateHelloWorld(); 34 | String[] wordsToCheck = {"Hello", "World"}; 35 | for (String wordToCheck : wordsToCheck) { 36 | assertFalse(String.format("Expected translation to no longer contain the english %s, received %s", 37 | wordToCheck, result), result.contains(wordToCheck) 38 | ); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/CustomInstruction.java: -------------------------------------------------------------------------------- 1 | // Copyright 2025 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | import com.google.gson.annotations.*; 7 | import org.jetbrains.annotations.*; 8 | 9 | /** Custom instruction for a style rule. */ 10 | public class CustomInstruction { 11 | @SerializedName(value = "label") 12 | private final String label; 13 | 14 | @SerializedName(value = "prompt") 15 | private final String prompt; 16 | 17 | @SerializedName(value = "source_language") 18 | @Nullable 19 | private final String sourceLanguage; 20 | 21 | /** 22 | * Initializes a new {@link CustomInstruction} containing a custom instruction for a style rule. 23 | * 24 | * @param label Label for the custom instruction. 25 | * @param prompt Prompt text for the custom instruction. 26 | * @param sourceLanguage Optional source language code for the custom instruction. 27 | */ 28 | public CustomInstruction(String label, String prompt, @Nullable String sourceLanguage) { 29 | this.label = label; 30 | this.prompt = prompt; 31 | this.sourceLanguage = sourceLanguage; 32 | } 33 | 34 | /** @return Label for the custom instruction. */ 35 | public String getLabel() { 36 | return label; 37 | } 38 | 39 | /** @return Prompt text for the custom instruction. */ 40 | public String getPrompt() { 41 | return prompt; 42 | } 43 | 44 | /** @return Optional source language code for the custom instruction. */ 45 | @Nullable 46 | public String getSourceLanguage() { 47 | return sourceLanguage; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/MultilingualGlossaryDictionaryInfo.java: -------------------------------------------------------------------------------- 1 | // Copyright 2025 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | import com.google.gson.annotations.SerializedName; 7 | 8 | /** Stores the entries of a glossary. */ 9 | public class MultilingualGlossaryDictionaryInfo { 10 | @SerializedName(value = "source_lang") 11 | private final String sourceLanguageCode; 12 | 13 | @SerializedName(value = "target_lang") 14 | private final String targetLanguageCode; 15 | 16 | @SerializedName(value = "entry_count") 17 | private final long entryCount; 18 | 19 | /** 20 | * Initializes a new {@link MultilingualGlossaryDictionaryInfo} containing information about a 21 | * glossary dictionary. 22 | * 23 | * @param sourceLanguageCode the source language for this dictionary 24 | * @param targetLanguageCode the target language for this dictionary 25 | * @param entryCount the number of entries in this dictionary 26 | */ 27 | public MultilingualGlossaryDictionaryInfo( 28 | String sourceLanguageCode, String targetLanguageCode, long entryCount) { 29 | this.sourceLanguageCode = sourceLanguageCode; 30 | this.targetLanguageCode = targetLanguageCode; 31 | this.entryCount = entryCount; 32 | } 33 | 34 | /** @return the source language code */ 35 | public String getSourceLanguageCode() { 36 | return this.sourceLanguageCode; 37 | } 38 | 39 | /** @return the target language code */ 40 | public String getTargetLanguageCode() { 41 | return this.targetLanguageCode; 42 | } 43 | 44 | /** @return the entry count */ 45 | public long getEntryCount() { 46 | return this.entryCount; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /deepl-java/src/test/java/com/deepl/api/GlossaryCleanupUtility.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | import java.util.*; 7 | 8 | public class GlossaryCleanupUtility implements AutoCloseable { 9 | private final String glossaryName; 10 | private final Translator translator; 11 | 12 | public GlossaryCleanupUtility(Translator translator) { 13 | this(translator, ""); 14 | } 15 | 16 | public GlossaryCleanupUtility(Translator translator, String testNameSuffix) { 17 | String callingFunc = getCallerFunction(); 18 | String uuid = UUID.randomUUID().toString(); 19 | 20 | this.glossaryName = 21 | String.format("deepl-java-test-glossary: %s%s %s", callingFunc, testNameSuffix, uuid); 22 | this.translator = translator; 23 | } 24 | 25 | public String getGlossaryName() { 26 | return glossaryName; 27 | } 28 | 29 | @Override 30 | public void close() throws Exception { 31 | List glossaries = translator.listGlossaries(); 32 | for (GlossaryInfo glossary : glossaries) { 33 | if (Objects.equals(glossary.getName(), glossaryName)) { 34 | try { 35 | translator.deleteGlossary(glossary); 36 | } catch (Exception exception) { 37 | // Ignore 38 | } 39 | } 40 | } 41 | } 42 | 43 | private static String getCallerFunction() { 44 | StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace(); 45 | // Find the first function outside this class following functions in this class 46 | for (int i = 1; i < stacktrace.length; i++) { 47 | if (!stacktrace[i].getClassName().equals(GlossaryCleanupUtility.class.getName()) 48 | && stacktrace[i - 1].getClassName().equals(GlossaryCleanupUtility.class.getName())) { 49 | return stacktrace[i].getMethodName(); 50 | } 51 | } 52 | return "unknown"; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /examples/maven/deepl-test-app/src/main/java/com/deepl/deepltestapp/App.java: -------------------------------------------------------------------------------- 1 | // Copyright 2023 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.deepltestapp; 5 | import com.deepl.api.*; 6 | import java.lang.System.*; 7 | 8 | /** 9 | * Hello world translation example 10 | * 11 | */ 12 | public class App { 13 | /** 14 | * Hello world example - insert your API key to test the library. 15 | */ 16 | public static void main( String[] args ) throws InterruptedException, DeepLException { 17 | String authKey = "f63c02c5-f056-..."; // Replace with your key 18 | Translator translator = new Translator(authKey); 19 | TextResult result = 20 | translator.translateText("Hello, world!", null, "fr"); 21 | System.out.println(result.getText()); // "Bonjour, le monde !" 22 | } 23 | 24 | ///////////////////////////////////////////////////////////////////////////////////// 25 | /// These methods are for a test using DeepLs CI pipeline, ignore. 26 | 27 | public static String getEnvironmentVariableValue(String envVar) { 28 | return System.getenv(envVar); 29 | } 30 | 31 | public static String getAuthKeyFromEnvironmentVariables() { 32 | return getEnvironmentVariableValue("DEEPL_AUTH_KEY"); 33 | } 34 | 35 | public static String getServerUrlFromEnvironmentVariables() { 36 | return getEnvironmentVariableValue("DEEPL_SERVER_URL"); 37 | } 38 | 39 | public static String translateHelloWorld() throws InterruptedException, DeepLException { 40 | Translator translator = new Translator(getAuthKeyFromEnvironmentVariables(), 41 | (new TranslatorOptions()).setServerUrl(getServerUrlFromEnvironmentVariables())); 42 | TextResult result = 43 | translator.translateText("Hello, world!", null, "fr"); 44 | String translatedText = result.getText(); 45 | return translatedText; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/utils/StreamUtil.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api.utils; 5 | 6 | import java.io.*; 7 | import java.nio.charset.*; 8 | 9 | public class StreamUtil { 10 | public static final int DEFAULT_BUFFER_SIZE = 1024; 11 | 12 | public static String readStream(InputStream inputStream) throws IOException { 13 | Charset charset = StandardCharsets.UTF_8; 14 | final char[] buffer = new char[DEFAULT_BUFFER_SIZE]; 15 | final StringBuilder sb = new StringBuilder(); 16 | try (InputStreamReader isr = new InputStreamReader(inputStream, charset); 17 | final Reader in = new BufferedReader(isr)) { 18 | int charsRead; 19 | while ((charsRead = in.read(buffer, 0, DEFAULT_BUFFER_SIZE)) > 0) { 20 | sb.append(buffer, 0, charsRead); 21 | } 22 | } 23 | return sb.toString(); 24 | } 25 | 26 | /** 27 | * Reads all bytes from input stream and writes the bytes to the given output stream in the order 28 | * that they are read. On return, input stream will be at end of stream. This method does not 29 | * close either stream. 30 | * 31 | *

Implementation based on {@link InputStream#transferTo(OutputStream)} added in Java 9. 32 | * 33 | * @param inputStream The input stream, non-null. 34 | * @param outputStream The output stream, non-null. 35 | * @return Number of bytes transferred. 36 | * @throws IOException if an I/O error occurs when reading or writing. 37 | */ 38 | public static long transferTo(InputStream inputStream, OutputStream outputStream) 39 | throws IOException { 40 | long transferred = 0; 41 | final byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; 42 | int read; 43 | while ((read = inputStream.read(buffer, 0, DEFAULT_BUFFER_SIZE)) >= 0) { 44 | outputStream.write(buffer, 0, read); 45 | transferred += read; 46 | } 47 | return transferred; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/parsing/MultilingualGlossaryDictionaryEntriesResponse.java: -------------------------------------------------------------------------------- 1 | // Copyright 2025 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api.parsing; 5 | 6 | import com.deepl.api.GlossaryEntries; 7 | import com.deepl.api.MultilingualGlossaryDictionaryEntries; 8 | import com.google.gson.annotations.SerializedName; 9 | 10 | /** 11 | * Class representing v3 list-glossaries response by the DeepL API. 12 | * 13 | *

This class is internal; you should not use this class directly. 14 | */ 15 | public class MultilingualGlossaryDictionaryEntriesResponse { 16 | 17 | @SerializedName(value = "source_lang") 18 | private final String sourceLanguageCode; 19 | 20 | @SerializedName(value = "target_lang") 21 | private final String targetLanguageCode; 22 | 23 | @SerializedName(value = "entries") 24 | private final String entries; 25 | 26 | @SerializedName(value = "entries_format") 27 | private final String entriesFormat; 28 | 29 | /** 30 | * Initializes a new {@link MultilingualGlossaryDictionaryEntriesResponse} containing information 31 | * about a glossary dictionary. 32 | * 33 | * @param sourceLanguageCode the source language for this dictionary 34 | * @param targetLanguageCode the target language for this dictionary 35 | * @param entries the entries in this dictionary 36 | * @param entriesFormat the format of the entries in this dictionary 37 | */ 38 | public MultilingualGlossaryDictionaryEntriesResponse( 39 | String sourceLanguageCode, String targetLanguageCode, String entries, String entriesFormat) { 40 | this.sourceLanguageCode = sourceLanguageCode; 41 | this.targetLanguageCode = targetLanguageCode; 42 | this.entries = entries; 43 | this.entriesFormat = entriesFormat; 44 | } 45 | 46 | public MultilingualGlossaryDictionaryEntries getDictionaryEntries() { 47 | return new MultilingualGlossaryDictionaryEntries( 48 | this.sourceLanguageCode, 49 | this.targetLanguageCode, 50 | new GlossaryEntries(GlossaryEntries.fromTsv(this.entries))); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/MultilingualGlossaryInfo.java: -------------------------------------------------------------------------------- 1 | // Copyright 2025 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | import com.google.gson.annotations.*; 7 | import java.util.*; 8 | 9 | /** Information about a glossary, excluding the entry list. */ 10 | public class MultilingualGlossaryInfo implements IGlossary { 11 | @SerializedName(value = "glossary_id") 12 | private final String glossaryId; 13 | 14 | @SerializedName(value = "name") 15 | private final String name; 16 | 17 | @SerializedName(value = "creation_time") 18 | private final Date creationTime; 19 | 20 | @SerializedName(value = "dictionaries") 21 | private final List dictionaries; 22 | 23 | /** 24 | * Initializes a new {@link MultilingualGlossaryInfo} containing information about a glossary. 25 | * 26 | * @param glossaryId ID of the associated glossary. 27 | * @param name Name of the glossary chosen during creation. 28 | * @param creationTime Time when the glossary was created. 29 | * @param dictionaries A list of dictionaries that are in this glossary 30 | */ 31 | public MultilingualGlossaryInfo( 32 | String glossaryId, 33 | String name, 34 | Date creationTime, 35 | List dictionaries) { 36 | this.glossaryId = glossaryId; 37 | this.name = name; 38 | this.creationTime = creationTime; 39 | this.dictionaries = dictionaries; 40 | } 41 | 42 | /** @return Unique ID assigned to the glossary. */ 43 | public String getGlossaryId() { 44 | return glossaryId; 45 | } 46 | 47 | /** @return User-defined name assigned to the glossary. */ 48 | public String getName() { 49 | return name; 50 | } 51 | 52 | /** @return Timestamp when the glossary was created. */ 53 | public Date getCreationTime() { 54 | return creationTime; 55 | } 56 | 57 | /** @return the list of dictionaries in this glossary */ 58 | public List getDictionaries() { 59 | return dictionaries; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/Language.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | /** 9 | * A language supported by DeepL translation. The {@link Translator} class provides functions to 10 | * retrieve the available source and target languages. 11 | * 12 | * @see Translator#getSourceLanguages() 13 | * @see Translator#getTargetLanguages() 14 | */ 15 | public class Language { 16 | private final String name; 17 | private final String code; 18 | private final @Nullable Boolean supportsFormality; 19 | 20 | /** 21 | * Initializes a new Language object. 22 | * 23 | * @param name The name of the language in English. 24 | * @param code The language code. 25 | * @param supportsFormality true for a target language that supports the {@link 26 | * TextTranslationOptions#setFormality} option for translations, false for other 27 | * target languages, or null for source languages. 28 | */ 29 | public Language(String name, String code, @Nullable Boolean supportsFormality) { 30 | this.name = name; 31 | this.code = LanguageCode.standardize(code); 32 | this.supportsFormality = supportsFormality; 33 | } 34 | 35 | /** @return The name of the language in English, for example "Italian" or "Romanian". */ 36 | public String getName() { 37 | return name; 38 | } 39 | 40 | /** 41 | * @return The language code, for example "it", "ro" or "en-US". Language codes follow ISO 639-1 42 | * with an optional regional code from ISO 3166-1. 43 | */ 44 | public String getCode() { 45 | return code; 46 | } 47 | 48 | /** 49 | * @return true if this language is a target language that supports the {@link 50 | * TextTranslationOptions#setFormality} option for translations, false if this 51 | * language is a target language that does not support formality, or null if this 52 | * language is a source language. 53 | */ 54 | public @Nullable Boolean getSupportsFormality() { 55 | return supportsFormality; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/utils/BackoffTimer.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api.utils; 5 | 6 | import java.time.Duration; 7 | import java.time.Instant; 8 | import java.util.concurrent.ThreadLocalRandom; 9 | 10 | public class BackoffTimer { 11 | 12 | private int numRetries; 13 | private Duration backoff; 14 | private final Duration minTimeout; 15 | private Instant deadline; 16 | 17 | private static final Duration backoffInitial = Duration.ofSeconds(1); 18 | private static final Duration backoffMax = Duration.ofSeconds(120); 19 | private static final float jitter = 0.23F; 20 | private static final float multiplier = 1.6F; 21 | 22 | public BackoffTimer(Duration minTimeout) { 23 | numRetries = 0; 24 | backoff = backoffInitial; 25 | this.minTimeout = minTimeout; 26 | deadline = Instant.now().plus(backoff); 27 | } 28 | 29 | public Duration getTimeout() { 30 | Duration timeToDeadline = getTimeUntilDeadline(); 31 | if (timeToDeadline.compareTo(minTimeout) < 0) return minTimeout; 32 | return timeToDeadline; 33 | } 34 | 35 | public long getTimeoutMillis() { 36 | return getTimeout().toMillis(); 37 | } 38 | 39 | public int getNumRetries() { 40 | return numRetries; 41 | } 42 | 43 | public void sleepUntilRetry() throws InterruptedException { 44 | try { 45 | Thread.sleep(getTimeUntilDeadline().toMillis()); 46 | } catch (InterruptedException exception) { 47 | Thread.currentThread().interrupt(); 48 | throw exception; 49 | } 50 | 51 | backoff = Duration.ofNanos((long) (backoff.toNanos() * multiplier)); 52 | if (backoff.compareTo(backoffMax) > 0) backoff = backoffMax; 53 | 54 | float randomJitter = (ThreadLocalRandom.current().nextFloat() * 2.0F - 1.0F) * jitter + 1.0F; 55 | Duration jitteredBackoff = Duration.ofNanos((long) (backoff.toNanos() * randomJitter)); 56 | deadline = Instant.now().plus(jitteredBackoff); 57 | ++numRetries; 58 | } 59 | 60 | private Duration getTimeUntilDeadline() { 61 | Instant currentTime = Instant.now(); 62 | if (currentTime.isAfter(deadline)) return Duration.ZERO; 63 | return Duration.between(currentTime, deadline); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /deepl-java/src/test/java/com/deepl/api/MultilingualGlossaryCleanupUtility.java: -------------------------------------------------------------------------------- 1 | // Copyright 2025 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | import java.util.List; 7 | import java.util.Objects; 8 | import java.util.UUID; 9 | 10 | public class MultilingualGlossaryCleanupUtility implements AutoCloseable { 11 | private final String glossaryName; 12 | private final DeepLClient deepLClient; 13 | 14 | public MultilingualGlossaryCleanupUtility(DeepLClient deepLClient) { 15 | this(deepLClient, ""); 16 | } 17 | 18 | public MultilingualGlossaryCleanupUtility(DeepLClient deepLClient, String testNameSuffix) { 19 | String callingFunc = getCallerFunction(); 20 | String uuid = UUID.randomUUID().toString(); 21 | 22 | this.glossaryName = 23 | String.format("deepl-java-test-glossary: %s%s %s", callingFunc, testNameSuffix, uuid); 24 | this.deepLClient = deepLClient; 25 | } 26 | 27 | public String getGlossaryName() { 28 | return glossaryName; 29 | } 30 | 31 | @Override 32 | public void close() throws Exception { 33 | List glossaries = deepLClient.listMultilingualGlossaries(); 34 | for (MultilingualGlossaryInfo glossary : glossaries) { 35 | if (Objects.equals(glossary.getName(), glossaryName)) { 36 | try { 37 | deepLClient.deleteMultilingualGlossary(glossary.getGlossaryId()); 38 | } catch (Exception exception) { 39 | // Ignore 40 | System.out.println( 41 | "Failed to delete glossary: " 42 | + glossaryName 43 | + "\nException: " 44 | + exception.getMessage()); 45 | } 46 | } 47 | } 48 | } 49 | 50 | private static String getCallerFunction() { 51 | StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace(); 52 | // Find the first function outside this class following functions in this class 53 | for (int i = 1; i < stacktrace.length; i++) { 54 | if (!stacktrace[i].getClassName().equals(MultilingualGlossaryCleanupUtility.class.getName()) 55 | && stacktrace[i - 1] 56 | .getClassName() 57 | .equals(MultilingualGlossaryCleanupUtility.class.getName())) { 58 | return stacktrace[i].getMethodName(); 59 | } 60 | } 61 | return "unknown"; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/DocumentTranslationOptions.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | /** 7 | * Options to control document translation behaviour. These options may be provided to {@link 8 | * Translator#translateDocument} overloads. 9 | * 10 | *

All properties have corresponding setters in fluent-style, so the following is possible: 11 | * 12 | * DocumentTranslationOptions options = new DocumentTranslationOptions() 13 | * .setFormality(Formality.Less).setGlossaryId("f63c02c5-f056-.."); 14 | * 15 | */ 16 | public class DocumentTranslationOptions extends BaseRequestOptions { 17 | private Formality formality; 18 | private String glossaryId; 19 | 20 | /** 21 | * Sets whether translations should lean toward formal or informal language. This option is only 22 | * applicable for target languages that support the formality option. By default, this value is 23 | * null and translations use the default formality. 24 | * 25 | * @see Language#getSupportsFormality() 26 | * @see Formality 27 | */ 28 | public DocumentTranslationOptions setFormality(Formality formality) { 29 | this.formality = formality; 30 | return this; 31 | } 32 | 33 | /** 34 | * Sets the ID of a glossary to use with the translation. By default, this value is 35 | * null and no glossary is used. 36 | */ 37 | public DocumentTranslationOptions setGlossaryId(String glossaryId) { 38 | this.glossaryId = glossaryId; 39 | return this; 40 | } 41 | 42 | /** 43 | * Sets the glossary to use with the translation. By default, this value is null and 44 | * no glossary is used. 45 | */ 46 | public DocumentTranslationOptions setGlossary(IGlossary glossary) { 47 | return setGlossary(glossary.getGlossaryId()); 48 | } 49 | 50 | /** 51 | * Sets the glossary to use with the translation. By default, this value is null and 52 | * no glossary is used. 53 | */ 54 | public DocumentTranslationOptions setGlossary(String glossaryId) { 55 | this.glossaryId = glossaryId; 56 | return this; 57 | } 58 | 59 | /** Gets the current formality setting. */ 60 | public Formality getFormality() { 61 | return formality; 62 | } 63 | 64 | /** Gets the current glossary ID. */ 65 | public String getGlossaryId() { 66 | return glossaryId; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /examples/maven/deepl-test-app/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.deepl.deeplTestApp 5 | deepl-test-app 6 | jar 7 | 1.0-SNAPSHOT 8 | deepl-test-app 9 | http://maven.apache.org 10 | 11 | 12 | 1.8 13 | 1.8 14 | 15 | 16 | 17 | 18 | junit 19 | junit 20 | 4.13.2 21 | test 22 | 23 | 24 | com.deepl.api 25 | deepl-java 26 | [1.0,2.0) 27 | 28 | 29 | 30 | 31 | 32 | 33 | buildProject 34 | 35 | 36 | 37 | org.apache.maven.plugins 38 | maven-surefire-plugin 39 | 3.1.2 40 | 41 | com.deepl.deepltestapp.annotation.IntegrationTest 42 | 43 | 44 | 45 | 46 | 47 | 48 | runIntegrationTests 49 | 50 | 51 | 52 | maven-surefire-plugin 53 | 3.1.2 54 | 55 | 56 | integration-test 57 | 58 | 59 | **/* 60 | 61 | com.deepl.deepltestapp.annotation.IntegrationTest 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /deepl-java/src/test/java/com/deepl/api/RephraseTextTest.java: -------------------------------------------------------------------------------- 1 | // Copyright 2025 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | import java.util.*; 7 | import org.junit.jupiter.api.Assertions; 8 | import org.junit.jupiter.api.Test; 9 | 10 | public class RephraseTextTest extends TestBase { 11 | 12 | @Test 13 | void testSingleText() throws DeepLException, InterruptedException { 14 | String inputText = exampleText.get("en"); 15 | DeepLClient client = createDeepLClient(); 16 | WriteResult result = client.rephraseText(inputText, "EN-GB", null); 17 | this.checkSanityOfImprovements(inputText, result, "EN", "EN-GB", 0.2f); 18 | } 19 | 20 | @Test 21 | void testTextArray() throws DeepLException, InterruptedException { 22 | DeepLClient client = createDeepLClient(); 23 | List texts = new ArrayList<>(); 24 | texts.add(exampleText.get("en")); 25 | texts.add(exampleText.get("en")); 26 | List results = client.rephraseText(texts, "EN-GB", null); 27 | for (int i = 0; i < texts.size(); i++) { 28 | this.checkSanityOfImprovements(texts.get(i), results.get(i), "EN", "EN-GB", 0.2f); 29 | } 30 | } 31 | 32 | @Test 33 | void testBusinessStyle() throws DeepLException, InterruptedException { 34 | String inputText = 35 | "As Gregor Samsa awoke one morning from uneasy dreams he found himself transformed in his bed into a gigantic insect."; 36 | DeepLClient client = createDeepLClient(); 37 | TextRephraseOptions options = 38 | (new TextRephraseOptions()).setWritingStyle(WritingStyle.Business.getValue()); 39 | WriteResult result = client.rephraseText(inputText, "EN-GB", options); 40 | if (!isMockServer) { 41 | this.checkSanityOfImprovements(inputText, result, "EN", "EN-GB", 0.2f); 42 | } 43 | } 44 | 45 | protected void checkSanityOfImprovements( 46 | String inputText, 47 | WriteResult result, 48 | String expectedSourceLanguageUppercase, 49 | String expectedTargetLanguageUppercase, 50 | float epsilon) { 51 | Assertions.assertEquals( 52 | expectedSourceLanguageUppercase, result.getDetectedSourceLanguage().toUpperCase()); 53 | Assertions.assertEquals( 54 | expectedTargetLanguageUppercase, result.getTargetLanguage().toUpperCase()); 55 | int nImproved = result.getText().length(); 56 | int nOriginal = inputText.length(); 57 | Assertions.assertTrue( 58 | 1 / (1 + epsilon) <= ((float) nImproved) / nOriginal, 59 | "Improved text is too short compared to original, improved:\n" 60 | + result.getText() 61 | + "\n, original:\n" 62 | + inputText); 63 | Assertions.assertTrue( 64 | nImproved / nOriginal <= (1 + epsilon), "Improved text is too long compared to original"); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /deepl-java/src/test/java/com/deepl/api/StyleRuleTest.java: -------------------------------------------------------------------------------- 1 | // Copyright 2025 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | import java.util.List; 7 | import org.junit.jupiter.api.*; 8 | 9 | public class StyleRuleTest extends TestBase { 10 | private static final String DEFAULT_STYLE_ID = "dca2e053-8ae5-45e6-a0d2-881156e7f4e4"; 11 | 12 | @Test 13 | void testGetAllStyleRules() throws Exception { 14 | Assumptions.assumeTrue(isMockServer); 15 | DeepLClient client = createDeepLClient(); 16 | List styleRules = client.getAllStyleRules(0, 10, true); 17 | 18 | Assertions.assertNotNull(styleRules); 19 | Assertions.assertFalse(styleRules.isEmpty()); 20 | Assertions.assertEquals(DEFAULT_STYLE_ID, styleRules.get(0).getStyleId()); 21 | Assertions.assertEquals("Default Style Rule", styleRules.get(0).getName()); 22 | Assertions.assertNotNull(styleRules.get(0).getCreationTime()); 23 | Assertions.assertNotNull(styleRules.get(0).getUpdatedTime()); 24 | Assertions.assertEquals("en", styleRules.get(0).getLanguage()); 25 | Assertions.assertEquals(1, styleRules.get(0).getVersion()); 26 | Assertions.assertNotNull(styleRules.get(0).getConfiguredRules()); 27 | Assertions.assertNotNull(styleRules.get(0).getCustomInstructions()); 28 | } 29 | 30 | @Test 31 | void testGetAllStyleRulesWithoutDetailed() throws Exception { 32 | Assumptions.assumeTrue(isMockServer); 33 | DeepLClient client = createDeepLClient(); 34 | List styleRules = client.getAllStyleRules(); 35 | 36 | Assertions.assertNotNull(styleRules); 37 | Assertions.assertFalse(styleRules.isEmpty()); 38 | Assertions.assertEquals(DEFAULT_STYLE_ID, styleRules.get(0).getStyleId()); 39 | Assertions.assertNull(styleRules.get(0).getConfiguredRules()); 40 | Assertions.assertNull(styleRules.get(0).getCustomInstructions()); 41 | } 42 | 43 | @Test 44 | void testTranslateTextWithStyleId() throws Exception { 45 | // Note: this test may use the mock server that will not translate the text 46 | // with a style rule, therefore we do not check the translated result. 47 | Assumptions.assumeTrue(isMockServer); 48 | DeepLClient client = createDeepLClient(); 49 | String text = "Hallo, Welt!"; 50 | 51 | TextResult result = 52 | client.translateText( 53 | text, "de", "en-US", new TextTranslationOptions().setStyleId(DEFAULT_STYLE_ID)); 54 | 55 | Assertions.assertNotNull(result); 56 | } 57 | 58 | @Test 59 | void testTranslateTextWithStyleRuleInfo() throws Exception { 60 | Assumptions.assumeTrue(isMockServer); 61 | DeepLClient client = createDeepLClient(); 62 | List styleRules = client.getAllStyleRules(); 63 | StyleRuleInfo rule = styleRules.get(0); 64 | String text = "Hallo, Welt!"; 65 | 66 | TextResult result = 67 | client.translateText(text, "de", "en-US", new TextTranslationOptions().setStyleRule(rule)); 68 | 69 | Assertions.assertNotNull(result); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/GlossaryInfo.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | import com.google.gson.annotations.*; 7 | import java.util.*; 8 | import org.jetbrains.annotations.*; 9 | 10 | /** Information about a glossary, excluding the entry list. */ 11 | public class GlossaryInfo implements IGlossary { 12 | 13 | @SerializedName(value = "glossary_id") 14 | private final String glossaryId; 15 | 16 | @SerializedName(value = "name") 17 | private final String name; 18 | 19 | @SerializedName(value = "ready") 20 | private final boolean ready; 21 | 22 | @SerializedName(value = "source_lang") 23 | private final String sourceLang; 24 | 25 | @SerializedName(value = "target_lang") 26 | private final String targetLang; 27 | 28 | @SerializedName(value = "creation_time") 29 | private final Date creationTime; 30 | 31 | @SerializedName(value = "entry_count") 32 | private final long entryCount; 33 | 34 | /** 35 | * Initializes a new {@link GlossaryInfo} containing information about a glossary. 36 | * 37 | * @param glossaryId ID of the associated glossary. 38 | * @param name Name of the glossary chosen during creation. 39 | * @param ready true if the glossary may be used for translations, otherwise false. 40 | * @param sourceLang Language code of the source terms in the glossary. 41 | * @param targetLang Language code of the target terms in the glossary. 42 | * @param creationTime Time when the glossary was created. 43 | * @param entryCount The number of source-target entry pairs in the glossary. 44 | */ 45 | public GlossaryInfo( 46 | String glossaryId, 47 | String name, 48 | boolean ready, 49 | String sourceLang, 50 | String targetLang, 51 | Date creationTime, 52 | long entryCount) { 53 | this.glossaryId = glossaryId; 54 | this.name = name; 55 | this.ready = ready; 56 | this.sourceLang = sourceLang; 57 | this.targetLang = targetLang; 58 | this.creationTime = creationTime; 59 | this.entryCount = entryCount; 60 | } 61 | 62 | /** @return Unique ID assigned to the glossary. */ 63 | public String getGlossaryId() { 64 | return glossaryId; 65 | } 66 | 67 | /** @return User-defined name assigned to the glossary. */ 68 | public String getName() { 69 | return name; 70 | } 71 | 72 | /** @return True if the glossary may be used for translations, otherwise false. */ 73 | public boolean isReady() { 74 | return ready; 75 | } 76 | 77 | /** @return Source language code of the glossary. */ 78 | public String getSourceLang() { 79 | return sourceLang; 80 | } 81 | 82 | /** @return Target language code of the glossary. */ 83 | public String getTargetLang() { 84 | return targetLang; 85 | } 86 | 87 | /** @return Timestamp when the glossary was created. */ 88 | public Date getCreationTime() { 89 | return creationTime; 90 | } 91 | 92 | /** @return The number of entries contained in the glossary. */ 93 | public long getEntryCount() { 94 | return entryCount; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | To report security concerns or vulnerabilities within deepl-java, please email 2 | us at [security@deepl.com](mailto:security@deepl.com). 3 | 4 | You can send us PGP-encrypted email using the following PGP public key: 5 | 6 | ``` 7 | -----BEGIN PGP PUBLIC KEY BLOCK----- 8 | 9 | mQINBF7WSmABEADzRUp22VY7bVfUWScKLi9o8BRSEL4u3aPn9WOQoRLQH0j3dNNQ 10 | FQlwTPn/Ez6qreEl8mX0aE+eLCEykXqsrU/UaTSTslF+H6UQyuGLXkRm8Lblt93I 11 | OEhL069fC7rm+/zJq72+hishBF8DXqa+WtFd8VfK3i211vRhU/teKeAKT0xiuN/5 12 | EQl1Bn7jR7mmQtNbPBhCsAlaC/tNUQ3Lyj6LYnnco7ums5Q/gCvfs2HM3mXJyvnG 13 | 1MC2IrECPowTt04W3V1uXuMcm766orTG/AmtBIbPmOzao4sfqwRVHGvc8zcr1az9 14 | 0nVyEJXx1eUVRDU1GAQuMjEkGgwvTd+nt6sHpn8C+9hMYJhon9veDSupViBuvNRC 15 | p1PnpSLYYy7tA7DPXMhP3cMXe+Z4bYzgwi3xjOwh6SDyB4OFIxtGyuMrLGfZnd6A 16 | hDH4H9zHPpciD5MxcOaqlKdgABQALvc6MvJ1Guf1ckGTbfHz1brtR1LPMK8rrnNu 17 | kwQzgkogYV6YAnt8LPXMPa2Vgy8TAiby7GPaATPeSWdNHtkuYGhWNVbnb60kEWiJ 18 | /RgHFZYfRT1dEcKoQEcDJ7AV14urEFIAfmhlsT8h7iJYUQMa45BakUubi3aWwcme 19 | ya+5WXvp2xU14VMfrscApA0e1v0VcTNVwlKambs/lwims0/xiSaXJS6gVwARAQAB 20 | tCNEZWVwTCBTZWN1cml0eSA8c2VjdXJpdHlAZGVlcGwuY29tPokCTgQTAQgAOBYh 21 | BGvTAPE3gtThLDZ9+ey96Y7yK41BBQJe1kpgAhsDBQsJCAcCBhUKCQgLAgQWAgMB 22 | Ah4BAheAAAoJEOy96Y7yK41BHVIP/04R08g4N32c47edY6z3sl3DAf+/6UI4Bc4S 23 | Jg5L4JcfrsKaDd55plps8nj31VXrxVBO0NrO6HLC50SXbYVrANyo0occ2mIoU8c2 24 | tNbYCUmJ3QjlUwDjHWlMV2J9FcfZkv7z+2TDY6DF8MKqCMi8j7Pnj0hlY0JytciH 25 | SGES1q8+//8tG9z6b6vvxBFfJI+iNXvcbn6uU1WRvGoBqq2A13fXuwTXiNNphsvu 26 | kHqBHSxnf/EAmcmBX0tm6yaWDdwy+rrcDNwXiqqvK6DFWEE7+/9t2FhlgzvuCOfx 27 | dQVMZL8WH2rr6OPQLDgtGxEUFmD+srmqbVn5NKdY6lQ/BEaraozDkuqJEb0/L/kb 28 | Dv+buz8rmKze0XPlrt1XTQ5ZDQp8AMAaPp1UsizVhasZgxxuUa+g5mMbJr7TSNJN 29 | CIqidnh1MEyIr3IccOAr2F51hn6keKIdVnO4dWrWNMTfk00dw3fPGFhNTniITTF2 30 | s3oJ8cy2NMNkVMP5XL3bulpgkKN+hXa4IHkTfWRv7hfYJ/3i3yTRNRjYGRoVp7eM 31 | iADumKaZy5Szl458txuI+p9DGAEvkSJoF7ptwedSvVZ/FZukS5mwYisRV9shzsXF 32 | 3jpcGZ1B3qS68r9ySqnPEWR6oT8p63fpMNVMjz5r4YEbvU0A62OhUk52drLM6SgC 33 | mdOZcmnHuQINBF7WSmABEADc6L/wSexm4l1GWZSQpJ35ldlu7jjWQGguQeeG2900 34 | aEI3UcftMCWg+apwf4h4Yj2YjzUncYAM6RenGvXgZUYQe3OHb8uqpkSmYHUdB/Uq 35 | I4NPO3e8RMDo9YohPKCpZ7jV70X8F9GOUkUgfp29CjrMOYgSLwkSyWotsQ9KtkEH 36 | Sx/h+gviIERe0dkiN9lCsReNigoWLleH4qBSZGPxqF4tzANJ6D2tnAv+6KUQvho3 37 | CdijBiia4o16p9M0altSqsZCEX1Y5BKmWIh9fvvS2uB7SdzS0gcASzlekMGCjG10 38 | dNji+uSNdHExlbl0kUpEL1TuY2hxPBa6lc1hckI3dGng0jIFlio4s8DG3Utmrj3C 39 | KQFxnjqtO+uaJ8HdNo8ObtEp/v9TpsHWUchBTrBP4XN5KwqkljF8XVBA6ceh8H38 40 | 7/RVWRcWp6h30ROm1DTnAGxJk02fbjpnEO0EvudxKTlnAJXV6z+Tm3yYaR4gQYa3 41 | /zfLZgz0z0MqNUsGephZGPzfUX7Lsz6HGUoo7I1KST6xD2QodJYOhHIEOgsqskk+ 42 | cgeXp45X5JLlCQaBLQoL8ut6CTcop1/6U+JZtrm6DdXTZfq57sqfDI+gkG8WljRY 43 | yhsCL+xWiwDjtt/8kpk+W75EQmwPuctoS85Rm6hEpffewdQtb2XCEWpbta6hE1r1 44 | kQARAQABiQI2BBgBCAAgFiEEa9MA8TeC1OEsNn357L3pjvIrjUEFAl7WSmACGwwA 45 | CgkQ7L3pjvIrjUHFvg/9GnIW9SM/nYJpi1xZVWWGwQ+/kTceD50bv8kyvNaia/9m 46 | HG6n83xHNTRBYnt8NtTqHvW0y20Cp3gUs2WxboDgCIb3+srI2ipwiaDJcq+rVr0f 47 | XkCe5MryioKRbTFQ8OgvKh9GK/tYtqZakn7Q9596ajUjHOQV1+Uw/jywLYRlcbqI 48 | zbxyNVWitxPs3Z7jUDAvhPOIOmhLFc+QxSYrs1W4ZEGnZ3+9utqzlEiMusy9Rq0T 49 | /W/wrG6SckebjhrwWZJmy/hkW6V6LUX4++vCVV5+zwsvgEortCV8bhvLfqQDr/WN 50 | fnmbNZtXJbyhTYbcYReOLeKidxO2lZEemnX6iOt5xCdoMcYU23xDT9+tE7Eh6Nfw 51 | einZemBwfku5vxxPF73pOoQUCRq9tgvUrEq+3BqkqidhnFUOPi0J5726q1PBG65x 52 | 5o+SQyvB3NA3al3mEH65z3V3/g0UHnhGcEMwVOXBkffgdKNhWYw59qhSVQnkiq0U 53 | MG10g/RL7VdiISAFPTDmKWUaEDYosinKqOMHwcaVdJq9ssvPf89et6yP/ZkbLIHs 54 | 2y3oiPonh2RMxi2OedlDz+Jp/A2o3qHmwNvBx/meGB0praGUonFVZTAA1EMS39Bi 55 | NhG/L8giTyzA0mMkTJAPXtUVlRe5rEjORgYJsgRqZxEfpsJC9OkvYS4ayO0eCEs= 56 | =jVHt 57 | -----END PGP PUBLIC KEY BLOCK----- 58 | ``` -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/ConfiguredRules.java: -------------------------------------------------------------------------------- 1 | // Copyright 2025 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | import com.google.gson.annotations.*; 7 | import java.util.*; 8 | import org.jetbrains.annotations.*; 9 | 10 | /** Configuration rules for a style rule list. */ 11 | public class ConfiguredRules { 12 | @SerializedName(value = "dates_and_times") 13 | @Nullable 14 | private final Map datesAndTimes; 15 | 16 | @SerializedName(value = "formatting") 17 | @Nullable 18 | private final Map formatting; 19 | 20 | @SerializedName(value = "numbers") 21 | @Nullable 22 | private final Map numbers; 23 | 24 | @SerializedName(value = "punctuation") 25 | @Nullable 26 | private final Map punctuation; 27 | 28 | @SerializedName(value = "spelling_and_grammar") 29 | @Nullable 30 | private final Map spellingAndGrammar; 31 | 32 | @SerializedName(value = "style_and_tone") 33 | @Nullable 34 | private final Map styleAndTone; 35 | 36 | @SerializedName(value = "vocabulary") 37 | @Nullable 38 | private final Map vocabulary; 39 | 40 | /** 41 | * Initializes a new {@link ConfiguredRules} containing configuration rules for a style rule list. 42 | * 43 | * @param datesAndTimes Date and time formatting rules. 44 | * @param formatting Text formatting rules. 45 | * @param numbers Number formatting rules. 46 | * @param punctuation Punctuation rules. 47 | * @param spellingAndGrammar Spelling and grammar rules. 48 | * @param styleAndTone Style and tone rules. 49 | * @param vocabulary Vocabulary rules. 50 | */ 51 | public ConfiguredRules( 52 | @Nullable Map datesAndTimes, 53 | @Nullable Map formatting, 54 | @Nullable Map numbers, 55 | @Nullable Map punctuation, 56 | @Nullable Map spellingAndGrammar, 57 | @Nullable Map styleAndTone, 58 | @Nullable Map vocabulary) { 59 | this.datesAndTimes = datesAndTimes; 60 | this.formatting = formatting; 61 | this.numbers = numbers; 62 | this.punctuation = punctuation; 63 | this.spellingAndGrammar = spellingAndGrammar; 64 | this.styleAndTone = styleAndTone; 65 | this.vocabulary = vocabulary; 66 | } 67 | 68 | /** @return Date and time formatting rules. */ 69 | @Nullable 70 | public Map getDatesAndTimes() { 71 | return datesAndTimes; 72 | } 73 | 74 | /** @return Text formatting rules. */ 75 | @Nullable 76 | public Map getFormatting() { 77 | return formatting; 78 | } 79 | 80 | /** @return Number formatting rules. */ 81 | @Nullable 82 | public Map getNumbers() { 83 | return numbers; 84 | } 85 | 86 | /** @return Punctuation rules. */ 87 | @Nullable 88 | public Map getPunctuation() { 89 | return punctuation; 90 | } 91 | 92 | /** @return Spelling and grammar rules. */ 93 | @Nullable 94 | public Map getSpellingAndGrammar() { 95 | return spellingAndGrammar; 96 | } 97 | 98 | /** @return Style and tone rules. */ 99 | @Nullable 100 | public Map getStyleAndTone() { 101 | return styleAndTone; 102 | } 103 | 104 | /** @return Vocabulary rules. */ 105 | @Nullable 106 | public Map getVocabulary() { 107 | return vocabulary; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/DocumentStatus.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | import com.google.gson.annotations.SerializedName; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | /** Status of an in-progress document translation. */ 10 | public class DocumentStatus { 11 | @SerializedName(value = "document_id") 12 | private final String documentId; 13 | 14 | @SerializedName(value = "status") 15 | private final StatusCode status; 16 | 17 | @SerializedName(value = "billed_characters") 18 | private final @Nullable Long billedCharacters; 19 | 20 | @SerializedName(value = "seconds_remaining") 21 | private final @Nullable Long secondsRemaining; 22 | 23 | @SerializedName(value = "error_message") 24 | private final @Nullable String errorMessage; 25 | 26 | /** Status code indicating status of the document translation. */ 27 | public enum StatusCode { 28 | /** Document translation has not yet started, but will begin soon. */ 29 | @SerializedName("queued") 30 | Queued, 31 | /** Document translation is in progress. */ 32 | @SerializedName("translating") 33 | Translating, 34 | /** 35 | * Document translation completed successfully, and the translated document may be downloaded. 36 | */ 37 | @SerializedName("done") 38 | Done, 39 | /** An error occurred during document translation. */ 40 | @SerializedName("error") 41 | Error, 42 | } 43 | 44 | public DocumentStatus( 45 | String documentId, 46 | StatusCode status, 47 | @Nullable Long billedCharacters, 48 | @Nullable Long secondsRemaining, 49 | @Nullable String errorMessage) { 50 | this.documentId = documentId; 51 | this.status = status; 52 | this.billedCharacters = billedCharacters; 53 | this.secondsRemaining = secondsRemaining; 54 | this.errorMessage = errorMessage; 55 | } 56 | 57 | /** @return Document ID of the associated document. */ 58 | public String getDocumentId() { 59 | return documentId; 60 | } 61 | 62 | /** @return Status of the document translation. */ 63 | public StatusCode getStatus() { 64 | return status; 65 | } 66 | 67 | /** 68 | * @return true if no error has occurred during document translation, otherwise 69 | * false. 70 | */ 71 | public boolean ok() { 72 | return status != null && status != StatusCode.Error; 73 | } 74 | 75 | /** 76 | * @return true if document translation has completed successfully, otherwise 77 | * false. 78 | */ 79 | public boolean done() { 80 | return status != null && status == StatusCode.Done; 81 | } 82 | 83 | /** 84 | * @return Number of seconds remaining until translation is complete if available, otherwise 85 | * null. Only available while document is in translating state. 86 | */ 87 | public @Nullable Long getSecondsRemaining() { 88 | return secondsRemaining; 89 | } 90 | 91 | /** 92 | * @return Number of characters billed for the translation of this document if available, 93 | * otherwise null. Only available after document translation is finished and the 94 | * status is {@link StatusCode#Done}, otherwise null. 95 | */ 96 | public @Nullable Long getBilledCharacters() { 97 | return billedCharacters; 98 | } 99 | 100 | /** @return Short description of the error if available, otherwise null. */ 101 | public @Nullable String getErrorMessage() { 102 | return errorMessage; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/StyleRuleInfo.java: -------------------------------------------------------------------------------- 1 | // Copyright 2025 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | import com.google.gson.annotations.*; 7 | import java.util.*; 8 | import org.jetbrains.annotations.*; 9 | 10 | /** Information about a style rule list. */ 11 | public class StyleRuleInfo { 12 | @SerializedName(value = "style_id") 13 | private final String styleId; 14 | 15 | @SerializedName(value = "name") 16 | private final String name; 17 | 18 | @SerializedName(value = "creation_time") 19 | private final Date creationTime; 20 | 21 | @SerializedName(value = "updated_time") 22 | private final Date updatedTime; 23 | 24 | @SerializedName(value = "language") 25 | private final String language; 26 | 27 | @SerializedName(value = "version") 28 | private final int version; 29 | 30 | @SerializedName(value = "configured_rules") 31 | @Nullable 32 | private final ConfiguredRules configuredRules; 33 | 34 | @SerializedName(value = "custom_instructions") 35 | @Nullable 36 | private final List customInstructions; 37 | 38 | /** 39 | * Initializes a new {@link StyleRuleInfo} containing information about a style rule list. 40 | * 41 | * @param styleId Unique ID assigned to the style rule list. 42 | * @param name User-defined name assigned to the style rule list. 43 | * @param creationTime Timestamp when the style rule list was created. 44 | * @param updatedTime Timestamp when the style rule list was last updated. 45 | * @param language Language code for the style rule list. 46 | * @param version Version number of the style rule list. 47 | * @param configuredRules The predefined rules that have been enabled. 48 | * @param customInstructions Optional list of custom instructions. 49 | */ 50 | public StyleRuleInfo( 51 | String styleId, 52 | String name, 53 | Date creationTime, 54 | Date updatedTime, 55 | String language, 56 | int version, 57 | @Nullable ConfiguredRules configuredRules, 58 | @Nullable List customInstructions) { 59 | this.styleId = styleId; 60 | this.name = name; 61 | this.creationTime = creationTime; 62 | this.updatedTime = updatedTime; 63 | this.language = language; 64 | this.version = version; 65 | this.configuredRules = configuredRules; 66 | this.customInstructions = customInstructions; 67 | } 68 | 69 | /** @return Unique ID assigned to the style rule list. */ 70 | public String getStyleId() { 71 | return styleId; 72 | } 73 | 74 | /** @return User-defined name assigned to the style rule list. */ 75 | public String getName() { 76 | return name; 77 | } 78 | 79 | /** @return Timestamp when the style rule list was created. */ 80 | public Date getCreationTime() { 81 | return creationTime; 82 | } 83 | 84 | /** @return Timestamp when the style rule list was last updated. */ 85 | public Date getUpdatedTime() { 86 | return updatedTime; 87 | } 88 | 89 | /** @return Language code for the style rule list. */ 90 | public String getLanguage() { 91 | return language; 92 | } 93 | 94 | /** @return Version number of the style rule list. */ 95 | public int getVersion() { 96 | return version; 97 | } 98 | 99 | /** @return The predefined rules that have been enabled. */ 100 | @Nullable 101 | public ConfiguredRules getConfiguredRules() { 102 | return configuredRules; 103 | } 104 | 105 | /** @return Optional list of custom instructions. */ 106 | @Nullable 107 | public List getCustomInstructions() { 108 | return customInstructions; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/Usage.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | import java.util.function.BiConsumer; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | /** 10 | * Information about DeepL account usage for the current billing period, for example the number of 11 | * characters translated. 12 | * 13 | *

Depending on the account type, some usage types will be omitted. See the API documentation for more information. 15 | */ 16 | public class Usage { 17 | private final @Nullable Detail character; 18 | private final @Nullable Detail document; 19 | private final @Nullable Detail teamDocument; 20 | 21 | /** The character usage if included for the account type, or null. */ 22 | public @Nullable Detail getCharacter() { 23 | return character; 24 | } 25 | 26 | /** The document usage if included for the account type, or null. */ 27 | public @Nullable Detail getDocument() { 28 | return document; 29 | } 30 | 31 | /** The team document usage if included for the account type, or null. */ 32 | public @Nullable Detail getTeamDocument() { 33 | return teamDocument; 34 | } 35 | 36 | /** Stores the amount used and maximum amount for one usage type. */ 37 | public static class Detail { 38 | private final long count; 39 | private final long limit; 40 | 41 | public Detail(long count, long limit) { 42 | this.count = count; 43 | this.limit = limit; 44 | } 45 | 46 | /** @return The currently used number of items for this usage type. */ 47 | public long getCount() { 48 | return count; 49 | } 50 | 51 | /** @return The maximum permitted number of items for this usage type. */ 52 | public long getLimit() { 53 | return limit; 54 | } 55 | 56 | /** 57 | * @return true if the amount used meets or exceeds the limit, otherwise 58 | * false. 59 | */ 60 | public boolean limitReached() { 61 | return getCount() >= getLimit(); 62 | } 63 | 64 | @Override 65 | public String toString() { 66 | return getCount() + " of " + getLimit(); 67 | } 68 | } 69 | 70 | public Usage( 71 | @Nullable Detail character, @Nullable Detail document, @Nullable Detail teamDocument) { 72 | this.character = character; 73 | this.document = document; 74 | this.teamDocument = teamDocument; 75 | } 76 | 77 | /** 78 | * @return true if any of the usage types included for the account type have been 79 | * reached, otherwise false. 80 | */ 81 | public boolean anyLimitReached() { 82 | return (getCharacter() != null && getCharacter().limitReached()) 83 | || (getDocument() != null && getDocument().limitReached()) 84 | || (getTeamDocument() != null && getTeamDocument().limitReached()); 85 | } 86 | 87 | /** 88 | * Returns a string representing the usage. This function is for diagnostic purposes only; the 89 | * content of the returned string is exempt from backwards compatibility. 90 | * 91 | * @return A string containing the usage for this billing period. 92 | */ 93 | @Override 94 | public String toString() { 95 | StringBuilder sb = new StringBuilder("Usage this billing period:"); 96 | 97 | BiConsumer addLabelledDetail = 98 | (label, detail) -> { 99 | if (detail != null) { 100 | sb.append("\n").append(label).append(": ").append(detail); 101 | } 102 | }; 103 | 104 | addLabelledDetail.accept("Characters", getCharacter()); 105 | addLabelledDetail.accept("Documents", getDocument()); 106 | addLabelledDetail.accept("Team documents", getTeamDocument()); 107 | return sb.toString(); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /deepl-java/src/test/java/com/deepl/api/SessionOptions.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | import java.time.Duration; 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | import java.util.UUID; 10 | 11 | public class SessionOptions { 12 | // Mock server session options 13 | public Integer noResponse; 14 | public Integer respondWith429; 15 | public Long initCharacterLimit; 16 | public Long initDocumentLimit; 17 | public Long initTeamDocumentLimit; 18 | public Integer documentFailure; 19 | public Duration documentQueueTime; 20 | public Duration documentTranslateTime; 21 | public Boolean expectProxy; 22 | 23 | public boolean randomAuthKey; 24 | 25 | SessionOptions() { 26 | randomAuthKey = false; 27 | } 28 | 29 | public Map createSessionHeaders() { 30 | Map headers = new HashMap<>(); 31 | 32 | String uuid = UUID.randomUUID().toString(); 33 | headers.put("mock-server-session", "deepl-java-test/" + uuid); 34 | 35 | if (noResponse != null) { 36 | headers.put("mock-server-session-no-response-count", noResponse.toString()); 37 | } 38 | if (respondWith429 != null) { 39 | headers.put("mock-server-session-429-count", respondWith429.toString()); 40 | } 41 | if (initCharacterLimit != null) { 42 | headers.put("mock-server-session-init-character-limit", initCharacterLimit.toString()); 43 | } 44 | if (initDocumentLimit != null) { 45 | headers.put("mock-server-session-init-document-limit", initDocumentLimit.toString()); 46 | } 47 | if (initTeamDocumentLimit != null) { 48 | headers.put("mock-server-session-init-team-document-limit", initTeamDocumentLimit.toString()); 49 | } 50 | if (documentFailure != null) { 51 | headers.put("mock-server-session-doc-failure", documentFailure.toString()); 52 | } 53 | if (documentQueueTime != null) { 54 | headers.put( 55 | "mock-server-session-doc-queue-time", Long.toString(documentQueueTime.toMillis())); 56 | } 57 | if (documentTranslateTime != null) { 58 | headers.put( 59 | "mock-server-session-doc-translate-time", 60 | Long.toString(documentTranslateTime.toMillis())); 61 | } 62 | if (expectProxy != null) { 63 | headers.put("mock-server-session-expect-proxy", expectProxy ? "1" : "0"); 64 | } 65 | 66 | return headers; 67 | } 68 | 69 | public SessionOptions setNoResponse(int noResponse) { 70 | this.noResponse = noResponse; 71 | return this; 72 | } 73 | 74 | public SessionOptions setRespondWith429(int respondWith429) { 75 | this.respondWith429 = respondWith429; 76 | return this; 77 | } 78 | 79 | public SessionOptions setInitCharacterLimit(long initCharacterLimit) { 80 | this.initCharacterLimit = initCharacterLimit; 81 | return this; 82 | } 83 | 84 | public SessionOptions setInitDocumentLimit(long initDocumentLimit) { 85 | this.initDocumentLimit = initDocumentLimit; 86 | return this; 87 | } 88 | 89 | public SessionOptions setInitTeamDocumentLimit(long initTeamDocumentLimit) { 90 | this.initTeamDocumentLimit = initTeamDocumentLimit; 91 | return this; 92 | } 93 | 94 | public SessionOptions setDocumentFailure(int documentFailure) { 95 | this.documentFailure = documentFailure; 96 | return this; 97 | } 98 | 99 | public SessionOptions setDocumentQueueTime(Duration documentQueueTime) { 100 | this.documentQueueTime = documentQueueTime; 101 | return this; 102 | } 103 | 104 | public SessionOptions setDocumentTranslateTime(Duration documentTranslateTime) { 105 | this.documentTranslateTime = documentTranslateTime; 106 | return this; 107 | } 108 | 109 | public SessionOptions setExpectProxy(boolean expectProxy) { 110 | this.expectProxy = expectProxy; 111 | return this; 112 | } 113 | 114 | public SessionOptions withRandomAuthKey() { 115 | this.randomAuthKey = true; 116 | return this; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/http/HttpContent.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api.http; 5 | 6 | import com.deepl.api.*; 7 | import com.deepl.api.utils.*; 8 | import java.io.*; 9 | import java.net.*; 10 | import java.nio.charset.*; 11 | import java.util.*; 12 | import org.jetbrains.annotations.*; 13 | 14 | public class HttpContent { 15 | private static final String LINE_BREAK = "\r\n"; 16 | private final String contentType; 17 | private final byte[] content; 18 | 19 | private HttpContent(String contentType, byte[] content) { 20 | this.contentType = contentType; 21 | this.content = content; 22 | } 23 | 24 | public byte[] getContent() { 25 | return content; 26 | } 27 | 28 | public String getContentType() { 29 | return contentType; 30 | } 31 | 32 | public static HttpContent buildFormURLEncodedContent( 33 | @Nullable Iterable> params) throws DeepLException { 34 | StringBuilder sb = new StringBuilder(); 35 | if (params != null) { 36 | for (KeyValuePair pair : params) { 37 | if (sb.length() != 0) sb.append("&"); 38 | sb.append(urlEncode(pair.getKey())); 39 | sb.append("="); 40 | sb.append(urlEncode(pair.getValue())); 41 | } 42 | } 43 | return new HttpContent( 44 | "application/x-www-form-urlencoded", sb.toString().getBytes(StandardCharsets.UTF_8)); 45 | } 46 | 47 | private static String urlEncode(String value) throws DeepLException { 48 | try { 49 | return URLEncoder.encode(value, StandardCharsets.UTF_8.name()); 50 | } catch (UnsupportedEncodingException exception) { 51 | throw new DeepLException("Error while URL-encoding request", exception); 52 | } 53 | } 54 | 55 | public static HttpContent buildMultipartFormDataContent( 56 | Iterable> params) throws Exception { 57 | String boundary = UUID.randomUUID().toString(); 58 | return buildMultipartFormDataContent(params, boundary); 59 | } 60 | 61 | private static HttpContent buildMultipartFormDataContent( 62 | Iterable> params, String boundary) throws Exception { 63 | try (ByteArrayOutputStream stream = new ByteArrayOutputStream(); 64 | OutputStreamWriter osw = new OutputStreamWriter(stream, StandardCharsets.UTF_8); 65 | PrintWriter writer = new PrintWriter(osw)) { 66 | 67 | if (params != null) { 68 | for (KeyValuePair entry : params) { 69 | String key = entry.getKey(); 70 | Object value = entry.getValue(); 71 | if (entry.getValue() instanceof NamedStream) { 72 | NamedStream namedStream = (NamedStream) entry.getValue(); 73 | String probableContentType = 74 | URLConnection.guessContentTypeFromName(namedStream.getFileName()); 75 | writer.append("--").append(boundary).append(LINE_BREAK); 76 | writer 77 | .append("Content-Disposition: form-data; name=\"") 78 | .append(key) 79 | .append("\"; filename=\"") 80 | .append(namedStream.getFileName()) 81 | .append("\"") 82 | .append(LINE_BREAK); 83 | writer.append("Content-Type: ").append(probableContentType).append(LINE_BREAK); 84 | writer.append("Content-Transfer-Encoding: binary").append(LINE_BREAK); 85 | writer.append(LINE_BREAK); 86 | writer.flush(); 87 | 88 | StreamUtil.transferTo(namedStream.getInputStream(), stream); 89 | 90 | writer.append(LINE_BREAK); 91 | writer.flush(); 92 | } else if (value instanceof String) { 93 | writer.append("--").append(boundary).append(LINE_BREAK); 94 | writer 95 | .append("Content-Disposition: form-data; name=\"") 96 | .append(key) 97 | .append("\"") 98 | .append(LINE_BREAK); 99 | writer.append(LINE_BREAK); 100 | writer.append((String) value).append(LINE_BREAK); 101 | writer.flush(); 102 | } else { 103 | throw new Exception("Unknown argument type: " + value.getClass().getName()); 104 | } 105 | } 106 | } 107 | 108 | writer.append("--").append(boundary).append("--").append(LINE_BREAK); 109 | writer.flush(); 110 | writer.close(); 111 | return new HttpContent("multipart/form-data; boundary=" + boundary, stream.toByteArray()); 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/parsing/Parser.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api.parsing; 5 | 6 | import com.deepl.api.*; 7 | import com.google.gson.*; 8 | import com.google.gson.reflect.*; 9 | import java.lang.reflect.*; 10 | import java.util.*; 11 | import org.jetbrains.annotations.*; 12 | 13 | /** 14 | * Parsing functions for responses from the DeepL API. 15 | * 16 | *

This class is internal; you should not use this class directly. 17 | */ 18 | public class Parser { 19 | private final Gson gson; 20 | 21 | public Parser() { 22 | GsonBuilder gsonBuilder = new GsonBuilder(); 23 | gsonBuilder.registerTypeAdapter(TextResult.class, new TextResultDeserializer()); 24 | gsonBuilder.registerTypeAdapter(WriteResult.class, new WriteResultDeserializer()); 25 | gsonBuilder.registerTypeAdapter(Language.class, new LanguageDeserializer()); 26 | gsonBuilder.registerTypeAdapter(Usage.class, new UsageDeserializer()); 27 | gson = gsonBuilder.create(); 28 | } 29 | 30 | public List parseTextResult(String json) { 31 | TextResponse result = gson.fromJson(json, TextResponse.class); 32 | return result.translations; 33 | } 34 | 35 | public List parseWriteResult(String json) { 36 | WriteResponse result = gson.fromJson(json, WriteResponse.class); 37 | return result.improvements; 38 | } 39 | 40 | public Usage parseUsage(String json) { 41 | return gson.fromJson(json, Usage.class); 42 | } 43 | 44 | public List parseLanguages(String json) { 45 | Type languageListType = new TypeToken>() {}.getType(); 46 | return gson.fromJson(json, languageListType); 47 | } 48 | 49 | public List parseGlossaryLanguageList(String json) { 50 | return gson.fromJson(json, GlossaryLanguagesResponse.class).getSupportedLanguages(); 51 | } 52 | 53 | public DocumentStatus parseDocumentStatus(String json) { 54 | return gson.fromJson(json, DocumentStatus.class); 55 | } 56 | 57 | public DocumentHandle parseDocumentHandle(String json) { 58 | return gson.fromJson(json, DocumentHandle.class); 59 | } 60 | 61 | public GlossaryInfo parseGlossaryInfo(String json) { 62 | return gson.fromJson(json, GlossaryInfo.class); 63 | } 64 | 65 | public MultilingualGlossaryInfo parseMultilingualGlossaryInfo(String json) { 66 | return gson.fromJson(json, MultilingualGlossaryInfo.class); 67 | } 68 | 69 | public MultilingualGlossaryDictionaryListResponse parseMultilingualGlossaryDictionaryListResponse( 70 | String json) { 71 | return gson.fromJson(json, MultilingualGlossaryDictionaryListResponse.class); 72 | } 73 | 74 | public List parseGlossaryInfoList(String json) { 75 | GlossaryListResponse result = gson.fromJson(json, GlossaryListResponse.class); 76 | return result.getGlossaries(); 77 | } 78 | 79 | public List parseMultilingualGlossaryInfoList(String json) { 80 | MultilingualGlossaryListResponse result = 81 | gson.fromJson(json, MultilingualGlossaryListResponse.class); 82 | return result.getGlossaries(); 83 | } 84 | 85 | public MultilingualGlossaryDictionaryInfo parseMultilingualGlossaryDictionaryInfo(String json) { 86 | return gson.fromJson(json, MultilingualGlossaryDictionaryInfo.class); 87 | } 88 | 89 | public List parseStyleRuleInfoList(String json) { 90 | StyleRuleListResponse result = gson.fromJson(json, StyleRuleListResponse.class); 91 | return result.getStyleRules(); 92 | } 93 | 94 | public String parseErrorMessage(String json) { 95 | ErrorResponse response = gson.fromJson(json, ErrorResponse.class); 96 | 97 | if (response != null) { 98 | return response.getErrorMessage(); 99 | } else { 100 | return ""; 101 | } 102 | } 103 | 104 | static @Nullable Integer getAsIntOrNull(JsonObject jsonObject, String parameterName) { 105 | if (!jsonObject.has(parameterName)) return null; 106 | return jsonObject.get(parameterName).getAsInt(); 107 | } 108 | 109 | static @Nullable Long getAsLongOrNull(JsonObject jsonObject, String parameterName) { 110 | if (!jsonObject.has(parameterName)) return null; 111 | return jsonObject.get(parameterName).getAsLong(); 112 | } 113 | 114 | static @Nullable String getAsStringOrNull(JsonObject jsonObject, String parameterName) { 115 | if (!jsonObject.has(parameterName)) return null; 116 | return jsonObject.get(parameterName).getAsString(); 117 | } 118 | 119 | static @Nullable Boolean getAsBooleanOrNull(JsonObject jsonObject, String parameterName) { 120 | if (!jsonObject.has(parameterName)) return null; 121 | return jsonObject.get(parameterName).getAsBoolean(); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/TranslatorOptions.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | import java.net.Proxy; 7 | import java.time.Duration; 8 | import java.util.Map; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | /** 12 | * Options to control translator behaviour. These options may be provided to the {@link Translator} 13 | * constructor. 14 | * 15 | *

All properties have corresponding setters in fluent-style, so the following is possible: 16 | * 17 | * TranslatorOptions options = new TranslatorOptions() 18 | * .setTimeout(Duration.ofSeconds(1)).setMaxRetries(2); 19 | * 20 | */ 21 | public class TranslatorOptions { 22 | private int maxRetries = 5; 23 | private Duration timeout = Duration.ofSeconds(10); 24 | @Nullable private Proxy proxy = null; 25 | @Nullable private Map headers = null; 26 | @Nullable private String serverUrl = null; 27 | private boolean sendPlatformInfo = true; 28 | @Nullable private AppInfo appInfo = null; 29 | @Nullable protected DeepLApiVersion apiVersion = null; 30 | 31 | /** @deprecated Use {@link DeepLClient} instead. */ 32 | @Deprecated 33 | public TranslatorOptions() { 34 | apiVersion = DeepLApiVersion.VERSION_2; 35 | } 36 | 37 | /** 38 | * Set the maximum number of failed attempts that {@link Translator} will retry, per request. By 39 | * default, 5 retries are made. Note: only errors due to transient conditions are retried. 40 | */ 41 | public TranslatorOptions setMaxRetries(int maxRetries) { 42 | this.maxRetries = maxRetries; 43 | return this; 44 | } 45 | 46 | /** Set the connection timeout used for each HTTP request retry, the default is 10 seconds. */ 47 | public TranslatorOptions setTimeout(Duration timeout) { 48 | this.timeout = timeout; 49 | return this; 50 | } 51 | 52 | /** 53 | * Set the proxy to use for HTTP requests. By default, this value is null and no 54 | * proxy will be used. 55 | */ 56 | public TranslatorOptions setProxy(Proxy proxy) { 57 | this.proxy = proxy; 58 | return this; 59 | } 60 | 61 | /** 62 | * Set HTTP headers attached to every HTTP request. By default, this value is null 63 | * and no extra headers are used. Note that in the {@link Translator} constructor the headers for 64 | * Authorization and User-Agent are added, unless they are overridden in this option. 65 | */ 66 | public TranslatorOptions setHeaders(Map headers) { 67 | this.headers = headers; 68 | return this; 69 | } 70 | 71 | /** 72 | * Set the base URL for DeepL API that may be overridden for testing purposes. By default, this 73 | * value is null and the correct DeepL API base URL is selected based on the API 74 | * account type (free or paid). 75 | */ 76 | public TranslatorOptions setServerUrl(String serverUrl) { 77 | this.serverUrl = serverUrl; 78 | return this; 79 | } 80 | 81 | /** 82 | * Set whether to send basic platform information with each API call to improve DeepL products. 83 | * Defaults to `true`, set to `false` to opt out. This option will be overriden if a 84 | * `'User-agent'` header is present in this objects `headers`. 85 | */ 86 | public TranslatorOptions setSendPlatformInfo(boolean sendPlatformInfo) { 87 | this.sendPlatformInfo = sendPlatformInfo; 88 | return this; 89 | } 90 | 91 | /** 92 | * Set an identifier and a version for the program/plugin that uses this Client Library. Example: 93 | * `Translator t = new Translator(myAuthKey, new TranslatorOptions() 94 | * .setAppInfo('deepl-hadoop-plugin', '1.2.0')) 95 | */ 96 | public TranslatorOptions setAppInfo(String appName, String appVersion) { 97 | this.appInfo = new AppInfo(appName, appVersion); 98 | return this; 99 | } 100 | 101 | /** Gets the current maximum number of retries. */ 102 | public int getMaxRetries() { 103 | return maxRetries; 104 | } 105 | 106 | /** Gets the current maximum request timeout. */ 107 | public Duration getTimeout() { 108 | return timeout; 109 | } 110 | 111 | /** Gets the current proxy. */ 112 | public @Nullable Proxy getProxy() { 113 | return proxy; 114 | } 115 | 116 | /** Gets the current HTTP headers. */ 117 | public @Nullable Map getHeaders() { 118 | return headers; 119 | } 120 | 121 | /** Gets the current custom server URL. */ 122 | public @Nullable String getServerUrl() { 123 | return serverUrl; 124 | } 125 | 126 | /** Gets the `sendPlatformInfo` option */ 127 | public boolean getSendPlatformInfo() { 128 | return sendPlatformInfo; 129 | } 130 | 131 | /** Gets the `appInfo` identifiers */ 132 | public @Nullable AppInfo getAppInfo() { 133 | return appInfo; 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /.github/workflows/run_ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | schedule: 9 | - cron: '20 0 * * *' 10 | 11 | env: 12 | GRADLE_OPTS: "-Dorg.gradle.daemon=false" 13 | JAVA_TOOL_OPTIONS: "" 14 | SECRET_DETECTION_JSON_REPORT_FILE: "gitleaks.json" 15 | 16 | jobs: 17 | spotless: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@v4 22 | - name: Spotless check 23 | run: ./gradlew spotlessCheck 24 | 25 | license_check: 26 | runs-on: ubuntu-latest 27 | steps: 28 | - name: Checkout 29 | uses: actions/checkout@v4 30 | - name: License check 31 | run: | 32 | ./license_checker.sh '*.java' | tee license_check_output.txt 33 | [ ! -s license_check_output.txt ] 34 | 35 | secret_detection: 36 | runs-on: ubuntu-latest 37 | steps: 38 | - name: Checkout 39 | uses: actions/checkout@v4 40 | with: 41 | fetch-depth: 0 42 | - name: Install and run secret detection 43 | run: | 44 | wget https://github.com/gitleaks/gitleaks/releases/download/v8.18.4/gitleaks_8.18.4_linux_x64.tar.gz 45 | tar -xzf gitleaks_8.18.4_linux_x64.tar.gz 46 | EXITCODE=0 47 | ./gitleaks detect -r ${SECRET_DETECTION_JSON_REPORT_FILE} --source . --log-opts="--all --full-history" || EXITCODE=$? 48 | if [[ $EXITCODE -ne 0 ]]; then 49 | exit $EXITCODE 50 | fi 51 | - name: Upload secret detection artifact 52 | uses: actions/upload-artifact@v4 53 | with: 54 | name: secret-detection-results 55 | path: gitleaks.json 56 | 57 | 58 | build: 59 | runs-on: ubuntu-latest 60 | steps: 61 | - name: Checkout 62 | uses: actions/checkout@v4 63 | - name: Build 64 | run: ./gradlew assemble 65 | - name: Upload artifacts 66 | uses: actions/upload-artifact@v4 67 | with: 68 | name: build-artifacts 69 | path: deepl-java/build/ 70 | 71 | 72 | 73 | # Test and `gradlew publish` stage are disabled for now. Code needs to be tested 74 | 75 | ####################################################### 76 | # test_cl: 77 | # runs-on: ${{ matrix.docker-image }} 78 | # strategy: 79 | # matrix: 80 | # docker-image: 81 | # - 'eclipse-temurin:8-focal' 82 | # - 'openjdk:8-alpine' 83 | # - 'eclipse-temurin:11-alpine' 84 | # - 'eclipse-temurin:17-alpine' 85 | # - 'eclipse-temurin:18-alpine' 86 | # - 'eclipse-temurin:19-alpine' 87 | # use-mock-server: 88 | # - '' 89 | # - 'use mock server' 90 | # env: 91 | # DEEPL_SERVER_URL: http://deepl-mock:3000 92 | # DEEPL_MOCK_SERVER_PORT: 3000 93 | # DEEPL_PROXY_URL: http://deepl-mock:3001 94 | # DEEPL_MOCK_PROXY_SERVER_PORT: 3001 95 | # steps: 96 | # - name: Checkout 97 | # uses: actions/checkout@v4 98 | # - name: Start mock server 99 | # if: ${{ matrix.use-mock-server == 'use mock server' }} 100 | # run: docker run --name deepl-mock -d -p 3000:3000 deepl-mock 101 | # - name: Start mock proxy server 102 | # if: ${{ matrix.use-mock-server == 'use mock server' }} 103 | # run: docker run --name deepl-mock-proxy -d -p 3001:3001 deepl-mock-proxy 104 | # - name: Test 105 | # run: | 106 | # if [[ ! -z "${{ matrix.use-mock-server }}" ]]; then 107 | # echo "Using mock server" 108 | # export DEEPL_SERVER_URL=http://deepl-mock:3000 109 | # export DEEPL_MOCK_SERVER_PORT=3000 110 | # export DEEPL_PROXY_URL=http://deepl-mock:3001 111 | # export DEEPL_MOCK_PROXY_SERVER_PORT=3001 112 | # fi 113 | # ./gradlew test -DrunV1ApiTests=true 114 | # - name: Stop mock proxy server 115 | # if: ${{ matrix.use-mock-server == 'use mock server' }} 116 | # run: docker stop deepl-mock-proxy 117 | # - name: Stop mock server 118 | # if: ${{ matrix.use-mock-server == 'use mock server' }} 119 | # run: docker stop deepl-mock 120 | # - name: Upload test results 121 | # uses: actions/upload-artifact@v4 122 | # with: 123 | # name: test-results 124 | # path: deepl-java/build/reports/tests/test 125 | 126 | # test_examples: 127 | # runs-on: ${{ matrix.docker-image }} 128 | # strategy: 129 | # matrix: 130 | # docker-image: 131 | # - 'maven:3.9' 132 | # - 'maven:3.8-openjdk-18-slim' 133 | # - 'maven:3.8-openjdk-8-slim' 134 | # - 'maven:3.9-sapmachine-17' 135 | # - 'maven:3.9-eclipse-temurin-8' 136 | # - 'maven:3.9-eclipse-temurin-21' 137 | # steps: 138 | # - name: Checkout 139 | # uses: actions/checkout@v4 140 | # - name: Test examples 141 | # run: | 142 | # cd examples/maven/deepl-test-app 143 | # mvn install -B -PbuildProject -l mvn_build.log 144 | # mvn verify -PrunIntegrationTests 145 | # - name: Upload test results 146 | # uses: actions/upload-artifact@v4 147 | # with: 148 | # name: test-results 149 | # path: examples/maven/deepl-test-app/mvn_build.log 150 | 151 | # publish: 152 | # runs-on: ubuntu-latest 153 | # needs: [ build ] 154 | # if: startsWith(github.ref, 'refs/tags/v') 155 | # steps: 156 | # - name: Checkout 157 | # uses: actions/checkout@v4 158 | # - name: Publish 159 | # run: ./gradlew publish 160 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, caste, color, religion, or sexual 10 | identity and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the overall 26 | community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or advances of 31 | any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email address, 35 | without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | [open-source@deepl.com](mailto:open-source@deepl.com). 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series of 86 | actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or permanent 93 | ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within the 113 | community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.1, available at 119 | [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. 120 | 121 | Community Impact Guidelines were inspired by 122 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. 123 | 124 | For answers to common questions about this code of conduct, see the FAQ at 125 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at 126 | [https://www.contributor-covenant.org/translations][translations]. 127 | 128 | [homepage]: https://www.contributor-covenant.org 129 | [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html 130 | [Mozilla CoC]: https://github.com/mozilla/diversity 131 | [FAQ]: https://www.contributor-covenant.org/faq 132 | [translations]: https://www.contributor-covenant.org/translations 133 | 134 | -------------------------------------------------------------------------------- /deepl-java/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import java.net.HttpURLConnection 2 | import java.net.URL 3 | import java.util.Base64 4 | 5 | plugins { 6 | `java-library` 7 | `maven-publish` 8 | signing 9 | id("com.diffplug.spotless") version "6.8.0" 10 | } 11 | 12 | group = "com.deepl.api" 13 | version = "1.14.0" 14 | 15 | val sharedManifest = the().manifest { 16 | attributes ( 17 | "Implementation-Title" to "Gradle", 18 | "Implementation-Version" to version 19 | ) 20 | } 21 | java { 22 | sourceCompatibility = JavaVersion.VERSION_1_8 23 | targetCompatibility = JavaVersion.VERSION_1_8 24 | } 25 | 26 | repositories { 27 | mavenCentral() 28 | } 29 | 30 | dependencies { 31 | implementation("org.jetbrains:annotations:20.1.0") 32 | testImplementation("org.junit.jupiter:junit-jupiter:5.10.0") 33 | testImplementation("org.mockito:mockito-inline:4.11.0") 34 | implementation("org.apache.httpcomponents:httpclient:4.5.14") { because("java.net.HttpURLConnection does not support PATCH") } 35 | 36 | // implementation("com.google.guava:guava:30.1.1-jre") 37 | implementation("com.google.code.gson:gson:2.10.1") 38 | } 39 | 40 | 41 | tasks.named("test") { 42 | useJUnitPlatform() 43 | } 44 | 45 | spotless { 46 | java { 47 | googleJavaFormat("1.7") 48 | removeUnusedImports() 49 | } 50 | } 51 | 52 | tasks.register("sourcesJar") { 53 | archiveClassifier.set("sources") 54 | from(sourceSets.main.get().allJava) 55 | manifest = project.the().manifest { 56 | from(sharedManifest) 57 | } 58 | } 59 | 60 | tasks.register("javadocJar") { 61 | archiveClassifier.set("javadoc") 62 | from(tasks.javadoc.get().destinationDir) 63 | manifest = project.the().manifest { 64 | from(sharedManifest) 65 | } 66 | } 67 | 68 | publishing { 69 | repositories { 70 | maven { 71 | val mavenUploadUsername: String? by project 72 | val mavenUploadPassword: String? by project 73 | name = "MavenCentral" 74 | val releasesRepoUrl = "https://ossrh-staging-api.central.sonatype.com/service/local/staging/deploy/maven2/" 75 | val snapshotsRepoUrl = "https://central.sonatype.com/repository/maven-snapshots/" 76 | url = uri(if (version.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl) 77 | credentials { 78 | username = mavenUploadUsername 79 | password = mavenUploadPassword 80 | } 81 | } 82 | } 83 | publications { 84 | create("mavenJava") { 85 | from(components["java"]) 86 | artifact(tasks["sourcesJar"]) 87 | artifact(tasks["javadocJar"]) 88 | pom { 89 | name.set("deepl-java") 90 | description.set("DeepL API Java Client Library") 91 | url.set("https://www.github.com/DeepLcom/deepl-java") 92 | properties.set(mapOf( 93 | "java.version" to "1.8", 94 | "project.build.sourceEncoding" to "UTF-8", 95 | "project.reporting.outputEncoding" to "UTF-8" 96 | )) 97 | licenses { 98 | license { 99 | name.set("MIT License") 100 | url.set("https://www.opensource.org/licenses/mit-license.php") 101 | } 102 | } 103 | developers { 104 | developer { 105 | id.set("deepl") 106 | name.set("DeepL SE") 107 | email.set("open-source@deepl.com") 108 | } 109 | } 110 | organization { 111 | name.set("DeepL SE") 112 | url.set("https://www.deepl.com") 113 | } 114 | scm { 115 | connection.set("scm:git:git://github.com/DeepLcom/deepl-java.git") 116 | developerConnection.set("scm:git:ssh://github.com/DeepLcom/deepl-java.git") 117 | url.set("https://www.github.com/DeepLcom/deepl-java") 118 | } 119 | } 120 | } 121 | } 122 | } 123 | 124 | signing { 125 | val signingKey: String? by project 126 | val signingPassword: String? by project 127 | useInMemoryPgpKeys(signingKey, signingPassword) 128 | sign(publishing.publications["mavenJava"]) 129 | } 130 | 131 | tasks.register("triggerDeployment") { 132 | doLast { 133 | val mavenUploadUsername: String? by project 134 | val mavenUploadPassword: String? by project 135 | val namespace = "com.deepl.api" 136 | val url = "https://ossrh-staging-api.central.sonatype.com/manual/upload/defaultRepository/$namespace?publishing_type=automatic" 137 | 138 | println("Triggering deployment for namespace: $namespace") 139 | println("Endpoint: $url") 140 | 141 | val credentials = "$mavenUploadUsername:$mavenUploadPassword" 142 | val encodedCredentials = Base64.getEncoder().encodeToString(credentials.toByteArray()) 143 | 144 | val connection = URL(url).openConnection() as HttpURLConnection 145 | connection.requestMethod = "POST" 146 | connection.setRequestProperty("Authorization", "Bearer $encodedCredentials") 147 | connection.doOutput = true 148 | connection.outputStream.write(byteArrayOf()) 149 | 150 | val responseCode = connection.responseCode 151 | val response = if (responseCode == 200) { 152 | connection.inputStream.bufferedReader().readText() 153 | } else { 154 | connection.errorStream?.bufferedReader()?.readText() ?: "No error response" 155 | } 156 | 157 | println("Response code: $responseCode") 158 | if (response.isNotBlank()) { 159 | println("Response body: $response") 160 | } 161 | 162 | if (responseCode != 200) { 163 | throw GradleException("Failed to trigger deployment: HTTP $responseCode - $response") 164 | } 165 | 166 | println("Deployment triggered successfully") 167 | } 168 | } 169 | 170 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/LanguageCode.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | import java.util.*; 7 | 8 | /** 9 | * Language codes for the languages currently supported by DeepL translation. New languages may be 10 | * added in the future; to retrieve the currently supported languages use {@link 11 | * Translator#getSourceLanguages()} and {@link Translator#getTargetLanguages()}. 12 | */ 13 | public class LanguageCode { 14 | /** Arabic (MSA) language code, may be used as source or target language */ 15 | public static final String Arabic = "ar"; 16 | 17 | /** Bulgarian language code, may be used as source or target language. */ 18 | public static final String Bulgarian = "bg"; 19 | 20 | /** Czech language code, may be used as source or target language. */ 21 | public static final String Czech = "cs"; 22 | 23 | /** Danish language code, may be used as source or target language. */ 24 | public static final String Danish = "da"; 25 | 26 | /** German language code, may be used as source or target language. */ 27 | public static final String German = "de"; 28 | 29 | /** Greek language code, may be used as source or target language. */ 30 | public static final String Greek = "el"; 31 | 32 | /** 33 | * English language code, may only be used as a source language. In input texts, this language 34 | * code supports all English variants. 35 | */ 36 | public static final String English = "en"; 37 | 38 | /** British English language code, may only be used as a target language. */ 39 | public static final String EnglishBritish = "en-GB"; 40 | 41 | /** American English language code, may only be used as a target language. */ 42 | public static final String EnglishAmerican = "en-US"; 43 | 44 | /** Spanish language code, may be used as source or target language. */ 45 | public static final String Spanish = "es"; 46 | 47 | /** Estonian language code, may be used as source or target language. */ 48 | public static final String Estonian = "et"; 49 | 50 | /** Finnish language code, may be used as source or target language. */ 51 | public static final String Finnish = "fi"; 52 | 53 | /** French language code, may be used as source or target language. */ 54 | public static final String French = "fr"; 55 | 56 | /** Hungarian language code, may be used as source or target language. */ 57 | public static final String Hungarian = "hu"; 58 | 59 | /** Indonesian language code, may be used as source or target language. */ 60 | public static final String Indonesian = "id"; 61 | 62 | /** Italian language code, may be used as source or target language. */ 63 | public static final String Italian = "it"; 64 | 65 | /** Japanese language code, may be used as source or target language. */ 66 | public static final String Japanese = "ja"; 67 | 68 | /** Korean language code, may be used as source or target language. */ 69 | public static final String Korean = "ko"; 70 | 71 | /** Lithuanian language code, may be used as source or target language. */ 72 | public static final String Lithuanian = "lt"; 73 | 74 | /** Latvian language code, may be used as source or target language. */ 75 | public static final String Latvian = "lv"; 76 | 77 | /** Norwegian (bokmål) language code, may be used as source or target language. */ 78 | public static final String Norwegian = "nb"; 79 | 80 | /** Dutch language code, may be used as source or target language. */ 81 | public static final String Dutch = "nl"; 82 | 83 | /** Polish language code, may be used as source or target language. */ 84 | public static final String Polish = "pl"; 85 | 86 | /** 87 | * Portuguese language code, may only be used as a source language. In input texts, this language 88 | * code supports all Portuguese variants. 89 | */ 90 | public static final String Portuguese = "pt"; 91 | 92 | /** Brazilian Portuguese language code, may only be used as a target language. */ 93 | public static final String PortugueseBrazilian = "pt-BR"; 94 | 95 | /** European Portuguese language code, may only be used as a target language. */ 96 | public static final String PortugueseEuropean = "pt-PT"; 97 | 98 | /** Romanian language code, may be used as source or target language. */ 99 | public static final String Romanian = "ro"; 100 | 101 | /** Russian language code, may be used as source or target language. */ 102 | public static final String Russian = "ru"; 103 | 104 | /** Slovak language code, may be used as source or target language. */ 105 | public static final String Slovak = "sk"; 106 | 107 | /** Slovenian language code, may be used as source or target language. */ 108 | public static final String Slovenian = "sl"; 109 | 110 | /** Swedish language code, may be used as source or target language. */ 111 | public static final String Swedish = "sv"; 112 | 113 | /** Turkish language code, may be used as source or target language. */ 114 | public static final String Turkish = "tr"; 115 | 116 | /** Ukrainian language code, may be used as source or target language. */ 117 | public static final String Ukrainian = "uk"; 118 | 119 | /** Chinese language code, may be used as source or target language. */ 120 | public static final String Chinese = "zh"; 121 | 122 | /** 123 | * Removes the regional variant (if any) from the given language code. 124 | * 125 | * @param langCode Language code possibly containing a regional variant. 126 | * @return The language code without a regional variant. 127 | */ 128 | public static String removeRegionalVariant(String langCode) { 129 | String[] parts = langCode.split("-", 2); 130 | return parts[0].toLowerCase(Locale.ENGLISH); 131 | } 132 | 133 | /** 134 | * Changes the upper- and lower-casing of the given language code to match ISO 639-1 with an 135 | * optional regional code from ISO 3166-1. 136 | * 137 | * @param langCode String containing language code to standardize. 138 | * @return String containing the standardized language code. 139 | */ 140 | public static String standardize(String langCode) { 141 | String[] parts = langCode.split("-", 2); 142 | if (parts.length == 1) { 143 | return parts[0].toLowerCase(Locale.ENGLISH); 144 | } else { 145 | return parts[0].toLowerCase(Locale.ENGLISH) + "-" + parts[1].toUpperCase(Locale.ENGLISH); 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | # Note: This GitLab CI configuration is used for internal testing, users can ignore it. 2 | include: 3 | - project: '${CI_PROJECT_NAMESPACE}/ci-libs-for-client-libraries' 4 | file: 5 | - '/${CI_PROJECT_NAME}/.gitlab-ci.yml' 6 | - project: 'deepl/ops/ci-cd-infrastructure/gitlab-ci-lib' 7 | file: 8 | - '/templates/.secret-detection.yml' 9 | - '/templates/.gitlab-release.yml' 10 | - template: Security/SAST.gitlab-ci.yml 11 | 12 | # Global -------------------------- 13 | 14 | # Use 17 (LTS) as base 15 | image: eclipse-temurin:17-alpine 16 | 17 | variables: 18 | GRADLE_OPTS: "-Dorg.gradle.daemon=false" 19 | JAVA_TOOL_OPTIONS: "" 20 | GITLAB_ADVANCED_SAST_ENABLED: 'true' 21 | 22 | workflow: 23 | rules: 24 | - if: $CI_PIPELINE_SOURCE == "merge_request_event" 25 | - if: $CI_COMMIT_TAG 26 | - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' 27 | 28 | stages: 29 | - check 30 | - build 31 | - test 32 | - publish 33 | 34 | before_script: 35 | - GRADLE_USER_HOME="$(pwd)/.gradle" 36 | - export GRADLE_USER_HOME 37 | 38 | # stage: check ---------------------- 39 | 40 | .spotless_base: 41 | stage: check 42 | script: ./gradlew spotlessCheck 43 | 44 | spotless_scheduled: 45 | extends: .spotless_base 46 | rules: 47 | - if: $CI_PIPELINE_SOURCE == "schedule" 48 | retry: 2 49 | 50 | spotless_manual: 51 | extends: .spotless_base 52 | rules: 53 | - if: $CI_PIPELINE_SOURCE != "schedule" 54 | 55 | .license_check_base: 56 | stage: check 57 | script: 58 | - ./license_checker.sh '*.java' | tee license_check_output.txt 59 | - '[ ! -s license_check_output.txt ]' 60 | 61 | license_check_scheduled: 62 | extends: .license_check_base 63 | rules: 64 | - if: $CI_PIPELINE_SOURCE == "schedule" 65 | retry: 2 66 | 67 | license_check_manual: 68 | extends: .license_check_base 69 | rules: 70 | - if: $CI_PIPELINE_SOURCE != "schedule" 71 | 72 | secret_detection: 73 | extends: .secret-detection 74 | stage: check 75 | image: !reference [.secret-detection, image] 76 | variables: 77 | SECRET_DETECTION_HISTORIC_SCAN: "true" 78 | before_script: 79 | - echo "overriding default before_script..." 80 | rules: 81 | - if: $CI_MERGE_REQUEST_ID 82 | 83 | gitlab-advanced-sast: 84 | stage: check 85 | rules: 86 | - when: always 87 | variables: 88 | SAST_EXCLUDED_PATHS: '$DEFAULT_SAST_EXCLUDED_PATHS' 89 | GIT_STRATEGY: clone 90 | 91 | semgrep-sast: 92 | stage: check 93 | rules: 94 | - when: always 95 | variables: 96 | SAST_EXCLUDED_PATHS: '$DEFAULT_SAST_EXCLUDED_PATHS' 97 | GIT_STRATEGY: clone 98 | 99 | # stage: build ---------------------- 100 | 101 | .build_base: 102 | stage: build 103 | script: 104 | - ./gradlew assemble 105 | artifacts: 106 | paths: 107 | - deepl-java/build/ 108 | 109 | build_scheduled: 110 | extends: .build_base 111 | rules: 112 | - if: $CI_PIPELINE_SOURCE == "schedule" 113 | retry: 2 114 | 115 | build_manual: 116 | extends: .build_base 117 | rules: 118 | - if: $CI_PIPELINE_SOURCE != "schedule" 119 | 120 | # stage: test ------------------------- 121 | 122 | .test_base: 123 | stage: test 124 | extends: .test 125 | variables: 126 | KUBERNETES_MEMORY_LIMIT: 8Gi 127 | parallel: 128 | matrix: 129 | - DOCKER_IMAGE: "eclipse-temurin:18-alpine" 130 | - DOCKER_IMAGE: "amazoncorretto:8-alpine" 131 | USE_MOCK_SERVER: "use mock server" 132 | - DOCKER_IMAGE: "eclipse-temurin:8-focal" 133 | USE_MOCK_SERVER: "use mock server" 134 | - DOCKER_IMAGE: "eclipse-temurin:11-alpine" 135 | USE_MOCK_SERVER: "use mock server" 136 | - DOCKER_IMAGE: "eclipse-temurin:17-alpine" 137 | USE_MOCK_SERVER: "use mock server" 138 | - DOCKER_IMAGE: "eclipse-temurin:19-alpine" 139 | USE_MOCK_SERVER: "use mock server" 140 | image: ${DOCKER_IMAGE} 141 | script: 142 | - > 143 | if [[ ! -z "${USE_MOCK_SERVER}" ]]; then 144 | echo "Using mock server" 145 | export DEEPL_SERVER_URL=http://deepl-mock:3000 146 | export DEEPL_MOCK_SERVER_PORT=3000 147 | export DEEPL_PROXY_URL=http://deepl-mock:3001 148 | export DEEPL_MOCK_PROXY_SERVER_PORT=3001 149 | fi 150 | - ./gradlew test -DrunV1ApiTests=true 151 | artifacts: 152 | paths: 153 | - deepl-java/build/reports/tests/test 154 | reports: 155 | junit: 156 | - deepl-java/build/reports/tests/test/index.html 157 | when: always 158 | 159 | test_scheduled: 160 | extends: .test_base 161 | rules: 162 | - if: $CI_PIPELINE_SOURCE == "schedule" 163 | retry: 2 164 | 165 | test_manual: 166 | stage: test 167 | extends: .test_base 168 | rules: 169 | - if: $CI_PIPELINE_SOURCE != "schedule" 170 | 171 | .test_examples_base: 172 | stage: test 173 | extends: .test 174 | variables: 175 | MAVEN_OPTS: -Dmaven.repo.local=.m2/repository 176 | parallel: 177 | matrix: 178 | - DOCKER_IMAGE: "maven:3.9" 179 | - DOCKER_IMAGE: "maven:3.8-openjdk-18-slim" 180 | - DOCKER_IMAGE: "maven:3.8-openjdk-8-slim" 181 | - DOCKER_IMAGE: "maven:3.9-sapmachine-17" 182 | - DOCKER_IMAGE: "maven:3.9-eclipse-temurin-8" 183 | - DOCKER_IMAGE: "maven:3.9-eclipse-temurin-21" 184 | image: ${DOCKER_IMAGE} 185 | script: 186 | - cd examples/maven/deepl-test-app 187 | - mvn install -B -PbuildProject -l mvn_build.log 188 | - mvn verify -PrunIntegrationTests 189 | artifacts: 190 | paths: 191 | - examples/maven/deepl-test-app/mvn_build.log 192 | when: always 193 | 194 | test_examples_scheduled: 195 | extends: .test_examples_base 196 | rules: 197 | - if: $CI_PIPELINE_SOURCE == "schedule" 198 | retry: 2 199 | 200 | test_examples_manual: 201 | extends: .test_examples_base 202 | rules: 203 | - if: $CI_PIPELINE_SOURCE != "schedule" 204 | 205 | # stage: publish ------------------------- 206 | 207 | .publish_base: 208 | stage: publish 209 | extends: .publish 210 | dependencies: 211 | - build_scheduled 212 | - build_manual 213 | script: 214 | - echo "=== Publishing to Maven Central ===" 215 | - echo "Runner node $(hostname)" 216 | - echo "" 217 | - echo "Step 1/2 Publishing artifacts to staging repository..." 218 | - ./gradlew publish --info 219 | - echo "" 220 | - echo "Step 2/2 Triggering deployment from same IP..." 221 | # Trigger deployment from same IP to ensure Sonatype can find the staging repository 222 | - ./gradlew triggerDeployment 223 | 224 | publish: 225 | extends: .publish_base 226 | rules: 227 | - if: '$CI_COMMIT_TAG =~ /^v[0-9]+\.[0-9]+\.[0-9]+$/' 228 | 229 | publish_manual: 230 | extends: .publish_base 231 | when: manual 232 | 233 | ## gitlab release 234 | 235 | gitlab release: 236 | stage: publish 237 | extends: .create_gitlab_release 238 | rules: 239 | - if: '$CI_COMMIT_TAG =~ /^v[0-9]+\.[0-9]+\.[0-9]+$/' -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/GlossaryEntries.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | import com.deepl.api.utils.*; 7 | import java.util.*; 8 | import org.jetbrains.annotations.*; 9 | 10 | /** Stores the entries of a glossary. */ 11 | public class GlossaryEntries implements Map { 12 | private final Map entries = new HashMap<>(); 13 | 14 | /** Construct an empty GlossaryEntries. */ 15 | public GlossaryEntries() {} 16 | 17 | /** Initializes a new GlossaryEntries with the entry pairs in the given map. */ 18 | public GlossaryEntries(Map entryPairs) { 19 | this.putAll(entryPairs); 20 | } 21 | 22 | /** 23 | * Converts the given tab-separated-value (TSV) string of glossary entries into a new 24 | * GlossaryEntries object. Whitespace is trimmed from the start and end of each term. 25 | */ 26 | public static GlossaryEntries fromTsv(String tsv) { 27 | GlossaryEntries result = new GlossaryEntries(); 28 | String[] lines = tsv.split("(\\r\\n|\\n|\\r)"); 29 | int lineNumber = 0; 30 | for (String line : lines) { 31 | ++lineNumber; 32 | String lineTrimmed = trimWhitespace(line); 33 | if (lineTrimmed.isEmpty()) { 34 | continue; 35 | } 36 | String[] splitLine = lineTrimmed.split("\\t"); 37 | if (splitLine.length < 2) { 38 | throw new IllegalArgumentException( 39 | String.format( 40 | "Entry on line %d does not contain a term separator: %s", lineNumber, lineTrimmed)); 41 | } else if (splitLine.length > 2) { 42 | throw new IllegalArgumentException( 43 | String.format( 44 | "Entry on line %d contains more than one term separator: %s", lineNumber, line)); 45 | } else { 46 | String sourceTerm = trimWhitespace(splitLine[0]); 47 | String targetTerm = trimWhitespace(splitLine[1]); 48 | validateGlossaryTerm(sourceTerm); 49 | validateGlossaryTerm(targetTerm); 50 | if (result.containsKey(sourceTerm)) { 51 | throw new IllegalArgumentException( 52 | String.format( 53 | "Entry on line %d duplicates source term '%s'", lineNumber, sourceTerm)); 54 | } 55 | result.put(sourceTerm, targetTerm); 56 | } 57 | } 58 | 59 | if (result.entries.isEmpty()) { 60 | throw new IllegalArgumentException("TSV string contains no valid entries"); 61 | } 62 | 63 | return result; 64 | } 65 | 66 | @Override 67 | public boolean equals(Object o) { 68 | if (this == o) return true; 69 | if (o == null) return false; 70 | if (getClass() != o.getClass()) return false; 71 | GlossaryEntries glossaryEntries = (GlossaryEntries) o; 72 | return glossaryEntries.entries.equals(entries); 73 | } 74 | 75 | @Override 76 | public int size() { 77 | return entries.size(); 78 | } 79 | 80 | @Override 81 | public boolean isEmpty() { 82 | return entries.isEmpty(); 83 | } 84 | 85 | @Override 86 | public boolean containsKey(Object key) { 87 | return entries.containsKey(key); 88 | } 89 | 90 | @Override 91 | public boolean containsValue(Object value) { 92 | return entries.containsValue(value); 93 | } 94 | 95 | @Override 96 | public String get(Object key) { 97 | return entries.get(key); 98 | } 99 | 100 | /** 101 | * Adds the given source term and target term to the glossary entries. 102 | * 103 | * @param sourceTerm key with which the specified value is to be associated 104 | * @param targetTerm value to be associated with the specified key 105 | * @return The previous target term associated with this source term, or null if this source term 106 | * was not present. 107 | */ 108 | public String put(String sourceTerm, String targetTerm) throws IllegalArgumentException { 109 | validateGlossaryTerm(sourceTerm); 110 | validateGlossaryTerm(targetTerm); 111 | return entries.put(sourceTerm, targetTerm); 112 | } 113 | 114 | @Override 115 | public String remove(Object key) { 116 | return entries.remove(key); 117 | } 118 | 119 | @Override 120 | public void putAll(@NotNull Map m) { 121 | for (Map.Entry entryPair : m.entrySet()) { 122 | put(entryPair.getKey(), entryPair.getValue()); 123 | } 124 | } 125 | 126 | @Override 127 | public void clear() { 128 | entries.clear(); 129 | } 130 | 131 | @NotNull 132 | @Override 133 | public Set keySet() { 134 | return entries.keySet(); 135 | } 136 | 137 | @NotNull 138 | @Override 139 | public Collection values() { 140 | return entries.values(); 141 | } 142 | 143 | @NotNull 144 | @Override 145 | public Set> entrySet() { 146 | return entries.entrySet(); 147 | } 148 | 149 | /** 150 | * Checks the validity of the given glossary term, for example that it contains no invalid 151 | * characters. Whitespace at the start and end of the term is ignored. Terms are considered valid 152 | * if they comprise at least one non-whitespace character, and contain no invalid characters: C0 153 | * and C1 control characters, and Unicode newlines. 154 | * 155 | * @param term String containing term to check. 156 | */ 157 | public static void validateGlossaryTerm(String term) throws IllegalArgumentException { 158 | String termTrimmed = trimWhitespace(term); 159 | if (termTrimmed.isEmpty()) { 160 | throw new IllegalArgumentException( 161 | String.format("Term '%s' contains no non-whitespace characters", term)); 162 | } 163 | for (int i = 0; i < termTrimmed.length(); ++i) { 164 | char ch = termTrimmed.charAt(i); 165 | if ((ch <= 31) || (128 <= ch && ch <= 159) || ch == '\u2028' || ch == '\u2029') { 166 | throw new IllegalArgumentException( 167 | String.format( 168 | "Term '%s' contains invalid character: '%c' (U+%04d)", term, ch, (int) ch)); 169 | } 170 | } 171 | } 172 | 173 | /** 174 | * Converts the glossary entries to a string containing the entries in tab-separated-value (TSV) 175 | * format. 176 | * 177 | * @return String containing the entries in TSV format. 178 | */ 179 | public String toTsv() { 180 | StringBuilder builder = new StringBuilder(); 181 | for (Map.Entry entryPair : entries.entrySet()) { 182 | if (builder.length() > 0) { 183 | builder.append("\n"); 184 | } 185 | builder.append(entryPair.getKey()).append("\t").append(entryPair.getValue()); 186 | } 187 | return builder.toString(); 188 | } 189 | 190 | /** 191 | * Strips whitespace characters from the beginning and end of the given string. Implemented here 192 | * because String.strip() is not available in Java 8. 193 | * 194 | * @param input String to have whitespace trimmed. 195 | * @return Input string with whitespace removed from ends. 196 | */ 197 | private static String trimWhitespace(String input) { 198 | int left = 0; 199 | for (; left < input.length(); left++) { 200 | char ch = input.charAt(left); 201 | if (ch != ' ' && ch != '\t') { 202 | break; 203 | } 204 | } 205 | if (left >= input.length()) { 206 | return ""; 207 | } 208 | int right = input.length() - 1; 209 | for (; left < right; right--) { 210 | char ch = input.charAt(right); 211 | if (ch != ' ' && ch != '\t') { 212 | break; 213 | } 214 | } 215 | return input.substring(left, right + 1); 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /deepl-java/src/test/java/com/deepl/api/TestBase.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | import com.deepl.api.utils.*; 7 | import java.io.*; 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | import java.util.UUID; 11 | 12 | public class TestBase { 13 | protected static final boolean isMockServer; 14 | protected static final boolean isMockProxyServer; 15 | protected static final String authKey; 16 | protected static final String serverUrl; 17 | protected static final String proxyUrl; 18 | 19 | protected static final Map exampleText; 20 | 21 | private static final String tempDirBase; 22 | 23 | final String exampleInput = exampleText.get("en"); 24 | final String exampleLargeInput = repeatString(exampleText.get("en") + "\n", 1000); 25 | final String exampleOutput = exampleText.get("de"); 26 | final String exampleLargeOutput = repeatString(exampleText.get("de") + "\n", 1000); 27 | final String tempDir; 28 | 29 | static { 30 | isMockServer = System.getenv("DEEPL_MOCK_SERVER_PORT") != null; 31 | serverUrl = System.getenv("DEEPL_SERVER_URL"); 32 | proxyUrl = System.getenv("DEEPL_PROXY_URL"); 33 | isMockProxyServer = proxyUrl != null; 34 | if (isMockServer) { 35 | authKey = "mock_server"; 36 | if (serverUrl == null) { 37 | System.err.println( 38 | "DEEPL_SERVER_URL environment variable must be set when using mock server."); 39 | System.exit(1); 40 | } 41 | } else { 42 | authKey = System.getenv("DEEPL_AUTH_KEY"); 43 | if (authKey == null) { 44 | System.err.println( 45 | "DEEPL_AUTH_KEY environment variable must be set unless using mock server."); 46 | System.exit(1); 47 | } 48 | } 49 | 50 | exampleText = new HashMap<>(); 51 | exampleText.put("ar", "شعاع البروتون"); 52 | exampleText.put("bg", "протонен лъч"); 53 | exampleText.put("cs", "protonový paprsek"); 54 | exampleText.put("da", "protonstråle"); 55 | exampleText.put("de", "Protonenstrahl"); 56 | exampleText.put("el", "δέσμη πρωτονίων"); 57 | exampleText.put("en", "proton beam"); 58 | exampleText.put("en-US", "proton beam"); 59 | exampleText.put("en-GB", "proton beam"); 60 | exampleText.put("es", "haz de protones"); 61 | exampleText.put("et", "prootonikiirgus"); 62 | exampleText.put("fi", "protonisäde"); 63 | exampleText.put("fr", "faisceau de protons"); 64 | exampleText.put("hu", "protonnyaláb"); 65 | exampleText.put("id", "berkas proton"); 66 | exampleText.put("it", "fascio di protoni"); 67 | exampleText.put("ja", "陽子ビーム"); 68 | exampleText.put("ko", "양성자 빔"); 69 | exampleText.put("lt", "protonų spindulys"); 70 | exampleText.put("lv", "protonu staru kūlis"); 71 | exampleText.put("nb", "protonstråle"); 72 | exampleText.put("nl", "protonenbundel"); 73 | exampleText.put("pl", "wiązka protonów"); 74 | exampleText.put("pt", "feixe de prótons"); 75 | exampleText.put("pt-BR", "feixe de prótons"); 76 | exampleText.put("pt-PT", "feixe de prótons"); 77 | exampleText.put("ro", "fascicul de protoni"); 78 | exampleText.put("ru", "протонный луч"); 79 | exampleText.put("sk", "protónový lúč"); 80 | exampleText.put("sl", "protonski žarek"); 81 | exampleText.put("sv", "protonstråle"); 82 | exampleText.put("tr", "proton ışını"); 83 | exampleText.put("uk", "Протонний промінь"); 84 | exampleText.put("zh", "质子束"); 85 | 86 | String tmpdir = System.getProperty("java.io.tmpdir"); 87 | tempDirBase = tmpdir.endsWith("/") ? tmpdir : tmpdir + "/"; 88 | } 89 | 90 | protected TestBase() { 91 | tempDir = createTempDir(); 92 | } 93 | 94 | // TODO: Delete `createTranslator` methods, replace with `createDeepLClient` 95 | protected Translator createTranslator() { 96 | SessionOptions sessionOptions = new SessionOptions(); 97 | return createTranslator(sessionOptions); 98 | } 99 | 100 | protected Translator createTranslator(SessionOptions sessionOptions) { 101 | TranslatorOptions translatorOptions = new TranslatorOptions(); 102 | return createTranslator(sessionOptions, translatorOptions); 103 | } 104 | 105 | protected Translator createTranslator( 106 | SessionOptions sessionOptions, TranslatorOptions translatorOptions) { 107 | Map headers = sessionOptions.createSessionHeaders(); 108 | 109 | if (translatorOptions.getServerUrl() == null) { 110 | translatorOptions.setServerUrl(serverUrl); 111 | } 112 | 113 | if (translatorOptions.getHeaders() != null) { 114 | headers.putAll(translatorOptions.getHeaders()); 115 | } 116 | translatorOptions.setHeaders(headers); 117 | 118 | String authKey = sessionOptions.randomAuthKey ? UUID.randomUUID().toString() : TestBase.authKey; 119 | 120 | try { 121 | return new Translator(authKey, translatorOptions); 122 | } catch (IllegalArgumentException e) { 123 | e.printStackTrace(); 124 | System.exit(1); 125 | return null; 126 | } 127 | } 128 | 129 | protected DeepLClient createDeepLClient() { 130 | SessionOptions sessionOptions = new SessionOptions(); 131 | return createDeepLClient(sessionOptions); 132 | } 133 | 134 | protected DeepLClient createDeepLClient(SessionOptions sessionOptions) { 135 | TranslatorOptions translatorOptions = new TranslatorOptions(); 136 | return createDeepLClient(sessionOptions, translatorOptions); 137 | } 138 | 139 | protected DeepLClient createDeepLClient( 140 | SessionOptions sessionOptions, TranslatorOptions translatorOptions) { 141 | Map headers = sessionOptions.createSessionHeaders(); 142 | 143 | if (translatorOptions.getServerUrl() == null) { 144 | translatorOptions.setServerUrl(serverUrl); 145 | } 146 | 147 | if (translatorOptions.getHeaders() != null) { 148 | headers.putAll(translatorOptions.getHeaders()); 149 | } 150 | translatorOptions.setHeaders(headers); 151 | 152 | String authKey = sessionOptions.randomAuthKey ? UUID.randomUUID().toString() : TestBase.authKey; 153 | 154 | try { 155 | return new DeepLClient(authKey, translatorOptions); 156 | } catch (IllegalArgumentException e) { 157 | e.printStackTrace(); 158 | System.exit(1); 159 | return null; 160 | } 161 | } 162 | 163 | protected String createTempDir() { 164 | String newTempDir = tempDirBase + UUID.randomUUID(); 165 | boolean created = new File(newTempDir).mkdirs(); 166 | return newTempDir; 167 | } 168 | 169 | protected void writeToFile(File file, String content) throws IOException { 170 | Boolean justCreated = file.createNewFile(); 171 | FileWriter writer = new FileWriter(file); 172 | writer.write(content); 173 | writer.flush(); 174 | writer.close(); 175 | } 176 | 177 | protected String readFromFile(File file) throws IOException { 178 | if (!file.exists()) return ""; 179 | return StreamUtil.readStream(new FileInputStream(file)); 180 | } 181 | 182 | /** 183 | * Returns a string containing the input string repeated given number of times. Note: 184 | * String.repeat() was added in Java 11. 185 | * 186 | * @param input Input string to be repeated. 187 | * @param number Number of times to repeat string. 188 | * @return Input string repeated given number of times. 189 | */ 190 | protected static String repeatString(String input, int number) { 191 | StringBuilder sb = new StringBuilder(input.length() * number); 192 | for (int i = 0; i < number; i++) { 193 | sb.append(input); 194 | } 195 | return sb.toString(); 196 | } 197 | 198 | protected File createInputFile() throws IOException { 199 | return createInputFile(exampleInput); 200 | } 201 | 202 | protected File createInputFile(String content) throws IOException { 203 | File inputFile = new File(tempDir + "/example_document.txt"); 204 | boolean ignored = inputFile.delete(); 205 | ignored = inputFile.createNewFile(); 206 | writeToFile(inputFile, content); 207 | return inputFile; 208 | } 209 | 210 | protected File createOutputFile() { 211 | File outputFile = new File(tempDir + "/output/example_document.txt"); 212 | boolean ignored = new File(outputFile.getParent()).mkdir(); 213 | ignored = outputFile.delete(); 214 | return outputFile; 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /deepl-java/src/test/java/com/deepl/api/TranslateDocumentTest.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | import java.io.File; 7 | import java.io.IOException; 8 | import java.time.Duration; 9 | import java.util.Date; 10 | import org.junit.jupiter.api.Assertions; 11 | import org.junit.jupiter.api.Assumptions; 12 | import org.junit.jupiter.api.Test; 13 | 14 | public class TranslateDocumentTest extends TestBase { 15 | @Test 16 | void testTranslateDocument() throws Exception { 17 | Translator translator = createTranslator(); 18 | 19 | File inputFile = createInputFile(); 20 | File outputFile = createOutputFile(); 21 | 22 | translator.translateDocument(inputFile, outputFile, "en", "de"); 23 | Assertions.assertEquals(exampleOutput, readFromFile(outputFile)); 24 | 25 | // Test with output path occupied 26 | Assertions.assertThrows( 27 | IOException.class, 28 | () -> { 29 | translator.translateDocument(inputFile, outputFile, "en", "de"); 30 | }); 31 | } 32 | 33 | @Test 34 | void testTranslateDocumentFailsWithOutputOccupied() throws Exception { 35 | Translator translator = createTranslator(); 36 | 37 | File inputFile = createInputFile(); 38 | File outputFile = createOutputFile(); 39 | outputFile.createNewFile(); 40 | 41 | // Test with output path occupied 42 | Assertions.assertThrows( 43 | IOException.class, 44 | () -> { 45 | translator.translateDocument(inputFile, outputFile, "en", "de"); 46 | }); 47 | } 48 | 49 | @Test 50 | void testTranslateDocumentWithRetry() throws Exception { 51 | Assumptions.assumeTrue(isMockServer); 52 | Translator translator = 53 | createTranslator( 54 | new SessionOptions().setNoResponse(1), 55 | new TranslatorOptions().setTimeout(Duration.ofSeconds(1))); 56 | 57 | File outputFile = createOutputFile(); 58 | translator.translateDocument(createInputFile(), outputFile, "en", "de"); 59 | Assertions.assertEquals(exampleOutput, readFromFile(outputFile)); 60 | } 61 | 62 | @Test 63 | void testTranslateDocumentWithWaiting() throws Exception { 64 | Assumptions.assumeTrue(isMockServer); 65 | Translator translator = 66 | createTranslator( 67 | new SessionOptions() 68 | .setDocumentTranslateTime(Duration.ofSeconds(2)) 69 | .setDocumentQueueTime(Duration.ofSeconds(2))); 70 | File outputFile = createOutputFile(); 71 | translator.translateDocument(createInputFile(), outputFile, "en", "de"); 72 | Assertions.assertEquals(exampleOutput, readFromFile(outputFile)); 73 | } 74 | 75 | @Test 76 | void testTranslateLargeDocument() throws Exception { 77 | Assumptions.assumeTrue(isMockServer); 78 | Translator translator = createTranslator(); 79 | File inputFile = createInputFile(exampleLargeInput); 80 | File outputFile = createOutputFile(); 81 | translator.translateDocument(inputFile, outputFile, "en", "de"); 82 | Assertions.assertEquals(exampleLargeOutput, readFromFile(outputFile)); 83 | } 84 | 85 | @Test 86 | void testTranslateDocumentFormality() throws Exception { 87 | Translator translator = createTranslator(); 88 | File inputFile = createInputFile("How are you?"); 89 | File outputFile = createOutputFile(); 90 | translator.translateDocument( 91 | inputFile, 92 | outputFile, 93 | "en", 94 | "de", 95 | new DocumentTranslationOptions().setFormality(Formality.More)); 96 | if (!isMockServer) { 97 | Assertions.assertTrue(readFromFile(outputFile).contains("Ihnen")); 98 | } 99 | 100 | outputFile.delete(); 101 | 102 | translator.translateDocument( 103 | inputFile, 104 | outputFile, 105 | "en", 106 | "de", 107 | new DocumentTranslationOptions().setFormality(Formality.Less)); 108 | if (!isMockServer) { 109 | Assertions.assertTrue(readFromFile(outputFile).contains("dir")); 110 | } 111 | } 112 | 113 | @Test 114 | void testTranslateDocumentFailureDuringTranslation() throws Exception { 115 | Translator translator = createTranslator(); 116 | 117 | // Translating text from DE to DE will trigger error 118 | File inputFile = createInputFile(exampleText.get("de")); 119 | File outputFile = createOutputFile(); 120 | 121 | DocumentTranslationException exception = 122 | Assertions.assertThrows( 123 | DocumentTranslationException.class, 124 | () -> { 125 | translator.translateDocument(inputFile, outputFile, null, "de"); 126 | }); 127 | Assertions.assertTrue(exception.getMessage().contains("Source and target language")); 128 | } 129 | 130 | @Test 131 | void testInvalidDocument() throws Exception { 132 | Translator translator = createTranslator(); 133 | File inputFile = new File(tempDir + "/document.xyz"); 134 | writeToFile(inputFile, exampleText.get("en")); 135 | File outputFile = new File(tempDir + "/output_document.xyz"); 136 | outputFile.delete(); 137 | 138 | DocumentTranslationException exception = 139 | Assertions.assertThrows( 140 | DocumentTranslationException.class, 141 | () -> { 142 | translator.translateDocument(inputFile, outputFile, "en", "de"); 143 | }); 144 | Assertions.assertNull(exception.getHandle()); 145 | } 146 | 147 | @Test 148 | void testTranslateDocumentLowLevel() throws Exception { 149 | Assumptions.assumeTrue(isMockServer); 150 | // Set a small document queue time to attempt downloading a queued document 151 | Translator translator = 152 | createTranslator(new SessionOptions().setDocumentQueueTime(Duration.ofMillis(100))); 153 | 154 | File inputFile = createInputFile(); 155 | File outputFile = createOutputFile(); 156 | final DocumentHandle handle = translator.translateDocumentUpload(inputFile, "en", "de"); 157 | 158 | DocumentStatus status = translator.translateDocumentStatus(handle); 159 | Assertions.assertEquals(handle.getDocumentId(), status.getDocumentId()); 160 | Assertions.assertTrue(status.ok()); 161 | Assertions.assertFalse(status.done()); 162 | 163 | // Test recreating a document handle from id & key 164 | String documentId = handle.getDocumentId(); 165 | String documentKey = handle.getDocumentKey(); 166 | DocumentHandle recreatedHandle = new DocumentHandle(documentId, documentKey); 167 | status = translator.translateDocumentStatus(recreatedHandle); 168 | Assertions.assertTrue(status.ok()); 169 | 170 | while (status.ok() && !status.done()) { 171 | Thread.sleep(200); 172 | status = translator.translateDocumentStatus(recreatedHandle); 173 | } 174 | 175 | Assertions.assertTrue(status.ok() && status.done()); 176 | translator.translateDocumentDownload(recreatedHandle, outputFile); 177 | Assertions.assertEquals(exampleOutput, readFromFile(outputFile)); 178 | } 179 | 180 | @Test 181 | void testTranslateDocumentRequestFields() throws Exception { 182 | Assumptions.assumeTrue(isMockServer); 183 | Translator translator = 184 | createTranslator( 185 | new SessionOptions() 186 | .setDocumentTranslateTime(Duration.ofSeconds(2)) 187 | .setDocumentQueueTime(Duration.ofSeconds(2))); 188 | File inputFile = createInputFile(); 189 | File outputFile = createOutputFile(); 190 | 191 | long timeBefore = new Date().getTime(); 192 | DocumentHandle handle = translator.translateDocumentUpload(inputFile, "en", "de"); 193 | DocumentStatus status = translator.translateDocumentStatus(handle); 194 | Assertions.assertTrue(status.ok()); 195 | Assertions.assertTrue( 196 | status.getSecondsRemaining() == null || status.getSecondsRemaining() >= 0); 197 | status = translator.translateDocumentWaitUntilDone(handle); 198 | translator.translateDocumentDownload(handle, outputFile); 199 | long timeAfter = new Date().getTime(); 200 | 201 | Assertions.assertEquals(exampleInput.length(), status.getBilledCharacters()); 202 | Assertions.assertTrue(timeAfter - timeBefore > 4000); 203 | Assertions.assertEquals(exampleOutput, readFromFile(outputFile)); 204 | } 205 | 206 | @Test 207 | void testRecreateDocumentHandleInvalid() { 208 | Translator translator = createTranslator(); 209 | String documentId = repeatString("12AB", 8); 210 | String documentKey = repeatString("CD34", 16); 211 | DocumentHandle handle = new DocumentHandle(documentId, documentKey); 212 | Assertions.assertThrows( 213 | NotFoundException.class, 214 | () -> { 215 | translator.translateDocumentStatus(handle); 216 | }); 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 84 | 85 | APP_NAME="Gradle" 86 | APP_BASE_NAME=${0##*/} 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | MAX_FD=$( ulimit -H -n ) || 147 | warn "Could not query maximum file descriptor limit" 148 | esac 149 | case $MAX_FD in #( 150 | '' | soft) :;; #( 151 | *) 152 | ulimit -n "$MAX_FD" || 153 | warn "Could not set maximum file descriptor limit to $MAX_FD" 154 | esac 155 | fi 156 | 157 | # Collect all arguments for the java command, stacking in reverse order: 158 | # * args from the command line 159 | # * the main class name 160 | # * -classpath 161 | # * -D...appname settings 162 | # * --module-path (only if needed) 163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 164 | 165 | # For Cygwin or MSYS, switch paths to Windows format before running java 166 | if "$cygwin" || "$msys" ; then 167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 169 | 170 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 171 | 172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 173 | for arg do 174 | if 175 | case $arg in #( 176 | -*) false ;; # don't mess with options #( 177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 178 | [ -e "$t" ] ;; #( 179 | *) false ;; 180 | esac 181 | then 182 | arg=$( cygpath --path --ignore --mixed "$arg" ) 183 | fi 184 | # Roll the args list around exactly as many times as the number of 185 | # args, so each arg winds up back in the position where it started, but 186 | # possibly modified. 187 | # 188 | # NB: a `for` loop captures its iteration list before it begins, so 189 | # changing the positional parameters here affects neither the number of 190 | # iterations, nor the values presented in `arg`. 191 | shift # remove old arg 192 | set -- "$@" "$arg" # push replacement arg 193 | done 194 | fi 195 | 196 | # Collect all arguments for the java command; 197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 198 | # shell script including quotes and variable substitutions, so put them in 199 | # double quotes to make sure that they get re-expanded; and 200 | # * put everything else in single quotes, so that it's not re-expanded. 201 | 202 | set -- \ 203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 204 | -classpath "$CLASSPATH" \ 205 | org.gradle.wrapper.GradleWrapperMain \ 206 | "$@" 207 | 208 | # Use "xargs" to parse quoted args. 209 | # 210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 211 | # 212 | # In Bash we could simply go: 213 | # 214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 215 | # set -- "${ARGS[@]}" "$@" 216 | # 217 | # but POSIX shell has neither arrays nor command substitution, so instead we 218 | # post-process each arg (as a line of input to sed) to backslash-escape any 219 | # character that might be a shell metacharacter, then use eval to reverse 220 | # that process (while maintaining the separation between arguments), and wrap 221 | # the whole thing up as a single "set" statement. 222 | # 223 | # This will of course break if any of these variables contains a newline or 224 | # an unmatched quote. 225 | # 226 | 227 | eval "set -- $( 228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 229 | xargs -n1 | 230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 231 | tr '\n' ' ' 232 | )" '"$@"' 233 | 234 | exec "$JAVACMD" "$@" 235 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/HttpClientWrapper.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | import com.deepl.api.http.*; 7 | import com.deepl.api.utils.*; 8 | import java.io.*; 9 | import java.net.*; 10 | import java.time.*; 11 | import java.util.*; 12 | import org.apache.http.client.config.RequestConfig; 13 | import org.apache.http.client.methods.HttpPatch; 14 | import org.apache.http.entity.ByteArrayEntity; 15 | import org.apache.http.impl.client.CloseableHttpClient; 16 | import org.apache.http.impl.client.HttpClients; 17 | import org.apache.http.util.EntityUtils; 18 | import org.jetbrains.annotations.*; 19 | 20 | /** 21 | * Helper class providing functions to make HTTP requests and retry with exponential-backoff. 22 | * 23 | *

This class is internal; you should not use this class directly. 24 | */ 25 | class HttpClientWrapper { 26 | private static final String CONTENT_TYPE = "Content-Type"; 27 | private static final String GET = "GET"; 28 | private static final String POST = "POST"; 29 | private static final String DELETE = "DELETE"; 30 | private static final String PUT = "PUT"; 31 | private final String serverUrl; 32 | private final Map headers; 33 | private final Duration minTimeout; 34 | private final @Nullable Proxy proxy; 35 | private final int maxRetries; 36 | 37 | public HttpClientWrapper( 38 | String serverUrl, 39 | Map headers, 40 | Duration minTimeout, 41 | @Nullable Proxy proxy, 42 | int maxRetries) { 43 | this.serverUrl = serverUrl; 44 | this.headers = headers; 45 | this.minTimeout = minTimeout; 46 | this.proxy = proxy; 47 | this.maxRetries = maxRetries; 48 | } 49 | 50 | public HttpResponse sendGetRequestWithBackoff(String relativeUrl) 51 | throws InterruptedException, DeepLException { 52 | return sendRequestWithBackoff(GET, relativeUrl, null).toStringResponse(); 53 | } 54 | 55 | public HttpResponse sendDeleteRequestWithBackoff( 56 | String relativeUrl, @Nullable Iterable> params) 57 | throws InterruptedException, DeepLException { 58 | HttpContent content = HttpContent.buildFormURLEncodedContent(params); 59 | return sendRequestWithBackoff(DELETE, relativeUrl, content).toStringResponse(); 60 | } 61 | 62 | public HttpResponse sendDeleteRequestWithBackoff(String relativeUrl) 63 | throws InterruptedException, DeepLException { 64 | return sendDeleteRequestWithBackoff(relativeUrl, null); 65 | } 66 | 67 | public HttpResponse sendRequestWithBackoff(String relativeUrl) 68 | throws InterruptedException, DeepLException { 69 | return sendRequestWithBackoff(POST, relativeUrl, null).toStringResponse(); 70 | } 71 | 72 | public HttpResponse sendRequestWithBackoff( 73 | String relativeUrl, @Nullable Iterable> params) 74 | throws InterruptedException, DeepLException { 75 | HttpContent content = HttpContent.buildFormURLEncodedContent(params); 76 | return sendRequestWithBackoff(POST, relativeUrl, content).toStringResponse(); 77 | } 78 | 79 | public HttpResponse sendPutRequestWithBackoff( 80 | String relativeUrl, @Nullable Iterable> params) 81 | throws InterruptedException, DeepLException { 82 | HttpContent content = HttpContent.buildFormURLEncodedContent(params); 83 | return sendRequestWithBackoff(PUT, relativeUrl, content).toStringResponse(); 84 | } 85 | 86 | public HttpResponse sendPatchRequestWithBackoff( 87 | String relativeUrl, @Nullable Iterable> params) 88 | throws DeepLException { 89 | try (CloseableHttpClient httpClient = HttpClients.createDefault()) { 90 | HttpContent content = HttpContent.buildFormURLEncodedContent(params); 91 | HttpPatch request = new HttpPatch(serverUrl + relativeUrl); 92 | 93 | // Set timeouts 94 | BackoffTimer backoffTimer = new BackoffTimer(this.minTimeout); 95 | RequestConfig requestConfig = 96 | RequestConfig.custom() 97 | .setConnectTimeout((int) backoffTimer.getTimeoutMillis()) 98 | .setSocketTimeout((int) backoffTimer.getTimeoutMillis()) 99 | .build(); 100 | request.setConfig(requestConfig); 101 | 102 | // Set headers 103 | for (Map.Entry entry : this.headers.entrySet()) { 104 | request.setHeader(entry.getKey(), entry.getValue()); 105 | } 106 | 107 | request.setHeader("Content-Type", content.getContentType()); 108 | request.setEntity(new ByteArrayEntity(content.getContent())); 109 | 110 | // Execute the request 111 | org.apache.http.HttpResponse response = httpClient.execute(request); 112 | 113 | // Get the response stream 114 | InputStream responseStream = 115 | (response.getStatusLine().getStatusCode() >= 200 116 | && response.getStatusLine().getStatusCode() < 400) 117 | ? response.getEntity().getContent() 118 | : new ByteArrayInputStream(EntityUtils.toByteArray(response.getEntity())); 119 | 120 | return new HttpResponseStream(response.getStatusLine().getStatusCode(), responseStream) 121 | .toStringResponse(); 122 | } catch (SocketTimeoutException e) { 123 | throw new ConnectionException(e.getMessage(), true, e); 124 | } catch (RuntimeException | IOException e) { 125 | throw new ConnectionException(e.getMessage(), false, e); 126 | } 127 | } 128 | 129 | public HttpResponseStream downloadWithBackoff( 130 | String relativeUrl, @Nullable Iterable> params) 131 | throws InterruptedException, DeepLException { 132 | HttpContent content = HttpContent.buildFormURLEncodedContent(params); 133 | return sendRequestWithBackoff(POST, relativeUrl, content); 134 | } 135 | 136 | public HttpResponse uploadWithBackoff( 137 | String relativeUrl, 138 | @Nullable Iterable> params, 139 | String fileName, 140 | InputStream inputStream) 141 | throws InterruptedException, DeepLException { 142 | ArrayList> fields = new ArrayList<>(); 143 | fields.add(new KeyValuePair<>("file", new NamedStream(fileName, inputStream))); 144 | if (params != null) { 145 | params.forEach( 146 | (KeyValuePair entry) -> { 147 | fields.add(new KeyValuePair<>(entry.getKey(), entry.getValue())); 148 | }); 149 | } 150 | HttpContent content; 151 | try { 152 | content = HttpContent.buildMultipartFormDataContent(fields); 153 | } catch (Exception e) { 154 | throw new DeepLException("Failed building request", e); 155 | } 156 | return sendRequestWithBackoff(POST, relativeUrl, content).toStringResponse(); 157 | } 158 | 159 | // Sends a request with exponential backoff 160 | private HttpResponseStream sendRequestWithBackoff( 161 | String method, String relativeUrl, HttpContent content) 162 | throws InterruptedException, DeepLException { 163 | BackoffTimer backoffTimer = new BackoffTimer(this.minTimeout); 164 | while (true) { 165 | try { 166 | HttpResponseStream response = 167 | sendRequest(method, serverUrl + relativeUrl, backoffTimer.getTimeoutMillis(), content); 168 | if (backoffTimer.getNumRetries() >= this.maxRetries) { 169 | return response; 170 | } else if (response.getCode() != 429 && response.getCode() < 500) { 171 | return response; 172 | } 173 | response.close(); 174 | } catch (ConnectionException exception) { 175 | if (!exception.getShouldRetry() || backoffTimer.getNumRetries() >= this.maxRetries) { 176 | throw exception; 177 | } 178 | } 179 | backoffTimer.sleepUntilRetry(); 180 | } 181 | } 182 | 183 | private HttpResponseStream sendRequest( 184 | String method, String urlString, long timeoutMs, HttpContent content) 185 | throws ConnectionException { 186 | try { 187 | URL url = new URL(urlString); 188 | HttpURLConnection connection = 189 | (HttpURLConnection) (proxy != null ? url.openConnection(proxy) : url.openConnection()); 190 | 191 | connection.setRequestMethod(method); 192 | connection.setConnectTimeout((int) timeoutMs); 193 | connection.setReadTimeout((int) timeoutMs); 194 | connection.setUseCaches(false); 195 | 196 | for (Map.Entry entry : this.headers.entrySet()) { 197 | connection.setRequestProperty(entry.getKey(), entry.getValue()); 198 | } 199 | 200 | if (content != null) { 201 | connection.setDoOutput(true); 202 | connection.setRequestProperty(CONTENT_TYPE, content.getContentType()); 203 | 204 | try (OutputStream output = connection.getOutputStream()) { 205 | output.write(content.getContent()); 206 | } 207 | } 208 | 209 | int responseCode = connection.getResponseCode(); 210 | InputStream responseStream = 211 | (responseCode >= 200 && responseCode < 400) 212 | ? connection.getInputStream() 213 | : connection.getErrorStream(); 214 | return new HttpResponseStream(responseCode, responseStream); 215 | } catch (SocketTimeoutException e) { 216 | throw new ConnectionException(e.getMessage(), true, e); 217 | } catch (RuntimeException | IOException e) { 218 | throw new ConnectionException(e.getMessage(), false, e); 219 | } 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /deepl-java/src/main/java/com/deepl/api/TextTranslationOptions.java: -------------------------------------------------------------------------------- 1 | // Copyright 2022 DeepL SE (https://www.deepl.com) 2 | // Use of this source code is governed by an MIT 3 | // license that can be found in the LICENSE file. 4 | package com.deepl.api; 5 | 6 | /** 7 | * Options to control text translation behaviour. These options may be provided to {@link 8 | * Translator#translateText} overloads. 9 | * 10 | *

All properties have corresponding setters in fluent-style, so the following is possible: 11 | * 12 | * TextTranslationOptions options = new TextTranslationOptions() 13 | * .setFormality(Formality.Less).setGlossaryId("f63c02c5-f056-.."); 14 | * 15 | */ 16 | public class TextTranslationOptions extends BaseRequestOptions { 17 | private Formality formality; 18 | private String glossaryId; 19 | private String styleId; 20 | private SentenceSplittingMode sentenceSplittingMode; 21 | private boolean preserveFormatting = false; 22 | private String context; 23 | private String tagHandling; 24 | private String tagHandlingVersion; 25 | private String modelType; 26 | private boolean outlineDetection = true; 27 | private Iterable ignoreTags; 28 | private Iterable nonSplittingTags; 29 | private Iterable splittingTags; 30 | private Iterable customInstructions; 31 | 32 | /** 33 | * Sets whether translations should lean toward formal or informal language. This option is only 34 | * applicable for target languages that support the formality option. By default, this value is 35 | * null andnull translations use the default formality. 36 | * 37 | * @see Language#getSupportsFormality() 38 | * @see Formality 39 | */ 40 | public TextTranslationOptions setFormality(Formality formality) { 41 | this.formality = formality; 42 | return this; 43 | } 44 | 45 | /** 46 | * Sets the ID of a glossary to use with the translation. By default, this value is 47 | * null and no glossary is used. 48 | */ 49 | public TextTranslationOptions setGlossaryId(String glossaryId) { 50 | this.glossaryId = glossaryId; 51 | return this; 52 | } 53 | 54 | /** 55 | * Sets the glossary to use with the translation. By default, this value is null and 56 | * no glossary is used. 57 | */ 58 | public TextTranslationOptions setGlossary(IGlossary glossary) { 59 | return setGlossary(glossary.getGlossaryId()); 60 | } 61 | 62 | /** 63 | * Sets the glossary to use with the translation. By default, this value is null and 64 | * no glossary is used. 65 | */ 66 | public TextTranslationOptions setGlossary(String glossaryId) { 67 | this.glossaryId = glossaryId; 68 | return this; 69 | } 70 | 71 | /** 72 | * Sets the ID of a style rule to use with the translation. By default, this value is 73 | * null and no style rule is used. 74 | */ 75 | public TextTranslationOptions setStyleId(String styleId) { 76 | this.styleId = styleId; 77 | return this; 78 | } 79 | 80 | /** 81 | * Sets the style rule to use with the translation. By default, this value is null 82 | * and no style rule is used. 83 | */ 84 | public TextTranslationOptions setStyleRule(StyleRuleInfo styleRule) { 85 | return setStyleId(styleRule.getStyleId()); 86 | } 87 | 88 | /** 89 | * Specifies additional context to influence translations, that is not translated itself. 90 | * Characters in the `context` parameter are not counted toward billing. See the API documentation 91 | * for more information and example usage. 92 | */ 93 | public TextTranslationOptions setContext(String context) { 94 | this.context = context; 95 | return this; 96 | } 97 | 98 | /** 99 | * Specifies how input translation text should be split into sentences. By default, this value is 100 | * null and the default sentence splitting mode is used. 101 | * 102 | * @see SentenceSplittingMode 103 | */ 104 | public TextTranslationOptions setSentenceSplittingMode( 105 | SentenceSplittingMode sentenceSplittingMode) { 106 | this.sentenceSplittingMode = sentenceSplittingMode; 107 | return this; 108 | } 109 | 110 | /** 111 | * Sets whether formatting should be preserved in translations. Set to true to 112 | * prevent the translation engine from correcting some formatting aspects, and instead leave the 113 | * formatting unchanged, default is false. 114 | */ 115 | public TextTranslationOptions setPreserveFormatting(boolean preserveFormatting) { 116 | this.preserveFormatting = preserveFormatting; 117 | return this; 118 | } 119 | 120 | /** 121 | * Set the type of tags to parse before translation, only "xml" and "html" 122 | * are currently available. By default, this value is null and no 123 | * tag-handling is used. 124 | */ 125 | public TextTranslationOptions setTagHandling(String tagHandling) { 126 | this.tagHandling = tagHandling; 127 | return this; 128 | } 129 | 130 | /** 131 | * Sets the version of tag handling algorithm to use. By default, this value is null 132 | * and the API default is used. 133 | */ 134 | public TextTranslationOptions setTagHandlingVersion(String tagHandlingVersion) { 135 | this.tagHandlingVersion = tagHandlingVersion; 136 | return this; 137 | } 138 | 139 | /** 140 | * Set the type of model to use for a text translation. Currently supported values: 141 | * "quality_optimized" use a translation model that maximizes translation quality, at the 142 | * cost of response time. This option may be unavailable for some language pairs and the API would 143 | * respond with an error in this case; "prefer_quality_optimized" use the 144 | * highest-quality translation model for the given language pair; "latency_optimized" 145 | * use a translation model that minimizes response time, at the cost of translation 146 | * quality. 147 | */ 148 | public TextTranslationOptions setModelType(String modelType) { 149 | this.modelType = modelType; 150 | return this; 151 | } 152 | 153 | /** 154 | * Sets whether outline detection is used; set to false to disable automatic tag 155 | * detection, default is true. 156 | */ 157 | public TextTranslationOptions setOutlineDetection(boolean outlineDetection) { 158 | this.outlineDetection = outlineDetection; 159 | return this; 160 | } 161 | 162 | /** 163 | * Sets the list of XML tags containing content that should not be translated. By default, this 164 | * value is null and no tags are specified. 165 | */ 166 | public TextTranslationOptions setIgnoreTags(Iterable ignoreTags) { 167 | this.ignoreTags = ignoreTags; 168 | return this; 169 | } 170 | 171 | /** 172 | * Sets the list of XML tags that should not be used to split text into sentences. By default, 173 | * this value is null and no tags are specified. 174 | */ 175 | public TextTranslationOptions setNonSplittingTags(Iterable nonSplittingTags) { 176 | this.nonSplittingTags = nonSplittingTags; 177 | return this; 178 | } 179 | 180 | /** 181 | * Set the list of XML tags that should be used to split text into sentences. By default, this 182 | * value is null and no tags are specified. 183 | */ 184 | public TextTranslationOptions setSplittingTags(Iterable splittingTags) { 185 | this.splittingTags = splittingTags; 186 | return this; 187 | } 188 | 189 | /** 190 | * Sets custom instructions to influence translations. By default, this value is null 191 | * and no custom instructions are used. 192 | */ 193 | public TextTranslationOptions setCustomInstructions(Iterable customInstructions) { 194 | this.customInstructions = customInstructions; 195 | return this; 196 | } 197 | 198 | /** Gets the current formality setting. */ 199 | public Formality getFormality() { 200 | return formality; 201 | } 202 | 203 | /** Gets the current glossary ID. */ 204 | public String getGlossaryId() { 205 | return glossaryId; 206 | } 207 | 208 | /** Gets the current style rule ID. */ 209 | public String getStyleId() { 210 | return styleId; 211 | } 212 | 213 | /** Gets the current sentence splitting mode. */ 214 | public SentenceSplittingMode getSentenceSplittingMode() { 215 | return sentenceSplittingMode; 216 | } 217 | 218 | /** Gets the current preserve formatting setting. */ 219 | public boolean isPreserveFormatting() { 220 | return preserveFormatting; 221 | } 222 | 223 | /** Gets the current context. */ 224 | public String getContext() { 225 | return context; 226 | } 227 | 228 | /** Gets the current model type. */ 229 | public String getModelType() { 230 | return modelType; 231 | } 232 | 233 | /** Gets the current tag handling setting. */ 234 | public String getTagHandling() { 235 | return tagHandling; 236 | } 237 | 238 | /** Gets the current tag handling version. */ 239 | public String getTagHandlingVersion() { 240 | return tagHandlingVersion; 241 | } 242 | 243 | /** Gets the current outline detection setting. */ 244 | public boolean isOutlineDetection() { 245 | return outlineDetection; 246 | } 247 | 248 | /** Gets the current ignore tags list. */ 249 | public Iterable getIgnoreTags() { 250 | return ignoreTags; 251 | } 252 | 253 | /** Gets the current non-splitting tags list. */ 254 | public Iterable getNonSplittingTags() { 255 | return nonSplittingTags; 256 | } 257 | 258 | /** Gets the current splitting tags list. */ 259 | public Iterable getSplittingTags() { 260 | return splittingTags; 261 | } 262 | 263 | /** Gets the current custom instructions list. */ 264 | public Iterable getCustomInstructions() { 265 | return customInstructions; 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [Unreleased] 8 | 9 | ## [1.14.0] - 2025-12-10 10 | ### Added 11 | - Added `setTagHandlingVersion()` method to `TextTranslationOptions` to specify which version of the tag handling algorithm to use. Options are `v1` and `v2`. 12 | 13 | ## [1.13.0] - 2025-12-03 14 | ### Added 15 | - Added `setCustomInstructions()` method to `TextTranslationOptions` to 16 | customize translation behavior with up to 10 instructions (max 300 characters 17 | each). Only supported for target languages: `de`, `en`, `es`, `fr`, `it`, 18 | `ja`, `ko`, `zh` and their variants. 19 | 20 | Note: using the custom instructions parameter will use `quality_optimized` 21 | model type as the default. Requests combining custom instructions and the 22 | `latency_optimized` model type will be rejected. 23 | 24 | ## [1.12.0] - 2025-11-12 25 | ### Added 26 | - Added support for the `GET /v3/style_rules` endpoint in the client library, the 27 | implementation can be found in the `DeepLClient` class. Please refer to the 28 | README for usage instructions 29 | - Added `styleId` option to `translateText()` which allows text translation with 30 | style rules. 31 | 32 | ## [1.11.0] - 2025-11-04 33 | ### Added 34 | - Added `extraRequestParameters` option to text and document translation methods to pass arbitrary parameters in the request body. This can be used to access beta features or override built-in parameters (such as `target_lang`, `source_lang`, etc.). 35 | 36 | ## [1.10.3] - 2025-08-22 37 | ### Security 38 | - Updated `org.apache.httpcomponents:httpclient` to 4.5.14 due to [CVE-2020-13956](https://nvd.nist.gov/vuln/detail/CVE-2020-13956). 39 | * Thanks to [warm-tune](https://github.com/warm-tune) for reporting in [#72](https://github.com/DeepLcom/deepl-java/issues/72). 40 | 41 | ## [1.10.2] - 2025-07-11 42 | ### Changed 43 | - Migrate to Sonatype Portal OSSRH Staging API due to legacy OSSRH being sunsetted. 44 | - Whitespace surrounding auth key is now stripped. 45 | * Thanks to [timazhum](https://github.com/timazhum) for the fix in [#64](https://github.com/DeepLcom/deepl-java/pull/69). 46 | 47 | ## [1.10.1] - 2025-06-18 48 | ### Fixed 49 | - Fixed `DeepLClient::deleteMultilingualGlossary(String glossaryId)` being package private, made it public instead. 50 | * Thanks to [MTSxoff](https://github.com/MTSxoff) for the report in [#68](https://github.com/DeepLcom/deepl-java/issues/68) and the fix in [#69](https://github.com/DeepLcom/deepl-java/pull/69). 51 | 52 | ## [1.10.0] - 2025-04-30 53 | ### Added 54 | - Added support for the /v3 Multilingual Glossary APIs in the client library 55 | while providing backwards compatability for the previous /v2 Glossary 56 | endpoints. Please refer to the README or 57 | [upgrading_to_multilingual_glossaries.md](upgrading_to_multilingual_glossaries.md) 58 | for usage instructions. 59 | - Added Ukrainian language code 60 | 61 | ## [1.9.0] - 2025-02-21 62 | ### Added 63 | - Allow specifying the API version to use. This is mostly for users who have an 64 | API subscription that includes an API key for CAT tool usage, who need to use 65 | the v1 API. 66 | 67 | ## [1.8.1] - 2025-02-07 68 | ### Fixed 69 | - Added a constructor for `DeepLClient` that only takes an `authKey`, to fix the 70 | README example and be in line with `Translator`. 71 | - Un-deprecated the `Translator` and `TranslatorOptions` class and moved it to 72 | their constructors. The functionality in them continues to work and be supported, 73 | user code should just use `DeepLClient` and `DeepLClientOptions`. 74 | 75 | ## [1.8.0] - 2025-01-17 76 | ### Added 77 | - Added support for the Write API in the client library, the implementation 78 | can be found in the `DeepLClient` class. Please refer to the README for usage 79 | instructions. 80 | 81 | ### Changed 82 | - The main functionality of the library is now also exposed via the `DeepLClient` 83 | class. Please change your code to use this over the `Translator` class whenever 84 | convenient. 85 | 86 | ## [1.7.0] - 2024-11-15 87 | ### Added 88 | - Added `modelType` option to `translateText()` to use models with higher 89 | translation quality (available for some language pairs), or better latency. 90 | Options are `'quality_optimized'`, `'latency_optimized'`, and `'prefer_quality_optimized'` 91 | - Added the `modelTypeUsed` field to `translateText()` response, that 92 | indicates the translation model used when the `modelType` option is 93 | specified. 94 | 95 | ## [1.6.0] - 2024-09-17 96 | ### Added 97 | - Added `getBilledCharacters()` to text translation response. 98 | 99 | ## [1.5.1] - 2024-09-05 100 | ### Fixed 101 | - Fixed parsing for usage count and limit for large values. 102 | * Thanks to [lubo-dev](https://github.com/lubo-dev) in [#45](https://github.com/DeepLcom/deepl-java/pull/45). 103 | 104 | ## [1.5.0] - 2024-04-10 105 | ### Added 106 | - New language available: Arabic (MSA) (`'ar'`). Add language code constants and tests. 107 | 108 | Note: older library versions also support the new language, this update only 109 | adds new code constants. 110 | 111 | ### Fixed 112 | - Change document upload to use the path `/v2/document` instead of `/v2/document/` (no trailing `/`). 113 | Both paths will continue to work in the v2 version of the API, but `/v2/document` is the intended one. 114 | 115 | ## [1.4.0] - 2023-11-03 116 | ### Added 117 | - Add optional `context` parameter for text translation, that specifies 118 | additional context to influence translations, that is not translated itself. 119 | 120 | ### Fixed 121 | - Remove unused `commons-math` dependency 122 | 123 | ## [1.3.0] - 2023-06-09 124 | ### Fixed 125 | - Changed document translation to poll the server every 5 seconds. This should greatly reduce observed document translation processing time. 126 | - Fix getUsage request to be a HTTP GET request, not POST. 127 | 128 | ## [1.2.0] - 2023-03-22 129 | ### Added 130 | - Script to check our source code for license headers and a step for them in the CI. 131 | - Added system and java version information to the user-agent string that is sent with API calls, along with an opt-out. 132 | - Added method for applications that use this library to identify themselves in API requests they make. 133 | 134 | ## [1.1.0] - 2023-01-26 135 | ### Added 136 | - Add example maven project using this library. 137 | - New languages available: Korean (`'ko'`) and Norwegian (bokmål) (`'nb'`). Add 138 | language code constants and tests. 139 | 140 | Note: older library versions also support the new languages, this update only 141 | adds new code constants. 142 | 143 | ### Fixed 144 | - Send Formality options in API requests even if it is default. 145 | 146 | ## [1.0.1] - 2023-01-02 147 | ### Fixed 148 | - Always send SentenceSplittingMode option in requests. 149 | * [#3](https://github.com/DeepLcom/deepl-java/issues/3) thanks to 150 | [nicStuff](https://github.com/nicStuff) 151 | 152 | ## [1.0.0] - 2022-12-15 153 | ### Added 154 | - Add support for glossary management functions. 155 | 156 | ### Changed 157 | - `parsing.ErrorResponse` fields `message` and `detail` are now private, 158 | encapsulated with getters. 159 | 160 | ## [0.2.1] - 2022-10-19 161 | ### Fixed 162 | - Handle case where HTTP response is not valid JSON. 163 | 164 | ## [0.2.0] - 2022-09-26 165 | ### Added 166 | - Add new `Formality` options: `PreferLess` and `PreferMore`. 167 | 168 | ### Changed 169 | - Requests resulting in `503 Service Unavailable` errors are now retried. 170 | Attempting to download a document before translation is completed will now 171 | wait and retry (up to 5 times by default), rather than throwing an exception. 172 | 173 | ### Fixed 174 | - Use `Locale.ENGLISH` when changing string case. 175 | * Thanks to [seratch](https://github.com/seratch). 176 | - Avoid cases in `HttpContent` and `StreamUtils` where temporary objects might 177 | not be closed. 178 | * Thanks to [seratch](https://github.com/seratch). 179 | 180 | ## [0.1.3] - 2022-09-09 181 | ### Fixed 182 | - Fixed examples in readme. 183 | - `Usage.Detail` `count` and `limit` properties type changed from `int` to `long`. 184 | 185 | ## [0.1.2] - 2022-09-08 186 | ### Fixed 187 | - Fix publishing to Maven Central by including sourcesJar and javadocJar. 188 | 189 | ## [0.1.1] - 2022-09-08 190 | ### Fixed 191 | - Fix CI publishing step. 192 | 193 | ## [0.1.0] - 2022-09-08 194 | Initial version. 195 | 196 | [Unreleased]: https://github.com/DeepLcom/deepl-java/compare/v1.14.0...HEAD 197 | [1.14.0]: https://github.com/DeepLcom/deepl-java/compare/v1.13.0...v1.14.0 198 | [1.13.0]: https://github.com/DeepLcom/deepl-java/compare/v1.12.0...v1.13.0 199 | [1.12.0]: https://github.com/DeepLcom/deepl-java/compare/v1.11.0...v1.12.0 200 | [1.11.0]: https://github.com/DeepLcom/deepl-java/compare/v1.10.3...v1.11.0 201 | [1.10.3]: https://github.com/DeepLcom/deepl-java/compare/v1.10.2...v1.10.3 202 | [1.10.2]: https://github.com/DeepLcom/deepl-java/compare/v1.10.1...v1.10.2 203 | [1.10.1]: https://github.com/DeepLcom/deepl-java/compare/v1.10.0...v1.10.1 204 | [1.10.0]: https://github.com/DeepLcom/deepl-java/compare/v1.9.0...v1.10.0 205 | [1.9.0]: https://github.com/DeepLcom/deepl-java/compare/v1.8.1...v1.9.0 206 | [1.8.1]: https://github.com/DeepLcom/deepl-java/compare/v1.8.0...v1.8.1 207 | [1.8.0]: https://github.com/DeepLcom/deepl-java/compare/v1.7.0...v1.8.0 208 | [1.7.0]: https://github.com/DeepLcom/deepl-java/compare/v1.6.0...v1.7.0 209 | [1.6.0]: https://github.com/DeepLcom/deepl-java/compare/v1.5.1...v1.6.0 210 | [1.5.1]: https://github.com/DeepLcom/deepl-java/compare/v1.5.0...v1.5.1 211 | [1.5.0]: https://github.com/DeepLcom/deepl-java/compare/v1.4.0...v1.5.0 212 | [1.4.0]: https://github.com/DeepLcom/deepl-java/compare/v1.3.0...v1.4.0 213 | [1.3.0]: https://github.com/DeepLcom/deepl-java/compare/v1.2.0...v1.3.0 214 | [1.2.0]: https://github.com/DeepLcom/deepl-java/compare/v1.1.0...v1.2.0 215 | [1.1.0]: https://github.com/DeepLcom/deepl-java/compare/v1.0.1...v1.1.0 216 | [1.0.1]: https://github.com/DeepLcom/deepl-java/compare/v1.0.0...v1.0.1 217 | [1.0.0]: https://github.com/DeepLcom/deepl-java/compare/v0.2.1...v1.0.0 218 | [0.2.1]: https://github.com/DeepLcom/deepl-java/compare/v0.2.0...v0.2.1 219 | [0.2.0]: https://github.com/DeepLcom/deepl-java/compare/v0.1.3...v0.2.0 220 | [0.1.3]: https://github.com/DeepLcom/deepl-java/compare/v0.1.2...v0.1.3 221 | [0.1.2]: https://github.com/DeepLcom/deepl-java/compare/v0.1.1...v0.1.2 222 | [0.1.1]: https://github.com/DeepLcom/deepl-java/compare/v0.1.0...v0.1.1 223 | [0.1.0]: https://github.com/DeepLcom/deepl-java/releases/tag/v0.1.0 224 | --------------------------------------------------------------------------------