1use log::debug;
2
3#[derive(Debug, thiserror::Error)]
4pub enum TranslateError {
5 #[error("Query Send Error")]
6 QuerySendError,
7 #[error("Response Error. Maybe Query is Too long, Max Query length is 5000. because of the padding for special characters, recommend is 3000 to 4000 characters")]
8 ResponseError,
9 #[error("Error: {0}")]
10 Other(String),
11}
12
13#[derive(Default, Debug, Clone)]
15pub struct TranslateResult {
16 pub input_lang: String,
17 pub output_lang: String,
18 pub input_text: Vec<String>,
20 pub output_text: Vec<Vec<String>>,
24 pub input_tts: Option<Vec<String>>,
26 pub output_tts: Option<Vec<String>>,
28}
29
30mod lang;
31pub use lang::{InputLang, OutputLang};
32
33pub fn build_google_api_query<T, Y>(text: &String, input_lang: T, output_lang: Y) -> String
37where
38 T: Into<InputLang>,
39 Y: Into<OutputLang>,
40{
41 let input_lang: InputLang = input_lang.into();
42 let output_lang: OutputLang = output_lang.into();
43
44 let text = text
46 .replace("\\", "\\\\")
47 .replace("\r\n", "\\n")
48 .replace("\n", "\\n")
49 .replace("\\", "\\\\")
50 .replace("\"", "\\\\\\\"");
51 let query = format!(
53 r#"[[["MkEWBc","[[\"{}\",\"{}\",\"{}\",true],[null]]",null,"generic"]]]"#,
55 text,
56 input_lang.to_string(),
57 output_lang.to_string()
58 );
59 debug!("Built Query : {}", query);
60 query
61}
62
63pub async fn send_google_api_query(query: String) -> Result<String, Box<dyn std::error::Error>> {
67 let client = reqwest::Client::new();
69
70 let builder =
72 client.post("https://translate.google.com/_/TranslateWebserverUi/data/batchexecute");
73 let builder = builder.header("content-length", "0");
75 let builder = builder.query(&[("f.req", query)]);
77 let response = builder.send().await?;
78 let text = response.text().await?;
79 debug!("Google Response : {}", text);
80
81 let text = text
83 .split_at(6)
84 .1
85 .replace("\\\\", "\\")
86 .replace("\\\"", "\"");
87 let text = text
88 .split_at(21)
89 .1
90 .split_once(r#"",null,null,null,"generic"],["#)
91 .ok_or(TranslateError::ResponseError)?
92 .0
93 .to_owned();
94 debug!("Stripped Response : {}", text);
95 Ok(text)
96}
97
98pub fn response_to_result(response: String) -> TranslateResult {
100 let mut result = TranslateResult::default();
102 let response = json::parse(&response).unwrap();
103
104 result.input_lang = response[1][4][1].to_string();
106 result.output_lang = response[1][4][2].to_string();
107
108 result.input_text = response[1][4][0]
110 .to_string()
111 .split('\n')
112 .map(|x| x.to_owned())
113 .collect();
114
115 for line in response[1][0][0][5].members().step_by(2) {
117 let mut line_result = Vec::new();
118 line_result.push(line[0].to_string());
120 for side in line[4].members().skip(1) {
121 line_result.push(side[0].to_string());
123 }
124 result.output_text.push(line_result);
125 }
126
127 result.input_tts = if !response[0][0].is_null() {
129 Some(
130 response[0][0]
131 .to_string()
132 .split('\n')
133 .map(|x| x.to_owned())
134 .collect(),
135 )
136 } else {
137 None
138 };
139 result.output_tts = if !response[1][0][0][1].is_null() {
140 Some(
141 response[1][0][0][1]
142 .to_string()
143 .split('\n')
144 .map(|x| x.to_owned())
145 .collect(),
146 )
147 } else {
148 None
149 };
150
151 result
152}
153
154pub async fn translate<T, Y>(
159 text: Vec<String>,
160 input_lang: T,
161 output_lang: Y,
162) -> Result<TranslateResult, TranslateError>
163where
164 T: Into<InputLang>,
165 Y: Into<OutputLang>,
166{
167 let input_lang: InputLang = input_lang.into();
168 let output_lang: OutputLang = output_lang.into();
169
170 let text = text.join("\n");
172
173 let query = build_google_api_query(&text, input_lang, output_lang);
175
176 let response = match send_google_api_query(query).await {
178 Ok(response) => response,
179 Err(_) => return Err(TranslateError::QuerySendError),
180 };
181
182 let result = response_to_result(response);
183
184 Ok(result)
185}
186
187pub async fn translate_one_line<T, Y>(
188 text: String,
189 input_lang: T,
190 output_lang: Y,
191) -> Result<String, TranslateError>
192where
193 T: Into<InputLang>,
194 Y: Into<OutputLang>,
195{
196 let text = vec![text];
197
198 let result = translate(text, input_lang, output_lang).await?;
199
200 Ok(result.output_text[0][0].clone())
201}
202
203#[cfg(test)]
208mod tests {
209 use super::*;
210
211 #[tokio::test]
212 async fn test_translate_one_line() {
213 let text = "Hello, world!".to_string();
214 let input_lang = "en";
215 let output_lang = "ko";
216 let result = translate_one_line(text, input_lang, output_lang)
217 .await
218 .unwrap();
219 dbg!(result);
220 assert!(true);
221 }
222
223 #[tokio::test]
224 async fn test_translate_multi_lines() {
225 let text = vec!["Hello, world!", "내 이름은 민수야.", "나는 20살이야."]
226 .iter()
227 .map(|x| x.to_string())
228 .collect();
229 let input_lang = "auto";
230 let output_lang = "fr";
231 let result = translate(text, input_lang, output_lang).await.unwrap();
232 dbg!(result);
233 assert!(true);
234 }
235}