use crate::poly_translator::async_translator::{AsyncTranslator, Language, TranslationListOutput, TranslationOutput};
use crate::poly_translator::translator_error::TranslatorError;
use reqwest::Client;
use serde::{Deserialize, Serialize};
#[derive(Serialize)]
#[allow(dead_code)]
struct CaiyunRequest<'a> {
trans_type: String,
source: &'a [String],
#[serde(skip_serializing_if = "Option::is_none")]
detect: Option<bool>,
request_id: &'a str,
}
#[derive(Deserialize)]
#[allow(dead_code)]
struct CaiyunResponse {
target: Option<Vec<String>>,
}
pub struct CaiyunTranslator {
client: Client,
token: String,
request_id: String,
}
#[async_trait::async_trait]
impl AsyncTranslator for CaiyunTranslator {
fn local(&self) -> bool {
false
}
async fn translate(
&self,
query: &str,
from: Option<Language>,
to: &Language,
) -> anyhow::Result<TranslationOutput> {
let mut v = self
.translate_vec(&[query.to_owned()], from, to)
.await?;
Ok(TranslationOutput {
text: v.text.remove(0),
lang: Some(*to),
})
}
async fn translate_vec(
&self,
query: &[String],
from: Option<Language>,
to: &Language,
) -> anyhow::Result<TranslationListOutput> {
let f = from;
let from = match from {
Some(from) => from.to_caiyun().ok_or(TranslatorError::UnknownLanguage(from))?,
None => "auto",
};
let trans_type = format!(
"{}2{}",
from,
to.to_caiyun().ok_or(TranslatorError::UnknownLanguage(*to))?
);
let request = CaiyunRequest {
trans_type,
source: query,
detect: if f.is_none() { Some(true) } else { None },
request_id: &self.request_id,
};
let data: CaiyunResponse = self
.client
.post("https://api.interpreter.caiyunai.com/v1/translator")
.header("content-type", "application/json")
.header("x-authorization", format!("token {}", self.token))
.json(&request)
.send()
.await?
.json()
.await?;
Ok(TranslationListOutput {
text: data.target.unwrap_or_default(),
lang: None,
})
}
}
impl CaiyunTranslator {
pub fn new(token: &str, request_id: &str) -> Self {
Self {
client: Client::new(),
token: token.to_string(),
request_id: request_id.to_string(),
}
}
}
#[cfg(test)]
mod tests {
use crate::poly_translator::async_translator::{AsyncTranslator, Language};
use crate::poly_translator::caiyun_translator::CaiyunTranslator;
#[tokio::test]
async fn test_create_translator() {
let translator = CaiyunTranslator::new("test_token", "test_request_id");
assert!(!translator.local());
}
#[tokio::test]
async fn test_new_with_default_request_id() {
let translator = CaiyunTranslator::new("test_token", "demo");
assert!(!translator.local());
assert_eq!(translator.request_id, "demo");
}
#[tokio::test]
async fn test_translator_fields() {
let token = "my_token";
let request_id = "my_request_id";
let translator = CaiyunTranslator::new(token, request_id);
assert_eq!(translator.token, token);
assert_eq!(translator.request_id, request_id);
}
#[cfg(test)]
#[tokio::test]
async fn test_translate_chinese_to_english() {
dotenv::dotenv().ok();
let token = std::env::var("CAIYUN_TOKEN").expect("请设置 CAIYUN_TOKEN 环境变量");
let request_id = std::env::var("CAIYUN_REQUEST_ID").unwrap_or_else(|_| "demo".to_string());
let translator = CaiyunTranslator::new(&token, &request_id);
let result = translator
.translate("你好世界", Some(Language::Chinese), &Language::English)
.await
.expect("翻译失败");
assert!(!result.text.is_empty());
println!("中译英结果: {}", result.text);
}
#[cfg(test)]
#[tokio::test]
async fn test_translate_english_to_chinese() {
dotenv::dotenv().ok();
let token = std::env::var("CAIYUN_TOKEN").expect("请设置 CAIYUN_TOKEN 环境变量");
let request_id = std::env::var("CAIYUN_REQUEST_ID").unwrap_or_else(|_| "demo".to_string());
let translator = CaiyunTranslator::new(&token, &request_id);
let result = translator
.translate("Hello World", Some(Language::English), &Language::Chinese)
.await
.expect("翻译失败");
assert!(!result.text.is_empty());
println!("英译中结果: {}", result.text);
}
}