deeprl

Access the DeepL translation engine through a quick and reliable interface. We aim to provide the full suite of tools DeepL offers. See the DeepL API docs for detailed resources.

Note:

This crate uses a blocking http client, and as such is only suitable for use in synchronous (blocking) applications. If you intend to use the library functions in an async app, there is a crate for that.

Quickstart

Create a new client with a valid API token to access the associated methods. For instance, you may wish to translate a simple text string to some target language.

```rust use deeprl::{DeepL, Language, TextOptions};

let key = std::env::var("DEEPLAPIKEY").unwrap(); let dl = DeepL::new(&key);

// Translate 'good morning' to German let opt = TextOptions::new(Language::DE);

let text = vec![ "good morning".to_string(), ];

let result = dl.translate(opt, text).unwrap(); assert!(!result.translations.is_empty());

let translation = &result.translations[0]; assert_eq!(translation.text, "Guten Morgen"); ```

As a helpful sanity check, make sure you're able to return account usage statistics.

```rust use deeprl::DeepL;

let dl = DeepL::new( &std::env::var("DEEPLAPIKEY").unwrap() );

let usage = dl.usage().unwrap(); assert!(usage.character_limit > 0);

let count = usage.charactercount; let limit = usage.characterlimit; println!("Used: {count}/{limit}"); // Used: 42/500000 ```

Walkthrough

Contents: - Configuration - Errors - Get available languages - Translate text options - Tag handling - Translate documents - Manage glossaries

Configuration

The library supports a number of configuration options, one of which is the ability to swap out the default client for a new instance of reqwest::blocking::Client.

As before, we create a new instance of DeepL, but this time we declare it mut. Then we call client on the object and pass in our custom client.

```rust let mut dl = DeepL::new( &std::env::var("DEEPLAPIKEY").unwrap() );

let client = reqwest::blocking::Client::builder() .timeout(std::time::Duration::from_secs(21)) .build() .unwrap();

dl.client(client); ```

We support sending a custom user agent along with requests. So for instance if you're using this library in another application, say My App v1.2.3, you can set the app name and version using set_app_info. ```rust // snip--

dl.setappinfo( "my-app/1.2.3".to_string() ); ```

Errors

Errors are encapsulated in the Error enum whose variants may be one of: - Client: A generic client-side error - Server: An error sent by the server - Deserialize: An error occurred while deserializing the response - InvalidRequest: Error sending an http request - InvalidResponse: Error parsing the response - InvalidLanguage: Error matching a user-supplied string to a Language

The library functions we'll look at below are all methods on the DeepL client, and many return a Result type (enum) that either resolves to a value of the type we expect, or one of the above Errors. So for some type T, the return type of a function returning a result is Result<T, Error>. While in the examples we unwrap the Result to pull out a value, it's common to implement more robust error handling in production code.

Get languages

Getting available languages requires specifying LanguageType as either Source or Target and returns a Result whose success value is a Vec<LanguageInfo>.

All instances of LanguageInfo contain language and name attributes. Target languages contain a third field, supports_formality which is true or false.

```rust // snip--

let source_langs = dl.languages(LanguageType::Source).unwrap();

for lang in source_langs { let code = lang.language; let name = lang.name;

println!("{code} {name}");
// BG Bulgarian

} ```

Translate text options

Translating text allows setting a number of options through the TextOptions builder, only one of which is required, namely the target_lang for the translation.

The list of options for text translation is as follows:

Tag handling

The following are translation options related to tag handling

Below is a more complex translation where we want to specify a source language, ignore newlines in the input, preserve formatting, and set a desired formality. We'll also use a custom glossary, ensuring the given glossary matches both the source and target language of this translation.

The function translate expects two arguments: a TextOptions object, and a Vec<String> containing one or more texts to be translated. It returns a Result whose Ok value is a TranslateTextResult with a single field, translations that holds a Vec<Translation>.

```rust // snip--

// Translate 'you are nice' to French // where the text contains a '\n' let text = vec![ "you\nare nice".to_string(), ];

let glossary = String::from("abc-123"); // your glossary id

let opt = TextOptions::new(Language::FR) // note new expects the required target lang .sourcelang(Language::EN) .splitsentences(SplitSentences::NoNewlines) .preserveformatting(true) .formality(Formality::PreferLess) .glossaryid(glossary);

let result = dl.translate(opt, text).unwrap();

let translation = &result.translations[0]; println!("{}", translation.text); // tu es gentille ```

A Translation has two attributes: text containing the translated text string, and detected_source_language, a string containing the language code of the source language detected by the server.

Here's an example where the input contains xml and where we only want to translate content inside the <p> tags.

```rust // snip--

let xml = r" My English title

The red crab

Do you speak French?

" .to_string();

let text = vec![xml]; let split = "p".tostring(); // split on

tags let ignore = "title".tostring(); // ignore tags</p> <p>let opt = TextOptions::new(Language::FR) .source<em>lang(Language::EN) .tag</em>handling(TagHandling::Xml) .outline<em>detection(false) .splitting</em>tags(split) .ignore_tags(ignore);</p> <p>let result = dl.translate(opt, text).unwrap();</p> <p>let text = &result.translations[0].text; assert!(text.contains("My English title")); assert!(text.contains("Le crabe rouge")); ```</p> <h3>Translate documents</h3> <p>Translating a document consists of three steps: 1) uploading a document, 2) polling the status of a translation in progress, and 3) requesting download of the translated document.</p> <p>First, we create an instance of <code>DocumentOptions</code> which requires we know the target language as well as the file path to a document stored locally and in a supported format. The list of document options is as follows:</p> <ul> <li><code>target_lang</code>: The target <code>Language</code> (required)</li> <li><code>file_path</code>: Path to the source file as <code>PathBuf</code> (required)</li> <li><code>source_lang</code>: The source <code>Language</code></li> <li><code>filename</code>: Name of the file, <code>String</code></li> <li><code>formality</code>: Formality preference, can be one of: <ul> <li><code>Formality::Default</code></li> <li><code>Formality::More</code></li> <li><code>Formality::Less</code></li> <li><code>Formality::PreferMore</code></li> <li><code>Formality::PreferLess</code></li> </ul></li> <li><code>glossary_id</code>: The id of the glossary to use for translation, <code>String</code></li> </ul> <p>```rust // snip--</p> <p>// Upload a file in the current directory called 'test.txt' let target<em>lang = Language::DE; let file</em>path = std::path::PathBuf::from("test.txt"); let opt = DocumentOptions::new(target<em>lang, file</em>path);</p> <p>let doc = dl.document_upload(opt).unwrap();</p> <p>println!("Document Id: {}", doc.document<em>id); println!("Document Key: {}", doc.document</em>key); ```</p> <p><code>document_upload</code> expects an instance of <code>DocumentOptions</code> and returns a <code>Result</code> whose <code>Ok</code> value is a <code>Document</code> handle with two fields: <code>document_id</code> and <code>document_key</code> as strings.</p> <p>Before we can download a finished document, we need to check the status of the translation process. We do so by calling <code>document_status</code> on the client and passing in a reference to the <code>Document</code> handle we received previously. The method returns a <code>Result<DocumentStatus></code> where <code>DocumentStatus</code> is a struct with the following fields: <br /> - <code>document_id</code>: The unique document id <code>String</code> - <code>status</code>: An enum, <code>DocState</code> in one of the following states: - <code>DocState::Queued</code> - <code>DocState::Translating</code> - <code>DocState::Done</code> - <code>DocState::Error</code> - <code>seconds_remaining</code>: Estimated time until translation is complete <code>Option<u64></code> - <code>billed_characters</code>: Number of characters billed <code>Option<u64></code> - <code>error_message</code>: Message from the server in case of error <code>Option<String></code></p> <p>When translation is complete, <code>status</code> will be in a state of <code>DocState::Done</code>, and calling <code>is_done</code> on our <code>DocumentStatus</code> object returns true. We may then proceed with download.</p> <p>```rust // snip--</p> <p>// Get the status of a document translation in progress let status = dl.document_status(&doc).unwrap();</p> <p>if status.is<em>done() { // Download translation result let out</em>file = Some(PathBuf::from("de-test.txt")); let path = dl.document<em>download(doc, out</em>file).unwrap();</p> <pre><code>println!("New translation result at: {path}"); </code></pre> <p>} ```</p> <p><code>document_download</code> takes as arguments the same <code>Document</code> handle we received after uploading as well as an optional <code>PathBuf</code> denoting the path to the file where the finished document will be saved. The function returns <code>Result<PathBuf></code> where <code>PathBuf</code> is the path to the newly translated document. </p> <p>If the user-supplied file path for the outgoing file is <code>None</code>, a file will be created in the current directory whose name contains the unique <code>document_id</code>.</p> <h3>Glossaries</h3> <p>DeepL supports creating custom glossaries for several language pairs allowing the user to specify an exact translation to use for a given word in the source text. To demonstrate, first we'll query the list of supported glossary language pairs.</p> <p>The <code>glossary_languages</code> method takes no arguments and returns a <code>Result<GlossaryLanguagePairsResult></code> whose <code>Ok</code> value has a single field, <code>supported_languages</code> holding a <code>Vec<GlossaryLanguagePair></code>.</p> <p>A <code>GlossaryLanguagePair</code> contains two fields: <code>source_lang</code> and <code>target_lang</code> as strings.</p> <p>```rust // snip--</p> <p>// Get supported glossary language pairs let result = dl.glossary_languages().unwrap();</p> <p>let lang<em>pairs = result.supported</em>languages; assert!(!lang<em>pairs.is</em>empty());</p> <p>for pair in lang<em>pairs { println!("{} -> {}", pair.source</em>lang, pair.target_lang); // EN -> IT } ```</p> <p>Now let's create a glossary with source language English and target language Italian. To do so, we'll create a file called <em>my_glossary.csv</em> to hold the glossary entries. The entries are formatted as a comma-separated list with two columns (source,target) with one entry per line. Thus, our csv file with two glossary entries looks like this:</p> <blockquote> <p><code>my_glossary.csv</code> <code> hello,ciao goodbye,ciao </code></p> </blockquote> <p>Back in rust, we'll read the contents of the file to a string called <code>entries</code> and pass it to <code>glossary_new</code> together with the following parameters (note, DeepL accepts glossary entries as tab-separated values as well): - <code>name: String</code> - <code>source_lang: Language</code> - <code>target_lang: Language</code> - <code>entries: String</code> - <code>fmt</code>: The format of our entries, must be one of: - <code>GlossaryEntriesFormat::Csv</code> - <code>GlossaryEntriesFormat::Tsv</code></p> <p><code>glossary_new</code> returns a <code>Result<Glossary></code> where <code>Glossary</code> is a struct with the following fields: - <code>glossary_id: String</code> - <code>ready: bool</code> - <code>name: String</code> - <code>source_lang: String</code> - <code>target_lang: String</code> - <code>creation_time: String</code> - <code>entry_count: u64</code></p> <p>```rust // snip--</p> <p>// Create a new glossary let name = "my<em>glossary".to</em>string(); let src = Language::EN; let trg = Language::IT; let entries = std::fs::read<em>to</em>string("my_glossary.csv").unwrap(); let fmt = GlossaryEntriesFormat::Csv;</p> <p>let glossary = dl.glossary<em>new(name, src, trg, entries, fmt).unwrap(); assert</em>eq!(glossary.entry_count, 2);</p> <p>let glos<em>id = glossary.glossary</em>id; // remember this!</p> <p>// List glossaries let result = dl.glossaries().unwrap(); let glossaries = result.glossaries; assert!(!glossaries.is_empty()); ```</p> <p>Listing available <code>glossaries</code> returns a <code>Result<GlossariesResult></code> whose inner value has an attribute <code>glossaries</code> that holds a <code>Vec<Glossary></code>.</p> <p>We can get information from a glossary in different ways. Calling <code>glossary_info</code> with a valid <code>glossary_id</code> returns a <code>Result<Glossary></code> containing the attributes mentioned above. This method returns glossary metadata only. </p> <p>To retrieve the actual entries, we use the <code>glossary_entries</code> method with a valid <code>glossary_id</code>. The function returns a <code>Result<HashMap<String, String>></code> where the <code>Ok</code> value is a collection mapping a unique source word to its target translation.</p> <p>```rust // snip--</p> <p>// Get glossary info // recall <code>glos_id</code> is the glossary id we obtained earlier let glossary = dl.glossary<em>info(&glos</em>id).unwrap();</p> <p>println!("{}", glossary.name); // my_glossary</p> <p>// Get entries from a glossary let entries = dl.glossary<em>entries(&glos</em>id).unwrap();</p> <p>for (key, value) in entries { println!("{key} {value}");</p> <pre><code>/* hello ciao goodbye ciao */ </code></pre> <p>}</p> <p>// Remove an unwanted glossary let result = dl.glossary<em>del(&glos</em>id); assert!(result.is_ok()); ```</p> <p>To remove a glossary, call the <code>glossary_del</code> method passing a reference to the <code>glossary_id</code>. The function returns <code>Result<()></code> where the success value is the empty type <code>()</code>.</p> </body></html>