5个不稳定版本

0.3.0 2024年4月21日
0.2.1 2023年9月13日
0.2.0 2023年7月20日
0.1.1 2023年7月13日
0.1.0 2023年7月12日

#101HTTP客户端

Download history 181/week @ 2024-04-20 5/week @ 2024-04-27 2/week @ 2024-06-01 9/week @ 2024-06-29 48/week @ 2024-07-27

每月56次下载

MIT 许可证

60KB
958

deeprl

通过一个快速可靠的接口访问DeepL翻译引擎。我们旨在提供DeepL提供的全部工具套件。请参阅DeepL API文档以获取详细资源。

注意

此crate使用阻塞HTTP客户端,因此仅适用于同步(阻塞)应用程序。如果您打算在异步应用程序中使用库函数,有一个crate适合该用途。

快速入门

创建一个新的客户端,使用有效的API令牌访问相关方法。例如,您可能希望将简单的文本字符串翻译成某种目标语言。

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

let key = std::env::var("DEEPL_API_KEY").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");

作为一个有用的健全性检查,确保您能够返回账户使用统计信息。

use deeprl::DeepL;

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

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

let count = usage.character_count;
let limit = usage.character_limit;
println!("Used: {count}/{limit}");
// Used: 42/500000

教程

内容

配置

库支持许多配置选项,其中之一是能够用新的reqwest::blocking::Client实例替换默认客户端。

与之前一样,我们创建了一个新的DeepL实例,但这次我们声明它为mut。然后我们在对象上调用client并传入我们的自定义客户端。

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

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

dl.client(client);

我们支持在请求中发送自定义用户代理。例如,如果您在其他应用程序中使用此库,例如My App v1.2.3,您可以使用set_app_info设置应用程序名称和版本。

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

错误

错误封装在Error枚举中,其变体可能为以下之一

  • Client:通用的客户端错误
  • Server:服务器发送的错误
  • Deserialize:反序列化响应时发生错误
  • InvalidRequest:发送HTTP请求时出错
  • InvalidResponse:解析响应时出错
  • InvalidLanguage:错误匹配用户提供的字符串到Language

下面我们将要查看的库函数都是DeepL客户端的方法,许多函数返回一个Result类型(枚举),这个类型要么解析为我们期望的类型,要么是上述的Error之一。因此,对于某些类型T,返回一个结果的函数类型是Result<T, Error>。虽然在我们提供的例子中我们使用unwrap来展开Result以提取值,但在生产代码中实现更健壮的错误处理是常见的。

获取语言

获取可用languages需要指定LanguageTypeSourceTarget,并返回一个Result,其成功值是一个包含Vec<LanguageInfo>的向量。

所有LanguageInfo实例都包含languagename属性。目标语言包含第三个字段,supports_formality,该字段为truefalse

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
}

文本翻译选项

翻译文本可以通过TextOptions构建器设置多个选项,其中之一是必需的,即翻译的target_lang

文本翻译的选项列表如下

  • target_lang:翻译的目标Language(必需)
  • source_lang:源Language
  • split_sentences:决定如何从输入文本中分割句子。可以是以下之一
    • SplitSentences::None不分割句子。
    • SplitSentences::Default在标点符号和新行处分割(默认)。
    • SplitSentences::NoNewLines仅在标点符号处分割。
  • preserve_formatting:翻译器是否应保留文本的原始格式。(默认false
  • formality:在目标语言中期望的正式程度。并非所有目标语言都支持正式程度。选项包括
    • 正式程度::默认
    • 正式程度::更正式
    • 正式程度::不太正式
    • 正式程度::更倾向于正式
    • 正式程度::更倾向于不正式
  • glossary_id:用于翻译的术语表ID String

标签处理

以下是与标签处理相关的翻译选项

  • tag_handling:启用处理输入文本中的标签。可以是以下之一
    • 标签处理::XML
    • 标签处理::HTML
  • outline_detection:翻译器是否应自动检测大纲(默认true
  • splitting_tags:用于分割句子的逗号分隔的标签列表,例如 "head,title,body" String
  • non_splitting_tags:不分割句子的逗号分隔的标签列表,String
  • ignore_tags:逗号分隔的不要翻译的标签列表,String

以下是一个更复杂的翻译示例,其中我们希望指定源语言,忽略输入中的换行符,保留格式,并设置所需的正式程度。我们还将使用自定义词汇表,确保提供的词汇表与翻译的源语言和目标语言相匹配。

函数 translate 需要两个参数:一个 TextOptions 对象,以及一个包含一个或多个待翻译文本的 Vec<String>。它返回一个 Result,其 Ok 值是一个包含单个字段 translationsTranslateTextResult,该字段包含一个 Vec<Translation>

// Translate 'you are nice' to French 
let text = vec![
    "you are nice".to_string(),
];
let opt = TextOptions::new(Language::FR) // note `new` expects the required target lang
    .source_lang(Language::EN)
    .formality(Formality::PreferLess)

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

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

Translation 有两个属性:包含翻译文本字符串的 text 和包含由服务器检测到的源语言语言代码的字符串 detected_source_language

以下是一个示例,其中输入包含 xml,我们只想翻译 <p> 标签内的内容。

let xml = r"
<xml>
    <head>
        <title>My English title</title>
    </head>
    <body>
        <p>The red crab</p>
        <p>Do you speak French?</p>
    </body>
</xml>"
    .to_string();

let text = vec![xml];
let split = "p".to_string(); // split on <p> tags
let ignore = "title".to_string(); // ignore <title> tags

let opt = TextOptions::new(Language::FR)
    .source_lang(Language::EN)
    .tag_handling(TagHandling::Xml)
    .outline_detection(false)
    .splitting_tags(split)
    .ignore_tags(ignore);

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

let text = &result.translations[0].text;
assert!(text.contains("My English title"));
assert!(text.contains("Le crabe rouge"));

翻译文档

翻译文档包括三个步骤:1)上传文档,2)轮询正在进行的翻译状态,3)请求下载翻译后的文档。

首先,我们创建一个 DocumentOptions 实例,这需要我们知道目标语言以及存储在本地且格式受支持的文档的文件路径。文档选项列表如下

  • target_lang:翻译的目标Language(必需)
  • file_path:源文件的路径作为 PathBuf(必需)
  • source_lang:源Language
  • filename:文件名,String
  • formality:正式程度偏好,可以是以下之一
    • 正式程度::默认
    • 正式程度::更正式
    • 正式程度::不太正式
    • 正式程度::更倾向于正式
    • 正式程度::更倾向于不正式
  • glossary_id:用于翻译的词汇表的 id,String
// Upload a file in the current directory called 'test.txt'
let target_lang = Language::DE;
let file_path = std::path::PathBuf::from("test.txt");
let opt = DocumentOptions::new(target_lang, file_path);

let doc = dl.document_upload(opt).unwrap();

println!("Document Id: {}", doc.document_id);
println!("Document Key: {}", doc.document_key);

document_upload 期望一个 DocumentOptions 实例,并返回一个 Result,其 Ok 值是一个包含两个字段的 Document 处理程序:作为字符串的 document_iddocument_key

在我们能够下载完成的文档之前,我们需要检查翻译过程的状态。我们通过在客户端调用 document_status 并传递我们之前收到的 Document 处理程序的引用来实现这一点。该方法返回一个 Result<DocumentStatus>,其中 DocumentStatus 是一个具有以下字段的结构体

  • document_id:唯一的文档 id,String
  • status:一个枚举,DocState 在以下状态之一
    • DocState::Queued
    • DocState::Translating
    • DocState::Done
    • DocState::Error
  • seconds_remaining:翻译完成预计剩余时间,Option<u64>
  • billed_characters:计费字符数,Option<u64>
  • error_message:错误情况下来自服务器的消息 Option<String>

翻译完成后,status将处于DocState::Done状态,在我们的DocumentStatus对象上调用is_done将返回true。然后我们可以进行下载。

// Get the status of a document translation in progress
let status = dl.document_status(&doc).unwrap();

if status.is_done() {
    // Download translation result
    let out_file = PathBuf::from("test-translated.txt");
    let _ = dl.document_download(doc, Some(out_file.clone())).unwrap();
    let content = std::fs::read_to_string(out_file).unwrap();
    assert(!content.is_empty());
}

document_download函数接受作为参数的是我们在上传后收到的相同的Document句柄,以及一个表示完成文档将保存的文件路径的可选PathBuf。该函数返回一个Result<PathBuf>,其中PathBuf是新翻译文档的路径。

如果用户提供的输出文件路径为None,将在当前目录中创建一个文件,其名称包含唯一的document_id

术语表

DeepL支持为多个语言对创建自定义术语表,允许用户指定用于源文本中给定单词的确切翻译。为了演示,首先我们将查询支持的术语表语言对列表。

glossary_languages方法不接受任何参数,并返回一个Result<GlossaryLanguagePairsResult>,其Ok值有一个单字段,即supported_languages,它包含一个Vec<GlossaryLanguagePair>

GlossaryLanguagePair包含两个字段:作为字符串的source_langtarget_lang

// Get supported glossary language pairs
let result = dl.glossary_languages().unwrap();

let lang_pairs = result.supported_languages;
assert!(!lang_pairs.is_empty());

for pair in lang_pairs {
    println!("{} -> {}", pair.source_lang, pair.target_lang);
    // EN -> IT
}

现在,让我们创建一个源语言为英语、目标语言为意大利的术语表。为此,我们将创建一个名为my_glossary.csv的文件来保存术语表条目。条目格式为逗号分隔列表,有两列(源,目标),每行一个条目。因此,包含两个术语表条目的csv文件如下所示

my_glossary.csv

hello,ciao
goodbye,ciao

在rust中,我们将文件内容读取到名为entries的字符串中,并将其与以下参数一起传递给glossary_new(注意,DeepL还接受作为制表符分隔值的术语表条目)

  • name: String
  • source_lang:Language
  • target_lang:Language
  • entries: String
  • fmt:我们的条目格式,必须是以下之一
    • GlossaryEntriesFormat::Csv
    • GlossaryEntriesFormat::Tsv

glossary_new返回一个Result<Glossary>,其中Glossary是一个具有以下字段的struct

  • glossary_id: String
  • ready: bool
  • name: String
  • source_lang: String
  • target_lang: String
  • creation_time: String
  • entry_count: u64
// Create a new glossary
let name = "my_glossary".to_string();
let src = Language::EN;
let trg = Language::IT;
let entries = std::fs::read_to_string("my_glossary.csv").unwrap();
let fmt = GlossaryEntriesFormat::Csv;

let glossary = dl.glossary_new(name, src, trg, entries, fmt).unwrap();
assert_eq!(glossary.entry_count, 2);

let glos_id = glossary.glossary_id; // remember this!

// List glossaries
let result = dl.glossaries().unwrap();
let glossaries = result.glossaries;
assert!(!glossaries.is_empty());

列出可用的glossaries返回一个Result<GlossariesResult>,其内部值有一个属性glossaries,它包含一个Vec<Glossary>

我们可以通过不同的方式从术语表中获取信息。使用有效的glossary_id调用glossary_info将返回一个包含上述属性的Result<Glossary>。此方法仅返回术语表元数据。

要获取实际的条目,我们使用带有有效 glossary_idglossary_entries 方法。该函数返回一个 Result<HashMap<String, String>>,其中 Ok 的值是一个将唯一源词映射到其目标翻译的集合。

// Get glossary info
// recall `glos_id` is the glossary id we obtained earlier
let glossary = dl.glossary_info(&glos_id).unwrap();

println!("{}", glossary.name);
// my_glossary

// Get entries from a glossary
let entries = dl.glossary_entries(&glos_id).unwrap();

for (key, value) in entries {
    println!("{key} {value}");
    
    /*
    hello ciao
    goodbye ciao
    */
}

// Remove an unwanted glossary
let result = dl.glossary_delete(&glos_id);
assert!(result.is_ok());

要删除术语表,调用 glossary_delete 方法并传递 glossary_id 的引用。该函数返回 Result<()>,其中成功值是一个空元组。

依赖关系

~4–15MB
~229K SLoC