deepl-rb 3.8.0 → 3.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitlab-ci.yml +1 -1
- data/CHANGELOG.md +23 -1
- data/README.md +213 -4
- data/VERSION +1 -1
- data/deepl-rb.gemspec +34 -6
- data/lib/deepl/document_api.rb +11 -1
- data/lib/deepl/requests/base.rb +55 -3
- data/lib/deepl/requests/document/upload.rb +50 -1
- data/lib/deepl/requests/translate.rb +4 -1
- data/lib/deepl/requests/translation_memory/base.rb +47 -0
- data/lib/deepl/requests/translation_memory/create_export.rb +41 -0
- data/lib/deepl/requests/translation_memory/create_import.rb +53 -0
- data/lib/deepl/requests/translation_memory/destroy.rb +37 -0
- data/lib/deepl/requests/translation_memory/download_export.rb +35 -0
- data/lib/deepl/requests/translation_memory/find.rb +38 -0
- data/lib/deepl/requests/translation_memory/find_job.rb +38 -0
- data/lib/deepl/requests/translation_memory/list.rb +5 -18
- data/lib/deepl/requests/translation_memory/segments.rb +46 -0
- data/lib/deepl/requests/translation_memory/storage_base.rb +47 -0
- data/lib/deepl/requests/translation_memory/upload_file.rb +47 -0
- data/lib/deepl/resources/translation_memory.rb +4 -1
- data/lib/deepl/resources/translation_memory_export.rb +37 -0
- data/lib/deepl/resources/translation_memory_import.rb +29 -0
- data/lib/deepl/resources/translation_memory_job.rb +142 -0
- data/lib/deepl/resources/translation_memory_segments.rb +78 -0
- data/lib/deepl/translation_memory_api.rb +293 -1
- data/lib/deepl/utils/time_parser.rb +26 -0
- data/lib/deepl.rb +31 -0
- data/lib/version.rb +1 -1
- data/spec/integration_tests/document_api_spec.rb +47 -0
- data/spec/integration_tests/integration_test_utils.rb +8 -0
- data/spec/integration_tests/translate_api_spec.rb +19 -0
- data/spec/integration_tests/translation_memory_api_spec.rb +192 -2
- data/spec/integration_tests/translation_memory_error_paths_spec.rb +68 -0
- data/spec/requests/document/upload_spec.rb +130 -0
- data/spec/requests/translate_spec.rb +55 -0
- data/spec/requests/translation_memory/create_export_spec.rb +37 -0
- data/spec/requests/translation_memory/create_import_spec.rb +61 -0
- data/spec/requests/translation_memory/destroy_spec.rb +36 -0
- data/spec/requests/translation_memory/download_export_spec.rb +54 -0
- data/spec/requests/translation_memory/find_job_spec.rb +37 -0
- data/spec/requests/translation_memory/find_spec.rb +36 -0
- data/spec/requests/translation_memory/segments_spec.rb +58 -0
- data/spec/requests/translation_memory/upload_file_spec.rb +55 -0
- data/spec/resources/translation_memory_export_spec.rb +38 -0
- data/spec/resources/translation_memory_import_spec.rb +30 -0
- data/spec/resources/translation_memory_job_spec.rb +109 -0
- data/spec/resources/translation_memory_segments_spec.rb +78 -0
- data/spec/resources/translation_memory_spec.rb +18 -1
- data/spec/support/managed_glossary.rb +48 -0
- data/spec/support/managed_translation_memory.rb +48 -0
- metadata +34 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 35f6c08b68a4b9aa8907b543ebbfbab38e9f9f35f7e2dc60decaa4bac57340f5
|
|
4
|
+
data.tar.gz: 975d1d68cd43fec41b93252b609bc67a619d08d4fd2e6d6bc9b822ca01fd78ac
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 749cd28143798f5e08ba848bc4eaa476254ef24656e690fcb3e5ca84314eb54f68ffc7f3675ae74a0cc24c01d194d9dfebcd2d819e0982fad44aba5869e37344
|
|
7
|
+
data.tar.gz: 4a3b7ca19832725c49b0708f544f9842de7733cc8777b41f66835e4dbead815994ca6dab8b8b3052d172417240dcf5fff5934fc3959ef226001e8ad4e0769735
|
data/.gitlab-ci.yml
CHANGED
|
@@ -4,7 +4,7 @@ include:
|
|
|
4
4
|
- project: '${CI_PROJECT_NAMESPACE}/ci-libs-for-client-libraries'
|
|
5
5
|
file:
|
|
6
6
|
- '/${CI_PROJECT_NAME}/.gitlab-ci.yml'
|
|
7
|
-
- project: 'deepl/
|
|
7
|
+
- project: 'deepl-org/deepl/devex/gitlab-ci-lib'
|
|
8
8
|
file:
|
|
9
9
|
- '/templates/.buildkit.yml'
|
|
10
10
|
- '/templates/.secret-detection.yml'
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,27 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [3.9.0] - 2026-08-11
|
|
10
|
+
### Added
|
|
11
|
+
- Added support for using multiple glossaries in text and document translation
|
|
12
|
+
via the `glossary_ids` option (up to 5 glossary IDs) in `translate` and
|
|
13
|
+
document upload.
|
|
14
|
+
- Added support for using a style rule in document translation via the
|
|
15
|
+
`style_id` option (the `style_rule` option) in document upload.
|
|
16
|
+
- Added support for using a translation memory in document translation via the
|
|
17
|
+
`translation_memory_id` option (the `translation_memory` and
|
|
18
|
+
`translation_memory_threshold` options) in document upload.
|
|
19
|
+
- Added support for translation memory management via `DeepL.translation_memories`:
|
|
20
|
+
`find()`, `segments()`, `destroy()`, `create_import()`, `upload_file()`,
|
|
21
|
+
`create_export()`, `find_job()`, `wait_until_job_done()`, `download_export()`,
|
|
22
|
+
and the convenience methods `import_from_filepath()` and `export_to_filepath()`.
|
|
23
|
+
Every method taking a translation memory accepts a string ID or a
|
|
24
|
+
`TranslationMemory` object. `wait_until_job_done()`, `import_from_filepath()`
|
|
25
|
+
and `export_to_filepath()` accept an optional `timeout_s` to stop waiting for
|
|
26
|
+
a job that does not finish. Please refer to the README for usage instructions.
|
|
27
|
+
- Added the `creation_time` and `updated_time` attributes to `TranslationMemory`.
|
|
28
|
+
|
|
8
29
|
### Changed
|
|
9
30
|
- Reworked the test suite to run against the `deepl-mock` server with OpenAPI
|
|
10
31
|
request and response validation enabled in CI, replacing recorded VCR
|
|
@@ -134,7 +155,8 @@ The change in major version is only due to the change in maintainership, there i
|
|
|
134
155
|
### Fixed
|
|
135
156
|
- Make RequestEntityTooLarge error message more clear
|
|
136
157
|
|
|
137
|
-
[Unreleased]: https://github.com/DeepLcom/deepl-rb/compare/v3.
|
|
158
|
+
[Unreleased]: https://github.com/DeepLcom/deepl-rb/compare/v3.9.0...HEAD
|
|
159
|
+
[3.9.0]: https://github.com/DeepLcom/deepl-rb/compare/v3.8.0...v3.9.0
|
|
138
160
|
[3.8.0]: https://github.com/DeepLcom/deepl-rb/compare/v3.7.0...v3.8.0
|
|
139
161
|
[3.7.0]: https://github.com/DeepLcom/deepl-rb/compare/v3.6.1...v3.7.0
|
|
140
162
|
[3.6.1]: https://github.com/DeepLcom/deepl-rb/compare/v3.6.0...v3.6.1
|
data/README.md
CHANGED
|
@@ -267,6 +267,25 @@ puts translation.text
|
|
|
267
267
|
# => Ojalá tuviéramos un auto.
|
|
268
268
|
```
|
|
269
269
|
|
|
270
|
+
To use more than one glossary at once, specify the `glossary_ids` option with an
|
|
271
|
+
array of up to 5 glossary IDs (as strings or `DeepL::Resources::Glossary`
|
|
272
|
+
objects) instead of `glossary_id`. This works for both text and document
|
|
273
|
+
translation. `glossary_ids` requires `source_lang` to be set, cannot be combined
|
|
274
|
+
with `glossary_id`, and raises `ArgumentError` if these rules are violated or
|
|
275
|
+
more than 5 IDs are provided:
|
|
276
|
+
|
|
277
|
+
```rb
|
|
278
|
+
# Text translation with multiple glossaries
|
|
279
|
+
translation = DeepL.translate 'Hello World', 'EN', 'ES',
|
|
280
|
+
glossary_ids: ['aa48c7f0-0d02-413e-8a06-d5bbf0ca7a6e',
|
|
281
|
+
'bb59d8g1-1e13-524f-9b17-e6ccg1db8b7f']
|
|
282
|
+
|
|
283
|
+
# Document translation with multiple glossaries
|
|
284
|
+
handle = DeepL.document.upload 'my_document.docx', 'EN', 'ES', 'my_document.docx',
|
|
285
|
+
glossary_ids: ['aa48c7f0-0d02-413e-8a06-d5bbf0ca7a6e',
|
|
286
|
+
'bb59d8g1-1e13-524f-9b17-e6ccg1db8b7f']
|
|
287
|
+
```
|
|
288
|
+
|
|
270
289
|
To list all the glossaries available, use the `glossaries.list` method:
|
|
271
290
|
|
|
272
291
|
```rb
|
|
@@ -457,6 +476,15 @@ style_rules = DeepL.style_rules.list
|
|
|
457
476
|
translation = DeepL.translate 'Hello World', 'EN', 'ES', style_rule: style_rules.first
|
|
458
477
|
```
|
|
459
478
|
|
|
479
|
+
The same `style_rule` option can be passed to document translation via
|
|
480
|
+
`DeepL.document.upload` (or `DeepL.document.translate_document`), accepting either
|
|
481
|
+
a style rule ID string or a `StyleRule` object:
|
|
482
|
+
|
|
483
|
+
```rb
|
|
484
|
+
handle = DeepL.document.upload 'my_document.docx', 'EN', 'ES', 'my_document.docx',
|
|
485
|
+
style_rule: 'dca2e053-8ae5-45e6-a0d2-881156e7f4e4'
|
|
486
|
+
```
|
|
487
|
+
|
|
460
488
|
### Translation Memories
|
|
461
489
|
|
|
462
490
|
Translation memories allow you to store and reuse previously created translations.
|
|
@@ -464,11 +492,11 @@ They can be used in text translation requests to improve consistency by matching
|
|
|
464
492
|
against stored segments. Multiple translation memories can be stored with your
|
|
465
493
|
account, each with a source language and one or more target languages.
|
|
466
494
|
|
|
467
|
-
|
|
495
|
+
Translation memories can also be managed in the DeepL UI via
|
|
496
|
+
https://www.deepl.com/translation-memory.
|
|
468
497
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
the API will come shortly.
|
|
498
|
+
Every method that takes a translation memory accepts either a string containing
|
|
499
|
+
the translation memory ID or a `TranslationMemory` object.
|
|
472
500
|
|
|
473
501
|
#### Listing translation memories
|
|
474
502
|
|
|
@@ -490,6 +518,176 @@ end
|
|
|
490
518
|
translation_memories = DeepL.translation_memories.list(page: 0, page_size: 10)
|
|
491
519
|
```
|
|
492
520
|
|
|
521
|
+
#### Retrieving a single translation memory
|
|
522
|
+
|
|
523
|
+
`translation_memories.find` retrieves one translation memory by ID. In addition
|
|
524
|
+
to the fields returned by `list`, the resource carries the `creation_time` and
|
|
525
|
+
`updated_time` timestamps.
|
|
526
|
+
|
|
527
|
+
```rb
|
|
528
|
+
tm = DeepL.translation_memories.find 'YOUR_TM_ID'
|
|
529
|
+
|
|
530
|
+
puts tm.class
|
|
531
|
+
# => DeepL::Resources::TranslationMemory
|
|
532
|
+
puts tm.name
|
|
533
|
+
# => 'Legal'
|
|
534
|
+
puts tm.segment_count
|
|
535
|
+
# => 12
|
|
536
|
+
puts tm.creation_time.class
|
|
537
|
+
# => Time
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
#### Listing the segments of a translation memory
|
|
541
|
+
|
|
542
|
+
`translation_memories.segments` returns one page of the segments of a
|
|
543
|
+
translation memory as a `TranslationMemorySegments` object. Each segment holds
|
|
544
|
+
the source text and one target per target language of the translation memory.
|
|
545
|
+
|
|
546
|
+
Pagination is cursor-based: omit `page_cursor` on the first call, then pass the
|
|
547
|
+
`next_page_cursor` of the previous response until `next_page?` is `false`. The
|
|
548
|
+
method also accepts `page_size` (1-100, defaults to 50), `filter_text` (a
|
|
549
|
+
substring matched against the source and target texts, at least 2 characters)
|
|
550
|
+
and `filter_case_sensitive` (defaults to `false`).
|
|
551
|
+
|
|
552
|
+
Note that `segment_count` is the number of segments stored in the translation
|
|
553
|
+
memory; a text filter does not reduce it.
|
|
554
|
+
|
|
555
|
+
```rb
|
|
556
|
+
page = DeepL.translation_memories.segments 'YOUR_TM_ID', page_size: 50
|
|
557
|
+
|
|
558
|
+
puts page.class
|
|
559
|
+
# => DeepL::Resources::TranslationMemorySegments
|
|
560
|
+
puts page.segment_count
|
|
561
|
+
# => 12
|
|
562
|
+
puts page.segments.first.source_text
|
|
563
|
+
# => 'Quelltext Nummer 0'
|
|
564
|
+
puts page.segments.first.targets.first.target_text
|
|
565
|
+
# => 'Source text number 0'
|
|
566
|
+
|
|
567
|
+
# Walk through every page of segments
|
|
568
|
+
while page.next_page?
|
|
569
|
+
page = DeepL.translation_memories.segments 'YOUR_TM_ID',
|
|
570
|
+
page_size: 50,
|
|
571
|
+
page_cursor: page.next_page_cursor
|
|
572
|
+
end
|
|
573
|
+
|
|
574
|
+
# Only the segments matching a text
|
|
575
|
+
page = DeepL.translation_memories.segments 'YOUR_TM_ID',
|
|
576
|
+
filter_text: 'Nummer 1',
|
|
577
|
+
filter_case_sensitive: true
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
#### Importing a translation memory
|
|
581
|
+
|
|
582
|
+
`translation_memories.import_from_filepath` creates a new translation memory
|
|
583
|
+
from a TMX file. It creates the import job, uploads the file and waits for the
|
|
584
|
+
processing to finish, and returns the finished `TranslationMemoryJob`. Its
|
|
585
|
+
result carries the ID of the newly created translation memory.
|
|
586
|
+
|
|
587
|
+
```rb
|
|
588
|
+
job = DeepL.translation_memories.import_from_filepath 'legal.tmx',
|
|
589
|
+
display_name: 'Legal',
|
|
590
|
+
timeout_s: 300
|
|
591
|
+
|
|
592
|
+
puts job.class
|
|
593
|
+
# => DeepL::Resources::TranslationMemoryJob
|
|
594
|
+
puts job.status
|
|
595
|
+
# => 'completed'
|
|
596
|
+
puts job.result.translation_memory_id
|
|
597
|
+
# => 'a74d88fb-ed2a-4943-a664-a4512398b994'
|
|
598
|
+
puts job.result.skipped_segment_count
|
|
599
|
+
# => 0
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
The three steps can also be performed separately, for example to upload a file
|
|
603
|
+
that is not available on the local file system. The upload URL is a pre-signed
|
|
604
|
+
storage URL outside of the DeepL API, so no authorization header is sent with
|
|
605
|
+
the upload.
|
|
606
|
+
|
|
607
|
+
```rb
|
|
608
|
+
content = File.binread 'legal.tmx'
|
|
609
|
+
created = DeepL.translation_memories.create_import 'legal.tmx', content.bytesize,
|
|
610
|
+
content_type: 'application/xml',
|
|
611
|
+
display_name: 'Legal'
|
|
612
|
+
|
|
613
|
+
puts created.upload_url
|
|
614
|
+
# => 'https://...'
|
|
615
|
+
|
|
616
|
+
DeepL.translation_memories.upload_file created, content
|
|
617
|
+
job = DeepL.translation_memories.wait_until_job_done created.job_id
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
Until the file is uploaded the job stays in the `awaiting_input` status and
|
|
621
|
+
`result.required_action` describes what is missing. The API detects the upload
|
|
622
|
+
asynchronously, so the job keeps reporting `awaiting_input` for a while
|
|
623
|
+
afterwards, typically around half a minute, before it completes.
|
|
624
|
+
`wait_until_job_done` therefore polls through that status like any other
|
|
625
|
+
non-terminal one. A job whose file is never uploaded does not finish on its own,
|
|
626
|
+
so pass `timeout_s` when that is a possibility.
|
|
627
|
+
|
|
628
|
+
#### Exporting a translation memory
|
|
629
|
+
|
|
630
|
+
`translation_memories.export_to_filepath` writes a translation memory to a TMX
|
|
631
|
+
file. It creates the export job, waits for it to finish and downloads the
|
|
632
|
+
result, overwriting the output file if it already exists.
|
|
633
|
+
|
|
634
|
+
```rb
|
|
635
|
+
job = DeepL.translation_memories.export_to_filepath 'YOUR_TM_ID', 'export.tmx'
|
|
636
|
+
|
|
637
|
+
puts job.status
|
|
638
|
+
# => 'completed'
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
The steps can be performed separately as well. Repeating the export of an
|
|
642
|
+
unchanged translation memory reuses the previously completed job instead of
|
|
643
|
+
starting a new one, which `reused_existing?` reports. Just like the upload URL,
|
|
644
|
+
the download URL is a pre-signed storage URL and is requested without an
|
|
645
|
+
authorization header.
|
|
646
|
+
|
|
647
|
+
```rb
|
|
648
|
+
created = DeepL.translation_memories.create_export 'YOUR_TM_ID'
|
|
649
|
+
|
|
650
|
+
puts created.reused_existing?
|
|
651
|
+
# => false
|
|
652
|
+
|
|
653
|
+
job = DeepL.translation_memories.wait_until_job_done created.job_id
|
|
654
|
+
|
|
655
|
+
puts job.result.download_url
|
|
656
|
+
# => 'https://...'
|
|
657
|
+
|
|
658
|
+
DeepL.translation_memories.download_export job, 'export.tmx'
|
|
659
|
+
```
|
|
660
|
+
|
|
661
|
+
#### Tracking import and export jobs
|
|
662
|
+
|
|
663
|
+
`translation_memories.find_job` returns the current status of an import or
|
|
664
|
+
export job, and `translation_memories.wait_until_job_done` polls it every five
|
|
665
|
+
seconds until it finished, raising if the job failed or expired. Pass
|
|
666
|
+
`timeout_s` to give up after a number of seconds instead of waiting forever.
|
|
667
|
+
|
|
668
|
+
```rb
|
|
669
|
+
job = DeepL.translation_memories.find_job 'YOUR_JOB_ID'
|
|
670
|
+
|
|
671
|
+
puts job.operation
|
|
672
|
+
# => 'import'
|
|
673
|
+
puts job.status
|
|
674
|
+
# => 'processing'
|
|
675
|
+
puts job.finished?
|
|
676
|
+
# => false
|
|
677
|
+
```
|
|
678
|
+
|
|
679
|
+
The status is one of `awaiting_input`, `processing`, `completed`, `downloaded`,
|
|
680
|
+
`failed` or `expired`.
|
|
681
|
+
|
|
682
|
+
#### Deleting a translation memory
|
|
683
|
+
|
|
684
|
+
`translation_memories.destroy` deletes a translation memory and returns its ID.
|
|
685
|
+
|
|
686
|
+
```rb
|
|
687
|
+
DeepL.translation_memories.destroy 'YOUR_TM_ID'
|
|
688
|
+
# => 'YOUR_TM_ID'
|
|
689
|
+
```
|
|
690
|
+
|
|
493
691
|
#### Using a translation memory in translations
|
|
494
692
|
|
|
495
693
|
Pass the `translation_memory` parameter to `translate` to use a translation
|
|
@@ -510,6 +708,17 @@ translation = DeepL.translate 'Hello, world!', 'EN', 'DE',
|
|
|
510
708
|
translation_memory: translation_memories.first
|
|
511
709
|
```
|
|
512
710
|
|
|
711
|
+
The same `translation_memory` and `translation_memory_threshold` options can be
|
|
712
|
+
passed to document translation via `DeepL.document.upload` (or
|
|
713
|
+
`DeepL.document.translate_document`). The `translation_memory` option accepts
|
|
714
|
+
either a translation memory ID string or a `TranslationMemory` object:
|
|
715
|
+
|
|
716
|
+
```rb
|
|
717
|
+
handle = DeepL.document.upload 'my_document.docx', 'EN', 'DE', 'my_document.docx',
|
|
718
|
+
translation_memory: 'YOUR_TM_ID',
|
|
719
|
+
translation_memory_threshold: 80
|
|
720
|
+
```
|
|
721
|
+
|
|
513
722
|
### Monitor usage
|
|
514
723
|
|
|
515
724
|
To check current API usage, use:
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.
|
|
1
|
+
3.9.0
|
data/deepl-rb.gemspec
CHANGED
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
|
3
3
|
# Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
|
|
4
4
|
# -*- encoding: utf-8 -*-
|
|
5
|
-
# stub: deepl-rb 3.
|
|
5
|
+
# stub: deepl-rb 3.9.0 ruby lib
|
|
6
6
|
|
|
7
7
|
Gem::Specification.new do |s|
|
|
8
8
|
s.name = "deepl-rb".freeze
|
|
9
|
-
s.version = "3.
|
|
9
|
+
s.version = "3.9.0"
|
|
10
10
|
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
|
12
12
|
s.metadata = { "bug_tracker_uri" => "https://github.com/DeepLcom/deepl-rb/issues", "changelog_uri" => "https://github.com/DeepLcom/deepl-rb/blob/main/CHANGELOG.md", "documentation_uri" => "https://github.com/DeepLcom/deepl-rb/blob/main/README.md", "homepage_uri" => "https://github.com/DeepLcom/deepl-rb" } if s.respond_to? :metadata=
|
|
13
13
|
s.require_paths = ["lib".freeze]
|
|
14
14
|
s.authors = ["DeepL SE".freeze]
|
|
15
|
-
s.date = "2026-
|
|
15
|
+
s.date = "2026-08-12"
|
|
16
16
|
s.description = "Official Ruby library for the DeepL language translation API (v2). For more information, check this: https://www.deepl.com/docs/api-reference.html".freeze
|
|
17
17
|
s.email = "open-source@deepl.com".freeze
|
|
18
18
|
s.extra_rdoc_files = [
|
|
@@ -82,7 +82,17 @@ Gem::Specification.new do |s|
|
|
|
82
82
|
"lib/deepl/requests/style_rule/update_configured_rules.rb",
|
|
83
83
|
"lib/deepl/requests/style_rule/update_custom_instruction.rb",
|
|
84
84
|
"lib/deepl/requests/translate.rb",
|
|
85
|
+
"lib/deepl/requests/translation_memory/base.rb",
|
|
86
|
+
"lib/deepl/requests/translation_memory/create_export.rb",
|
|
87
|
+
"lib/deepl/requests/translation_memory/create_import.rb",
|
|
88
|
+
"lib/deepl/requests/translation_memory/destroy.rb",
|
|
89
|
+
"lib/deepl/requests/translation_memory/download_export.rb",
|
|
90
|
+
"lib/deepl/requests/translation_memory/find.rb",
|
|
91
|
+
"lib/deepl/requests/translation_memory/find_job.rb",
|
|
85
92
|
"lib/deepl/requests/translation_memory/list.rb",
|
|
93
|
+
"lib/deepl/requests/translation_memory/segments.rb",
|
|
94
|
+
"lib/deepl/requests/translation_memory/storage_base.rb",
|
|
95
|
+
"lib/deepl/requests/translation_memory/upload_file.rb",
|
|
86
96
|
"lib/deepl/requests/usage.rb",
|
|
87
97
|
"lib/deepl/resources/base.rb",
|
|
88
98
|
"lib/deepl/resources/document_handle.rb",
|
|
@@ -93,11 +103,16 @@ Gem::Specification.new do |s|
|
|
|
93
103
|
"lib/deepl/resources/style_rule.rb",
|
|
94
104
|
"lib/deepl/resources/text.rb",
|
|
95
105
|
"lib/deepl/resources/translation_memory.rb",
|
|
106
|
+
"lib/deepl/resources/translation_memory_export.rb",
|
|
107
|
+
"lib/deepl/resources/translation_memory_import.rb",
|
|
108
|
+
"lib/deepl/resources/translation_memory_job.rb",
|
|
109
|
+
"lib/deepl/resources/translation_memory_segments.rb",
|
|
96
110
|
"lib/deepl/resources/usage.rb",
|
|
97
111
|
"lib/deepl/style_rule_api.rb",
|
|
98
112
|
"lib/deepl/translation_memory_api.rb",
|
|
99
113
|
"lib/deepl/utils/backoff_timer.rb",
|
|
100
114
|
"lib/deepl/utils/exception_builder.rb",
|
|
115
|
+
"lib/deepl/utils/time_parser.rb",
|
|
101
116
|
"lib/http_client_options.rb",
|
|
102
117
|
"lib/version.rb",
|
|
103
118
|
"license_checker.sh",
|
|
@@ -123,6 +138,7 @@ Gem::Specification.new do |s|
|
|
|
123
138
|
"spec/integration_tests/translation_memory_error_paths_spec.rb",
|
|
124
139
|
"spec/integration_tests/usage_api_spec.rb",
|
|
125
140
|
"spec/integration_tests/usage_error_paths_spec.rb",
|
|
141
|
+
"spec/requests/document/upload_spec.rb",
|
|
126
142
|
"spec/requests/extra_body_parameters_types_spec.rb",
|
|
127
143
|
"spec/requests/glossary/create_spec.rb",
|
|
128
144
|
"spec/requests/glossary/destroy_spec.rb",
|
|
@@ -143,7 +159,15 @@ Gem::Specification.new do |s|
|
|
|
143
159
|
"spec/requests/style_rule/update_custom_instruction_spec.rb",
|
|
144
160
|
"spec/requests/style_rule/update_spec.rb",
|
|
145
161
|
"spec/requests/translate_spec.rb",
|
|
162
|
+
"spec/requests/translation_memory/create_export_spec.rb",
|
|
163
|
+
"spec/requests/translation_memory/create_import_spec.rb",
|
|
164
|
+
"spec/requests/translation_memory/destroy_spec.rb",
|
|
165
|
+
"spec/requests/translation_memory/download_export_spec.rb",
|
|
166
|
+
"spec/requests/translation_memory/find_job_spec.rb",
|
|
167
|
+
"spec/requests/translation_memory/find_spec.rb",
|
|
146
168
|
"spec/requests/translation_memory/list_spec.rb",
|
|
169
|
+
"spec/requests/translation_memory/segments_spec.rb",
|
|
170
|
+
"spec/requests/translation_memory/upload_file_spec.rb",
|
|
147
171
|
"spec/requests/usage_spec.rb",
|
|
148
172
|
"spec/resources/custom_instruction_spec.rb",
|
|
149
173
|
"spec/resources/glossary_spec.rb",
|
|
@@ -151,6 +175,10 @@ Gem::Specification.new do |s|
|
|
|
151
175
|
"spec/resources/language_spec.rb",
|
|
152
176
|
"spec/resources/style_rule_spec.rb",
|
|
153
177
|
"spec/resources/text_spec.rb",
|
|
178
|
+
"spec/resources/translation_memory_export_spec.rb",
|
|
179
|
+
"spec/resources/translation_memory_import_spec.rb",
|
|
180
|
+
"spec/resources/translation_memory_job_spec.rb",
|
|
181
|
+
"spec/resources/translation_memory_segments_spec.rb",
|
|
154
182
|
"spec/resources/translation_memory_spec.rb",
|
|
155
183
|
"spec/resources/usage_spec.rb",
|
|
156
184
|
"spec/spec_helper.rb",
|
|
@@ -161,12 +189,12 @@ Gem::Specification.new do |s|
|
|
|
161
189
|
]
|
|
162
190
|
s.homepage = "https://github.com/DeepLcom/deepl-rb".freeze
|
|
163
191
|
s.licenses = ["MIT".freeze]
|
|
164
|
-
s.rubygems_version = "3.
|
|
192
|
+
s.rubygems_version = "3.4.10".freeze
|
|
165
193
|
s.summary = "Official Ruby library for the DeepL language translation API.".freeze
|
|
166
194
|
|
|
167
195
|
s.specification_version = 4
|
|
168
196
|
|
|
169
|
-
s.add_development_dependency(%q<juwelier>.freeze, [">= 0"
|
|
170
|
-
s.add_development_dependency(%q<byebug>.freeze, [">= 0"
|
|
197
|
+
s.add_development_dependency(%q<juwelier>.freeze, [">= 0"])
|
|
198
|
+
s.add_development_dependency(%q<byebug>.freeze, [">= 0"])
|
|
171
199
|
end
|
|
172
200
|
|
data/lib/deepl/document_api.rb
CHANGED
|
@@ -25,7 +25,17 @@ module DeepL
|
|
|
25
25
|
# @param [String, nil] filename The filename of the file, including its extension. Used to open
|
|
26
26
|
# the different kinds of documents (PDFs, etc). If nil, will use
|
|
27
27
|
# the filename of +input_file_path+.
|
|
28
|
-
# @param [Hash] options Additional (body) options for the upload.
|
|
28
|
+
# @param [Hash] options Additional (body) options for the upload. Notable glossary options:
|
|
29
|
+
# +:glossary_id+ (a single glossary ID) or +:glossary_ids+ (an array of
|
|
30
|
+
# up to 5 glossary IDs, as strings or `DeepL::Resources::Glossary`
|
|
31
|
+
# objects). +:glossary_ids+ requires +source_lang+ to be set, cannot be
|
|
32
|
+
# combined with +:glossary_id+, and raises `ArgumentError` if these rules
|
|
33
|
+
# are violated or more than 5 IDs are provided. Notable style/translation
|
|
34
|
+
# memory options (mirroring text translation): +:style_rule+ (a style rule
|
|
35
|
+
# ID string or a `DeepL::Resources::StyleRule` object),
|
|
36
|
+
# +:translation_memory+ (a translation memory ID string or a
|
|
37
|
+
# `DeepL::Resources::TranslationMemory` object), and
|
|
38
|
+
# +:translation_memory_threshold+ (integer 0-100, recommended minimum 75).
|
|
29
39
|
# @param [Hash] additional_headers Additional HTTP headers for the upload.
|
|
30
40
|
# @return [DeepL::Resources::DocumentHandle] Document handle for the uploaded document.
|
|
31
41
|
|
data/lib/deepl/requests/base.rb
CHANGED
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
module DeepL
|
|
7
7
|
module Requests
|
|
8
8
|
class Base # rubocop:disable Metrics/ClassLength
|
|
9
|
+
# Maximum number of glossaries that may be provided via the +glossary_ids+ option.
|
|
10
|
+
MAX_GLOSSARY_IDS = 5
|
|
11
|
+
|
|
9
12
|
attr_reader :api, :response, :options
|
|
10
13
|
|
|
11
14
|
def initialize(api, options = {}, additional_headers = {})
|
|
@@ -85,7 +88,7 @@ module DeepL
|
|
|
85
88
|
api.configuration.logger&.info("Request to the DeepL API: #{self}")
|
|
86
89
|
api.configuration.logger&.debug("Request details: #{details}")
|
|
87
90
|
loop do
|
|
88
|
-
resp =
|
|
91
|
+
resp = http_client.request(req)
|
|
89
92
|
validate_response!(resp)
|
|
90
93
|
return [req, resp]
|
|
91
94
|
rescue DeepL::Exceptions::Error => e
|
|
@@ -95,7 +98,7 @@ module DeepL
|
|
|
95
98
|
api.configuration.logger&.info("Encountered a retryable exception: #{e.message}")
|
|
96
99
|
end
|
|
97
100
|
api.configuration.logger&.info("Starting retry #{@backoff_timer.num_retries + 1} for " \
|
|
98
|
-
"request #{
|
|
101
|
+
"request #{self} after sleeping for " \
|
|
99
102
|
"#{format('%.2f', @backoff_timer.time_until_deadline)}")
|
|
100
103
|
files_to_reset.each(&:rewind)
|
|
101
104
|
@backoff_timer.sleep_until_deadline
|
|
@@ -106,7 +109,7 @@ module DeepL
|
|
|
106
109
|
api.configuration.logger&.info("Encountered a retryable exception: #{e.message}")
|
|
107
110
|
end
|
|
108
111
|
api.configuration.logger&.info("Starting retry #{@backoff_timer.num_retries + 1} for " \
|
|
109
|
-
"request #{
|
|
112
|
+
"request #{self} after sleeping for " \
|
|
110
113
|
"#{format('%.2f', @backoff_timer.time_until_deadline)}")
|
|
111
114
|
files_to_reset.each(&:rewind)
|
|
112
115
|
@backoff_timer.sleep_until_deadline
|
|
@@ -172,6 +175,49 @@ module DeepL
|
|
|
172
175
|
raise Utils::ExceptionBuilder.new(response).build
|
|
173
176
|
end
|
|
174
177
|
|
|
178
|
+
# Validates the +glossary_ids+ option and resolves it to an array of glossary ID strings.
|
|
179
|
+
#
|
|
180
|
+
# The +glossary_ids+ option accepts an array of up to `MAX_GLOSSARY_IDS` glossary
|
|
181
|
+
# identifiers. Each entry may be a glossary ID string or a
|
|
182
|
+
# `DeepL::Resources::Glossary` object. It:
|
|
183
|
+
# * requires +source_lang+ to be set,
|
|
184
|
+
# * cannot be combined with the singular +glossary_id+ option,
|
|
185
|
+
# * allows at most `MAX_GLOSSARY_IDS` identifiers.
|
|
186
|
+
#
|
|
187
|
+
# The API expects +glossary_ids+ as an array (a JSON array in the JSON translate body).
|
|
188
|
+
# Callers sending multipart/form-data (e.g. document upload) should join the returned
|
|
189
|
+
# array into a comma-separated string.
|
|
190
|
+
#
|
|
191
|
+
# @param [String, nil] source_lang The source language for the request.
|
|
192
|
+
# @return [Array<String>, nil] The list of glossary IDs, or nil if the +glossary_ids+
|
|
193
|
+
# option was not provided.
|
|
194
|
+
# @raise [ArgumentError] If any of the validation rules above are violated.
|
|
195
|
+
def build_glossary_ids_param(source_lang)
|
|
196
|
+
return nil unless option?(:glossary_ids)
|
|
197
|
+
|
|
198
|
+
ids = Array(delete_option(:glossary_ids))
|
|
199
|
+
return nil if ids.empty?
|
|
200
|
+
|
|
201
|
+
validate_glossary_ids!(ids, source_lang)
|
|
202
|
+
ids.map do |glossary|
|
|
203
|
+
glossary.is_a?(DeepL::Resources::Glossary) ? glossary.id : glossary.to_s
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def validate_glossary_ids!(ids, source_lang)
|
|
208
|
+
if option?(:glossary_id)
|
|
209
|
+
raise ArgumentError,
|
|
210
|
+
'The `glossary_ids` option cannot be used together with the `glossary_id` option'
|
|
211
|
+
end
|
|
212
|
+
if source_lang.nil?
|
|
213
|
+
raise ArgumentError, 'The `glossary_ids` option requires `source_lang` to be set'
|
|
214
|
+
end
|
|
215
|
+
return unless ids.size > MAX_GLOSSARY_IDS
|
|
216
|
+
|
|
217
|
+
raise ArgumentError,
|
|
218
|
+
"A maximum of #{MAX_GLOSSARY_IDS} glossary IDs may be provided via `glossary_ids`"
|
|
219
|
+
end
|
|
220
|
+
|
|
175
221
|
def path
|
|
176
222
|
raise NotImplementedError
|
|
177
223
|
end
|
|
@@ -189,6 +235,12 @@ module DeepL
|
|
|
189
235
|
api.configuration.host
|
|
190
236
|
end
|
|
191
237
|
|
|
238
|
+
# Subclasses that talk to a host other than the DeepL API, such as a pre-signed storage
|
|
239
|
+
# URL, override this to request through their own connection.
|
|
240
|
+
def http_client
|
|
241
|
+
api.http_client
|
|
242
|
+
end
|
|
243
|
+
|
|
192
244
|
def query_params
|
|
193
245
|
options
|
|
194
246
|
end
|
|
@@ -49,10 +49,59 @@ module DeepL
|
|
|
49
49
|
]
|
|
50
50
|
filename_param = filename || File.basename(input_file_path)
|
|
51
51
|
form_data.push(['filename', filename_param]) unless filename_param.nil?
|
|
52
|
-
|
|
52
|
+
add_extra_options_to_form(form_data)
|
|
53
53
|
form_data
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
+
def add_extra_options_to_form(form_data)
|
|
57
|
+
add_supported_options_to_form(form_data)
|
|
58
|
+
add_glossary_ids_to_form(form_data)
|
|
59
|
+
add_style_rule_to_form(form_data)
|
|
60
|
+
add_translation_memory_to_form(form_data)
|
|
61
|
+
add_translation_memory_threshold_to_form(form_data)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Validates and serializes the `glossary_ids` option (see
|
|
65
|
+
# `DeepL::Requests::Base#build_glossary_ids_param`) and appends it to the form data as a
|
|
66
|
+
# comma-separated `glossary_ids` field. Requires `source_lang`, cannot be combined with
|
|
67
|
+
# the singular `glossary_id` option, and allows at most 5 IDs.
|
|
68
|
+
def add_glossary_ids_to_form(form_data)
|
|
69
|
+
glossary_ids = build_glossary_ids_param(source_lang)
|
|
70
|
+
form_data.push(['glossary_ids', glossary_ids.join(',')]) unless glossary_ids.nil?
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Serializes the `style_rule` option and appends it to the form data as a `style_id`
|
|
74
|
+
# field. Mirrors text translation: accepts either a style rule ID string or a
|
|
75
|
+
# `DeepL::Resources::StyleRule` object.
|
|
76
|
+
def add_style_rule_to_form(form_data)
|
|
77
|
+
return unless option?(:style_rule)
|
|
78
|
+
|
|
79
|
+
rule = delete_option(:style_rule)
|
|
80
|
+
style_id = rule.is_a?(DeepL::Resources::StyleRule) ? rule.style_id : rule
|
|
81
|
+
form_data.push(['style_id', style_id.to_s])
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Serializes the `translation_memory` option and appends it to the form data as a
|
|
85
|
+
# `translation_memory_id` field. Mirrors text translation: the `translation_memory`
|
|
86
|
+
# option accepts either a translation memory ID string or a
|
|
87
|
+
# `DeepL::Resources::TranslationMemory` object.
|
|
88
|
+
def add_translation_memory_to_form(form_data)
|
|
89
|
+
return unless option?(:translation_memory)
|
|
90
|
+
|
|
91
|
+
tm = delete_option(:translation_memory)
|
|
92
|
+
tm_id = tm.is_a?(DeepL::Resources::TranslationMemory) ? tm.translation_memory_id : tm
|
|
93
|
+
form_data.push(['translation_memory_id', tm_id.to_s])
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Serializes the `translation_memory_threshold` option (integer 0-100) and appends it to
|
|
97
|
+
# the form data as a `translation_memory_threshold` field, mirroring text translation.
|
|
98
|
+
def add_translation_memory_threshold_to_form(form_data)
|
|
99
|
+
return unless option?(:translation_memory_threshold)
|
|
100
|
+
|
|
101
|
+
threshold = delete_option(:translation_memory_threshold)
|
|
102
|
+
form_data.push(['translation_memory_threshold', threshold.to_s])
|
|
103
|
+
end
|
|
104
|
+
|
|
56
105
|
def add_supported_options_to_form(form_data)
|
|
57
106
|
SUPPORTED_OPTIONS.each do |option_name|
|
|
58
107
|
option_value = option(option_name)
|
|
@@ -39,7 +39,7 @@ module DeepL
|
|
|
39
39
|
tweak_parameters!
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
-
def request # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
42
|
+
def request # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
|
|
43
43
|
text_arrayified = text.is_a?(Array) ? text : [text]
|
|
44
44
|
payload = { text: text_arrayified, source_lang: source_lang, target_lang: target_lang }
|
|
45
45
|
|
|
@@ -65,6 +65,9 @@ module DeepL
|
|
|
65
65
|
payload[:translation_memory_threshold] = delete_option(:translation_memory_threshold)
|
|
66
66
|
end
|
|
67
67
|
|
|
68
|
+
glossary_ids = build_glossary_ids_param(source_lang)
|
|
69
|
+
payload[:glossary_ids] = glossary_ids unless glossary_ids.nil?
|
|
70
|
+
|
|
68
71
|
build_texts(*execute_request_with_retries(post_request(payload)))
|
|
69
72
|
end
|
|
70
73
|
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Copyright 2026 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.md file.
|
|
4
|
+
# frozen_string_literal: true
|
|
5
|
+
|
|
6
|
+
module DeepL
|
|
7
|
+
module Requests
|
|
8
|
+
module TranslationMemory
|
|
9
|
+
##
|
|
10
|
+
# Common behaviour of the translation memory endpoints: they are served under the `v3`
|
|
11
|
+
# prefix instead of the configured API version, and take their parameters in the query
|
|
12
|
+
# string rather than in the request body.
|
|
13
|
+
|
|
14
|
+
class Base < DeepL::Requests::Base
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def uri
|
|
18
|
+
@uri ||= begin
|
|
19
|
+
base_uri = URI("#{host}/v3/#{path}")
|
|
20
|
+
query_string = build_query_string
|
|
21
|
+
base_uri.query = query_string unless query_string.empty?
|
|
22
|
+
base_uri
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def build_query_string
|
|
27
|
+
# encode_www_form encodes a space as `+`, which is correct for a form body but not for
|
|
28
|
+
# a URI query string. A literal `+` is already escaped as %2B, so every remaining `+`
|
|
29
|
+
# is a space.
|
|
30
|
+
URI.encode_www_form(query_params).gsub('+', '%20')
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def query_params
|
|
34
|
+
{}
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def get_request # rubocop:disable Naming/AccessorMethodName
|
|
38
|
+
Net::HTTP::Get.new(uri.request_uri, add_json_content_type(headers))
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def delete_request
|
|
42
|
+
Net::HTTP::Delete.new(uri.request_uri, add_json_content_type(headers))
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|