async_translate/microsoft/
mod.rs

1//! 微软翻译器实现
2//!
3//! 该翻译器使用自动获取的临时认证token,无需手动配置API密钥。
4//! 每次翻译时会自动从Microsoft的边缘服务获取临时token。
5
6use crate::translator::Translator;
7use anyhow::Result;
8use reqwest::Client;
9use serde::{Deserialize, Serialize};
10use std::sync::Arc;
11use tokio::sync::{Mutex, Semaphore};
12
13/// 微软翻译器配置
14///
15/// 该配置用于设置微软翻译器的参数。由于使用自动认证方式,
16/// 无需配置API密钥,系统会自动获取临时认证token。
17#[derive(Debug, Clone)]
18pub struct MicrosoftConfig {
19    /// 微软翻译服务的端点
20    pub endpoint: String,
21    /// 并发请求数限制
22    pub concurrent_limit: usize,
23}
24
25impl Default for MicrosoftConfig {
26    fn default() -> Self {
27        Self {
28            endpoint: "https://api-edge.cognitive.microsofttranslator.com".to_string(),
29            concurrent_limit: 10,
30        }
31    }
32}
33
34/// 微软翻译器实现
35///
36/// 使用自动认证方式,通过临时token进行API调用。
37/// 不需要手动管理API密钥,系统会自动获取和刷新认证token。
38pub struct MicrosoftTranslator {
39    client: Client,
40    config: MicrosoftConfig,
41    /// 控制并发数的信号量
42    semaphore: Arc<Semaphore>,
43    /// 认证token
44    auth: Arc<Mutex<Option<String>>>,
45}
46
47impl MicrosoftTranslator {
48    /// 创建新的微软翻译器实例
49    ///
50    /// 该翻译器使用自动认证方式,无需配置API密钥。
51    ///
52    /// # 参数
53    ///
54    /// * `config` - 微软翻译器配置(包含端点和并发限制)
55    ///
56    /// # 返回值
57    ///
58    /// 返回配置好的微软翻译器实例
59    pub fn new(config: MicrosoftConfig) -> Self {
60        Self {
61            client: Client::new(),
62            config: config.clone(),
63            semaphore: Arc::new(Semaphore::new(config.concurrent_limit)),
64            auth: Arc::new(Mutex::new(None)),
65        }
66    }
67
68    /// 获取认证token
69    ///
70    /// 通过访问Microsoft的边缘认证服务自动获取临时token。
71    /// 该token具有一定的有效期,系统会自动管理token的获取和缓存。
72    ///
73    /// # 返回值
74    ///
75    /// 返回获取到的Bearer token字符串
76    async fn authenticate(&self) -> Result<String> {
77        let mut auth_attempts = 3;
78        while auth_attempts > 0 {
79            auth_attempts -= 1;
80            match self.client
81                .get("https://edge.microsoft.com/translate/auth")
82                .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")
83                .send()
84                .await
85            {
86                Ok(response) => {
87                    if response.status().is_success() {
88                        let token = response.text().await?;
89                        return Ok(token);
90                    } else {
91                        if auth_attempts <= 0 {
92                            return Err(anyhow::anyhow!("Failed to authenticate with Microsoft Translator: HTTP {}", response.status()));
93                        }
94                    }
95                }
96                Err(e) => {
97                    if auth_attempts <= 0 {
98                        return Err(anyhow::anyhow!("Failed to connect to Microsoft Translator: {}", e));
99                    }
100                }
101            }
102            tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
103        }
104        Err(anyhow::anyhow!("Failed to get Microsoft Translator authorization after retries"))
105    }
106}
107
108#[async_trait::async_trait]
109impl Translator for MicrosoftTranslator {
110    async fn translate(&self, text: &str, target_lang: &str) -> Result<String> {
111        // 获取并发许可
112        let _permit = self.semaphore.acquire().await?;
113
114        // 获取认证token
115        let token = {
116            let mut auth_guard = self.auth.lock().await;
117            if auth_guard.is_none() {
118                *auth_guard = Some(self.authenticate().await?);
119            }
120            auth_guard.clone().unwrap()
121        };
122
123        // 构造请求
124        #[derive(Serialize)]
125        struct Request {
126            text: String,
127        }
128
129        let request = vec![Request {
130            text: text.to_string(),
131        }];
132
133        // 构造查询参数
134        let params = [
135            ("api-version", "3.0"),
136            ("to", target_lang),
137            ("includeSentenceLength", "true"),
138        ];
139
140        // 发送请求
141        let response = self.client
142            .post(&format!("{}/translate", self.config.endpoint))
143            .header("Authorization", format!("Bearer {}", token))
144            .header("Content-Type", "application/json")
145            .query(&params)
146            .json(&request)
147            .send()
148            .await?;
149
150        // 检查HTTP状态码
151        if !response.status().is_success() {
152            let status = response.status();
153            let error_text = response.text().await?;
154            return Err(anyhow::anyhow!("HTTP error {}: {}", status, error_text));
155        }
156
157        // 解析响应
158        #[derive(Deserialize)]
159        struct Translation {
160            text: String,
161        }
162
163        #[derive(Deserialize)]
164        struct ResponseItem {
165            translations: Vec<Translation>,
166        }
167
168        let response_body: Vec<ResponseItem> = response.json().await?;
169
170        if response_body.is_empty() || response_body[0].translations.is_empty() {
171            return Err(anyhow::anyhow!("No translation results returned"));
172        }
173
174        Ok(response_body[0].translations[0].text.clone())
175    }
176}
177
178#[cfg(test)]
179mod tests;