├── .github ├── dependabot.yml └── workflows │ ├── add_issues_to_kanban.yml │ └── run_ci.yml ├── .gitignore ├── .gitlab-ci.yml ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SECURITY.md ├── deepl-java ├── build.gradle.kts └── src │ ├── main │ └── java │ │ └── com │ │ └── deepl │ │ └── api │ │ ├── AppInfo.java │ │ ├── AuthorizationException.java │ │ ├── ConnectionException.java │ │ ├── DeepLApiVersion.java │ │ ├── DeepLClient.java │ │ ├── DeepLClientOptions.java │ │ ├── DeepLException.java │ │ ├── DocumentHandle.java │ │ ├── DocumentNotReadyException.java │ │ ├── DocumentStatus.java │ │ ├── DocumentTranslationException.java │ │ ├── DocumentTranslationOptions.java │ │ ├── Formality.java │ │ ├── GlossaryEntries.java │ │ ├── GlossaryInfo.java │ │ ├── GlossaryLanguagePair.java │ │ ├── GlossaryNotFoundException.java │ │ ├── HttpClientWrapper.java │ │ ├── IGlossary.java │ │ ├── Language.java │ │ ├── LanguageCode.java │ │ ├── LanguageType.java │ │ ├── MultilingualGlossaryDictionaryEntries.java │ │ ├── MultilingualGlossaryDictionaryInfo.java │ │ ├── MultilingualGlossaryInfo.java │ │ ├── NotFoundException.java │ │ ├── QuotaExceededException.java │ │ ├── SentenceSplittingMode.java │ │ ├── TextRephraseOptions.java │ │ ├── TextResult.java │ │ ├── TextTranslationOptions.java │ │ ├── TooManyRequestsException.java │ │ ├── Translator.java │ │ ├── TranslatorOptions.java │ │ ├── Usage.java │ │ ├── WriteResult.java │ │ ├── WritingStyle.java │ │ ├── WritingTone.java │ │ ├── http │ │ ├── HttpContent.java │ │ ├── HttpResponse.java │ │ └── HttpResponseStream.java │ │ ├── parsing │ │ ├── ErrorResponse.java │ │ ├── GlossaryLanguagesResponse.java │ │ ├── GlossaryListResponse.java │ │ ├── LanguageDeserializer.java │ │ ├── MultilingualGlossaryDictionaryEntriesResponse.java │ │ ├── MultilingualGlossaryDictionaryListResponse.java │ │ ├── MultilingualGlossaryListResponse.java │ │ ├── Parser.java │ │ ├── TextResponse.java │ │ ├── TextResultDeserializer.java │ │ ├── UsageDeserializer.java │ │ ├── WriteResponse.java │ │ └── WriteResultDeserializer.java │ │ └── utils │ │ ├── BackoffTimer.java │ │ ├── KeyValuePair.java │ │ ├── NamedStream.java │ │ └── StreamUtil.java │ └── test │ └── java │ └── com │ └── deepl │ └── api │ ├── GeneralTest.java │ ├── GlossaryCleanupUtility.java │ ├── GlossaryTest.java │ ├── MultilingualGlossaryCleanupUtility.java │ ├── MultilingualGlossaryTest.java │ ├── RephraseTextTest.java │ ├── SessionOptions.java │ ├── TestBase.java │ ├── TranslateDocumentTest.java │ └── TranslateTextTest.java ├── examples └── maven │ └── deepl-test-app │ ├── pom.xml │ └── src │ ├── main │ └── java │ │ └── com │ │ └── deepl │ │ └── deepltestapp │ │ ├── App.java │ │ └── annotation │ │ └── IntegrationTest.java │ └── test │ └── java │ └── com │ └── deepl │ └── deepltestapp │ └── DeepLIntegrationTest.java ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── license_checker.sh ├── settings.gradle.kts └── upgrading_to_multilingual_glossaries.md /.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 -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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/ -------------------------------------------------------------------------------- /.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: "openjdk: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: 208 | stage: publish 209 | extends: .publish 210 | dependencies: 211 | - build_scheduled 212 | - build_manual 213 | rules: 214 | - if: '$CI_COMMIT_TAG =~ /^v[0-9]+\.[0-9]+\.[0-9]+$/' 215 | script: 216 | - ./gradlew publish 217 | 218 | publish_manual: 219 | stage: publish 220 | extends: .publish 221 | when: manual 222 | dependencies: 223 | - build_scheduled 224 | - build_manual 225 | rules: 226 | - if: '$CI_COMMIT_TAG =~ /^v[0-9]+\.[0-9]+\.[0-9]+$/' 227 | script: 228 | - ./gradlew publish 229 | 230 | gitlab release: 231 | stage: publish 232 | extends: .create_gitlab_release 233 | rules: 234 | - if: '$CI_COMMIT_TAG =~ /^v[0-9]+\.[0-9]+\.[0-9]+$/' -------------------------------------------------------------------------------- /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 | ### Added 9 | 10 | ### Changed 11 | 12 | 13 | 14 | ## [1.10.0] - 2025-04-30 15 | ### Added 16 | * Added support for the /v3 Multilingual Glossary APIs in the client library 17 | while providing backwards compatability for the previous /v2 Glossary 18 | endpoints. Please refer to the README or 19 | [upgrading_to_multilingual_glossaries.md](upgrading_to_multilingual_glossaries.md) 20 | for usage instructions. 21 | * Added Ukrainian language code 22 | 23 | 24 | ## [1.9.0] - 2025-02-21 25 | ### Added 26 | * Allow specifying the API version to use. This is mostly for users who have an 27 | API subscription that includes an API key for CAT tool usage, who need to use 28 | the v1 API. 29 | 30 | 31 | ## [1.8.1] - 2025-02-07 32 | ### Fixed 33 | * Added a constructor for `DeepLClient` that only takes an `authKey`, to fix the 34 | README example and be in line with `Translator`. 35 | * Un-deprecated the `Translator` and `TranslatorOptions` class and moved it to 36 | their constructors. The functionality in them continues to work and be supported, 37 | user code should just use `DeepLClient` and `DeepLClientOptions`. 38 | 39 | ## [1.8.0] - 2025-01-17 40 | ### Added 41 | * Added support for the Write API in the client library, the implementation 42 | can be found in the `DeepLClient` class. Please refer to the README for usage 43 | instructions. 44 | ### Changed 45 | * The main functionality of the library is now also exposed via the `DeepLClient` 46 | class. Please change your code to use this over the `Translator` class whenever 47 | convenient. 48 | 49 | ## [1.7.0] - 2024-11-15 50 | ### Added 51 | * Added `modelType` option to `translateText()` to use models with higher 52 | translation quality (available for some language pairs), or better latency. 53 | Options are `'quality_optimized'`, `'latency_optimized'`, and `'prefer_quality_optimized'` 54 | * Added the `modelTypeUsed` field to `translateText()` response, that 55 | indicates the translation model used when the `modelType` option is 56 | specified. 57 | 58 | 59 | ## [1.6.0] - 2024-09-17 60 | ### Added 61 | * Added `getBilledCharacters()` to text translation response. 62 | 63 | 64 | ## [1.5.1] - 2024-09-05 65 | ### Fixed 66 | * Fixed parsing for usage count and limit for large values. 67 | * Thanks to [lubo-dev](https://github.com/lubo-dev) in [#45](https://github.com/DeepLcom/deepl-java/pull/45). 68 | 69 | 70 | ## [1.5.0] - 2024-04-10 71 | ### Added 72 | * New language available: Arabic (MSA) (`'ar'`). Add language code constants and tests. 73 | 74 | Note: older library versions also support the new language, this update only 75 | adds new code constants. 76 | ### Fixed 77 | * Change document upload to use the path `/v2/document` instead of `/v2/document/` (no trailing `/`). 78 | Both paths will continue to work in the v2 version of the API, but `/v2/document` is the intended one. 79 | 80 | 81 | ## [1.4.0] - 2023-11-03 82 | ### Added 83 | * Add optional `context` parameter for text translation, that specifies 84 | additional context to influence translations, that is not translated itself. 85 | ### Fixed 86 | * Remove unused `commons-math` dependency 87 | 88 | 89 | ## [1.3.0] - 2023-06-09 90 | ### Fixed 91 | * Changed document translation to poll the server every 5 seconds. This should greatly reduce observed document translation processing time. 92 | * Fix getUsage request to be a HTTP GET request, not POST. 93 | 94 | 95 | ## [1.2.0] - 2023-03-22 96 | ### Added 97 | * Script to check our source code for license headers and a step for them in the CI. 98 | * Added system and java version information to the user-agent string that is sent with API calls, along with an opt-out. 99 | * Added method for applications that use this library to identify themselves in API requests they make. 100 | 101 | 102 | ## [1.1.0] - 2023-01-26 103 | ### Added 104 | * Add example maven project using this library. 105 | * New languages available: Korean (`'ko'`) and Norwegian (bokmål) (`'nb'`). Add 106 | language code constants and tests. 107 | 108 | Note: older library versions also support the new languages, this update only 109 | adds new code constants. 110 | ### Fixed 111 | * Send Formality options in API requests even if it is default. 112 | 113 | 114 | ## [1.0.1] - 2023-01-02 115 | ### Fixed 116 | * Always send SentenceSplittingMode option in requests. 117 | * [#3](https://github.com/DeepLcom/deepl-java/issues/3) thanks to 118 | [nicStuff](https://github.com/nicStuff) 119 | 120 | 121 | ## [1.0.0] - 2022-12-15 122 | ### Added 123 | * Add support for glossary management functions. 124 | ### Changed 125 | * `parsing.ErrorResponse` fields `message` and `detail` are now private, 126 | encapsulated with getters. 127 | 128 | 129 | ## [0.2.1] - 2022-10-19 130 | ### Fixed 131 | * Handle case where HTTP response is not valid JSON. 132 | 133 | 134 | ## [0.2.0] - 2022-09-26 135 | ### Added 136 | * Add new `Formality` options: `PreferLess` and `PreferMore`. 137 | ### Changed 138 | * Requests resulting in `503 Service Unavailable` errors are now retried. 139 | Attempting to download a document before translation is completed will now 140 | wait and retry (up to 5 times by default), rather than throwing an exception. 141 | ### Fixed 142 | * Use `Locale.ENGLISH` when changing string case. 143 | * Thanks to [seratch](https://github.com/seratch). 144 | * Avoid cases in `HttpContent` and `StreamUtils` where temporary objects might 145 | not be closed. 146 | * Thanks to [seratch](https://github.com/seratch). 147 | 148 | 149 | ## [0.1.3] - 2022-09-09 150 | ### Fixed 151 | * Fixed examples in readme. 152 | * `Usage.Detail` `count` and `limit` properties type changed from `int` to `long`. 153 | 154 | 155 | ## [0.1.2] - 2022-09-08 156 | ### Fixed 157 | * Fix publishing to Maven Central by including sourcesJar and javadocJar. 158 | 159 | 160 | ## [0.1.1] - 2022-09-08 161 | ### Fixed 162 | * Fix CI publishing step. 163 | 164 | 165 | ## [0.1.0] - 2022-09-08 166 | Initial version. 167 | 168 | 169 | [Unreleased]: https://github.com/DeepLcom/deepl-java/compare/v1.10.0...HEAD 170 | [1.10.0]: https://github.com/DeepLcom/deepl-java/compare/v1.9.0...v1.10.0 171 | [1.9.0]: https://github.com/DeepLcom/deepl-java/compare/v1.8.1...v1.9.0 172 | [1.8.1]: https://github.com/DeepLcom/deepl-java/compare/v1.8.0...v1.8.1 173 | [1.8.0]: https://github.com/DeepLcom/deepl-java/compare/v1.7.0...v1.8.0 174 | [1.7.0]: https://github.com/DeepLcom/deepl-java/compare/v1.6.0...v1.7.0 175 | [1.6.0]: https://github.com/DeepLcom/deepl-java/compare/v1.5.1...v1.6.0 176 | [1.5.1]: https://github.com/DeepLcom/deepl-java/compare/v1.5.0...v1.5.1 177 | [1.5.0]: https://github.com/DeepLcom/deepl-java/compare/v1.4.0...v1.5.0 178 | [1.4.0]: https://github.com/DeepLcom/deepl-java/compare/v1.3.0...v1.4.0 179 | [1.3.0]: https://github.com/DeepLcom/deepl-java/compare/v1.2.0...v1.3.0 180 | [1.2.0]: https://github.com/DeepLcom/deepl-java/compare/v1.1.0...v1.2.0 181 | [1.1.0]: https://github.com/DeepLcom/deepl-java/compare/v1.0.1...v1.1.0 182 | [1.0.1]: https://github.com/DeepLcom/deepl-java/compare/v1.0.0...v1.0.1 183 | [1.0.0]: https://github.com/DeepLcom/deepl-java/compare/v0.2.1...v1.0.0 184 | [0.2.1]: https://github.com/DeepLcom/deepl-java/compare/v0.2.0...v0.2.1 185 | [0.2.0]: https://github.com/DeepLcom/deepl-java/compare/v0.1.3...v0.2.0 186 | [0.1.3]: https://github.com/DeepLcom/deepl-java/compare/v0.1.2...v0.1.3 187 | [0.1.2]: https://github.com/DeepLcom/deepl-java/compare/v0.1.1...v0.1.2 188 | [0.1.1]: https://github.com/DeepLcom/deepl-java/compare/v0.1.0...v0.1.1 189 | [0.1.0]: https://github.com/DeepLcom/deepl-java/releases/tag/v0.1.0 190 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `java-library` 3 | `maven-publish` 4 | signing 5 | id("com.diffplug.spotless") version "6.8.0" 6 | } 7 | 8 | group = "com.deepl.api" 9 | version = "1.10.0" 10 | 11 | val sharedManifest = the().manifest { 12 | attributes ( 13 | "Implementation-Title" to "Gradle", 14 | "Implementation-Version" to version 15 | ) 16 | } 17 | java { 18 | sourceCompatibility = JavaVersion.VERSION_1_8 19 | targetCompatibility = JavaVersion.VERSION_1_8 20 | } 21 | 22 | repositories { 23 | mavenCentral() 24 | } 25 | 26 | dependencies { 27 | implementation("org.jetbrains:annotations:20.1.0") 28 | testImplementation("org.junit.jupiter:junit-jupiter:5.10.0") 29 | testImplementation("org.mockito:mockito-inline:4.11.0") 30 | implementation("org.apache.httpcomponents:httpclient:4.5.2") { because("java.net.HttpURLConnection does not support PATCH") } 31 | 32 | // implementation("com.google.guava:guava:30.1.1-jre") 33 | implementation("com.google.code.gson:gson:2.10.1") 34 | } 35 | 36 | 37 | tasks.named("test") { 38 | useJUnitPlatform() 39 | } 40 | 41 | spotless { 42 | java { 43 | googleJavaFormat("1.7") 44 | removeUnusedImports() 45 | } 46 | } 47 | 48 | tasks.register("sourcesJar") { 49 | archiveClassifier.set("sources") 50 | from(sourceSets.main.get().allJava) 51 | manifest = project.the().manifest { 52 | from(sharedManifest) 53 | } 54 | } 55 | 56 | tasks.register("javadocJar") { 57 | archiveClassifier.set("javadoc") 58 | from(tasks.javadoc.get().destinationDir) 59 | manifest = project.the().manifest { 60 | from(sharedManifest) 61 | } 62 | } 63 | 64 | publishing { 65 | repositories { 66 | maven { 67 | val mavenUploadUsername: String? by project 68 | val mavenUploadPassword: String? by project 69 | name = "MavenCentral" 70 | val releasesRepoUrl = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/" 71 | val snapshotsRepoUrl = "https://s01.oss.sonatype.org/content/repositories/snapshots/" 72 | url = uri(if (version.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl) 73 | credentials { 74 | username = mavenUploadUsername 75 | password = mavenUploadPassword 76 | } 77 | } 78 | } 79 | publications { 80 | create("mavenJava") { 81 | from(components["java"]) 82 | artifact(tasks["sourcesJar"]) 83 | artifact(tasks["javadocJar"]) 84 | pom { 85 | name.set("deepl-java") 86 | description.set("DeepL API Java Client Library") 87 | url.set("https://www.github.com/DeepLcom/deepl-java") 88 | properties.set(mapOf( 89 | "java.version" to "1.8", 90 | "project.build.sourceEncoding" to "UTF-8", 91 | "project.reporting.outputEncoding" to "UTF-8" 92 | )) 93 | licenses { 94 | license { 95 | name.set("MIT License") 96 | url.set("https://www.opensource.org/licenses/mit-license.php") 97 | } 98 | } 99 | developers { 100 | developer { 101 | id.set("deepl") 102 | name.set("DeepL SE") 103 | email.set("open-source@deepl.com") 104 | } 105 | } 106 | organization { 107 | name.set("DeepL SE") 108 | url.set("https://www.deepl.com") 109 | } 110 | scm { 111 | connection.set("scm:git:git://github.com/DeepLcom/deepl-java.git") 112 | developerConnection.set("scm:git:ssh://github.com/DeepLcom/deepl-java.git") 113 | url.set("https://www.github.com/DeepLcom/deepl-java") 114 | } 115 | } 116 | } 117 | } 118 | } 119 | 120 | signing { 121 | val signingKey: String? by project 122 | val signingPassword: String? by project 123 | useInMemoryPgpKeys(signingKey, signingPassword) 124 | sign(publishing.publications["mavenJava"]) 125 | } 126 | 127 | -------------------------------------------------------------------------------- /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/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/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/DeepLApiVersion.java: -------------------------------------------------------------------------------- 1 | package com.deepl.api; 2 | 3 | public enum DeepLApiVersion { 4 | VERSION_1("v1"), 5 | VERSION_2("v2"); 6 | 7 | /** 8 | * How the version is represented in the URL string. Does not include any slashes (/). Example: 9 | * "v2" 10 | */ 11 | private final String urlRepresentation; 12 | 13 | private DeepLApiVersion(String urlRepresentation) { 14 | this.urlRepresentation = urlRepresentation; 15 | } 16 | 17 | public String toString() { 18 | return this.urlRepresentation; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /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/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/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 | -------------------------------------------------------------------------------- /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/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/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/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 { 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 | -------------------------------------------------------------------------------- /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/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/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 | -------------------------------------------------------------------------------- /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/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/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/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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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/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/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/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/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/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/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/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 { 17 | private Formality formality; 18 | private String glossaryId; 19 | private SentenceSplittingMode sentenceSplittingMode; 20 | private boolean preserveFormatting = false; 21 | private String context; 22 | private String tagHandling; 23 | private String modelType; 24 | private boolean outlineDetection = true; 25 | private Iterable ignoreTags; 26 | private Iterable nonSplittingTags; 27 | private Iterable splittingTags; 28 | 29 | /** 30 | * Sets whether translations should lean toward formal or informal language. This option is only 31 | * applicable for target languages that support the formality option. By default, this value is 32 | * null andnull translations use the default formality. 33 | * 34 | * @see Language#getSupportsFormality() 35 | * @see Formality 36 | */ 37 | public TextTranslationOptions setFormality(Formality formality) { 38 | this.formality = formality; 39 | return this; 40 | } 41 | 42 | /** 43 | * Sets the ID of a glossary to use with the translation. By default, this value is 44 | * null and no glossary is used. 45 | */ 46 | public TextTranslationOptions setGlossaryId(String glossaryId) { 47 | this.glossaryId = glossaryId; 48 | return this; 49 | } 50 | 51 | /** 52 | * Sets the glossary to use with the translation. By default, this value is null and 53 | * no glossary is used. 54 | */ 55 | public TextTranslationOptions setGlossary(IGlossary glossary) { 56 | return setGlossary(glossary.getGlossaryId()); 57 | } 58 | 59 | /** 60 | * Sets the glossary to use with the translation. By default, this value is null and 61 | * no glossary is used. 62 | */ 63 | public TextTranslationOptions setGlossary(String glossaryId) { 64 | this.glossaryId = glossaryId; 65 | return this; 66 | } 67 | 68 | /** 69 | * Specifies additional context to influence translations, that is not translated itself. 70 | * Characters in the `context` parameter are not counted toward billing. See the API documentation 71 | * for more information and example usage. 72 | */ 73 | public TextTranslationOptions setContext(String context) { 74 | this.context = context; 75 | return this; 76 | } 77 | 78 | /** 79 | * Specifies how input translation text should be split into sentences. By default, this value is 80 | * null and the default sentence splitting mode is used. 81 | * 82 | * @see SentenceSplittingMode 83 | */ 84 | public TextTranslationOptions setSentenceSplittingMode( 85 | SentenceSplittingMode sentenceSplittingMode) { 86 | this.sentenceSplittingMode = sentenceSplittingMode; 87 | return this; 88 | } 89 | 90 | /** 91 | * Sets whether formatting should be preserved in translations. Set to true to 92 | * prevent the translation engine from correcting some formatting aspects, and instead leave the 93 | * formatting unchanged, default is false. 94 | */ 95 | public TextTranslationOptions setPreserveFormatting(boolean preserveFormatting) { 96 | this.preserveFormatting = preserveFormatting; 97 | return this; 98 | } 99 | 100 | /** 101 | * Set the type of tags to parse before translation, only "xml" and "html" 102 | * are currently available. By default, this value is null and no 103 | * tag-handling is used. 104 | */ 105 | public TextTranslationOptions setTagHandling(String tagHandling) { 106 | this.tagHandling = tagHandling; 107 | return this; 108 | } 109 | 110 | /** 111 | * Set the type of model to use for a text translation. Currently supported values: 112 | * "quality_optimized" use a translation model that maximizes translation quality, at the 113 | * cost of response time. This option may be unavailable for some language pairs and the API would 114 | * respond with an error in this case; "prefer_quality_optimized" use the 115 | * highest-quality translation model for the given language pair; "latency_optimized" 116 | * use a translation model that minimizes response time, at the cost of translation 117 | * quality. 118 | */ 119 | public TextTranslationOptions setModelType(String modelType) { 120 | this.modelType = modelType; 121 | return this; 122 | } 123 | 124 | /** 125 | * Sets whether outline detection is used; set to false to disable automatic tag 126 | * detection, default is true. 127 | */ 128 | public TextTranslationOptions setOutlineDetection(boolean outlineDetection) { 129 | this.outlineDetection = outlineDetection; 130 | return this; 131 | } 132 | 133 | /** 134 | * Sets the list of XML tags containing content that should not be translated. By default, this 135 | * value is null and no tags are specified. 136 | */ 137 | public TextTranslationOptions setIgnoreTags(Iterable ignoreTags) { 138 | this.ignoreTags = ignoreTags; 139 | return this; 140 | } 141 | 142 | /** 143 | * Sets the list of XML tags that should not be used to split text into sentences. By default, 144 | * this value is null and no tags are specified. 145 | */ 146 | public TextTranslationOptions setNonSplittingTags(Iterable nonSplittingTags) { 147 | this.nonSplittingTags = nonSplittingTags; 148 | return this; 149 | } 150 | 151 | /** 152 | * Set the list of XML tags that should be used to split text into sentences. By default, this 153 | * value is null and no tags are specified. 154 | */ 155 | public TextTranslationOptions setSplittingTags(Iterable splittingTags) { 156 | this.splittingTags = splittingTags; 157 | return this; 158 | } 159 | 160 | /** Gets the current formality setting. */ 161 | public Formality getFormality() { 162 | return formality; 163 | } 164 | 165 | /** Gets the current glossary ID. */ 166 | public String getGlossaryId() { 167 | return glossaryId; 168 | } 169 | 170 | /** Gets the current sentence splitting mode. */ 171 | public SentenceSplittingMode getSentenceSplittingMode() { 172 | return sentenceSplittingMode; 173 | } 174 | 175 | /** Gets the current preserve formatting setting. */ 176 | public boolean isPreserveFormatting() { 177 | return preserveFormatting; 178 | } 179 | 180 | /** Gets the current context. */ 181 | public String getContext() { 182 | return context; 183 | } 184 | 185 | /** Gets the current model type. */ 186 | public String getModelType() { 187 | return modelType; 188 | } 189 | 190 | /** Gets the current tag handling setting. */ 191 | public String getTagHandling() { 192 | return tagHandling; 193 | } 194 | 195 | /** Gets the current outline detection setting. */ 196 | public boolean isOutlineDetection() { 197 | return outlineDetection; 198 | } 199 | 200 | /** Gets the current ignore tags list. */ 201 | public Iterable getIgnoreTags() { 202 | return ignoreTags; 203 | } 204 | 205 | /** Gets the current non-splitting tags list. */ 206 | public Iterable getNonSplittingTags() { 207 | return nonSplittingTags; 208 | } 209 | 210 | /** Gets the current splitting tags list. */ 211 | public Iterable getSplittingTags() { 212 | return splittingTags; 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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/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/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/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/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/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 | -------------------------------------------------------------------------------- /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/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/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/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/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/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/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/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 String parseErrorMessage(String json) { 90 | ErrorResponse response = gson.fromJson(json, ErrorResponse.class); 91 | 92 | if (response != null) { 93 | return response.getErrorMessage(); 94 | } else { 95 | return ""; 96 | } 97 | } 98 | 99 | static @Nullable Integer getAsIntOrNull(JsonObject jsonObject, String parameterName) { 100 | if (!jsonObject.has(parameterName)) return null; 101 | return jsonObject.get(parameterName).getAsInt(); 102 | } 103 | 104 | static @Nullable Long getAsLongOrNull(JsonObject jsonObject, String parameterName) { 105 | if (!jsonObject.has(parameterName)) return null; 106 | return jsonObject.get(parameterName).getAsLong(); 107 | } 108 | 109 | static @Nullable String getAsStringOrNull(JsonObject jsonObject, String parameterName) { 110 | if (!jsonObject.has(parameterName)) return null; 111 | return jsonObject.get(parameterName).getAsString(); 112 | } 113 | 114 | static @Nullable Boolean getAsBooleanOrNull(JsonObject jsonObject, String parameterName) { 115 | if (!jsonObject.has(parameterName)) return null; 116 | return jsonObject.get(parameterName).getAsBoolean(); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /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/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/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/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/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/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/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/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/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/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 | -------------------------------------------------------------------------------- /deepl-java/src/test/java/com/deepl/api/GlossaryTest.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.*; 9 | import org.junit.jupiter.api.*; 10 | 11 | public class GlossaryTest extends TestBase { 12 | private final String invalidGlossaryId = "invalid_glossary_id"; 13 | private final String nonexistentGlossaryId = "96ab91fd-e715-41a1-adeb-5d701f84a483"; 14 | private final String sourceLang = "en"; 15 | private final String targetLang = "de"; 16 | 17 | private final GlossaryEntries testEntries = GlossaryEntries.fromTsv("Hello\tHallo"); 18 | 19 | @Test 20 | void TestGlossaryEntries() { 21 | GlossaryEntries testEntries = new GlossaryEntries(); 22 | testEntries.put("apple", "Apfel"); 23 | testEntries.put("crab apple", "Holzapfel"); 24 | Assertions.assertEquals( 25 | testEntries, GlossaryEntries.fromTsv("apple\tApfel\n crab apple \t Holzapfel ")); 26 | Assertions.assertThrows(Exception.class, () -> GlossaryEntries.fromTsv("")); 27 | Assertions.assertThrows( 28 | Exception.class, () -> GlossaryEntries.fromTsv("Küche\tKitchen\nKüche\tCuisine")); 29 | Assertions.assertThrows(Exception.class, () -> GlossaryEntries.fromTsv("A\tB\tC")); 30 | Assertions.assertThrows(Exception.class, () -> GlossaryEntries.fromTsv("A\t ")); 31 | 32 | Assertions.assertThrows( 33 | Exception.class, () -> new GlossaryEntries(Collections.singletonMap("A", "B\tC"))); 34 | } 35 | 36 | @Test 37 | void testGlossaryCreate() throws Exception { 38 | Translator translator = createTranslator(); 39 | try (GlossaryCleanupUtility cleanup = new GlossaryCleanupUtility(translator)) { 40 | GlossaryEntries entries = new GlossaryEntries(Collections.singletonMap("Hello", "Hallo")); 41 | System.out.println(entries); 42 | for (Map.Entry entry : entries.entrySet()) { 43 | System.out.println(entry.getKey() + ":" + entry.getValue()); 44 | } 45 | String glossaryName = cleanup.getGlossaryName(); 46 | GlossaryInfo glossary = 47 | translator.createGlossary(glossaryName, sourceLang, targetLang, entries); 48 | 49 | Assertions.assertEquals(glossaryName, glossary.getName()); 50 | Assertions.assertEquals(sourceLang, glossary.getSourceLang()); 51 | Assertions.assertEquals(targetLang, glossary.getTargetLang()); 52 | Assertions.assertEquals(1, glossary.getEntryCount()); 53 | 54 | GlossaryInfo getResult = translator.getGlossary(glossary.getGlossaryId()); 55 | Assertions.assertEquals(getResult.getName(), glossary.getName()); 56 | Assertions.assertEquals(getResult.getSourceLang(), glossary.getSourceLang()); 57 | Assertions.assertEquals(getResult.getTargetLang(), glossary.getTargetLang()); 58 | Assertions.assertEquals(getResult.getCreationTime(), glossary.getCreationTime()); 59 | Assertions.assertEquals(getResult.getEntryCount(), glossary.getEntryCount()); 60 | } 61 | } 62 | 63 | @Test 64 | void testGlossaryCreateLarge() throws Exception { 65 | Translator translator = createTranslator(); 66 | try (GlossaryCleanupUtility cleanup = new GlossaryCleanupUtility(translator)) { 67 | String glossaryName = cleanup.getGlossaryName(); 68 | 69 | Map entryPairs = new HashMap<>(); 70 | for (int i = 0; i < 10000; i++) { 71 | entryPairs.put(String.format("Source-%d", i), String.format("Target-%d", i)); 72 | } 73 | GlossaryEntries entries = new GlossaryEntries(entryPairs); 74 | Assertions.assertTrue(entries.toTsv().length() > 100000); 75 | GlossaryInfo glossary = 76 | translator.createGlossary(glossaryName, sourceLang, targetLang, entries); 77 | 78 | Assertions.assertEquals(glossaryName, glossary.getName()); 79 | Assertions.assertEquals(sourceLang, glossary.getSourceLang()); 80 | Assertions.assertEquals(targetLang, glossary.getTargetLang()); 81 | Assertions.assertEquals(entryPairs.size(), glossary.getEntryCount()); 82 | } 83 | } 84 | 85 | @Test 86 | void testGlossaryCreateCsv() throws Exception { 87 | Translator translator = createTranslator(); 88 | try (GlossaryCleanupUtility cleanup = new GlossaryCleanupUtility(translator)) { 89 | String glossaryName = cleanup.getGlossaryName(); 90 | Map expectedEntries = new HashMap<>(); 91 | expectedEntries.put("sourceEntry1", "targetEntry1"); 92 | expectedEntries.put("source\"Entry", "target,Entry"); 93 | 94 | String csvContent = 95 | "sourceEntry1,targetEntry1,en,de\n\"source\"\"Entry\",\"target,Entry\",en,de"; 96 | 97 | GlossaryInfo glossary = 98 | translator.createGlossaryFromCsv(glossaryName, sourceLang, targetLang, csvContent); 99 | 100 | GlossaryEntries entries = translator.getGlossaryEntries(glossary); 101 | Assertions.assertEquals(expectedEntries, entries); 102 | } 103 | } 104 | 105 | @Test 106 | void testGlossaryCreateInvalid() throws Exception { 107 | Translator translator = createTranslator(); 108 | try (GlossaryCleanupUtility cleanup = new GlossaryCleanupUtility(translator)) { 109 | String glossaryName = cleanup.getGlossaryName(); 110 | Assertions.assertThrows( 111 | Exception.class, 112 | () -> translator.createGlossary("", sourceLang, targetLang, testEntries)); 113 | Assertions.assertThrows( 114 | Exception.class, () -> translator.createGlossary(glossaryName, "en", "xx", testEntries)); 115 | } 116 | } 117 | 118 | @Test 119 | void testGlossaryGet() throws Exception { 120 | Translator translator = createTranslator(); 121 | try (GlossaryCleanupUtility cleanup = new GlossaryCleanupUtility(translator)) { 122 | String glossaryName = cleanup.getGlossaryName(); 123 | GlossaryInfo createdGlossary = 124 | translator.createGlossary(glossaryName, sourceLang, targetLang, testEntries); 125 | 126 | GlossaryInfo glossary = translator.getGlossary(createdGlossary.getGlossaryId()); 127 | Assertions.assertEquals(createdGlossary.getGlossaryId(), glossary.getGlossaryId()); 128 | Assertions.assertEquals(glossaryName, glossary.getName()); 129 | Assertions.assertEquals(sourceLang, glossary.getSourceLang()); 130 | Assertions.assertEquals(targetLang, glossary.getTargetLang()); 131 | Assertions.assertEquals(createdGlossary.getCreationTime(), glossary.getCreationTime()); 132 | Assertions.assertEquals(testEntries.size(), glossary.getEntryCount()); 133 | } 134 | Assertions.assertThrows(DeepLException.class, () -> translator.getGlossary(invalidGlossaryId)); 135 | Assertions.assertThrows( 136 | GlossaryNotFoundException.class, () -> translator.getGlossary(nonexistentGlossaryId)); 137 | } 138 | 139 | @Test 140 | void testGlossaryGetEntries() throws Exception { 141 | Translator translator = createTranslator(); 142 | try (GlossaryCleanupUtility cleanup = new GlossaryCleanupUtility(translator)) { 143 | String glossaryName = cleanup.getGlossaryName(); 144 | GlossaryEntries entries = new GlossaryEntries(); 145 | entries.put("Apple", "Apfel"); 146 | entries.put("Banana", "Banane"); 147 | entries.put("A%=&", "B&=%"); 148 | entries.put("\u0394\u3041", "\u6DF1"); 149 | entries.put("\uD83E\uDEA8", "\uD83E\uDEB5"); 150 | 151 | GlossaryInfo createdGlossary = 152 | translator.createGlossary(glossaryName, sourceLang, targetLang, entries); 153 | Assertions.assertEquals(entries, translator.getGlossaryEntries(createdGlossary)); 154 | Assertions.assertEquals( 155 | entries, translator.getGlossaryEntries(createdGlossary.getGlossaryId())); 156 | } 157 | 158 | Assertions.assertThrows( 159 | DeepLException.class, () -> translator.getGlossaryEntries(invalidGlossaryId)); 160 | Assertions.assertThrows( 161 | GlossaryNotFoundException.class, 162 | () -> translator.getGlossaryEntries(nonexistentGlossaryId)); 163 | } 164 | 165 | @Test 166 | void testGlossaryList() throws Exception { 167 | Translator translator = createTranslator(); 168 | try (GlossaryCleanupUtility cleanup = new GlossaryCleanupUtility(translator)) { 169 | String glossaryName = cleanup.getGlossaryName(); 170 | translator.createGlossary(glossaryName, sourceLang, targetLang, testEntries); 171 | 172 | List glossaries = translator.listGlossaries(); 173 | Assertions.assertTrue( 174 | glossaries.stream() 175 | .anyMatch((glossaryInfo -> Objects.equals(glossaryInfo.getName(), glossaryName)))); 176 | } 177 | } 178 | 179 | @Test 180 | void testGlossaryDelete() throws Exception { 181 | Translator translator = createTranslator(); 182 | try (GlossaryCleanupUtility cleanup = new GlossaryCleanupUtility(translator)) { 183 | String glossaryName = cleanup.getGlossaryName(); 184 | GlossaryInfo glossary = 185 | translator.createGlossary(glossaryName, sourceLang, targetLang, testEntries); 186 | 187 | translator.deleteGlossary(glossary); 188 | Assertions.assertThrows( 189 | GlossaryNotFoundException.class, () -> translator.deleteGlossary(glossary)); 190 | 191 | Assertions.assertThrows( 192 | DeepLException.class, () -> translator.deleteGlossary(invalidGlossaryId)); 193 | Assertions.assertThrows( 194 | GlossaryNotFoundException.class, () -> translator.deleteGlossary(nonexistentGlossaryId)); 195 | } 196 | } 197 | 198 | @Test 199 | void testGlossaryTranslateTextSentence() throws Exception { 200 | Translator translator = createTranslator(); 201 | try (GlossaryCleanupUtility cleanup = new GlossaryCleanupUtility(translator)) { 202 | String glossaryName = cleanup.getGlossaryName(); 203 | GlossaryEntries entries = 204 | new GlossaryEntries() { 205 | { 206 | put("artist", "Maler"); 207 | put("prize", "Gewinn"); 208 | } 209 | }; 210 | String inputText = "The artist was awarded a prize."; 211 | 212 | GlossaryInfo glossary = 213 | translator.createGlossary(glossaryName, sourceLang, targetLang, entries); 214 | 215 | TextResult result = 216 | translator.translateText( 217 | inputText, 218 | sourceLang, 219 | targetLang, 220 | new TextTranslationOptions().setGlossary(glossary.getGlossaryId())); 221 | if (!isMockServer) { 222 | Assertions.assertTrue(result.getText().contains("Maler")); 223 | Assertions.assertTrue(result.getText().contains("Gewinn")); 224 | } 225 | 226 | // It is also possible to specify GlossaryInfo 227 | result = 228 | translator.translateText( 229 | inputText, 230 | sourceLang, 231 | targetLang, 232 | new TextTranslationOptions().setGlossary(glossary)); 233 | if (!isMockServer) { 234 | Assertions.assertTrue(result.getText().contains("Maler")); 235 | Assertions.assertTrue(result.getText().contains("Gewinn")); 236 | } 237 | } 238 | } 239 | 240 | @Test 241 | void testGlossaryTranslateTextBasic() throws Exception { 242 | Translator translator = createTranslator(); 243 | try (GlossaryCleanupUtility cleanupEnDe = new GlossaryCleanupUtility(translator, "EnDe"); 244 | GlossaryCleanupUtility cleanupDeEn = new GlossaryCleanupUtility(translator, "DeEn")) { 245 | String glossaryNameEnDe = cleanupEnDe.getGlossaryName(); 246 | String glossaryNameDeEn = cleanupDeEn.getGlossaryName(); 247 | List textsEn = 248 | new ArrayList() { 249 | { 250 | add("Apple"); 251 | add("Banana"); 252 | } 253 | }; 254 | List textsDe = 255 | new ArrayList() { 256 | { 257 | add("Apfel"); 258 | add("Banane"); 259 | } 260 | }; 261 | GlossaryEntries glossaryEntriesEnDe = new GlossaryEntries(); 262 | GlossaryEntries glossaryEntriesDeEn = new GlossaryEntries(); 263 | for (int i = 0; i < textsEn.size(); i++) { 264 | glossaryEntriesEnDe.put(textsEn.get(i), textsDe.get(i)); 265 | glossaryEntriesDeEn.put(textsDe.get(i), textsEn.get(i)); 266 | } 267 | 268 | GlossaryInfo glossaryEnDe = 269 | translator.createGlossary(glossaryNameEnDe, "en", "de", glossaryEntriesEnDe); 270 | GlossaryInfo glossaryDeEn = 271 | translator.createGlossary(glossaryNameDeEn, "de", "en", glossaryEntriesDeEn); 272 | 273 | List result = 274 | translator.translateText( 275 | textsEn, "en", "de", new TextTranslationOptions().setGlossary(glossaryEnDe)); 276 | Assertions.assertArrayEquals( 277 | textsDe.toArray(), result.stream().map(TextResult::getText).toArray()); 278 | 279 | result = 280 | translator.translateText( 281 | textsDe, 282 | "de", 283 | "en-US", 284 | new TextTranslationOptions().setGlossary(glossaryDeEn.getGlossaryId())); 285 | Assertions.assertArrayEquals( 286 | textsEn.toArray(), result.stream().map(TextResult::getText).toArray()); 287 | } 288 | } 289 | 290 | @Test 291 | void testGlossaryTranslateDocument() throws Exception { 292 | Translator translator = createTranslator(); 293 | try (GlossaryCleanupUtility cleanup = new GlossaryCleanupUtility(translator)) { 294 | String glossaryName = cleanup.getGlossaryName(); 295 | File inputFile = createInputFile("artist\nprize"); 296 | File outputFile = createOutputFile(); 297 | String expectedOutput = "Maler\nGewinn"; 298 | GlossaryEntries entries = 299 | new GlossaryEntries() { 300 | { 301 | put("artist", "Maler"); 302 | put("prize", "Gewinn"); 303 | } 304 | }; 305 | 306 | GlossaryInfo glossary = 307 | translator.createGlossary(glossaryName, sourceLang, targetLang, entries); 308 | 309 | translator.translateDocument( 310 | inputFile, 311 | outputFile, 312 | sourceLang, 313 | targetLang, 314 | new DocumentTranslationOptions().setGlossary(glossary)); 315 | Assertions.assertEquals(expectedOutput, readFromFile(outputFile)); 316 | boolean ignored = outputFile.delete(); 317 | 318 | translator.translateDocument( 319 | inputFile, 320 | outputFile, 321 | sourceLang, 322 | targetLang, 323 | new DocumentTranslationOptions().setGlossary(glossary.getGlossaryId())); 324 | Assertions.assertEquals(expectedOutput, readFromFile(outputFile)); 325 | } 326 | } 327 | 328 | @Test 329 | void testGlossaryTranslateTextInvalid() throws Exception { 330 | Translator translator = createTranslator(); 331 | try (GlossaryCleanupUtility cleanupEnDe = new GlossaryCleanupUtility(translator, "EnDe"); 332 | GlossaryCleanupUtility cleanupDeEn = new GlossaryCleanupUtility(translator, "DeEn")) { 333 | String glossaryNameEnDe = cleanupEnDe.getGlossaryName(); 334 | String glossaryNameDeEn = cleanupDeEn.getGlossaryName(); 335 | 336 | GlossaryInfo glossaryEnDe = 337 | translator.createGlossary(glossaryNameEnDe, "en", "de", testEntries); 338 | GlossaryInfo glossaryDeEn = 339 | translator.createGlossary(glossaryNameDeEn, "de", "en", testEntries); 340 | 341 | IllegalArgumentException exception = 342 | Assertions.assertThrows( 343 | IllegalArgumentException.class, 344 | () -> 345 | translator.translateText( 346 | "test", null, "de", new TextTranslationOptions().setGlossary(glossaryEnDe))); 347 | Assertions.assertTrue(exception.getMessage().contains("sourceLang is required")); 348 | 349 | exception = 350 | Assertions.assertThrows( 351 | IllegalArgumentException.class, 352 | () -> 353 | translator.translateText( 354 | "test", "de", "en", new TextTranslationOptions().setGlossary(glossaryDeEn))); 355 | Assertions.assertTrue(exception.getMessage().contains("targetLang=\"en\" is not allowed")); 356 | } 357 | } 358 | } 359 | -------------------------------------------------------------------------------- /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 | // TODO replace with v3 delete glossary 38 | deepLClient.deleteGlossary(glossary.getGlossaryId()); 39 | } catch (Exception exception) { 40 | // Ignore 41 | } 42 | } 43 | } 44 | } 45 | 46 | private static String getCallerFunction() { 47 | StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace(); 48 | // Find the first function outside this class following functions in this class 49 | for (int i = 1; i < stacktrace.length; i++) { 50 | if (!stacktrace[i].getClassName().equals(MultilingualGlossaryCleanupUtility.class.getName()) 51 | && stacktrace[i - 1] 52 | .getClassName() 53 | .equals(MultilingualGlossaryCleanupUtility.class.getName())) { 54 | return stacktrace[i].getMethodName(); 55 | } 56 | } 57 | return "unknown"; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /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/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/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.assertEquals("Wie geht es Ihnen?", readFromFile(outputFile)); 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.assertEquals("Wie geht es dir?", readFromFile(outputFile)); 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 | -------------------------------------------------------------------------------- /deepl-java/src/test/java/com/deepl/api/TranslateTextTest.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 | import java.util.function.Consumer; 8 | import org.junit.jupiter.api.Assertions; 9 | import org.junit.jupiter.api.Assumptions; 10 | import org.junit.jupiter.api.Test; 11 | 12 | public class TranslateTextTest extends TestBase { 13 | 14 | @Test 15 | void testSingleText() throws DeepLException, InterruptedException { 16 | Translator translator = createTranslator(); 17 | TextResult result = translator.translateText(exampleText.get("en"), null, LanguageCode.German); 18 | Assertions.assertEquals(exampleText.get("de"), result.getText()); 19 | Assertions.assertEquals("en", result.getDetectedSourceLanguage()); 20 | Assertions.assertEquals(exampleText.get("en").length(), result.getBilledCharacters()); 21 | } 22 | 23 | @Test 24 | void testTextArray() throws DeepLException, InterruptedException { 25 | Translator translator = createTranslator(); 26 | List texts = new ArrayList<>(); 27 | texts.add(exampleText.get("fr")); 28 | texts.add(exampleText.get("en")); 29 | List result = translator.translateText(texts, null, LanguageCode.German); 30 | Assertions.assertEquals(exampleText.get("de"), result.get(0).getText()); 31 | Assertions.assertEquals(exampleText.get("de"), result.get(1).getText()); 32 | } 33 | 34 | @Test 35 | void testSourceLang() throws DeepLException, InterruptedException { 36 | Consumer checkResult = 37 | (result) -> { 38 | Assertions.assertEquals(exampleText.get("de"), result.getText()); 39 | Assertions.assertEquals("en", result.getDetectedSourceLanguage()); 40 | }; 41 | 42 | Translator translator = createTranslator(); 43 | checkResult.accept(translator.translateText(exampleText.get("en"), null, "DE")); 44 | checkResult.accept(translator.translateText(exampleText.get("en"), "En", "DE")); 45 | checkResult.accept(translator.translateText(exampleText.get("en"), "en", "DE")); 46 | 47 | List sourceLanguages = translator.getSourceLanguages(); 48 | Language sourceLanguageEn = 49 | sourceLanguages.stream() 50 | .filter((language -> Objects.equals(language.getCode(), "en"))) 51 | .findFirst() 52 | .orElse(null); 53 | Language sourceLanguageDe = 54 | sourceLanguages.stream() 55 | .filter((language -> Objects.equals(language.getCode(), "de"))) 56 | .findFirst() 57 | .orElse(null); 58 | Assertions.assertNotNull(sourceLanguageEn); 59 | Assertions.assertNotNull(sourceLanguageDe); 60 | checkResult.accept( 61 | translator.translateText(exampleText.get("en"), sourceLanguageEn, sourceLanguageDe)); 62 | } 63 | 64 | @Test 65 | void testTargetLang() throws DeepLException, InterruptedException { 66 | Consumer checkResult = 67 | (result) -> { 68 | Assertions.assertEquals(exampleText.get("de"), result.getText()); 69 | Assertions.assertEquals("en", result.getDetectedSourceLanguage()); 70 | }; 71 | 72 | Translator translator = createTranslator(); 73 | checkResult.accept(translator.translateText(exampleText.get("en"), null, "De")); 74 | checkResult.accept(translator.translateText(exampleText.get("en"), null, "de")); 75 | checkResult.accept(translator.translateText(exampleText.get("en"), null, "DE")); 76 | 77 | List targetLanguages = translator.getTargetLanguages(); 78 | Language targetLanguageDe = 79 | targetLanguages.stream() 80 | .filter((language -> Objects.equals(language.getCode(), "de"))) 81 | .findFirst() 82 | .orElse(null); 83 | Assertions.assertNotNull(targetLanguageDe); 84 | checkResult.accept(translator.translateText(exampleText.get("en"), null, targetLanguageDe)); 85 | 86 | // Check that en and pt as target languages throw an exception 87 | Assertions.assertThrows( 88 | IllegalArgumentException.class, 89 | () -> { 90 | translator.translateText(exampleText.get("de"), null, "en"); 91 | }); 92 | Assertions.assertThrows( 93 | IllegalArgumentException.class, 94 | () -> { 95 | translator.translateText(exampleText.get("de"), null, "pt"); 96 | }); 97 | } 98 | 99 | @Test 100 | void testInvalidLanguage() { 101 | Translator translator = createTranslator(); 102 | DeepLException thrown; 103 | thrown = 104 | Assertions.assertThrows( 105 | DeepLException.class, 106 | () -> { 107 | translator.translateText(exampleText.get("en"), null, "XX"); 108 | }); 109 | Assertions.assertTrue(thrown.getMessage().contains("target_lang")); 110 | 111 | thrown = 112 | Assertions.assertThrows( 113 | DeepLException.class, 114 | () -> { 115 | translator.translateText(exampleText.get("en"), "XX", "de"); 116 | }); 117 | Assertions.assertTrue(thrown.getMessage().contains("source_lang")); 118 | } 119 | 120 | @Test 121 | void testTranslateWithRetries() throws DeepLException, InterruptedException { 122 | Assumptions.assumeTrue(isMockServer); 123 | Translator translator = createTranslator(new SessionOptions().setRespondWith429(2)); 124 | 125 | long timeBefore = new Date().getTime(); 126 | List texts = new ArrayList<>(); 127 | texts.add(exampleText.get("en")); 128 | texts.add(exampleText.get("ja")); 129 | List result = translator.translateText(texts, null, "de"); 130 | long timeAfter = new Date().getTime(); 131 | 132 | Assertions.assertEquals(2, result.size()); 133 | Assertions.assertEquals(exampleText.get("de"), result.get(0).getText()); 134 | Assertions.assertEquals("en", result.get(0).getDetectedSourceLanguage()); 135 | Assertions.assertEquals(exampleText.get("de"), result.get(1).getText()); 136 | Assertions.assertEquals("ja", result.get(1).getDetectedSourceLanguage()); 137 | Assertions.assertTrue(timeAfter - timeBefore > 1000); 138 | } 139 | 140 | @Test 141 | void testFormality() throws DeepLException, InterruptedException { 142 | Translator translator = createTranslator(); 143 | TextResult result; 144 | 145 | result = 146 | translator.translateText( 147 | "How are you?", null, "de", new TextTranslationOptions().setFormality(Formality.Less)); 148 | if (!isMockServer) { 149 | Assertions.assertEquals("Wie geht es dir?", result.getText()); 150 | } 151 | 152 | result = 153 | translator.translateText( 154 | "How are you?", 155 | null, 156 | "de", 157 | new TextTranslationOptions().setFormality(Formality.Default)); 158 | if (!isMockServer) { 159 | Assertions.assertEquals("Wie geht es Ihnen?", result.getText()); 160 | } 161 | 162 | result = 163 | translator.translateText( 164 | "How are you?", null, "de", new TextTranslationOptions().setFormality(Formality.More)); 165 | if (!isMockServer) { 166 | Assertions.assertEquals("Wie geht es Ihnen?", result.getText()); 167 | } 168 | 169 | result = 170 | translator.translateText( 171 | "How are you?", 172 | null, 173 | "de", 174 | new TextTranslationOptions().setFormality(Formality.PreferLess)); 175 | if (!isMockServer) { 176 | Assertions.assertEquals("Wie geht es dir?", result.getText()); 177 | } 178 | 179 | result = 180 | translator.translateText( 181 | "How are you?", 182 | null, 183 | "de", 184 | new TextTranslationOptions().setFormality(Formality.PreferMore)); 185 | if (!isMockServer) { 186 | Assertions.assertEquals("Wie geht es Ihnen?", result.getText()); 187 | } 188 | } 189 | 190 | @Test 191 | void testContext() throws DeepLException, InterruptedException { 192 | // In German, "scharf" can mean: 193 | // - spicy/hot when referring to food, or 194 | // - sharp when referring to other objects such as a knife (Messer). 195 | Translator translator = createTranslator(); 196 | String text = "Das ist scharf!"; 197 | 198 | translator.translateText(text, null, "de"); 199 | // Result: "That is hot!" 200 | 201 | translator.translateText( 202 | text, null, "de", new TextTranslationOptions().setContext("Das ist ein Messer.")); 203 | // Result: "That is sharp!" 204 | } 205 | 206 | @Test 207 | void testSplitSentences() throws DeepLException, InterruptedException { 208 | Assumptions.assumeTrue(isMockServer); 209 | 210 | Translator translator = createTranslator(); 211 | String text = 212 | "If the implementation is hard to explain, it's a bad idea.\nIf the implementation is easy to explain, it may be a good idea."; 213 | 214 | translator.translateText( 215 | text, 216 | null, 217 | "de", 218 | new TextTranslationOptions().setSentenceSplittingMode(SentenceSplittingMode.Off)); 219 | translator.translateText( 220 | text, 221 | null, 222 | "de", 223 | new TextTranslationOptions().setSentenceSplittingMode(SentenceSplittingMode.All)); 224 | translator.translateText( 225 | text, 226 | null, 227 | "de", 228 | new TextTranslationOptions().setSentenceSplittingMode(SentenceSplittingMode.NoNewlines)); 229 | } 230 | 231 | @Test 232 | void testPreserveFormatting() throws DeepLException, InterruptedException { 233 | Assumptions.assumeTrue(isMockServer); 234 | 235 | Translator translator = createTranslator(); 236 | translator.translateText( 237 | exampleText.get("en"), 238 | null, 239 | "de", 240 | new TextTranslationOptions().setPreserveFormatting(true)); 241 | translator.translateText( 242 | exampleText.get("en"), 243 | null, 244 | "de", 245 | new TextTranslationOptions().setPreserveFormatting(false)); 246 | } 247 | 248 | @Test 249 | void testTagHandlingXML() throws DeepLException, InterruptedException { 250 | Translator translator = createTranslator(); 251 | String text = 252 | "A document's title" 253 | + "" 254 | + "This is a sentence split" 255 | + "across two <span> tags that should be treated as one." 256 | + "" 257 | + "" 258 | + "Here is a sentence. Followed by a second one." 259 | + "This sentence will not be translated." 260 | + "" 261 | + ""; 262 | TextResult result = 263 | translator.translateText( 264 | text, 265 | null, 266 | "de", 267 | new TextTranslationOptions() 268 | .setTagHandling("xml") 269 | .setOutlineDetection(false) 270 | .setNonSplittingTags(Arrays.asList("span")) 271 | .setSplittingTags(Arrays.asList("title", "par")) 272 | .setIgnoreTags(Arrays.asList("raw"))); 273 | if (!isMockServer) { 274 | Assertions.assertTrue( 275 | result.getText().contains("This sentence will not be translated.")); 276 | Assertions.assertTrue(result.getText().matches(".*.*Der Titel.*.*")); 277 | } 278 | } 279 | 280 | @Test 281 | void testTagHandlingHTML() throws DeepLException, InterruptedException { 282 | Translator translator = createTranslator(); 283 | String text = 284 | "" 285 | + "" 286 | + "" 287 | + "

My First Heading

" 288 | + "

My first paragraph.

" 289 | + "" 290 | + ""; 291 | 292 | TextResult result = 293 | translator.translateText( 294 | text, null, "de", new TextTranslationOptions().setTagHandling("html")); 295 | if (!isMockServer) { 296 | Assertions.assertTrue(result.getText().contains("

Meine erste Überschrift

")); 297 | Assertions.assertTrue( 298 | result.getText().contains("

My first paragraph.

")); 299 | } 300 | } 301 | 302 | @Test 303 | void testEmptyText() { 304 | Translator translator = createTranslator(); 305 | Assertions.assertThrows( 306 | IllegalArgumentException.class, 307 | () -> { 308 | translator.translateText("", null, "de"); 309 | }); 310 | } 311 | 312 | @Test 313 | void testMixedCaseLanguages() throws DeepLException, InterruptedException { 314 | Translator translator = createTranslator(); 315 | TextResult result; 316 | 317 | result = translator.translateText(exampleText.get("de"), null, "en-us"); 318 | Assertions.assertEquals(exampleText.get("en-US"), result.getText().toLowerCase(Locale.ENGLISH)); 319 | Assertions.assertEquals("de", result.getDetectedSourceLanguage()); 320 | 321 | result = translator.translateText(exampleText.get("de"), null, "EN-us"); 322 | Assertions.assertEquals(exampleText.get("en-US"), result.getText().toLowerCase(Locale.ENGLISH)); 323 | Assertions.assertEquals("de", result.getDetectedSourceLanguage()); 324 | 325 | result = translator.translateText(exampleText.get("de"), "de", "EN-US"); 326 | Assertions.assertEquals(exampleText.get("en-US"), result.getText().toLowerCase(Locale.ENGLISH)); 327 | Assertions.assertEquals("de", result.getDetectedSourceLanguage()); 328 | 329 | result = translator.translateText(exampleText.get("de"), "dE", "EN-US"); 330 | Assertions.assertEquals(exampleText.get("en-US"), result.getText().toLowerCase(Locale.ENGLISH)); 331 | Assertions.assertEquals("de", result.getDetectedSourceLanguage()); 332 | } 333 | } 334 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Dfile.encoding=UTF-8 -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeepLcom/deepl-java/383591439f65896cadd46ab8f4472dfe0bb2d182/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | 2 | rootProject.name = "deepl-java" 3 | include("deepl-java") 4 | --------------------------------------------------------------------------------